--- /dev/null
+/* Reconstructed Commander Keen 4-6 Source Code\r
+ * Copyright (C) 2021 K1n9_Duk3\r
+ *\r
+ * This file is loosely based on:\r
+ * Keen Dreams Source Code\r
+ * Copyright (C) 2014 Javier M. Chavez\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
+#include "CK_DEF.H"\r
+\r
+/*\r
+=============================================================================\r
+\r
+ GLOBAL VARIABLES\r
+\r
+=============================================================================\r
+*/\r
+\r
+Sint16 wallclip[8][16] = { // the height of a given point in a tile\r
+{ 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256},\r
+{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},\r
+{ 0,0x08,0x10,0x18,0x20,0x28,0x30,0x38,0x40,0x48,0x50,0x58,0x60,0x68,0x70,0x78},\r
+{0x80,0x88,0x90,0x98,0xa0,0xa8,0xb0,0xb8,0xc0,0xc8,0xd0,0xd8,0xe0,0xe8,0xf0,0xf8},\r
+{ 0,0x10,0x20,0x30,0x40,0x50,0x60,0x70,0x80,0x90,0xa0,0xb0,0xc0,0xd0,0xe0,0xf0},\r
+{0x78,0x70,0x68,0x60,0x58,0x50,0x48,0x40,0x38,0x30,0x28,0x20,0x18,0x10,0x08, 0},\r
+{0xf8,0xf0,0xe8,0xe0,0xd8,0xd0,0xc8,0xc0,0xb8,0xb0,0xa8,0xa0,0x98,0x90,0x88,0x80},\r
+{0xf0,0xe0,0xd0,0xc0,0xb0,0xa0,0x90,0x80,0x70,0x60,0x50,0x40,0x30,0x20,0x10, 0}\r
+};\r
+\r
+Sint16 xtry, ytry;\r
+boolean playerkludgeclipcancel;\r
+\r
+/*\r
+=============================================================================\r
+\r
+ LOCAL VARIABLES\r
+\r
+=============================================================================\r
+*/\r
+\r
+Uint16 oldtileleft, oldtiletop, oldtileright, oldtilebottom, oldtilemidx;\r
+Uint16 oldleft, oldtop, oldright, oldbottom, oldmidx;\r
+Sint16 leftmoved, topmoved, rightmoved, bottommoved, midxmoved;\r
+\r
+//==========================================================================\r
+\r
+/*\r
+====================\r
+=\r
+= MoveObjVert\r
+=\r
+====================\r
+*/\r
+\r
+void MoveObjVert(objtype *ob, Sint16 ymove)\r
+{\r
+ ob->y += ymove;\r
+ ob->top += ymove;\r
+ ob->bottom += ymove;\r
+ ob->tiletop = CONVERT_GLOBAL_TO_TILE(ob->top);\r
+ ob->tilebottom = CONVERT_GLOBAL_TO_TILE(ob->bottom);\r
+}\r
+\r
+/*\r
+====================\r
+=\r
+= MoveObjHoriz\r
+=\r
+====================\r
+*/\r
+\r
+void MoveObjHoriz(objtype *ob, Sint16 xmove)\r
+{\r
+ //BUG? ob->midx is not adjusted in Keen 4 & 5\r
+ ob->x += xmove;\r
+ ob->left += xmove;\r
+ ob->right += xmove;\r
+#ifdef KEEN6\r
+ ob->midx += xmove; //BUG? ob->tilemidx is not updated\r
+#endif\r
+ ob->tileleft = CONVERT_GLOBAL_TO_TILE(ob->left);\r
+ ob->tileright = CONVERT_GLOBAL_TO_TILE(ob->right);\r
+}\r
+\r
+//==========================================================================\r
+\r
+/*\r
+====================\r
+=\r
+= PlayerBottomKludge\r
+=\r
+====================\r
+*/\r
+\r
+void PlayerBottomKludge(objtype *ob)\r
+{\r
+ Uint16 far *map;\r
+ Uint16 wall, clip, xpix;\r
+ Sint16 xmove, ymove;\r
+\r
+ map = (Uint16 far *)mapsegs[1] + mapbwidthtable[ob->tilebottom-1]/2;\r
+ if (ob->xdir == 1)\r
+ {\r
+ xpix = 0;\r
+ map += ob->tileright;\r
+ xmove = ob->right - ob->midx;\r
+ if (tinf[*(map-mapwidth)+WESTWALL] || tinf[*map+WESTWALL])\r
+ {\r
+ return;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ xpix = 15;\r
+ map += ob->tileleft;\r
+ xmove = ob->left - ob->midx;\r
+ if (tinf[*(map-mapwidth)+EASTWALL] || tinf[*map+EASTWALL])\r
+ {\r
+ return;\r
+ }\r
+ }\r
+ if ((_AX = tinf[*map+NORTHWALL]) != 0) // the _AX = ... part is just to recreate the original code's quirks, feel free to delete this\r
+ {\r
+ return;\r
+ }\r
+ map += mapwidth;\r
+ if ((wall = tinf[*map+NORTHWALL]) != 1)\r
+ {\r
+ return;\r
+ }\r
+ clip = wallclip[wall&7][xpix];\r
+ ymove = CONVERT_TILE_TO_GLOBAL(ob->tilebottom) + clip - 1 -ob->bottom;\r
+ if (ymove <= 0 && ymove >= -bottommoved)\r
+ {\r
+ ob->hitnorth = wall;\r
+ MoveObjVert(ob, ymove);\r
+ MoveObjHoriz(ob, xmove);\r
+ }\r
+}\r
+\r
+/*\r
+====================\r
+=\r
+= PlayerTopKludge\r
+=\r
+====================\r
+*/\r
+\r
+void PlayerTopKludge(objtype *ob)\r
+{\r
+ Uint16 far *map;\r
+ Uint16 xpix, wall, clip;\r
+ Sint16 move;\r
+\r
+ map = (Uint16 far *)mapsegs[1] + mapbwidthtable[ob->tiletop+1]/2;\r
+ if (ob->xdir == 1)\r
+ {\r
+ xpix = 0;\r
+ map += ob->tileright;\r
+ if (tinf[*(map+mapwidth)+WESTWALL] || tinf[*(map+2*mapwidth)+WESTWALL])\r
+ {\r
+ return;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ xpix = 15;\r
+ map += ob->tileleft;\r
+ if (tinf[*(map+mapwidth)+EASTWALL] || tinf[*(map+2*mapwidth)+EASTWALL])\r
+ {\r
+ return;\r
+ }\r
+ }\r
+ if ((_AX = tinf[*map+SOUTHWALL]) != 0) // the _AX = ... part is just to recreate the original code's quirks, feel free to delete this\r
+ {\r
+ return;\r
+ }\r
+ map -= mapwidth;\r
+ if ((wall = tinf[*map+SOUTHWALL]) != 0)\r
+ {\r
+ clip = wallclip[wall&7][xpix];\r
+ move = CONVERT_TILE_TO_GLOBAL(ob->tiletop+1) - clip - ob->top;\r
+ if (move >= 0 && move <= -topmoved)\r
+ {\r
+ ob->hitsouth = wall;\r
+ MoveObjVert(ob, move);\r
+ }\r
+ }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= ClipToEnds\r
+=\r
+===========================\r
+*/\r
+\r
+void ClipToEnds(objtype *ob)\r
+{\r
+ Uint16 far *map;\r
+ Uint16 wall, y, clip;\r
+ Sint16 totalmove, maxmove, move;\r
+ Uint16 midxpix;\r
+ \r
+ midxpix = CONVERT_GLOBAL_TO_PIXEL(ob->midx & 0xF0);\r
+ maxmove = -abs(midxmoved)-bottommoved-16;\r
+ map = (Uint16 far *)mapsegs[1] + (mapbwidthtable-1)[oldtilebottom]/2 + ob->tilemidx;\r
+ for (y=oldtilebottom-1; y <= ob->tilebottom; y++,map+=mapwidth)\r
+ {\r
+ if ((wall = tinf[*map + NORTHWALL]) != 0)\r
+ {\r
+ clip = wallclip[wall&7][midxpix];\r
+ move = (CONVERT_TILE_TO_GLOBAL(y) + clip)-1-ob->bottom;\r
+ if (move < 0 && move >= maxmove)\r
+ {\r
+ ob->hitnorth = wall;\r
+ MoveObjVert(ob, move);\r
+ return;\r
+ }\r
+ }\r
+ }\r
+ maxmove = abs(midxmoved)-topmoved+16;\r
+ map = (Uint16 far *)mapsegs[1] + (mapbwidthtable+1)[oldtiletop]/2 + ob->tilemidx;\r
+ for (y=oldtiletop+1; y >= ob->tiletop; y--,map-=mapwidth) // BUG: unsigned comparison - loop never ends if ob->tiletop is 0\r
+ {\r
+ if ((wall = tinf[*map + SOUTHWALL]) != 0)\r
+ {\r
+ clip = wallclip[wall&7][midxpix];\r
+ move = CONVERT_TILE_TO_GLOBAL(y+1) - clip - ob->top;\r
+ if (move > 0 && move <= maxmove)\r
+ {\r
+ totalmove = ytry+move;\r
+ if (totalmove < 0x100 && totalmove > -0x100)\r
+ {\r
+ ob->hitsouth = wall;\r
+ MoveObjVert(ob, move);\r
+ //BUG? no return here\r
+ }\r
+ }\r
+ }\r
+ }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= ClipToSides\r
+=\r
+===========================\r
+*/\r
+\r
+void ClipToSides(objtype *ob)\r
+{\r
+ Sint16 move, y, top, bottom;\r
+ Uint16 far *map;\r
+ \r
+ top = ob->tiletop;\r
+ if (ob->hitsouth > 1)\r
+ {\r
+ top++;\r
+ }\r
+ bottom = ob->tilebottom;\r
+ if (ob->hitnorth > 1)\r
+ {\r
+ bottom--;\r
+ }\r
+ for (y=top; y<=bottom; y++)\r
+ {\r
+ map = (Uint16 far *)mapsegs[1] + mapbwidthtable[y]/2 + ob->tileleft;\r
+ if ((ob->hiteast = tinf[*map+EASTWALL]) != 0)\r
+ {\r
+ move = CONVERT_TILE_TO_GLOBAL(ob->tileleft+1) - ob->left;\r
+ MoveObjHoriz(ob, move);\r
+ return;\r
+ }\r
+ }\r
+ for (y=top; y<=bottom; y++)\r
+ {\r
+ map = (Uint16 far *)mapsegs[1] + mapbwidthtable[y]/2 + ob->tileright;\r
+ if ((ob->hitwest = tinf[*map+WESTWALL]) != 0)\r
+ {\r
+ move = (CONVERT_TILE_TO_GLOBAL(ob->tileright)-1)-ob->right;\r
+ MoveObjHoriz(ob, move);\r
+ return;\r
+ }\r
+ }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= CheckPosition\r
+=\r
+===========================\r
+*/\r
+\r
+boolean CheckPosition(objtype *ob)\r
+{\r
+#ifdef KEEN6Ev15\r
+ // This version is pretty much a compiler-optimized version of the other\r
+ // version below, but I simply could not get the compiler to optimize it\r
+ // in exactly the same way.\r
+\r
+ Uint16 tile, x, tileright;\r
+ Uint16 far *map;\r
+ Uint16 rowdiff;\r
+ Uint16 tileleft, y, tilebottom;\r
+ \r
+ map = (Uint16 far *)mapsegs[1] + mapbwidthtable[ob->tiletop]/2 + ob->tileleft;\r
+ rowdiff = mapwidth-(ob->tileright-ob->tileleft+1);\r
+\r
+ y = ob->tiletop;\r
+ tileleft = ob->tileleft;\r
+ tileright = _AX = ob->tileright;\r
+ tilebottom = ob->tilebottom;\r
+\r
+ for (; tilebottom>=y; y++,map+=rowdiff)\r
+ {\r
+ for (x=tileleft; tileright>=x; x++)\r
+ {\r
+ tile = *(map++);\r
+ if (tinf[tile+NORTHWALL] || tinf[tile+EASTWALL] || tinf[tile+SOUTHWALL] || tinf[tile+WESTWALL])\r
+ {\r
+ return false;\r
+ }\r
+ }\r
+ }\r
+ return true;\r
+#else\r
+ Uint16 tile, x, y;\r
+ Uint16 far *map;\r
+ Uint16 rowdiff;\r
+ \r
+ map = (Uint16 far *)mapsegs[1] + mapbwidthtable[ob->tiletop]/2 + ob->tileleft;\r
+ rowdiff = mapwidth-(ob->tileright-ob->tileleft+1);\r
+ for (y=ob->tiletop; y<=ob->tilebottom; y++,map+=rowdiff)\r
+ {\r
+ for (x=ob->tileleft; x<=ob->tileright; x++)\r
+ {\r
+ tile = *(map++);\r
+ if (tinf[tile+NORTHWALL] || tinf[tile+EASTWALL] || tinf[tile+SOUTHWALL] || tinf[tile+WESTWALL])\r
+ {\r
+ return false;\r
+ }\r
+ }\r
+ }\r
+ return true;\r
+#endif\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= StatePositionOk\r
+=\r
+===========================\r
+*/\r
+\r
+boolean StatePositionOk(objtype *ob, statetype *state)\r
+{\r
+ spritetabletype far *shape;\r
+\r
+ if (ob->xdir > 0)\r
+ {\r
+ ob->shapenum = state->rightshapenum;\r
+ }\r
+ else\r
+ {\r
+ ob->shapenum = state->leftshapenum;\r
+ }\r
+ shape = &spritetable[ob->shapenum-STARTSPRITES];\r
+ ob->left = ob->x + shape->xl;\r
+ ob->right = ob->x + shape->xh;\r
+ ob->top = ob->y + shape->yl;\r
+ ob->bottom = ob->y + shape->yh;\r
+ ob->midx = ob->left + (ob->right-ob->left)/2;\r
+ ob->tileleft = CONVERT_GLOBAL_TO_TILE(ob->left);\r
+ ob->tileright = CONVERT_GLOBAL_TO_TILE(ob->right);\r
+ ob->tiletop = CONVERT_GLOBAL_TO_TILE(ob->top);\r
+ ob->tilebottom = CONVERT_GLOBAL_TO_TILE(ob->bottom);\r
+ ob->tilemidx = CONVERT_GLOBAL_TO_TILE(ob->midx);\r
+ return CheckPosition(ob);\r
+}\r
+\r
+#ifdef KEEN5\r
+/*\r
+===========================\r
+=\r
+= CalcBounds\r
+=\r
+===========================\r
+*/\r
+\r
+void CalcBounds(objtype *ob) //not present in Keen 4 & 6\r
+{\r
+ spritetabletype far *shape;\r
+\r
+ shape = &spritetable[ob->shapenum-STARTSPRITES];\r
+ ob->left = ob->x + shape->xl;\r
+ ob->right = ob->x + shape->xh;\r
+ ob->top = ob->y + shape->yl;\r
+ ob->bottom = ob->y + shape->yh;\r
+ ob->midx = ob->left + (ob->right-ob->left)/2;\r
+}\r
+#endif\r
+\r
+//==========================================================================\r
+\r
+/*\r
+================\r
+=\r
+= ClipToWalls\r
+=\r
+= Moves the current object xtry/ytry units, clipping to walls\r
+=\r
+================\r
+*/\r
+\r
+void ClipToWalls(objtype *ob)\r
+{\r
+ Uint16 oldx, oldy;\r
+#ifdef KEEN6\r
+ Uint16 y;\r
+#endif\r
+ spritetabletype far *shape;\r
+ boolean pushed;\r
+\r
+ oldx = ob->x;\r
+ oldy = ob->y;\r
+ pushed = false;\r
+\r
+//\r
+// make sure it stays in contact with a 45 degree slope\r
+//\r
+ if (ob->state->pushtofloor)\r
+ {\r
+ if (ob->hitnorth == 25)\r
+ {\r
+ ytry = 145;\r
+ }\r
+ else\r
+ {\r
+ if (xtry > 0)\r
+ {\r
+ ytry = xtry+16;\r
+ }\r
+ else\r
+ {\r
+ ytry = -xtry+16;\r
+ }\r
+ pushed = true;\r
+ }\r
+ }\r
+\r
+//\r
+// move the shape\r
+//\r
+ if (xtry > 239)\r
+ {\r
+ xtry = 239;\r
+ }\r
+ else if (xtry < -239)\r
+ {\r
+ xtry = -239;\r
+ }\r
+ if (ytry > 255) // +16 for push to floor\r
+ {\r
+ ytry = 255;\r
+ }\r
+ else if (ytry < -239)\r
+ {\r
+ ytry = -239;\r
+ }\r
+\r
+ ob->x += xtry;\r
+ ob->y += ytry;\r
+\r
+ ob->needtoreact = true;\r
+\r
+ if (!ob->shapenum) // can't get a hit rect with no shape!\r
+ {\r
+ return;\r
+ }\r
+\r
+ shape = &spritetable[ob->shapenum-STARTSPRITES];\r
+\r
+ oldtileright = ob->tileright;\r
+ oldtiletop = ob->tiletop;\r
+ oldtileleft = ob->tileleft;\r
+ oldtilebottom = ob->tilebottom;\r
+ oldtilemidx = ob->tilemidx;\r
+\r
+ oldright = ob->right;\r
+ oldtop = ob->top;\r
+ oldleft = ob->left;\r
+ oldbottom = ob->bottom;\r
+ oldmidx = ob->midx;\r
+\r
+ ob->left = ob->x + shape->xl;\r
+ ob->right = ob->x + shape->xh;\r
+ ob->top = ob->y + shape->yl;\r
+ ob->bottom = ob->y + shape->yh;\r
+ ob->midx = ob->left + (ob->right-ob->left)/2;\r
+\r
+ ob->tileleft = CONVERT_GLOBAL_TO_TILE(ob->left);\r
+ ob->tileright = CONVERT_GLOBAL_TO_TILE(ob->right);\r
+ ob->tiletop = CONVERT_GLOBAL_TO_TILE(ob->top);\r
+ ob->tilebottom = CONVERT_GLOBAL_TO_TILE(ob->bottom);\r
+ ob->tilemidx = CONVERT_GLOBAL_TO_TILE(ob->midx);\r
+\r
+ ob->hitnorth=ob->hiteast=ob->hitsouth=ob->hitwest=0;\r
+\r
+ if (ob->needtoclip)\r
+ {\r
+ leftmoved = ob->left - oldleft;\r
+ rightmoved = ob->right - oldright;\r
+ topmoved = ob->top - oldtop;\r
+ bottommoved = ob->bottom - oldbottom;\r
+ midxmoved = ob->midx - oldmidx;\r
+\r
+ //\r
+ // clip it\r
+ //\r
+ ClipToEnds(ob);\r
+\r
+ if (ob == player && !playerkludgeclipcancel) // zero tolerance near the edge when player gets pushed!\r
+ {\r
+ if (!ob->hitnorth && bottommoved > 0)\r
+ {\r
+ PlayerBottomKludge(ob);\r
+ }\r
+ if (!ob->hitsouth && topmoved < 0)\r
+ {\r
+ PlayerTopKludge(ob);\r
+ }\r
+ }\r
+ ClipToSides(ob);\r
+\r
+#ifdef KEEN6\r
+ //\r
+ // special hack to prevent player from getting stuck on slopes?\r
+ //\r
+ if (ob == player && (ob->hitnorth & 7) > 1 && (ob->hiteast || ob->hitwest))\r
+ {\r
+ Uint16 far *map;\r
+ Uint16 pixx, clip, move;\r
+ Uint16 wall;\r
+\r
+ pixx = CONVERT_GLOBAL_TO_PIXEL(ob->midx & (15*PIXGLOBAL));\r
+ map = (Uint16 far *)mapsegs[1] + mapbwidthtable[oldtilebottom]/2 + ob->tilemidx;\r
+\r
+ for (y=oldtilebottom; ob->tilebottom+1 >= y; y++, map+=mapwidth)\r
+ {\r
+ if ((wall = tinf[*map + NORTHWALL]) != 0)\r
+ {\r
+ clip = wallclip[wall & 7][pixx];\r
+ move = CONVERT_TILE_TO_GLOBAL(y) + clip - 1 - ob->bottom;\r
+ ob->hitnorth = wall;\r
+ MoveObjVert(ob, move);\r
+ return;\r
+ }\r
+ }\r
+ }\r
+#endif\r
+ }\r
+ if (pushed && !ob->hitnorth)\r
+ {\r
+ ob->y = oldy;\r
+ ob->x = oldx + xtry;\r
+\r
+ ob->left = ob->x + shape->xl;\r
+ ob->right = ob->x + shape->xh;\r
+ ob->top = ob->y + shape->yl;\r
+ ob->bottom = ob->y + shape->yh;\r
+ ob->midx = ob->left + (ob->right-ob->left)/2;\r
+\r
+ ob->tileleft = CONVERT_GLOBAL_TO_TILE(ob->left);\r
+ ob->tileright = CONVERT_GLOBAL_TO_TILE(ob->right);\r
+ ob->tiletop = CONVERT_GLOBAL_TO_TILE(ob->top);\r
+ ob->tilebottom = CONVERT_GLOBAL_TO_TILE(ob->bottom);\r
+ ob->tilemidx = CONVERT_GLOBAL_TO_TILE(ob->midx);\r
+ }\r
+\r
+ ob->xmove = ob->xmove + (ob->x - oldx);\r
+ ob->ymove = ob->ymove + (ob->y - oldy);\r
+}\r
+\r
+/*\r
+================\r
+=\r
+= FullClipToWalls\r
+=\r
+= Moves the current object xtry/ytry units, clipping to walls\r
+=\r
+================\r
+*/\r
+\r
+void FullClipToWalls(objtype *ob)\r
+{\r
+ Uint16 oldx, oldy, w, h;\r
+ spritetabletype far *shape;\r
+\r
+ oldx = ob->x;\r
+ oldy = ob->y;\r
+\r
+//\r
+// move the shape\r
+//\r
+ if (xtry > 239)\r
+ {\r
+ xtry = 239;\r
+ }\r
+ else if (xtry < -239)\r
+ {\r
+ xtry = -239;\r
+ }\r
+ if (ytry > 239)\r
+ {\r
+ ytry = 239;\r
+ }\r
+ else if (ytry < -239)\r
+ {\r
+ ytry = -239;\r
+ }\r
+\r
+ ob->x += xtry;\r
+ ob->y += ytry;\r
+\r
+ ob->needtoreact = true;\r
+\r
+ shape = &spritetable[ob->shapenum-STARTSPRITES];\r
+\r
+ switch (ob->obclass)\r
+ {\r
+#if defined KEEN4\r
+ case keenobj:\r
+ w = 40*PIXGLOBAL;\r
+ h = 24*PIXGLOBAL;\r
+ break;\r
+ case eggbirdobj:\r
+ w = 64*PIXGLOBAL;\r
+ h = 32*PIXGLOBAL;\r
+ break;\r
+ case dopefishobj:\r
+ w = 88*PIXGLOBAL;\r
+ h = 64*PIXGLOBAL;\r
+ break;\r
+ case schoolfishobj:\r
+ w = 16*PIXGLOBAL;\r
+ h = 8*PIXGLOBAL;\r
+ break;\r
+#elif defined KEEN5\r
+ case slicestarobj:\r
+ case spherefulobj:\r
+ w = h = 32*PIXGLOBAL;\r
+ break;\r
+#elif defined KEEN6\r
+ case blorbobj:\r
+ w = h = 32*PIXGLOBAL;\r
+ break;\r
+#endif\r
+\r
+ default:\r
+ Quit("FullClipToWalls: Bad obclass");\r
+ break;\r
+ }\r
+\r
+ ob->right = ob->x + w;\r
+ ob->left = ob->x;\r
+ ob->top = ob->y;\r
+ ob->bottom = ob->y + h;\r
+\r
+ ob->tileleft = CONVERT_GLOBAL_TO_TILE(ob->left);\r
+ ob->tileright = CONVERT_GLOBAL_TO_TILE(ob->right);\r
+ ob->tiletop = CONVERT_GLOBAL_TO_TILE(ob->top);\r
+ ob->tilebottom = CONVERT_GLOBAL_TO_TILE(ob->bottom);\r
+\r
+ ob->hitnorth=ob->hiteast=ob->hitsouth=ob->hitwest=0;\r
+\r
+//\r
+// clip it\r
+//\r
+ if (!CheckPosition(ob))\r
+ {\r
+ MoveObjHoriz(ob, -xtry); //undo x movement\r
+ if (CheckPosition(ob))\r
+ {\r
+ if (xtry > 0)\r
+ {\r
+ ob->hitwest = 1;\r
+ }\r
+ else\r
+ {\r
+ ob->hiteast = 1;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ if (ytry > 0)\r
+ {\r
+ ob->hitnorth = 1;\r
+ }\r
+ else\r
+ {\r
+ ob->hitsouth = 1;\r
+ }\r
+ MoveObjHoriz(ob, xtry); //redo x movement\r
+ MoveObjVert(ob, -ytry); //undo y movement\r
+ if (!CheckPosition(ob))\r
+ {\r
+ MoveObjHoriz(ob, -xtry); //undo x movement\r
+ if (xtry > 0)\r
+ {\r
+ ob->hitwest = 1;\r
+ }\r
+ else\r
+ {\r
+ ob->hiteast = 1;\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
+ ob->xmove = ob->xmove + (ob->x - oldx);\r
+ ob->ymove = ob->ymove + (ob->y - oldy);\r
+\r
+ ob->left = ob->x + shape->xl;\r
+ ob->right = ob->x + shape->xh;\r
+ ob->top = ob->y + shape->yl;\r
+ ob->bottom = ob->y + shape->yh;\r
+ ob->midx = ob->left + (ob->right-ob->left)/2;\r
+}\r
+\r
+/*\r
+================\r
+=\r
+= PushObj\r
+=\r
+= Moves the current object xtry/ytry units, clipping to walls\r
+=\r
+================\r
+*/\r
+\r
+void PushObj(objtype *ob)\r
+{\r
+ Uint16 oldx, oldy;\r
+ spritetabletype far *shape;\r
+ \r
+ oldx = ob->x;\r
+ oldy = ob->y;\r
+\r
+//\r
+// move the shape\r
+//\r
+ ob->x += xtry;\r
+ ob->y += ytry;\r
+\r
+ ob->needtoreact = true;\r
+\r
+ if (!ob->shapenum) // can't get a hit rect with no shape!\r
+ {\r
+ return;\r
+ }\r
+\r
+ shape = &spritetable[ob->shapenum-STARTSPRITES];\r
+\r
+ oldtileright = ob->tileright;\r
+ oldtiletop = ob->tiletop;\r
+ oldtileleft = ob->tileleft;\r
+ oldtilebottom = ob->tilebottom;\r
+ oldtilemidx = ob->tilemidx;\r
+\r
+ oldright = ob->right;\r
+ oldtop = ob->top;\r
+ oldleft = ob->left;\r
+ oldbottom = ob->bottom;\r
+ oldmidx = ob->midx;\r
+\r
+ ob->left = ob->x + shape->xl;\r
+ ob->right = ob->x + shape->xh;\r
+ ob->top = ob->y + shape->yl;\r
+ ob->bottom = ob->y + shape->yh;\r
+ ob->midx = ob->left + (ob->right-ob->left)/2;\r
+\r
+ ob->tileleft = CONVERT_GLOBAL_TO_TILE(ob->left);\r
+ ob->tileright = CONVERT_GLOBAL_TO_TILE(ob->right);\r
+ ob->tiletop = CONVERT_GLOBAL_TO_TILE(ob->top);\r
+ ob->tilebottom = CONVERT_GLOBAL_TO_TILE(ob->bottom);\r
+ ob->tilemidx = CONVERT_GLOBAL_TO_TILE(ob->midx);\r
+\r
+ if (ob->needtoclip)\r
+ {\r
+ leftmoved = ob->left - oldleft;\r
+ rightmoved = ob->right - oldright;\r
+ topmoved = ob->top - oldtop;\r
+ bottommoved = ob->bottom - oldbottom;\r
+ midxmoved = ob->midx - oldmidx;\r
+\r
+ ClipToEnds(ob);\r
+ ClipToSides(ob);\r
+ }\r
+\r
+ ob->xmove = ob->xmove + (ob->x - oldx);\r
+ ob->ymove = ob->ymove + (ob->y - oldy);\r
+}\r
+\r
+//==========================================================================\r
+\r
+\r
+/*\r
+==================\r
+=\r
+= ClipToSpriteSide\r
+=\r
+= Clips push to solid\r
+=\r
+==================\r
+*/\r
+\r
+void ClipToSpriteSide(objtype *push, objtype *solid)\r
+{\r
+ Sint16 xmove, leftinto, rightinto;\r
+\r
+ //\r
+ // amount the push shape can be pushed\r
+ //\r
+ xmove = solid->xmove - push->xmove;\r
+\r
+ //\r
+ // amount it is inside\r
+ //\r
+ leftinto = solid->right - push->left;\r
+ rightinto = push->right - solid->left;\r
+\r
+ if (leftinto > 0 && leftinto <= xmove)\r
+ {\r
+ xtry = leftinto;\r
+ if (push->state->pushtofloor)\r
+ {\r
+ ytry = leftinto+16;\r
+ }\r
+ ClipToWalls(push);\r
+ push->hiteast = 1;\r
+ }\r
+ else if (rightinto > 0 && rightinto <= -xmove)\r
+ {\r
+ xtry = -rightinto;\r
+ if (push->state->pushtofloor)\r
+ {\r
+ ytry = rightinto+16;\r
+ }\r
+ ClipToWalls(push);\r
+ push->hitwest = 1;\r
+ }\r
+}\r
+\r
+//==========================================================================\r
+\r
+\r
+/*\r
+==================\r
+=\r
+= ClipToSpriteTop\r
+=\r
+= Clips push to solid\r
+=\r
+==================\r
+*/\r
+\r
+void ClipToSpriteTop(objtype *push, objtype *solid)\r
+{\r
+ Sint16 temp, ymove, bottominto;\r
+\r
+ //\r
+ // amount the push shape can be pushed\r
+ //\r
+ ymove = push->ymove - solid->ymove;\r
+\r
+ //\r
+ // amount it is inside\r
+ //\r
+ bottominto = push->bottom - solid->top;\r
+\r
+ if (bottominto >= 0 && bottominto <= ymove)\r
+ {\r
+ if (push == player)\r
+ {\r
+ gamestate.riding = solid;\r
+ }\r
+ ytry = -bottominto;\r
+ temp = push->state->pushtofloor;\r
+ push->state->pushtofloor = false;\r
+ ClipToWalls(push);\r
+ push->state->pushtofloor = temp;\r
+ if (!push->hitsouth)\r
+ {\r
+ push->hitnorth = 25;\r
+ }\r
+ }\r
+}\r
+\r
+//==========================================================================\r
+\r
+\r
+/*\r
+==================\r
+=\r
+= ClipToSprite\r
+=\r
+= Clips push to solid\r
+=\r
+==================\r
+*/\r
+\r
+void ClipToSprite(objtype *push, objtype *solid, boolean squish)\r
+{\r
+ Sint16 xmove, ymove, leftinto, rightinto, topinto, bottominto;\r
+ \r
+ xmove = solid->xmove - push->xmove;\r
+ xtry = ytry = 0;\r
+\r
+ //\r
+ // left / right\r
+ //\r
+ leftinto = solid->right - push->left;\r
+ rightinto = push->right - solid->left;\r
+\r
+ if (leftinto > 0 && xmove+1 >= leftinto)\r
+ {\r
+ xtry = leftinto;\r
+ push->xspeed = 0;\r
+ PushObj(push);\r
+ if (squish && push->hitwest)\r
+ {\r
+ KillKeen();\r
+ }\r
+ push->hiteast = 1;\r
+ return;\r
+ }\r
+ else if (rightinto > 0 && -xmove+1 >= rightinto)\r
+ {\r
+ xtry = -rightinto;\r
+ push->xspeed = 0;\r
+ PushObj(push);\r
+ if (squish && push->hiteast)\r
+ {\r
+ KillKeen();\r
+ }\r
+ push->hitwest = 1;\r
+ return;\r
+ }\r
+\r
+ //\r
+ // top / bottom\r
+ //\r
+ ymove = push->ymove - solid->ymove;\r
+ topinto = solid->bottom - push->top;\r
+ bottominto = push->bottom - solid->top;\r
+ if (bottominto >= 0 && bottominto <= ymove)\r
+ {\r
+ if (push == player)\r
+ {\r
+ gamestate.riding = solid;\r
+ }\r
+ ytry = -bottominto;\r
+ PushObj(push);\r
+ if (squish && push->hitsouth)\r
+ {\r
+ KillKeen();\r
+ }\r
+ if (!push->hitsouth)\r
+ {\r
+ push->hitnorth = 25;\r
+ }\r
+ return;\r
+ }\r
+ else if (topinto >= 0 && topinto <= ymove) // BUG: should be 'topinto <= -ymove'\r
+ {\r
+ ytry = topinto;\r
+ ClipToWalls(push);\r
+ if (squish && push->hitnorth)\r
+ {\r
+ KillKeen();\r
+ }\r
+ push->hitsouth = 25;\r
+ }\r
+}\r
+\r
+//==========================================================================\r
+\r
+\r
+/*\r
+==================\r
+=\r
+= DoActor\r
+=\r
+= Moves an actor in its current state by a given number of tics.\r
+= If that time takes it into the next state, it changes the state\r
+= and returns the number of excess tics after the state change\r
+=\r
+==================\r
+*/\r
+\r
+Sint16 DoActor(objtype *ob, Sint16 numtics)\r
+{\r
+ Sint16 ticcount, usedtics, excesstics;\r
+ statetype *state;\r
+ \r
+ state = ob->state;\r
+\r
+ if (state->progress == think)\r
+ {\r
+ if (state->think)\r
+ {\r
+ if (ob->nothink)\r
+ {\r
+ ob->nothink--;\r
+ }\r
+ else\r
+ {\r
+ state->think(ob);\r
+ }\r
+ }\r
+ return 0;\r
+ }\r
+\r
+ ticcount = ob->ticcount + numtics;\r
+\r
+ if (state->tictime > ticcount || state->tictime == 0)\r
+ {\r
+ ob->ticcount = ticcount;\r
+ if (state->progress == slide || state->progress == slidethink)\r
+ {\r
+ if (ob->xdir)\r
+ {\r
+ xtry += ob->xdir == 1? numtics*state->xmove : -numtics*state->xmove;\r
+ }\r
+ if (ob->ydir)\r
+ {\r
+ ytry += ob->ydir == 1? numtics*state->ymove : -numtics*state->ymove;\r
+ }\r
+ }\r
+ if ((state->progress == slidethink || state->progress == stepthink) && state->think)\r
+ {\r
+ if (ob->nothink)\r
+ {\r
+ ob->nothink--;\r
+ }\r
+ else\r
+ {\r
+ state->think(ob);\r
+ }\r
+ }\r
+ return 0;\r
+ }\r
+ else\r
+ {\r
+ usedtics = state->tictime - ob->ticcount;\r
+ excesstics = ticcount - state->tictime;\r
+ ob->ticcount = 0;\r
+ if (state->progress == slide || state->progress == slidethink)\r
+ {\r
+ if (ob->xdir)\r
+ {\r
+ xtry += ob->xdir == 1? usedtics*state->xmove : -usedtics*state->xmove;\r
+ }\r
+ if (ob->ydir)\r
+ {\r
+ ytry += ob->ydir == 1? usedtics*state->ymove : -usedtics*state->ymove;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ if (ob->xdir)\r
+ {\r
+ xtry += ob->xdir == 1? state->xmove : -state->xmove;\r
+ }\r
+ if (ob->ydir)\r
+ {\r
+ ytry += ob->ydir == 1? state->ymove : -state->ymove;\r
+ }\r
+ }\r
+\r
+ if (state->think)\r
+ {\r
+ if (ob->nothink)\r
+ {\r
+ ob->nothink--;\r
+ }\r
+ else\r
+ {\r
+ state->think(ob);\r
+ }\r
+ }\r
+\r
+ if (state == ob->state)\r
+ {\r
+ ob->state = state->nextstate; // go to next state\r
+ }\r
+ else if (!ob->state)\r
+ {\r
+ return 0; // object removed itself\r
+ }\r
+ return excesstics;\r
+ }\r
+}\r
+\r
+//==========================================================================\r
+\r
+\r
+/*\r
+====================\r
+=\r
+= StateMachine\r
+=\r
+= Change state and give directions\r
+=\r
+====================\r
+*/\r
+\r
+void StateMachine(objtype *ob)\r
+{\r
+ Sint16 excesstics, oldshapenum;\r
+ statetype *state;\r
+ \r
+ ob->xmove=ob->ymove=xtry=ytry=0;\r
+ oldshapenum = ob->shapenum;\r
+\r
+ state = ob->state;\r
+\r
+ excesstics = DoActor(ob, tics);\r
+ if (ob->state != state)\r
+ {\r
+ ob->ticcount = 0; // start the new state at 0, then use excess\r
+ state = ob->state;\r
+ }\r
+\r
+ while (excesstics)\r
+ {\r
+ //\r
+ // passed through to next state\r
+ //\r
+ if (!state->skippable && state->tictime <= excesstics)\r
+ {\r
+ excesstics = DoActor(ob, state->tictime-1);\r
+ }\r
+ else\r
+ {\r
+ excesstics = DoActor(ob, excesstics);\r
+ }\r
+ if (ob->state != state)\r
+ {\r
+ ob->ticcount = 0; // start the new state at 0, then use excess\r
+ state = ob->state;\r
+ }\r
+ }\r
+\r
+ if (!state) // object removed itself\r
+ {\r
+ RemoveObj(ob);\r
+ return;\r
+ }\r
+\r
+ //\r
+ // if state->rightshapenum == NULL, the state does not have a standard\r
+ // shape (the think routine should have set it)\r
+ //\r
+ if (state->rightshapenum)\r
+ {\r
+ if (ob->xdir > 0)\r
+ {\r
+ ob->shapenum = state->rightshapenum;\r
+ }\r
+ else\r
+ {\r
+ ob->shapenum = state->leftshapenum;\r
+ }\r
+ }\r
+ if ((Sint16)ob->shapenum == -1)\r
+ {\r
+ ob->shapenum = 0; // make it invisable this time\r
+ }\r
+\r
+ if (xtry || ytry || ob->shapenum != oldshapenum || ob->hitnorth == 25)\r
+ {\r
+ //\r
+ // actor moved or changed shape\r
+ // make sure the movement is within limits (one tile)\r
+ //\r
+ if (ob->needtoclip == cl_fullclip)\r
+ {\r
+ FullClipToWalls(ob);\r
+ }\r
+ else\r
+ {\r
+ ClipToWalls(ob);\r
+ }\r
+ }\r
+}\r
+\r
+//==========================================================================\r
+\r
+\r
+/*\r
+====================\r
+=\r
+= NewState\r
+=\r
+====================\r
+*/\r
+\r
+void NewState(objtype *ob, statetype *state)\r
+{\r
+ Sint16 temp;\r
+ \r
+ ob->state = state;\r
+\r
+ if (state->rightshapenum)\r
+ {\r
+ if (ob->xdir > 0)\r
+ {\r
+ ob->shapenum = state->rightshapenum;\r
+ }\r
+ else\r
+ {\r
+ ob->shapenum = state->leftshapenum;\r
+ }\r
+ }\r
+\r
+ if ((Sint16)ob->shapenum == -1)\r
+ {\r
+ ob->shapenum = 0;\r
+ }\r
+\r
+ temp = ob->needtoclip;\r
+ ob->needtoclip = cl_noclip;\r
+\r
+ xtry = ytry = 0; // no movement\r
+ ClipToWalls(ob); // just calculate values\r
+\r
+ ob->needtoclip = temp;\r
+\r
+ if (ob->needtoclip == cl_fullclip)\r
+ {\r
+ FullClipToWalls(ob);\r
+ }\r
+ else if (ob->needtoclip == cl_midclip)\r
+ {\r
+ ClipToWalls(ob);\r
+ }\r
+}\r
+\r
+//==========================================================================\r
+\r
+\r
+/*\r
+====================\r
+=\r
+= ChangeState\r
+=\r
+====================\r
+*/\r
+\r
+void ChangeState(objtype *ob, statetype *state)\r
+{\r
+ ob->state = state;\r
+ ob->ticcount = 0;\r
+ if (state->rightshapenum)\r
+ {\r
+ if (ob->xdir > 0)\r
+ {\r
+ ob->shapenum = state->rightshapenum;\r
+ }\r
+ else\r
+ {\r
+ ob->shapenum = state->leftshapenum;\r
+ }\r
+ }\r
+\r
+ if ((Sint16)ob->shapenum == -1)\r
+ {\r
+ ob->shapenum = 0;\r
+ }\r
+\r
+ ob->needtoreact = true; // it will need to be redrawn this frame\r
+\r
+ xtry = ytry = 0; // no movement\r
+\r
+ if (ob->hitnorth != 25)\r
+ {\r
+ ClipToWalls(ob);\r
+ }\r
+}\r
+\r
+//==========================================================================\r
+\r
+\r
+/*\r
+====================\r
+=\r
+= OnScreen\r
+=\r
+====================\r
+*/\r
+\r
+boolean OnScreen(objtype *ob)\r
+{\r
+ if (ob->tileright < originxtile || ob->tilebottom < originytile\r
+ || ob->tileleft > originxtilemax || ob->tiletop > originytilemax)\r
+ {\r
+ return false;\r
+ }\r
+ return true;\r
+}\r
+\r
+//==========================================================================\r
+\r
+\r
+/*\r
+====================\r
+=\r
+= DoGravity\r
+=\r
+====================\r
+*/\r
+\r
+void DoGravity(objtype *ob)\r
+{\r
+ Sint32 i;\r
+//\r
+// only accelerate on odd tics, because of limited precision\r
+//\r
+ for (i = lasttimecount-tics; i<lasttimecount; i++)\r
+ {\r
+ if (i&1)\r
+ {\r
+ if (ob->yspeed < 0 && ob->yspeed >= -4)\r
+ {\r
+ ytry += ob->yspeed;\r
+ ob->yspeed = 0;\r
+ return;\r
+ }\r
+ ob->yspeed += 4;\r
+ if (ob->yspeed > 70)\r
+ {\r
+ ob->yspeed = 70;\r
+ }\r
+ }\r
+ ytry += ob->yspeed;\r
+ }\r
+}\r
+\r
+\r
+/*\r
+====================\r
+=\r
+= DoWeakGravity\r
+=\r
+====================\r
+*/\r
+\r
+void DoWeakGravity(objtype *ob)\r
+{\r
+ Sint32 i;\r
+//\r
+// only accelerate on odd tics, because of limited precision\r
+//\r
+ for (i = lasttimecount-tics; i<lasttimecount; i++)\r
+ {\r
+ if (i&1)\r
+ {\r
+ if (ob->yspeed < 0 && ob->yspeed >= -3)\r
+ {\r
+ ytry += ob->yspeed;\r
+ ob->yspeed = 0;\r
+ return;\r
+ }\r
+ ob->yspeed += 3;\r
+ if (ob->yspeed > 70)\r
+ {\r
+ ob->yspeed = 70;\r
+ }\r
+ }\r
+ ytry += ob->yspeed;\r
+ }\r
+}\r
+\r
+\r
+/*\r
+====================\r
+=\r
+= DoTinyGravity\r
+=\r
+====================\r
+*/\r
+\r
+void DoTinyGravity(objtype *ob)\r
+{\r
+ Sint32 i;\r
+//\r
+// only accelerate every 4 tics, because of limited precision\r
+//\r
+ for (i = lasttimecount-tics; i<lasttimecount; i++)\r
+ {\r
+ if (!i & 3) //BUG: this is equal to ((!i) & 3), not (!(i & 3))\r
+ {\r
+ ob->yspeed++;\r
+ if (ob->yspeed > 70)\r
+ {\r
+ ob->yspeed = 70;\r
+ }\r
+ }\r
+ ytry += ob->yspeed;\r
+ }\r
+}\r
+\r
+\r
+/*\r
+===============\r
+=\r
+= AccelerateX\r
+=\r
+===============\r
+*/\r
+\r
+void AccelerateX(objtype *ob, Sint16 dir, Sint16 maxspeed)\r
+{\r
+ Sint32 i;\r
+ Uint16 oldsign;\r
+ \r
+ oldsign = ob->xspeed & 0x8000;\r
+//\r
+// only accelerate on odd tics, because of limited precision\r
+//\r
+ for (i=lasttimecount-tics; i<lasttimecount; i++)\r
+ {\r
+ if (i & 1)\r
+ {\r
+ ob->xspeed += dir;\r
+ if ((ob->xspeed & 0x8000) != oldsign)\r
+ {\r
+ oldsign = ob->xspeed & 0x8000;\r
+ ob->xdir = oldsign? -1 : 1;\r
+ }\r
+ if (ob->xspeed > maxspeed)\r
+ {\r
+ ob->xspeed = maxspeed;\r
+ }\r
+ else if (ob->xspeed < -maxspeed)\r
+ {\r
+ ob->xspeed = -maxspeed;\r
+ }\r
+ }\r
+ xtry += ob->xspeed;\r
+ }\r
+}\r
+\r
+\r
+/*\r
+===============\r
+=\r
+= AccelerateXv\r
+=\r
+= Doesn't change object's xdir\r
+=\r
+===============\r
+*/\r
+\r
+void AccelerateXv(objtype *ob, Sint16 dir, Sint16 maxspeed)\r
+{\r
+ Sint32 i;\r
+\r
+//\r
+// only accelerate on odd tics, because of limited precision\r
+//\r
+ for (i=lasttimecount-tics; i<lasttimecount; i++)\r
+ {\r
+ if (i & 1)\r
+ {\r
+ ob->xspeed += dir;\r
+ if (ob->xspeed > maxspeed)\r
+ {\r
+ ob->xspeed = maxspeed;\r
+ }\r
+ else if (ob->xspeed < -maxspeed)\r
+ {\r
+ ob->xspeed = -maxspeed;\r
+ }\r
+ }\r
+ xtry += ob->xspeed;\r
+ }\r
+}\r
+\r
+\r
+/*\r
+===============\r
+=\r
+= AccelerateY\r
+=\r
+===============\r
+*/\r
+\r
+void AccelerateY(objtype *ob, Sint16 dir, Sint16 maxspeed)\r
+{\r
+ Sint32 i;\r
+\r
+//\r
+// only accelerate on odd tics, because of limited precision\r
+//\r
+ for (i=lasttimecount-tics; i<lasttimecount; i++)\r
+ {\r
+ if (i & 1)\r
+ {\r
+ ob->yspeed += dir;\r
+ if (ob->yspeed > maxspeed)\r
+ {\r
+ ob->yspeed = maxspeed;\r
+ }\r
+ else if (ob->yspeed < -maxspeed)\r
+ {\r
+ ob->yspeed = -maxspeed;\r
+ }\r
+ }\r
+ ytry += ob->yspeed;\r
+ }\r
+}\r
+\r
+\r
+/*\r
+===============\r
+=\r
+= FrictionX\r
+=\r
+===============\r
+*/\r
+\r
+void FrictionX(objtype *ob)\r
+{\r
+ Sint16 friction, oldsign;\r
+ Sint32 i;\r
+\r
+ oldsign = ob->xspeed & 0x8000;\r
+ if (ob->xspeed > 0)\r
+ {\r
+ friction = -1;\r
+ }\r
+ else if (ob->xspeed < 0)\r
+ {\r
+ friction = 1;\r
+ }\r
+ else\r
+ {\r
+ friction = 0;\r
+ }\r
+//\r
+// only accelerate on odd tics, because of limited precision\r
+//\r
+\r
+ for (i=lasttimecount-tics; i<lasttimecount; i++)\r
+ {\r
+ if (i & 1)\r
+ {\r
+ ob->xspeed += friction;\r
+ if ((ob->xspeed & 0x8000) != oldsign)\r
+ {\r
+ ob->xspeed = 0;\r
+ }\r
+ }\r
+ xtry += ob->xspeed;\r
+ }\r
+}\r
+\r
+\r
+/*\r
+===============\r
+=\r
+= FrictionY\r
+=\r
+===============\r
+*/\r
+\r
+void FrictionY(objtype *ob)\r
+{\r
+ Sint16 friction, oldsign;\r
+ Sint32 i;\r
+\r
+ if (ob->yspeed > 0)\r
+ {\r
+ friction = -1;\r
+ }\r
+ else if (ob->yspeed < 0)\r
+ {\r
+ friction = 1;\r
+ }\r
+ else\r
+ {\r
+ friction = 0;\r
+ }\r
+//\r
+// only accelerate on odd tics, because of limited precision\r
+//\r
+ for (i=lasttimecount-tics; i<lasttimecount; i++)\r
+ {\r
+ if (i & 1)\r
+ {\r
+ ob->yspeed += friction;\r
+ if ((ob->yspeed & 0x8000) != oldsign) //BUG: oldsign is not initialized!\r
+ {\r
+ ob->yspeed = 0;\r
+ }\r
+ }\r
+ ytry += ob->yspeed;\r
+ }\r
+}\r
+\r
+//==========================================================================\r
+\r
+/*\r
+===============\r
+=\r
+= StunObj\r
+=\r
+===============\r
+*/\r
+\r
+void StunObj(objtype *ob, objtype *shot, statetype *stunstate)\r
+{\r
+ ExplodeShot(shot);\r
+ ob->temp1 = ob->temp2 = ob->temp3 = 0; // Note: ob->nothink should also be set to 0\r
+ ob->temp4 = ob->obclass;\r
+ ChangeState(ob, stunstate);\r
+ ob->obclass = stunnedobj;\r
+#ifndef KEEN4\r
+ ob->yspeed -= 24;\r
+ if (ob->yspeed < -48)\r
+ ob->yspeed = -48;\r
+#endif\r
+}\r
+\r
+//==========================================================================\r
+\r
+/*\r
+===============\r
+=\r
+= T_Projectile\r
+=\r
+===============\r
+*/\r
+\r
+void T_Projectile(objtype *ob)\r
+{\r
+ DoGravity(ob);\r
+ xtry = ob->xspeed*tics;\r
+}\r
+\r
+\r
+/*\r
+===============\r
+=\r
+= T_WeakProjectile\r
+=\r
+===============\r
+*/\r
+\r
+void T_WeakProjectile(objtype *ob)\r
+{\r
+ DoWeakGravity(ob);\r
+ xtry = ob->xspeed*tics;\r
+}\r
+\r
+\r
+/*\r
+===============\r
+=\r
+= T_Velocity\r
+=\r
+===============\r
+*/\r
+\r
+void T_Velocity(objtype *ob)\r
+{\r
+ xtry = ob->xspeed*tics;\r
+ ytry = ob->yspeed*tics;\r
+}\r
+\r
+\r
+/*\r
+===============\r
+=\r
+= SetReactThink\r
+=\r
+===============\r
+*/\r
+\r
+void SetReactThink(objtype *ob)\r
+{\r
+ ob->needtoreact = true;\r
+}\r
+\r
+\r
+/*\r
+===============\r
+=\r
+= T_Stunned\r
+=\r
+===============\r
+*/\r
+\r
+void T_Stunned(objtype *ob)\r
+{\r
+ ob->temp1 = 0;\r
+ ob->needtoreact = true;\r
+ if (++ob->temp2 == 3)\r
+ ob->temp2 = 0;\r
+}\r
+\r
+\r
+/*\r
+===============\r
+=\r
+= C_Lethal\r
+=\r
+===============\r
+*/\r
+\r
+void C_Lethal(objtype *ob, objtype *hit)\r
+{\r
+ ob++; // shut up compiler\r
+ if (hit->obclass == keenobj)\r
+ {\r
+ KillKeen();\r
+ }\r
+}\r
+\r
+\r
+/*\r
+===============\r
+=\r
+= R_Draw\r
+=\r
+===============\r
+*/\r
+\r
+void R_Draw(objtype *ob)\r
+{\r
+ RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);\r
+}\r
+\r
+\r
+/*\r
+===============\r
+=\r
+= R_Walk\r
+=\r
+===============\r
+*/\r
+\r
+void R_Walk(objtype *ob)\r
+{\r
+ if (ob->xdir == 1 && ob->hitwest)\r
+ {\r
+ ob->x -= ob->xmove;\r
+ ob->xdir = -1;\r
+ ob->nothink = US_RndT() >> 5;\r
+ ChangeState(ob, ob->state);\r
+ }\r
+ else if (ob->xdir == -1 && ob->hiteast)\r
+ {\r
+ ob->x -= ob->xmove;\r
+ ob->xdir = 1;\r
+ ob->nothink = US_RndT() >> 5;\r
+ ChangeState(ob, ob->state);\r
+ }\r
+ else if (!ob->hitnorth)\r
+ {\r
+ ob->x -= ob->xmove;\r
+ ob->xdir = -ob->xdir;\r
+ ob->nothink = US_RndT() >> 5;\r
+ ChangeState(ob, ob->state);\r
+ }\r
+ RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);\r
+}\r
+\r
+\r
+/*\r
+===============\r
+=\r
+= R_WalkNormal\r
+=\r
+= Actor will not walk onto tiles with special (e.g. deadly) north walls\r
+=\r
+===============\r
+*/\r
+\r
+void R_WalkNormal(objtype *ob)\r
+{\r
+ if (ob->xdir == 1 && ob->hitwest)\r
+ {\r
+ ob->x -= ob->xmove;\r
+ ob->xdir = -1;\r
+ ob->nothink = US_RndT() >> 5;\r
+ ChangeState(ob, ob->state);\r
+ }\r
+ else if (ob->xdir == -1 && ob->hiteast)\r
+ {\r
+ ob->x -= ob->xmove;\r
+ ob->xdir = 1;\r
+ ob->nothink = US_RndT() >> 5;\r
+ ChangeState(ob, ob->state);\r
+ }\r
+ else if (!ob->hitnorth || (ob->hitnorth & ~7))\r
+ {\r
+ ob->x -= ob->xmove*2;\r
+ ob->xdir = -ob->xdir;\r
+ ob->nothink = US_RndT() >> 5;\r
+ ChangeState(ob, ob->state);\r
+ }\r
+ RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);\r
+}\r
+\r
+\r
+/*\r
+===============\r
+=\r
+= BadState\r
+=\r
+===============\r
+*/\r
+\r
+void BadState(void)\r
+{\r
+ Quit("Object with bad state!");\r
+}\r
+\r
+\r
+/*\r
+===============\r
+=\r
+= R_Stunned\r
+=\r
+===============\r
+*/\r
+\r
+void R_Stunned(objtype *ob)\r
+{\r
+ Sint16 starx, stary;\r
+\r
+ if (ob->hitwest || ob->hiteast)\r
+ ob->xspeed = 0;\r
+\r
+ if (ob->hitsouth)\r
+ ob->yspeed = 0;\r
+\r
+ if (ob->hitnorth)\r
+ {\r
+ ob->xspeed = ob->yspeed = 0;\r
+ if (ob->state->nextstate)\r
+ ChangeState(ob, ob->state->nextstate);\r
+ }\r
+\r
+ RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);\r
+\r
+ starx = stary = 0;\r
+ switch (ob->temp4)\r
+ {\r
+#if defined KEEN4\r
+ case mimrockobj:\r
+ stary = -4*PIXGLOBAL;\r
+ break;\r
+ case eggobj:\r
+ starx = 8*PIXGLOBAL;\r
+ stary = -8*PIXGLOBAL;\r
+ break;\r
+ case treasureeaterobj:\r
+ starx = 8*PIXGLOBAL;\r
+ break;\r
+ case bounderobj:\r
+ starx = 4*PIXGLOBAL;\r
+ stary = -8*PIXGLOBAL;\r
+ break;\r
+ case wormouthobj:\r
+ starx = 4*PIXGLOBAL;\r
+ stary = -350; // -21.875 pixels (this one is a bit strange)\r
+ break;\r
+ case lickobj:\r
+ stary = -8*PIXGLOBAL;\r
+ break;\r
+ case inchwormobj:\r
+ starx = -4*PIXGLOBAL;\r
+ stary = -8*PIXGLOBAL;\r
+ break;\r
+ case slugobj:\r
+ stary = -8*PIXGLOBAL;\r
+ break;\r
+#elif defined KEEN5\r
+ case sparkyobj:\r
+ starx += 4*PIXGLOBAL;\r
+ break;\r
+ case amptonobj:\r
+ stary -= 8*PIXGLOBAL;\r
+ asm jmp done; // just to recreate the original code's quirks, feel free to delete this\r
+ break;\r
+ case scottieobj:\r
+ stary -= 8*PIXGLOBAL;\r
+ break;\r
+#elif defined KEEN6\r
+ case blooguardobj:\r
+ starx = 16*PIXGLOBAL;\r
+ stary = -4*PIXGLOBAL;\r
+ break;\r
+ case flectobj:\r
+ starx = 4*PIXGLOBAL;\r
+ stary = -4*PIXGLOBAL;\r
+ break;\r
+ case bloogobj:\r
+ case nospikeobj:\r
+ starx = 8*PIXGLOBAL;\r
+ stary = -4*PIXGLOBAL;\r
+ break;\r
+ case bloogletobj:\r
+ case babobbaobj:\r
+ stary = -8*PIXGLOBAL;\r
+ break;\r
+ case fleexobj:\r
+ starx = 16*PIXGLOBAL;\r
+ stary = 8*PIXGLOBAL;\r
+ break;\r
+ case ceilickobj:\r
+ stary = 12*PIXGLOBAL;\r
+ break;\r
+ default:\r
+ Quit("No star spec for object!");\r
+#endif\r
+ }\r
+done:\r
+\r
+ ob->temp1 = ob->temp1 + tics;\r
+ if (ob->temp1 > 10)\r
+ {\r
+ ob->temp1 -= 10;\r
+ if (++ob->temp2 == 3)\r
+ ob->temp2 = 0;\r
+ }\r
+\r
+ RF_PlaceSprite((void **)&ob->temp3, ob->x+starx, ob->y+stary, ob->temp2+STUNSTARS1SPR, spritedraw, 3);\r
+}\r
+\r
+//==========================================================================\r
+\r
+statetype sc_deadstate = {0, 0, think, false, false, 0, 0, 0, NULL, NULL, NULL, NULL};\r
+#pragma warn -sus //BadState is not a valid think/contact/react function. Nobody cares.\r
+statetype sc_badstate = {0, 0, think, false, false, 0, 0, 0, &BadState, &BadState, &BadState, NULL};\r
+#pragma warn +sus
\ No newline at end of file