]> 4ch.mooo.com Git - 16.git/blobdiff - 16/keen456/KEEN4-6/CK_STATE.C
extrcted keen code remake
[16.git] / 16 / keen456 / KEEN4-6 / CK_STATE.C
diff --git a/16/keen456/KEEN4-6/CK_STATE.C b/16/keen456/KEEN4-6/CK_STATE.C
new file mode 100755 (executable)
index 0000000..25d3be6
--- /dev/null
@@ -0,0 +1,1968 @@
+/* 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