1 /* Reconstructed Commander Keen 4-6 Source Code
\r
2 * Copyright (C) 2021 K1n9_Duk3
\r
4 * This file is loosely based on:
\r
5 * Keen Dreams Source Code
\r
6 * Copyright (C) 2014 Javier M. Chavez
\r
8 * This program is free software; you can redistribute it and/or modify
\r
9 * it under the terms of the GNU General Public License as published by
\r
10 * the Free Software Foundation; either version 2 of the License, or
\r
11 * (at your option) any later version.
\r
13 * This program is distributed in the hope that it will be useful,
\r
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
\r
16 * GNU General Public License for more details.
\r
18 * You should have received a copy of the GNU General Public License along
\r
19 * with this program; if not, write to the Free Software Foundation, Inc.,
\r
20 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
\r
26 =============================================================================
\r
30 =============================================================================
\r
33 Sint16 wallclip[8][16] = { // the height of a given point in a tile
\r
34 { 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256},
\r
35 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
\r
36 { 0,0x08,0x10,0x18,0x20,0x28,0x30,0x38,0x40,0x48,0x50,0x58,0x60,0x68,0x70,0x78},
\r
37 {0x80,0x88,0x90,0x98,0xa0,0xa8,0xb0,0xb8,0xc0,0xc8,0xd0,0xd8,0xe0,0xe8,0xf0,0xf8},
\r
38 { 0,0x10,0x20,0x30,0x40,0x50,0x60,0x70,0x80,0x90,0xa0,0xb0,0xc0,0xd0,0xe0,0xf0},
\r
39 {0x78,0x70,0x68,0x60,0x58,0x50,0x48,0x40,0x38,0x30,0x28,0x20,0x18,0x10,0x08, 0},
\r
40 {0xf8,0xf0,0xe8,0xe0,0xd8,0xd0,0xc8,0xc0,0xb8,0xb0,0xa8,0xa0,0x98,0x90,0x88,0x80},
\r
41 {0xf0,0xe0,0xd0,0xc0,0xb0,0xa0,0x90,0x80,0x70,0x60,0x50,0x40,0x30,0x20,0x10, 0}
\r
45 boolean playerkludgeclipcancel;
\r
48 =============================================================================
\r
52 =============================================================================
\r
55 Uint16 oldtileleft, oldtiletop, oldtileright, oldtilebottom, oldtilemidx;
\r
56 Uint16 oldleft, oldtop, oldright, oldbottom, oldmidx;
\r
57 Sint16 leftmoved, topmoved, rightmoved, bottommoved, midxmoved;
\r
59 //==========================================================================
\r
62 ====================
\r
66 ====================
\r
69 void MoveObjVert(objtype *ob, Sint16 ymove)
\r
73 ob->bottom += ymove;
\r
74 ob->tiletop = CONVERT_GLOBAL_TO_TILE(ob->top);
\r
75 ob->tilebottom = CONVERT_GLOBAL_TO_TILE(ob->bottom);
\r
79 ====================
\r
83 ====================
\r
86 void MoveObjHoriz(objtype *ob, Sint16 xmove)
\r
88 //BUG? ob->midx is not adjusted in Keen 4 & 5
\r
93 ob->midx += xmove; //BUG? ob->tilemidx is not updated
\r
95 ob->tileleft = CONVERT_GLOBAL_TO_TILE(ob->left);
\r
96 ob->tileright = CONVERT_GLOBAL_TO_TILE(ob->right);
\r
99 //==========================================================================
\r
102 ====================
\r
104 = PlayerBottomKludge
\r
106 ====================
\r
109 void PlayerBottomKludge(objtype *ob)
\r
112 Uint16 wall, clip, xpix;
\r
113 Sint16 xmove, ymove;
\r
115 map = (Uint16 far *)mapsegs[1] + mapbwidthtable[ob->tilebottom-1]/2;
\r
119 map += ob->tileright;
\r
120 xmove = ob->right - ob->midx;
\r
121 if (tinf[*(map-mapwidth)+WESTWALL] || tinf[*map+WESTWALL])
\r
129 map += ob->tileleft;
\r
130 xmove = ob->left - ob->midx;
\r
131 if (tinf[*(map-mapwidth)+EASTWALL] || tinf[*map+EASTWALL])
\r
136 if ((_AX = tinf[*map+NORTHWALL]) != 0) // the _AX = ... part is just to recreate the original code's quirks, feel free to delete this
\r
141 if ((wall = tinf[*map+NORTHWALL]) != 1)
\r
145 clip = wallclip[wall&7][xpix];
\r
146 ymove = CONVERT_TILE_TO_GLOBAL(ob->tilebottom) + clip - 1 -ob->bottom;
\r
147 if (ymove <= 0 && ymove >= -bottommoved)
\r
149 ob->hitnorth = wall;
\r
150 MoveObjVert(ob, ymove);
\r
151 MoveObjHoriz(ob, xmove);
\r
156 ====================
\r
160 ====================
\r
163 void PlayerTopKludge(objtype *ob)
\r
166 Uint16 xpix, wall, clip;
\r
169 map = (Uint16 far *)mapsegs[1] + mapbwidthtable[ob->tiletop+1]/2;
\r
173 map += ob->tileright;
\r
174 if (tinf[*(map+mapwidth)+WESTWALL] || tinf[*(map+2*mapwidth)+WESTWALL])
\r
182 map += ob->tileleft;
\r
183 if (tinf[*(map+mapwidth)+EASTWALL] || tinf[*(map+2*mapwidth)+EASTWALL])
\r
188 if ((_AX = tinf[*map+SOUTHWALL]) != 0) // the _AX = ... part is just to recreate the original code's quirks, feel free to delete this
\r
193 if ((wall = tinf[*map+SOUTHWALL]) != 0)
\r
195 clip = wallclip[wall&7][xpix];
\r
196 move = CONVERT_TILE_TO_GLOBAL(ob->tiletop+1) - clip - ob->top;
\r
197 if (move >= 0 && move <= -topmoved)
\r
199 ob->hitsouth = wall;
\r
200 MoveObjVert(ob, move);
\r
206 ===========================
\r
210 ===========================
\r
213 void ClipToEnds(objtype *ob)
\r
216 Uint16 wall, y, clip;
\r
217 Sint16 totalmove, maxmove, move;
\r
220 midxpix = CONVERT_GLOBAL_TO_PIXEL(ob->midx & 0xF0);
\r
221 maxmove = -abs(midxmoved)-bottommoved-16;
\r
222 map = (Uint16 far *)mapsegs[1] + (mapbwidthtable-1)[oldtilebottom]/2 + ob->tilemidx;
\r
223 for (y=oldtilebottom-1; y <= ob->tilebottom; y++,map+=mapwidth)
\r
225 if ((wall = tinf[*map + NORTHWALL]) != 0)
\r
227 clip = wallclip[wall&7][midxpix];
\r
228 move = (CONVERT_TILE_TO_GLOBAL(y) + clip)-1-ob->bottom;
\r
229 if (move < 0 && move >= maxmove)
\r
231 ob->hitnorth = wall;
\r
232 MoveObjVert(ob, move);
\r
237 maxmove = abs(midxmoved)-topmoved+16;
\r
238 map = (Uint16 far *)mapsegs[1] + (mapbwidthtable+1)[oldtiletop]/2 + ob->tilemidx;
\r
239 for (y=oldtiletop+1; y >= ob->tiletop; y--,map-=mapwidth) // BUG: unsigned comparison - loop never ends if ob->tiletop is 0
\r
241 if ((wall = tinf[*map + SOUTHWALL]) != 0)
\r
243 clip = wallclip[wall&7][midxpix];
\r
244 move = CONVERT_TILE_TO_GLOBAL(y+1) - clip - ob->top;
\r
245 if (move > 0 && move <= maxmove)
\r
247 totalmove = ytry+move;
\r
248 if (totalmove < 0x100 && totalmove > -0x100)
\r
250 ob->hitsouth = wall;
\r
251 MoveObjVert(ob, move);
\r
252 //BUG? no return here
\r
260 ===========================
\r
264 ===========================
\r
267 void ClipToSides(objtype *ob)
\r
269 Sint16 move, y, top, bottom;
\r
273 if (ob->hitsouth > 1)
\r
277 bottom = ob->tilebottom;
\r
278 if (ob->hitnorth > 1)
\r
282 for (y=top; y<=bottom; y++)
\r
284 map = (Uint16 far *)mapsegs[1] + mapbwidthtable[y]/2 + ob->tileleft;
\r
285 if ((ob->hiteast = tinf[*map+EASTWALL]) != 0)
\r
287 move = CONVERT_TILE_TO_GLOBAL(ob->tileleft+1) - ob->left;
\r
288 MoveObjHoriz(ob, move);
\r
292 for (y=top; y<=bottom; y++)
\r
294 map = (Uint16 far *)mapsegs[1] + mapbwidthtable[y]/2 + ob->tileright;
\r
295 if ((ob->hitwest = tinf[*map+WESTWALL]) != 0)
\r
297 move = (CONVERT_TILE_TO_GLOBAL(ob->tileright)-1)-ob->right;
\r
298 MoveObjHoriz(ob, move);
\r
305 ===========================
\r
309 ===========================
\r
312 boolean CheckPosition(objtype *ob)
\r
315 // This version is pretty much a compiler-optimized version of the other
\r
316 // version below, but I simply could not get the compiler to optimize it
\r
317 // in exactly the same way.
\r
319 Uint16 tile, x, tileright;
\r
322 Uint16 tileleft, y, tilebottom;
\r
324 map = (Uint16 far *)mapsegs[1] + mapbwidthtable[ob->tiletop]/2 + ob->tileleft;
\r
325 rowdiff = mapwidth-(ob->tileright-ob->tileleft+1);
\r
328 tileleft = ob->tileleft;
\r
329 tileright = _AX = ob->tileright;
\r
330 tilebottom = ob->tilebottom;
\r
332 for (; tilebottom>=y; y++,map+=rowdiff)
\r
334 for (x=tileleft; tileright>=x; x++)
\r
337 if (tinf[tile+NORTHWALL] || tinf[tile+EASTWALL] || tinf[tile+SOUTHWALL] || tinf[tile+WESTWALL])
\r
349 map = (Uint16 far *)mapsegs[1] + mapbwidthtable[ob->tiletop]/2 + ob->tileleft;
\r
350 rowdiff = mapwidth-(ob->tileright-ob->tileleft+1);
\r
351 for (y=ob->tiletop; y<=ob->tilebottom; y++,map+=rowdiff)
\r
353 for (x=ob->tileleft; x<=ob->tileright; x++)
\r
356 if (tinf[tile+NORTHWALL] || tinf[tile+EASTWALL] || tinf[tile+SOUTHWALL] || tinf[tile+WESTWALL])
\r
367 ===========================
\r
371 ===========================
\r
374 boolean StatePositionOk(objtype *ob, statetype *state)
\r
376 spritetabletype far *shape;
\r
380 ob->shapenum = state->rightshapenum;
\r
384 ob->shapenum = state->leftshapenum;
\r
386 shape = &spritetable[ob->shapenum-STARTSPRITES];
\r
387 ob->left = ob->x + shape->xl;
\r
388 ob->right = ob->x + shape->xh;
\r
389 ob->top = ob->y + shape->yl;
\r
390 ob->bottom = ob->y + shape->yh;
\r
391 ob->midx = ob->left + (ob->right-ob->left)/2;
\r
392 ob->tileleft = CONVERT_GLOBAL_TO_TILE(ob->left);
\r
393 ob->tileright = CONVERT_GLOBAL_TO_TILE(ob->right);
\r
394 ob->tiletop = CONVERT_GLOBAL_TO_TILE(ob->top);
\r
395 ob->tilebottom = CONVERT_GLOBAL_TO_TILE(ob->bottom);
\r
396 ob->tilemidx = CONVERT_GLOBAL_TO_TILE(ob->midx);
\r
397 return CheckPosition(ob);
\r
402 ===========================
\r
406 ===========================
\r
409 void CalcBounds(objtype *ob) //not present in Keen 4 & 6
\r
411 spritetabletype far *shape;
\r
413 shape = &spritetable[ob->shapenum-STARTSPRITES];
\r
414 ob->left = ob->x + shape->xl;
\r
415 ob->right = ob->x + shape->xh;
\r
416 ob->top = ob->y + shape->yl;
\r
417 ob->bottom = ob->y + shape->yh;
\r
418 ob->midx = ob->left + (ob->right-ob->left)/2;
\r
422 //==========================================================================
\r
429 = Moves the current object xtry/ytry units, clipping to walls
\r
434 void ClipToWalls(objtype *ob)
\r
440 spritetabletype far *shape;
\r
448 // make sure it stays in contact with a 45 degree slope
\r
450 if (ob->state->pushtofloor)
\r
452 if (ob->hitnorth == 25)
\r
477 else if (xtry < -239)
\r
481 if (ytry > 255) // +16 for push to floor
\r
485 else if (ytry < -239)
\r
493 ob->needtoreact = true;
\r
495 if (!ob->shapenum) // can't get a hit rect with no shape!
\r
500 shape = &spritetable[ob->shapenum-STARTSPRITES];
\r
502 oldtileright = ob->tileright;
\r
503 oldtiletop = ob->tiletop;
\r
504 oldtileleft = ob->tileleft;
\r
505 oldtilebottom = ob->tilebottom;
\r
506 oldtilemidx = ob->tilemidx;
\r
508 oldright = ob->right;
\r
510 oldleft = ob->left;
\r
511 oldbottom = ob->bottom;
\r
512 oldmidx = ob->midx;
\r
514 ob->left = ob->x + shape->xl;
\r
515 ob->right = ob->x + shape->xh;
\r
516 ob->top = ob->y + shape->yl;
\r
517 ob->bottom = ob->y + shape->yh;
\r
518 ob->midx = ob->left + (ob->right-ob->left)/2;
\r
520 ob->tileleft = CONVERT_GLOBAL_TO_TILE(ob->left);
\r
521 ob->tileright = CONVERT_GLOBAL_TO_TILE(ob->right);
\r
522 ob->tiletop = CONVERT_GLOBAL_TO_TILE(ob->top);
\r
523 ob->tilebottom = CONVERT_GLOBAL_TO_TILE(ob->bottom);
\r
524 ob->tilemidx = CONVERT_GLOBAL_TO_TILE(ob->midx);
\r
526 ob->hitnorth=ob->hiteast=ob->hitsouth=ob->hitwest=0;
\r
528 if (ob->needtoclip)
\r
530 leftmoved = ob->left - oldleft;
\r
531 rightmoved = ob->right - oldright;
\r
532 topmoved = ob->top - oldtop;
\r
533 bottommoved = ob->bottom - oldbottom;
\r
534 midxmoved = ob->midx - oldmidx;
\r
541 if (ob == player && !playerkludgeclipcancel) // zero tolerance near the edge when player gets pushed!
\r
543 if (!ob->hitnorth && bottommoved > 0)
\r
545 PlayerBottomKludge(ob);
\r
547 if (!ob->hitsouth && topmoved < 0)
\r
549 PlayerTopKludge(ob);
\r
556 // special hack to prevent player from getting stuck on slopes?
\r
558 if (ob == player && (ob->hitnorth & 7) > 1 && (ob->hiteast || ob->hitwest))
\r
561 Uint16 pixx, clip, move;
\r
564 pixx = CONVERT_GLOBAL_TO_PIXEL(ob->midx & (15*PIXGLOBAL));
\r
565 map = (Uint16 far *)mapsegs[1] + mapbwidthtable[oldtilebottom]/2 + ob->tilemidx;
\r
567 for (y=oldtilebottom; ob->tilebottom+1 >= y; y++, map+=mapwidth)
\r
569 if ((wall = tinf[*map + NORTHWALL]) != 0)
\r
571 clip = wallclip[wall & 7][pixx];
\r
572 move = CONVERT_TILE_TO_GLOBAL(y) + clip - 1 - ob->bottom;
\r
573 ob->hitnorth = wall;
\r
574 MoveObjVert(ob, move);
\r
581 if (pushed && !ob->hitnorth)
\r
584 ob->x = oldx + xtry;
\r
586 ob->left = ob->x + shape->xl;
\r
587 ob->right = ob->x + shape->xh;
\r
588 ob->top = ob->y + shape->yl;
\r
589 ob->bottom = ob->y + shape->yh;
\r
590 ob->midx = ob->left + (ob->right-ob->left)/2;
\r
592 ob->tileleft = CONVERT_GLOBAL_TO_TILE(ob->left);
\r
593 ob->tileright = CONVERT_GLOBAL_TO_TILE(ob->right);
\r
594 ob->tiletop = CONVERT_GLOBAL_TO_TILE(ob->top);
\r
595 ob->tilebottom = CONVERT_GLOBAL_TO_TILE(ob->bottom);
\r
596 ob->tilemidx = CONVERT_GLOBAL_TO_TILE(ob->midx);
\r
599 ob->xmove = ob->xmove + (ob->x - oldx);
\r
600 ob->ymove = ob->ymove + (ob->y - oldy);
\r
608 = Moves the current object xtry/ytry units, clipping to walls
\r
613 void FullClipToWalls(objtype *ob)
\r
615 Uint16 oldx, oldy, w, h;
\r
616 spritetabletype far *shape;
\r
628 else if (xtry < -239)
\r
636 else if (ytry < -239)
\r
644 ob->needtoreact = true;
\r
646 shape = &spritetable[ob->shapenum-STARTSPRITES];
\r
648 switch (ob->obclass)
\r
663 case schoolfishobj:
\r
667 #elif defined KEEN5
\r
670 w = h = 32*PIXGLOBAL;
\r
672 #elif defined KEEN6
\r
674 w = h = 32*PIXGLOBAL;
\r
679 Quit("FullClipToWalls: Bad obclass");
\r
683 ob->right = ob->x + w;
\r
686 ob->bottom = ob->y + h;
\r
688 ob->tileleft = CONVERT_GLOBAL_TO_TILE(ob->left);
\r
689 ob->tileright = CONVERT_GLOBAL_TO_TILE(ob->right);
\r
690 ob->tiletop = CONVERT_GLOBAL_TO_TILE(ob->top);
\r
691 ob->tilebottom = CONVERT_GLOBAL_TO_TILE(ob->bottom);
\r
693 ob->hitnorth=ob->hiteast=ob->hitsouth=ob->hitwest=0;
\r
698 if (!CheckPosition(ob))
\r
700 MoveObjHoriz(ob, -xtry); //undo x movement
\r
701 if (CheckPosition(ob))
\r
722 MoveObjHoriz(ob, xtry); //redo x movement
\r
723 MoveObjVert(ob, -ytry); //undo y movement
\r
724 if (!CheckPosition(ob))
\r
726 MoveObjHoriz(ob, -xtry); //undo x movement
\r
739 ob->xmove = ob->xmove + (ob->x - oldx);
\r
740 ob->ymove = ob->ymove + (ob->y - oldy);
\r
742 ob->left = ob->x + shape->xl;
\r
743 ob->right = ob->x + shape->xh;
\r
744 ob->top = ob->y + shape->yl;
\r
745 ob->bottom = ob->y + shape->yh;
\r
746 ob->midx = ob->left + (ob->right-ob->left)/2;
\r
754 = Moves the current object xtry/ytry units, clipping to walls
\r
759 void PushObj(objtype *ob)
\r
762 spritetabletype far *shape;
\r
773 ob->needtoreact = true;
\r
775 if (!ob->shapenum) // can't get a hit rect with no shape!
\r
780 shape = &spritetable[ob->shapenum-STARTSPRITES];
\r
782 oldtileright = ob->tileright;
\r
783 oldtiletop = ob->tiletop;
\r
784 oldtileleft = ob->tileleft;
\r
785 oldtilebottom = ob->tilebottom;
\r
786 oldtilemidx = ob->tilemidx;
\r
788 oldright = ob->right;
\r
790 oldleft = ob->left;
\r
791 oldbottom = ob->bottom;
\r
792 oldmidx = ob->midx;
\r
794 ob->left = ob->x + shape->xl;
\r
795 ob->right = ob->x + shape->xh;
\r
796 ob->top = ob->y + shape->yl;
\r
797 ob->bottom = ob->y + shape->yh;
\r
798 ob->midx = ob->left + (ob->right-ob->left)/2;
\r
800 ob->tileleft = CONVERT_GLOBAL_TO_TILE(ob->left);
\r
801 ob->tileright = CONVERT_GLOBAL_TO_TILE(ob->right);
\r
802 ob->tiletop = CONVERT_GLOBAL_TO_TILE(ob->top);
\r
803 ob->tilebottom = CONVERT_GLOBAL_TO_TILE(ob->bottom);
\r
804 ob->tilemidx = CONVERT_GLOBAL_TO_TILE(ob->midx);
\r
806 if (ob->needtoclip)
\r
808 leftmoved = ob->left - oldleft;
\r
809 rightmoved = ob->right - oldright;
\r
810 topmoved = ob->top - oldtop;
\r
811 bottommoved = ob->bottom - oldbottom;
\r
812 midxmoved = ob->midx - oldmidx;
\r
818 ob->xmove = ob->xmove + (ob->x - oldx);
\r
819 ob->ymove = ob->ymove + (ob->y - oldy);
\r
822 //==========================================================================
\r
830 = Clips push to solid
\r
835 void ClipToSpriteSide(objtype *push, objtype *solid)
\r
837 Sint16 xmove, leftinto, rightinto;
\r
840 // amount the push shape can be pushed
\r
842 xmove = solid->xmove - push->xmove;
\r
845 // amount it is inside
\r
847 leftinto = solid->right - push->left;
\r
848 rightinto = push->right - solid->left;
\r
850 if (leftinto > 0 && leftinto <= xmove)
\r
853 if (push->state->pushtofloor)
\r
855 ytry = leftinto+16;
\r
860 else if (rightinto > 0 && rightinto <= -xmove)
\r
863 if (push->state->pushtofloor)
\r
865 ytry = rightinto+16;
\r
872 //==========================================================================
\r
880 = Clips push to solid
\r
885 void ClipToSpriteTop(objtype *push, objtype *solid)
\r
887 Sint16 temp, ymove, bottominto;
\r
890 // amount the push shape can be pushed
\r
892 ymove = push->ymove - solid->ymove;
\r
895 // amount it is inside
\r
897 bottominto = push->bottom - solid->top;
\r
899 if (bottominto >= 0 && bottominto <= ymove)
\r
901 if (push == player)
\r
903 gamestate.riding = solid;
\r
905 ytry = -bottominto;
\r
906 temp = push->state->pushtofloor;
\r
907 push->state->pushtofloor = false;
\r
909 push->state->pushtofloor = temp;
\r
910 if (!push->hitsouth)
\r
912 push->hitnorth = 25;
\r
917 //==========================================================================
\r
925 = Clips push to solid
\r
930 void ClipToSprite(objtype *push, objtype *solid, boolean squish)
\r
932 Sint16 xmove, ymove, leftinto, rightinto, topinto, bottominto;
\r
934 xmove = solid->xmove - push->xmove;
\r
940 leftinto = solid->right - push->left;
\r
941 rightinto = push->right - solid->left;
\r
943 if (leftinto > 0 && xmove+1 >= leftinto)
\r
948 if (squish && push->hitwest)
\r
955 else if (rightinto > 0 && -xmove+1 >= rightinto)
\r
960 if (squish && push->hiteast)
\r
971 ymove = push->ymove - solid->ymove;
\r
972 topinto = solid->bottom - push->top;
\r
973 bottominto = push->bottom - solid->top;
\r
974 if (bottominto >= 0 && bottominto <= ymove)
\r
976 if (push == player)
\r
978 gamestate.riding = solid;
\r
980 ytry = -bottominto;
\r
982 if (squish && push->hitsouth)
\r
986 if (!push->hitsouth)
\r
988 push->hitnorth = 25;
\r
992 else if (topinto >= 0 && topinto <= ymove) // BUG: should be 'topinto <= -ymove'
\r
996 if (squish && push->hitnorth)
\r
1000 push->hitsouth = 25;
\r
1004 //==========================================================================
\r
1008 ==================
\r
1012 = Moves an actor in its current state by a given number of tics.
\r
1013 = If that time takes it into the next state, it changes the state
\r
1014 = and returns the number of excess tics after the state change
\r
1016 ==================
\r
1019 Sint16 DoActor(objtype *ob, Sint16 numtics)
\r
1021 Sint16 ticcount, usedtics, excesstics;
\r
1024 state = ob->state;
\r
1026 if (state->progress == think)
\r
1042 ticcount = ob->ticcount + numtics;
\r
1044 if (state->tictime > ticcount || state->tictime == 0)
\r
1046 ob->ticcount = ticcount;
\r
1047 if (state->progress == slide || state->progress == slidethink)
\r
1051 xtry += ob->xdir == 1? numtics*state->xmove : -numtics*state->xmove;
\r
1055 ytry += ob->ydir == 1? numtics*state->ymove : -numtics*state->ymove;
\r
1058 if ((state->progress == slidethink || state->progress == stepthink) && state->think)
\r
1073 usedtics = state->tictime - ob->ticcount;
\r
1074 excesstics = ticcount - state->tictime;
\r
1076 if (state->progress == slide || state->progress == slidethink)
\r
1080 xtry += ob->xdir == 1? usedtics*state->xmove : -usedtics*state->xmove;
\r
1084 ytry += ob->ydir == 1? usedtics*state->ymove : -usedtics*state->ymove;
\r
1091 xtry += ob->xdir == 1? state->xmove : -state->xmove;
\r
1095 ytry += ob->ydir == 1? state->ymove : -state->ymove;
\r
1111 if (state == ob->state)
\r
1113 ob->state = state->nextstate; // go to next state
\r
1115 else if (!ob->state)
\r
1117 return 0; // object removed itself
\r
1119 return excesstics;
\r
1123 //==========================================================================
\r
1127 ====================
\r
1131 = Change state and give directions
\r
1133 ====================
\r
1136 void StateMachine(objtype *ob)
\r
1138 Sint16 excesstics, oldshapenum;
\r
1141 ob->xmove=ob->ymove=xtry=ytry=0;
\r
1142 oldshapenum = ob->shapenum;
\r
1144 state = ob->state;
\r
1146 excesstics = DoActor(ob, tics);
\r
1147 if (ob->state != state)
\r
1149 ob->ticcount = 0; // start the new state at 0, then use excess
\r
1150 state = ob->state;
\r
1153 while (excesstics)
\r
1156 // passed through to next state
\r
1158 if (!state->skippable && state->tictime <= excesstics)
\r
1160 excesstics = DoActor(ob, state->tictime-1);
\r
1164 excesstics = DoActor(ob, excesstics);
\r
1166 if (ob->state != state)
\r
1168 ob->ticcount = 0; // start the new state at 0, then use excess
\r
1169 state = ob->state;
\r
1173 if (!state) // object removed itself
\r
1180 // if state->rightshapenum == NULL, the state does not have a standard
\r
1181 // shape (the think routine should have set it)
\r
1183 if (state->rightshapenum)
\r
1187 ob->shapenum = state->rightshapenum;
\r
1191 ob->shapenum = state->leftshapenum;
\r
1194 if ((Sint16)ob->shapenum == -1)
\r
1196 ob->shapenum = 0; // make it invisable this time
\r
1199 if (xtry || ytry || ob->shapenum != oldshapenum || ob->hitnorth == 25)
\r
1202 // actor moved or changed shape
\r
1203 // make sure the movement is within limits (one tile)
\r
1205 if (ob->needtoclip == cl_fullclip)
\r
1207 FullClipToWalls(ob);
\r
1216 //==========================================================================
\r
1220 ====================
\r
1224 ====================
\r
1227 void NewState(objtype *ob, statetype *state)
\r
1231 ob->state = state;
\r
1233 if (state->rightshapenum)
\r
1237 ob->shapenum = state->rightshapenum;
\r
1241 ob->shapenum = state->leftshapenum;
\r
1245 if ((Sint16)ob->shapenum == -1)
\r
1250 temp = ob->needtoclip;
\r
1251 ob->needtoclip = cl_noclip;
\r
1253 xtry = ytry = 0; // no movement
\r
1254 ClipToWalls(ob); // just calculate values
\r
1256 ob->needtoclip = temp;
\r
1258 if (ob->needtoclip == cl_fullclip)
\r
1260 FullClipToWalls(ob);
\r
1262 else if (ob->needtoclip == cl_midclip)
\r
1268 //==========================================================================
\r
1272 ====================
\r
1276 ====================
\r
1279 void ChangeState(objtype *ob, statetype *state)
\r
1281 ob->state = state;
\r
1283 if (state->rightshapenum)
\r
1287 ob->shapenum = state->rightshapenum;
\r
1291 ob->shapenum = state->leftshapenum;
\r
1295 if ((Sint16)ob->shapenum == -1)
\r
1300 ob->needtoreact = true; // it will need to be redrawn this frame
\r
1302 xtry = ytry = 0; // no movement
\r
1304 if (ob->hitnorth != 25)
\r
1310 //==========================================================================
\r
1314 ====================
\r
1318 ====================
\r
1321 boolean OnScreen(objtype *ob)
\r
1323 if (ob->tileright < originxtile || ob->tilebottom < originytile
\r
1324 || ob->tileleft > originxtilemax || ob->tiletop > originytilemax)
\r
1331 //==========================================================================
\r
1335 ====================
\r
1339 ====================
\r
1342 void DoGravity(objtype *ob)
\r
1346 // only accelerate on odd tics, because of limited precision
\r
1348 for (i = lasttimecount-tics; i<lasttimecount; i++)
\r
1352 if (ob->yspeed < 0 && ob->yspeed >= -4)
\r
1354 ytry += ob->yspeed;
\r
1359 if (ob->yspeed > 70)
\r
1364 ytry += ob->yspeed;
\r
1370 ====================
\r
1374 ====================
\r
1377 void DoWeakGravity(objtype *ob)
\r
1381 // only accelerate on odd tics, because of limited precision
\r
1383 for (i = lasttimecount-tics; i<lasttimecount; i++)
\r
1387 if (ob->yspeed < 0 && ob->yspeed >= -3)
\r
1389 ytry += ob->yspeed;
\r
1394 if (ob->yspeed > 70)
\r
1399 ytry += ob->yspeed;
\r
1405 ====================
\r
1409 ====================
\r
1412 void DoTinyGravity(objtype *ob)
\r
1416 // only accelerate every 4 tics, because of limited precision
\r
1418 for (i = lasttimecount-tics; i<lasttimecount; i++)
\r
1420 if (!i & 3) //BUG: this is equal to ((!i) & 3), not (!(i & 3))
\r
1423 if (ob->yspeed > 70)
\r
1428 ytry += ob->yspeed;
\r
1441 void AccelerateX(objtype *ob, Sint16 dir, Sint16 maxspeed)
\r
1446 oldsign = ob->xspeed & 0x8000;
\r
1448 // only accelerate on odd tics, because of limited precision
\r
1450 for (i=lasttimecount-tics; i<lasttimecount; i++)
\r
1454 ob->xspeed += dir;
\r
1455 if ((ob->xspeed & 0x8000) != oldsign)
\r
1457 oldsign = ob->xspeed & 0x8000;
\r
1458 ob->xdir = oldsign? -1 : 1;
\r
1460 if (ob->xspeed > maxspeed)
\r
1462 ob->xspeed = maxspeed;
\r
1464 else if (ob->xspeed < -maxspeed)
\r
1466 ob->xspeed = -maxspeed;
\r
1469 xtry += ob->xspeed;
\r
1479 = Doesn't change object's xdir
\r
1484 void AccelerateXv(objtype *ob, Sint16 dir, Sint16 maxspeed)
\r
1489 // only accelerate on odd tics, because of limited precision
\r
1491 for (i=lasttimecount-tics; i<lasttimecount; i++)
\r
1495 ob->xspeed += dir;
\r
1496 if (ob->xspeed > maxspeed)
\r
1498 ob->xspeed = maxspeed;
\r
1500 else if (ob->xspeed < -maxspeed)
\r
1502 ob->xspeed = -maxspeed;
\r
1505 xtry += ob->xspeed;
\r
1518 void AccelerateY(objtype *ob, Sint16 dir, Sint16 maxspeed)
\r
1523 // only accelerate on odd tics, because of limited precision
\r
1525 for (i=lasttimecount-tics; i<lasttimecount; i++)
\r
1529 ob->yspeed += dir;
\r
1530 if (ob->yspeed > maxspeed)
\r
1532 ob->yspeed = maxspeed;
\r
1534 else if (ob->yspeed < -maxspeed)
\r
1536 ob->yspeed = -maxspeed;
\r
1539 ytry += ob->yspeed;
\r
1552 void FrictionX(objtype *ob)
\r
1554 Sint16 friction, oldsign;
\r
1557 oldsign = ob->xspeed & 0x8000;
\r
1558 if (ob->xspeed > 0)
\r
1562 else if (ob->xspeed < 0)
\r
1571 // only accelerate on odd tics, because of limited precision
\r
1574 for (i=lasttimecount-tics; i<lasttimecount; i++)
\r
1578 ob->xspeed += friction;
\r
1579 if ((ob->xspeed & 0x8000) != oldsign)
\r
1584 xtry += ob->xspeed;
\r
1597 void FrictionY(objtype *ob)
\r
1599 Sint16 friction, oldsign;
\r
1602 if (ob->yspeed > 0)
\r
1606 else if (ob->yspeed < 0)
\r
1615 // only accelerate on odd tics, because of limited precision
\r
1617 for (i=lasttimecount-tics; i<lasttimecount; i++)
\r
1621 ob->yspeed += friction;
\r
1622 if ((ob->yspeed & 0x8000) != oldsign) //BUG: oldsign is not initialized!
\r
1627 ytry += ob->yspeed;
\r
1631 //==========================================================================
\r
1641 void StunObj(objtype *ob, objtype *shot, statetype *stunstate)
\r
1643 ExplodeShot(shot);
\r
1644 ob->temp1 = ob->temp2 = ob->temp3 = 0; // Note: ob->nothink should also be set to 0
\r
1645 ob->temp4 = ob->obclass;
\r
1646 ChangeState(ob, stunstate);
\r
1647 ob->obclass = stunnedobj;
\r
1650 if (ob->yspeed < -48)
\r
1655 //==========================================================================
\r
1665 void T_Projectile(objtype *ob)
\r
1668 xtry = ob->xspeed*tics;
\r
1675 = T_WeakProjectile
\r
1680 void T_WeakProjectile(objtype *ob)
\r
1682 DoWeakGravity(ob);
\r
1683 xtry = ob->xspeed*tics;
\r
1695 void T_Velocity(objtype *ob)
\r
1697 xtry = ob->xspeed*tics;
\r
1698 ytry = ob->yspeed*tics;
\r
1710 void SetReactThink(objtype *ob)
\r
1712 ob->needtoreact = true;
\r
1724 void T_Stunned(objtype *ob)
\r
1727 ob->needtoreact = true;
\r
1728 if (++ob->temp2 == 3)
\r
1741 void C_Lethal(objtype *ob, objtype *hit)
\r
1743 ob++; // shut up compiler
\r
1744 if (hit->obclass == keenobj)
\r
1759 void R_Draw(objtype *ob)
\r
1761 RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);
\r
1773 void R_Walk(objtype *ob)
\r
1775 if (ob->xdir == 1 && ob->hitwest)
\r
1777 ob->x -= ob->xmove;
\r
1779 ob->nothink = US_RndT() >> 5;
\r
1780 ChangeState(ob, ob->state);
\r
1782 else if (ob->xdir == -1 && ob->hiteast)
\r
1784 ob->x -= ob->xmove;
\r
1786 ob->nothink = US_RndT() >> 5;
\r
1787 ChangeState(ob, ob->state);
\r
1789 else if (!ob->hitnorth)
\r
1791 ob->x -= ob->xmove;
\r
1792 ob->xdir = -ob->xdir;
\r
1793 ob->nothink = US_RndT() >> 5;
\r
1794 ChangeState(ob, ob->state);
\r
1796 RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);
\r
1805 = Actor will not walk onto tiles with special (e.g. deadly) north walls
\r
1810 void R_WalkNormal(objtype *ob)
\r
1812 if (ob->xdir == 1 && ob->hitwest)
\r
1814 ob->x -= ob->xmove;
\r
1816 ob->nothink = US_RndT() >> 5;
\r
1817 ChangeState(ob, ob->state);
\r
1819 else if (ob->xdir == -1 && ob->hiteast)
\r
1821 ob->x -= ob->xmove;
\r
1823 ob->nothink = US_RndT() >> 5;
\r
1824 ChangeState(ob, ob->state);
\r
1826 else if (!ob->hitnorth || (ob->hitnorth & ~7))
\r
1828 ob->x -= ob->xmove*2;
\r
1829 ob->xdir = -ob->xdir;
\r
1830 ob->nothink = US_RndT() >> 5;
\r
1831 ChangeState(ob, ob->state);
\r
1833 RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);
\r
1845 void BadState(void)
\r
1847 Quit("Object with bad state!");
\r
1859 void R_Stunned(objtype *ob)
\r
1861 Sint16 starx, stary;
\r
1863 if (ob->hitwest || ob->hiteast)
\r
1871 ob->xspeed = ob->yspeed = 0;
\r
1872 if (ob->state->nextstate)
\r
1873 ChangeState(ob, ob->state->nextstate);
\r
1876 RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);
\r
1878 starx = stary = 0;
\r
1879 switch (ob->temp4)
\r
1883 stary = -4*PIXGLOBAL;
\r
1886 starx = 8*PIXGLOBAL;
\r
1887 stary = -8*PIXGLOBAL;
\r
1889 case treasureeaterobj:
\r
1890 starx = 8*PIXGLOBAL;
\r
1893 starx = 4*PIXGLOBAL;
\r
1894 stary = -8*PIXGLOBAL;
\r
1897 starx = 4*PIXGLOBAL;
\r
1898 stary = -350; // -21.875 pixels (this one is a bit strange)
\r
1901 stary = -8*PIXGLOBAL;
\r
1904 starx = -4*PIXGLOBAL;
\r
1905 stary = -8*PIXGLOBAL;
\r
1908 stary = -8*PIXGLOBAL;
\r
1910 #elif defined KEEN5
\r
1912 starx += 4*PIXGLOBAL;
\r
1915 stary -= 8*PIXGLOBAL;
\r
1916 asm jmp done; // just to recreate the original code's quirks, feel free to delete this
\r
1919 stary -= 8*PIXGLOBAL;
\r
1921 #elif defined KEEN6
\r
1922 case blooguardobj:
\r
1923 starx = 16*PIXGLOBAL;
\r
1924 stary = -4*PIXGLOBAL;
\r
1927 starx = 4*PIXGLOBAL;
\r
1928 stary = -4*PIXGLOBAL;
\r
1932 starx = 8*PIXGLOBAL;
\r
1933 stary = -4*PIXGLOBAL;
\r
1937 stary = -8*PIXGLOBAL;
\r
1940 starx = 16*PIXGLOBAL;
\r
1941 stary = 8*PIXGLOBAL;
\r
1944 stary = 12*PIXGLOBAL;
\r
1947 Quit("No star spec for object!");
\r
1952 ob->temp1 = ob->temp1 + tics;
\r
1953 if (ob->temp1 > 10)
\r
1956 if (++ob->temp2 == 3)
\r
1960 RF_PlaceSprite((void **)&ob->temp3, ob->x+starx, ob->y+stary, ob->temp2+STUNSTARS1SPR, spritedraw, 3);
\r
1963 //==========================================================================
\r
1965 statetype sc_deadstate = {0, 0, think, false, false, 0, 0, 0, NULL, NULL, NULL, NULL};
\r
1966 #pragma warn -sus //BadState is not a valid think/contact/react function. Nobody cares.
\r
1967 statetype sc_badstate = {0, 0, think, false, false, 0, 0, 0, &BadState, &BadState, &BadState, NULL};
\r