--- /dev/null
+/* Catacomb 3-D Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+// C3_STATE.C\r
+\r
+#include "C3_DEF.H"\r
+#pragma hdrstop\r
+\r
+/*\r
+=============================================================================\r
+\r
+ LOCAL CONSTANTS\r
+\r
+=============================================================================\r
+*/\r
+\r
+\r
+/*\r
+=============================================================================\r
+\r
+ GLOBAL VARIABLES\r
+\r
+=============================================================================\r
+*/\r
+\r
+\r
+\r
+/*\r
+=============================================================================\r
+\r
+ LOCAL VARIABLES\r
+\r
+=============================================================================\r
+*/\r
+\r
+\r
+dirtype opposite[9] =\r
+ {south,west,north,east,southwest,northwest,northeast,southeast,nodir};\r
+\r
+\r
+\r
+//===========================================================================\r
+\r
+\r
+/*\r
+===================\r
+=\r
+= SpawnNewObj\r
+=\r
+===================\r
+*/\r
+\r
+void SpawnNewObj (unsigned x, unsigned y, statetype *state, unsigned size)\r
+{\r
+ GetNewObj (false);\r
+ new->size = size;\r
+ new->state = state;\r
+ new->ticcount = random (state->tictime)+1;\r
+\r
+ new->tilex = x;\r
+ new->tiley = y;\r
+ new->x = ((long)x<<TILESHIFT)+TILEGLOBAL/2;\r
+ new->y = ((long)y<<TILESHIFT)+TILEGLOBAL/2;\r
+ CalcBounds(new);\r
+ new->dir = nodir;\r
+\r
+ actorat[new->tilex][new->tiley] = new;\r
+}\r
+\r
+void SpawnNewObjFrac (long x, long y, statetype *state, unsigned size)\r
+{\r
+ GetNewObj (false);\r
+ new->size = size;\r
+ new->state = state;\r
+ new->ticcount = random (state->tictime)+1;\r
+ new->active = true;\r
+\r
+ new->x = x;\r
+ new->y = y;\r
+ new->tilex = x>>TILESHIFT;\r
+ new->tiley = y>>TILESHIFT;\r
+ CalcBounds(new);\r
+ new->distance = 100;\r
+ new->dir = nodir;\r
+}\r
+\r
+\r
+\r
+/*\r
+===================\r
+=\r
+= CheckHandAttack\r
+=\r
+= If the object can move next to the player, it will return true\r
+=\r
+===================\r
+*/\r
+\r
+boolean CheckHandAttack (objtype *ob)\r
+{\r
+ long deltax,deltay,size;\r
+\r
+ size = (long)ob->size + player->size + ob->speed*tics;\r
+ deltax = ob->x - player->x;\r
+ deltay = ob->y - player->y;\r
+\r
+ if (deltax > size || deltax < -size || deltay > size || deltay < -size)\r
+ return false;\r
+\r
+ return true;\r
+}\r
+\r
+\r
+/*\r
+===================\r
+=\r
+= T_DoDamage\r
+=\r
+= Attacks the player if still nearby, then immediately changes to next state\r
+=\r
+===================\r
+*/\r
+\r
+void T_DoDamage (objtype *ob)\r
+{\r
+ int points;\r
+\r
+\r
+ if (!CheckHandAttack (ob))\r
+ {\r
+ SD_PlaySound (MONSTERMISSSND);\r
+ }\r
+ else\r
+ {\r
+ points = 0;\r
+\r
+ switch (ob->obclass)\r
+ {\r
+ case orcobj:\r
+ points = 4;\r
+ break;\r
+ case trollobj:\r
+ points = 8;\r
+ break;\r
+ case demonobj:\r
+ points = 15;\r
+ break;\r
+ }\r
+ TakeDamage (points);\r
+ }\r
+\r
+ ob->state = ob->state->next;\r
+}\r
+\r
+\r
+//==========================================================================\r
+\r
+/*\r
+==================================\r
+=\r
+= Walk\r
+=\r
+==================================\r
+*/\r
+\r
+boolean Walk (objtype *ob)\r
+{\r
+ switch (ob->dir)\r
+ {\r
+ case north:\r
+ if (actorat[ob->tilex][ob->tiley-1])\r
+ return false;\r
+ ob->tiley--;\r
+ ob->distance = TILEGLOBAL;\r
+ return true;\r
+\r
+ case northeast:\r
+ if (actorat[ob->tilex+1][ob->tiley-1])\r
+ return false;\r
+ ob->tilex++;\r
+ ob->tiley--;\r
+ ob->distance = TILEGLOBAL;\r
+ return true;\r
+\r
+ case east:\r
+ if (actorat[ob->tilex+1][ob->tiley])\r
+ return false;\r
+ ob->tilex++;\r
+ ob->distance = TILEGLOBAL;\r
+ return true;\r
+\r
+ case southeast:\r
+ if (actorat[ob->tilex+1][ob->tiley+1])\r
+ return false;\r
+ ob->tilex++;\r
+ ob->tiley++;\r
+ ob->distance = TILEGLOBAL;\r
+ return true;\r
+\r
+ case south:\r
+ if (actorat[ob->tilex][ob->tiley+1])\r
+ return false;\r
+ ob->tiley++;\r
+ ob->distance = TILEGLOBAL;\r
+ return true;\r
+\r
+ case southwest:\r
+ if (actorat[ob->tilex-1][ob->tiley+1])\r
+ return false;\r
+ ob->tilex--;\r
+ ob->tiley++;\r
+ ob->distance = TILEGLOBAL;\r
+ return true;\r
+\r
+ case west:\r
+ if (actorat[ob->tilex-1][ob->tiley])\r
+ return false;\r
+ ob->tilex--;\r
+ ob->distance = TILEGLOBAL;\r
+ return true;\r
+\r
+ case northwest:\r
+ if (actorat[ob->tilex-1][ob->tiley-1])\r
+ return false;\r
+ ob->tilex--;\r
+ ob->tiley--;\r
+ ob->distance = TILEGLOBAL;\r
+ return true;\r
+\r
+ case nodir:\r
+ return false;\r
+ }\r
+\r
+ Quit ("Walk: Bad dir");\r
+ return false;\r
+}\r
+\r
+\r
+\r
+/*\r
+==================================\r
+=\r
+= ChaseThink\r
+= have the current monster go after the player,\r
+= either diagonally or straight on\r
+=\r
+==================================\r
+*/\r
+\r
+void ChaseThink (objtype *obj, boolean diagonal)\r
+{\r
+ int deltax,deltay,i;\r
+ dirtype d[3];\r
+ dirtype tdir, olddir, turnaround;\r
+\r
+\r
+ olddir=obj->dir;\r
+ turnaround=opposite[olddir];\r
+\r
+ deltax=player->tilex - obj->tilex;\r
+ deltay=player->tiley - obj->tiley;\r
+\r
+ d[1]=nodir;\r
+ d[2]=nodir;\r
+\r
+ if (deltax>0)\r
+ d[1]= east;\r
+ if (deltax<0)\r
+ d[1]= west;\r
+ if (deltay>0)\r
+ d[2]=south;\r
+ if (deltay<0)\r
+ d[2]=north;\r
+\r
+ if (abs(deltay)>abs(deltax))\r
+ {\r
+ tdir=d[1];\r
+ d[1]=d[2];\r
+ d[2]=tdir;\r
+ }\r
+\r
+ if (d[1]==turnaround)\r
+ d[1]=nodir;\r
+ if (d[2]==turnaround)\r
+ d[2]=nodir;\r
+\r
+\r
+ if (diagonal)\r
+ { /*ramdiagonals try the best dir first*/\r
+ if (d[1]!=nodir)\r
+ {\r
+ obj->dir=d[1];\r
+ if (Walk(obj))\r
+ return; /*either moved forward or attacked*/\r
+ }\r
+\r
+ if (d[2]!=nodir)\r
+ {\r
+ obj->dir=d[2];\r
+ if (Walk(obj))\r
+ return;\r
+ }\r
+ }\r
+ else\r
+ { /*ramstraights try the second best dir first*/\r
+\r
+ if (d[2]!=nodir)\r
+ {\r
+ obj->dir=d[2];\r
+ if (Walk(obj))\r
+ return;\r
+ }\r
+\r
+ if (d[1]!=nodir)\r
+ {\r
+ obj->dir=d[1];\r
+ if (Walk(obj))\r
+ return;\r
+ }\r
+ }\r
+\r
+/* there is no direct path to the player, so pick another direction */\r
+\r
+ obj->dir=olddir;\r
+ if (Walk(obj))\r
+ return;\r
+\r
+ if (US_RndT()>128) /*randomly determine direction of search*/\r
+ {\r
+ for (tdir=north;tdir<=west;tdir++)\r
+ {\r
+ if (tdir!=turnaround)\r
+ {\r
+ obj->dir=tdir;\r
+ if (Walk(obj))\r
+ return;\r
+ }\r
+ }\r
+ }\r
+ else\r
+ {\r
+ for (tdir=west;tdir>=north;tdir--)\r
+ {\r
+ if (tdir!=turnaround)\r
+ {\r
+ obj->dir=tdir;\r
+ if (Walk(obj))\r
+ return;\r
+ }\r
+ }\r
+ }\r
+\r
+ obj->dir=turnaround;\r
+ Walk(obj); /*last chance, don't worry about returned value*/\r
+}\r
+\r
+\r
+/*\r
+=================\r
+=\r
+= MoveObj\r
+=\r
+=================\r
+*/\r
+\r
+void MoveObj (objtype *ob, long move)\r
+{\r
+ ob->distance -=move;\r
+\r
+ switch (ob->dir)\r
+ {\r
+ case north:\r
+ ob->y -= move;\r
+ return;\r
+ case northeast:\r
+ ob->x += move;\r
+ ob->y -= move;\r
+ return;\r
+ case east:\r
+ ob->x += move;\r
+ return;\r
+ case southeast:\r
+ ob->x += move;\r
+ ob->y += move;\r
+ return;\r
+ case south:\r
+ ob->y += move;\r
+ return;\r
+ case southwest:\r
+ ob->x -= move;\r
+ ob->y += move;\r
+ return;\r
+ case west:\r
+ ob->x -= move;\r
+ return;\r
+ case northwest:\r
+ ob->x -= move;\r
+ ob->y -= move;\r
+ return;\r
+\r
+ case nodir:\r
+ return;\r
+ }\r
+}\r
+\r
+\r
+/*\r
+=================\r
+=\r
+= Chase\r
+=\r
+= returns true if hand attack range\r
+=\r
+=================\r
+*/\r
+\r
+boolean Chase (objtype *ob, boolean diagonal)\r
+{\r
+ long move;\r
+ long deltax,deltay,size;\r
+\r
+ move = ob->speed*tics;\r
+ size = (long)ob->size + player->size + move;\r
+\r
+ while (move)\r
+ {\r
+ deltax = ob->x - player->x;\r
+ deltay = ob->y - player->y;\r
+\r
+ if (deltax <= size && deltax >= -size\r
+ && deltay <= size && deltay >= -size)\r
+ {\r
+ CalcBounds (ob);\r
+ return true;\r
+ }\r
+\r
+ if (move < ob->distance)\r
+ {\r
+ MoveObj (ob,move);\r
+ break;\r
+ }\r
+ actorat[ob->tilex][ob->tiley] = 0; // pick up marker from goal\r
+ if (ob->dir == nodir)\r
+ ob->dir = north;\r
+\r
+ ob->x = ((long)ob->tilex<<TILESHIFT)+TILEGLOBAL/2;\r
+ ob->y = ((long)ob->tiley<<TILESHIFT)+TILEGLOBAL/2;\r
+ move -= ob->distance;\r
+\r
+ ChaseThink (ob,diagonal);\r
+ if (!ob->distance)\r
+ break; // no possible move\r
+ actorat[ob->tilex][ob->tiley] = ob; // set down a new goal marker\r
+ }\r
+ CalcBounds (ob);\r
+ return false;\r
+}\r
+\r
+//===========================================================================\r
+\r
+\r
+/*\r
+===================\r
+=\r
+= ShootActor\r
+=\r
+===================\r
+*/\r
+\r
+void ShootActor (objtype *ob, unsigned damage)\r
+{\r
+ ob->hitpoints -= damage;\r
+ if (ob->hitpoints<=0)\r
+ {\r
+ switch (ob->obclass)\r
+ {\r
+ case orcobj:\r
+ ob->state = &s_orcdie1;\r
+ GivePoints (100);\r
+ break;\r
+ case trollobj:\r
+ ob->state = &s_trolldie1;\r
+ GivePoints (400);\r
+ break;\r
+ case demonobj:\r
+ ob->state = &s_demondie1;\r
+ GivePoints (1000);\r
+ break;\r
+ case mageobj:\r
+ ob->state = &s_magedie1;\r
+ GivePoints (600);\r
+ break;\r
+ case batobj:\r
+ ob->state = &s_batdie1;\r
+ GivePoints (100);\r
+ break;\r
+ case grelmobj:\r
+ ob->state = &s_greldie1;\r
+ GivePoints (10000);\r
+ break;\r
+\r
+ }\r
+ ob->obclass = inertobj;\r
+ ob->shootable = false;\r
+ actorat[ob->tilex][ob->tiley] = NULL;\r
+ }\r
+ else\r
+ {\r
+ switch (ob->obclass)\r
+ {\r
+ case orcobj:\r
+ ob->state = &s_orcouch;\r
+ break;\r
+ case trollobj:\r
+ ob->state = &s_trollouch;\r
+ break;\r
+ case demonobj:\r
+ ob->state = &s_demonouch;\r
+ break;\r
+ case mageobj:\r
+ ob->state = &s_mageouch;\r
+ break;\r
+ case grelmobj:\r
+ ob->state = &s_grelouch;\r
+ break;\r
+\r
+ }\r
+ }\r
+ ob->ticcount = ob->state->tictime;\r
+}\r
+\r