]> 4ch.mooo.com Git - 16.git/blob - 16/keen456/KEEN4-6/CK_STATE.C
extrcted keen code remake
[16.git] / 16 / keen456 / KEEN4-6 / CK_STATE.C
1 /* Reconstructed Commander Keen 4-6 Source Code\r
2  * Copyright (C) 2021 K1n9_Duk3\r
3  *\r
4  * This file is loosely based on:\r
5  * Keen Dreams Source Code\r
6  * Copyright (C) 2014 Javier M. Chavez\r
7  *\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
12  *\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
17  *\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
21  */\r
22 \r
23 #include "CK_DEF.H"\r
24 \r
25 /*\r
26 =============================================================================\r
27 \r
28                                                  GLOBAL VARIABLES\r
29 \r
30 =============================================================================\r
31 */\r
32 \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
42 };\r
43 \r
44 Sint16 xtry, ytry;\r
45 boolean playerkludgeclipcancel;\r
46 \r
47 /*\r
48 =============================================================================\r
49 \r
50                                                  LOCAL VARIABLES\r
51 \r
52 =============================================================================\r
53 */\r
54 \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
58 \r
59 //==========================================================================\r
60 \r
61 /*\r
62 ====================\r
63 =\r
64 = MoveObjVert\r
65 =\r
66 ====================\r
67 */\r
68 \r
69 void MoveObjVert(objtype *ob, Sint16 ymove)\r
70 {\r
71         ob->y += ymove;\r
72         ob->top += 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
76 }\r
77 \r
78 /*\r
79 ====================\r
80 =\r
81 = MoveObjHoriz\r
82 =\r
83 ====================\r
84 */\r
85 \r
86 void MoveObjHoriz(objtype *ob, Sint16 xmove)\r
87 {\r
88         //BUG? ob->midx is not adjusted in Keen 4 & 5\r
89         ob->x += xmove;\r
90         ob->left += xmove;\r
91         ob->right += xmove;\r
92 #ifdef KEEN6\r
93         ob->midx += xmove;      //BUG? ob->tilemidx is not updated\r
94 #endif\r
95         ob->tileleft = CONVERT_GLOBAL_TO_TILE(ob->left);\r
96         ob->tileright = CONVERT_GLOBAL_TO_TILE(ob->right);\r
97 }\r
98 \r
99 //==========================================================================\r
100 \r
101 /*\r
102 ====================\r
103 =\r
104 = PlayerBottomKludge\r
105 =\r
106 ====================\r
107 */\r
108 \r
109 void PlayerBottomKludge(objtype *ob)\r
110 {\r
111         Uint16 far *map;\r
112         Uint16 wall, clip, xpix;\r
113         Sint16 xmove, ymove;\r
114 \r
115         map = (Uint16 far *)mapsegs[1] + mapbwidthtable[ob->tilebottom-1]/2;\r
116         if (ob->xdir == 1)\r
117         {\r
118                 xpix = 0;\r
119                 map += ob->tileright;\r
120                 xmove = ob->right - ob->midx;\r
121                 if (tinf[*(map-mapwidth)+WESTWALL] || tinf[*map+WESTWALL])\r
122                 {\r
123                         return;\r
124                 }\r
125         }\r
126         else\r
127         {\r
128                 xpix = 15;\r
129                 map += ob->tileleft;\r
130                 xmove = ob->left - ob->midx;\r
131                 if (tinf[*(map-mapwidth)+EASTWALL] || tinf[*map+EASTWALL])\r
132                 {\r
133                         return;\r
134                 }\r
135         }\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
137         {\r
138                 return;\r
139         }\r
140         map += mapwidth;\r
141         if ((wall = tinf[*map+NORTHWALL]) != 1)\r
142         {\r
143                 return;\r
144         }\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
148         {\r
149                 ob->hitnorth = wall;\r
150                 MoveObjVert(ob, ymove);\r
151                 MoveObjHoriz(ob, xmove);\r
152         }\r
153 }\r
154 \r
155 /*\r
156 ====================\r
157 =\r
158 = PlayerTopKludge\r
159 =\r
160 ====================\r
161 */\r
162 \r
163 void PlayerTopKludge(objtype *ob)\r
164 {\r
165         Uint16 far *map;\r
166         Uint16 xpix, wall, clip;\r
167         Sint16 move;\r
168 \r
169         map = (Uint16 far *)mapsegs[1] + mapbwidthtable[ob->tiletop+1]/2;\r
170         if (ob->xdir == 1)\r
171         {\r
172                 xpix = 0;\r
173                 map += ob->tileright;\r
174                 if (tinf[*(map+mapwidth)+WESTWALL] || tinf[*(map+2*mapwidth)+WESTWALL])\r
175                 {\r
176                         return;\r
177                 }\r
178         }\r
179         else\r
180         {\r
181                 xpix = 15;\r
182                 map += ob->tileleft;\r
183                 if (tinf[*(map+mapwidth)+EASTWALL] || tinf[*(map+2*mapwidth)+EASTWALL])\r
184                 {\r
185                         return;\r
186                 }\r
187         }\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
189         {\r
190                 return;\r
191         }\r
192         map -= mapwidth;\r
193         if ((wall = tinf[*map+SOUTHWALL]) != 0)\r
194         {\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
198                 {\r
199                         ob->hitsouth = wall;\r
200                         MoveObjVert(ob, move);\r
201                 }\r
202         }\r
203 }\r
204 \r
205 /*\r
206 ===========================\r
207 =\r
208 = ClipToEnds\r
209 =\r
210 ===========================\r
211 */\r
212 \r
213 void ClipToEnds(objtype *ob)\r
214 {\r
215         Uint16 far *map;\r
216         Uint16 wall, y, clip;\r
217         Sint16 totalmove, maxmove, move;\r
218         Uint16 midxpix;\r
219         \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
224         {\r
225                 if ((wall = tinf[*map + NORTHWALL]) != 0)\r
226                 {\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
230                         {\r
231                                 ob->hitnorth = wall;\r
232                                 MoveObjVert(ob, move);\r
233                                 return;\r
234                         }\r
235                 }\r
236         }\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
240         {\r
241                 if ((wall = tinf[*map + SOUTHWALL]) != 0)\r
242                 {\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
246                         {\r
247                                 totalmove = ytry+move;\r
248                                 if (totalmove < 0x100 && totalmove > -0x100)\r
249                                 {\r
250                                         ob->hitsouth = wall;\r
251                                         MoveObjVert(ob, move);\r
252                                         //BUG? no return here\r
253                                 }\r
254                         }\r
255                 }\r
256         }\r
257 }\r
258 \r
259 /*\r
260 ===========================\r
261 =\r
262 = ClipToSides\r
263 =\r
264 ===========================\r
265 */\r
266 \r
267 void ClipToSides(objtype *ob)\r
268 {\r
269         Sint16 move, y, top, bottom;\r
270         Uint16 far *map;\r
271         \r
272         top = ob->tiletop;\r
273         if (ob->hitsouth > 1)\r
274         {\r
275                 top++;\r
276         }\r
277         bottom = ob->tilebottom;\r
278         if (ob->hitnorth > 1)\r
279         {\r
280                 bottom--;\r
281         }\r
282         for (y=top; y<=bottom; y++)\r
283         {\r
284                 map = (Uint16 far *)mapsegs[1] + mapbwidthtable[y]/2 + ob->tileleft;\r
285                 if ((ob->hiteast = tinf[*map+EASTWALL]) != 0)\r
286                 {\r
287                         move = CONVERT_TILE_TO_GLOBAL(ob->tileleft+1) - ob->left;\r
288                         MoveObjHoriz(ob, move);\r
289                         return;\r
290                 }\r
291         }\r
292         for (y=top; y<=bottom; y++)\r
293         {\r
294                 map = (Uint16 far *)mapsegs[1] + mapbwidthtable[y]/2 + ob->tileright;\r
295                 if ((ob->hitwest = tinf[*map+WESTWALL]) != 0)\r
296                 {\r
297                         move = (CONVERT_TILE_TO_GLOBAL(ob->tileright)-1)-ob->right;\r
298                         MoveObjHoriz(ob, move);\r
299                         return;\r
300                 }\r
301         }\r
302 }\r
303 \r
304 /*\r
305 ===========================\r
306 =\r
307 = CheckPosition\r
308 =\r
309 ===========================\r
310 */\r
311 \r
312 boolean CheckPosition(objtype *ob)\r
313 {\r
314 #ifdef KEEN6Ev15\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
318 \r
319         Uint16 tile, x, tileright;\r
320         Uint16 far *map;\r
321         Uint16 rowdiff;\r
322         Uint16 tileleft, y, tilebottom;\r
323         \r
324         map = (Uint16 far *)mapsegs[1] + mapbwidthtable[ob->tiletop]/2 + ob->tileleft;\r
325         rowdiff = mapwidth-(ob->tileright-ob->tileleft+1);\r
326 \r
327         y = ob->tiletop;\r
328         tileleft = ob->tileleft;\r
329         tileright = _AX = ob->tileright;\r
330         tilebottom = ob->tilebottom;\r
331 \r
332         for (; tilebottom>=y; y++,map+=rowdiff)\r
333         {\r
334                 for (x=tileleft; tileright>=x; x++)\r
335                 {\r
336                         tile = *(map++);\r
337                         if (tinf[tile+NORTHWALL] || tinf[tile+EASTWALL] || tinf[tile+SOUTHWALL] || tinf[tile+WESTWALL])\r
338                         {\r
339                                 return false;\r
340                         }\r
341                 }\r
342         }\r
343         return true;\r
344 #else\r
345         Uint16 tile, x, y;\r
346         Uint16 far *map;\r
347         Uint16 rowdiff;\r
348         \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
352         {\r
353                 for (x=ob->tileleft; x<=ob->tileright; x++)\r
354                 {\r
355                         tile = *(map++);\r
356                         if (tinf[tile+NORTHWALL] || tinf[tile+EASTWALL] || tinf[tile+SOUTHWALL] || tinf[tile+WESTWALL])\r
357                         {\r
358                                 return false;\r
359                         }\r
360                 }\r
361         }\r
362         return true;\r
363 #endif\r
364 }\r
365 \r
366 /*\r
367 ===========================\r
368 =\r
369 = StatePositionOk\r
370 =\r
371 ===========================\r
372 */\r
373 \r
374 boolean StatePositionOk(objtype *ob, statetype *state)\r
375 {\r
376         spritetabletype far *shape;\r
377 \r
378         if (ob->xdir > 0)\r
379         {\r
380                 ob->shapenum = state->rightshapenum;\r
381         }\r
382         else\r
383         {\r
384                 ob->shapenum = state->leftshapenum;\r
385         }\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
398 }\r
399 \r
400 #ifdef KEEN5\r
401 /*\r
402 ===========================\r
403 =\r
404 = CalcBounds\r
405 =\r
406 ===========================\r
407 */\r
408 \r
409 void CalcBounds(objtype *ob)    //not present in Keen 4 & 6\r
410 {\r
411         spritetabletype far *shape;\r
412 \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
419 }\r
420 #endif\r
421 \r
422 //==========================================================================\r
423 \r
424 /*\r
425 ================\r
426 =\r
427 = ClipToWalls\r
428 =\r
429 = Moves the current object xtry/ytry units, clipping to walls\r
430 =\r
431 ================\r
432 */\r
433 \r
434 void ClipToWalls(objtype *ob)\r
435 {\r
436         Uint16 oldx, oldy;\r
437 #ifdef KEEN6\r
438         Uint16 y;\r
439 #endif\r
440         spritetabletype far *shape;\r
441         boolean pushed;\r
442 \r
443         oldx = ob->x;\r
444         oldy = ob->y;\r
445         pushed = false;\r
446 \r
447 //\r
448 // make sure it stays in contact with a 45 degree slope\r
449 //\r
450         if (ob->state->pushtofloor)\r
451         {\r
452                 if (ob->hitnorth == 25)\r
453                 {\r
454                         ytry = 145;\r
455                 }\r
456                 else\r
457                 {\r
458                         if (xtry > 0)\r
459                         {\r
460                                 ytry = xtry+16;\r
461                         }\r
462                         else\r
463                         {\r
464                                 ytry = -xtry+16;\r
465                         }\r
466                         pushed = true;\r
467                 }\r
468         }\r
469 \r
470 //\r
471 // move the shape\r
472 //\r
473         if (xtry > 239)\r
474         {\r
475                 xtry = 239;\r
476         }\r
477         else if (xtry < -239)\r
478         {\r
479                 xtry = -239;\r
480         }\r
481         if (ytry > 255)                 // +16 for push to floor\r
482         {\r
483                 ytry = 255;\r
484         }\r
485         else if (ytry < -239)\r
486         {\r
487                 ytry = -239;\r
488         }\r
489 \r
490         ob->x += xtry;\r
491         ob->y += ytry;\r
492 \r
493         ob->needtoreact = true;\r
494 \r
495         if (!ob->shapenum)                              // can't get a hit rect with no shape!\r
496         {\r
497                 return;\r
498         }\r
499 \r
500         shape = &spritetable[ob->shapenum-STARTSPRITES];\r
501 \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
507 \r
508         oldright = ob->right;\r
509         oldtop = ob->top;\r
510         oldleft = ob->left;\r
511         oldbottom = ob->bottom;\r
512         oldmidx = ob->midx;\r
513 \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
519 \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
525 \r
526         ob->hitnorth=ob->hiteast=ob->hitsouth=ob->hitwest=0;\r
527 \r
528         if (ob->needtoclip)\r
529         {\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
535 \r
536         //\r
537         // clip it\r
538         //\r
539                 ClipToEnds(ob);\r
540 \r
541                 if (ob == player && !playerkludgeclipcancel)    // zero tolerance near the edge when player gets pushed!\r
542                 {\r
543                         if (!ob->hitnorth && bottommoved > 0)\r
544                         {\r
545                                 PlayerBottomKludge(ob);\r
546                         }\r
547                         if (!ob->hitsouth && topmoved < 0)\r
548                         {\r
549                                 PlayerTopKludge(ob);\r
550                         }\r
551                 }\r
552                 ClipToSides(ob);\r
553 \r
554 #ifdef KEEN6\r
555                 //\r
556                 // special hack to prevent player from getting stuck on slopes?\r
557                 //\r
558                 if (ob == player && (ob->hitnorth & 7) > 1 && (ob->hiteast || ob->hitwest))\r
559                 {\r
560                         Uint16 far *map;\r
561                         Uint16 pixx, clip, move;\r
562                         Uint16 wall;\r
563 \r
564                         pixx = CONVERT_GLOBAL_TO_PIXEL(ob->midx & (15*PIXGLOBAL));\r
565                         map = (Uint16 far *)mapsegs[1] + mapbwidthtable[oldtilebottom]/2 + ob->tilemidx;\r
566 \r
567                         for (y=oldtilebottom; ob->tilebottom+1 >= y; y++, map+=mapwidth)\r
568                         {\r
569                                 if ((wall = tinf[*map + NORTHWALL]) != 0)\r
570                                 {\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
575                                         return;\r
576                                 }\r
577                         }\r
578                 }\r
579 #endif\r
580         }\r
581         if (pushed && !ob->hitnorth)\r
582         {\r
583                 ob->y = oldy;\r
584                 ob->x = oldx + xtry;\r
585 \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
591 \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
597         }\r
598 \r
599         ob->xmove = ob->xmove + (ob->x - oldx);\r
600         ob->ymove = ob->ymove + (ob->y - oldy);\r
601 }\r
602 \r
603 /*\r
604 ================\r
605 =\r
606 = FullClipToWalls\r
607 =\r
608 = Moves the current object xtry/ytry units, clipping to walls\r
609 =\r
610 ================\r
611 */\r
612 \r
613 void FullClipToWalls(objtype *ob)\r
614 {\r
615         Uint16 oldx, oldy, w, h;\r
616         spritetabletype far *shape;\r
617 \r
618         oldx = ob->x;\r
619         oldy = ob->y;\r
620 \r
621 //\r
622 // move the shape\r
623 //\r
624         if (xtry > 239)\r
625         {\r
626                 xtry = 239;\r
627         }\r
628         else if (xtry < -239)\r
629         {\r
630                 xtry = -239;\r
631         }\r
632         if (ytry > 239)\r
633         {\r
634                 ytry = 239;\r
635         }\r
636         else if (ytry < -239)\r
637         {\r
638                 ytry = -239;\r
639         }\r
640 \r
641         ob->x += xtry;\r
642         ob->y += ytry;\r
643 \r
644         ob->needtoreact = true;\r
645 \r
646         shape = &spritetable[ob->shapenum-STARTSPRITES];\r
647 \r
648         switch (ob->obclass)\r
649         {\r
650 #if defined KEEN4\r
651         case keenobj:\r
652                 w = 40*PIXGLOBAL;\r
653                 h = 24*PIXGLOBAL;\r
654                 break;\r
655         case eggbirdobj:\r
656                 w = 64*PIXGLOBAL;\r
657                 h = 32*PIXGLOBAL;\r
658                 break;\r
659         case dopefishobj:\r
660                 w = 88*PIXGLOBAL;\r
661                 h = 64*PIXGLOBAL;\r
662                 break;\r
663         case schoolfishobj:\r
664                 w = 16*PIXGLOBAL;\r
665                 h = 8*PIXGLOBAL;\r
666                 break;\r
667 #elif defined KEEN5\r
668         case slicestarobj:\r
669         case spherefulobj:\r
670                 w = h = 32*PIXGLOBAL;\r
671                 break;\r
672 #elif defined KEEN6\r
673         case blorbobj:\r
674                 w = h = 32*PIXGLOBAL;\r
675                 break;\r
676 #endif\r
677 \r
678         default:\r
679                 Quit("FullClipToWalls: Bad obclass");\r
680                 break;\r
681         }\r
682 \r
683         ob->right = ob->x + w;\r
684         ob->left = ob->x;\r
685         ob->top = ob->y;\r
686         ob->bottom = ob->y + h;\r
687 \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
692 \r
693         ob->hitnorth=ob->hiteast=ob->hitsouth=ob->hitwest=0;\r
694 \r
695 //\r
696 // clip it\r
697 //\r
698         if (!CheckPosition(ob))\r
699         {\r
700                 MoveObjHoriz(ob, -xtry);        //undo x movement\r
701                 if (CheckPosition(ob))\r
702                 {\r
703                         if (xtry > 0)\r
704                         {\r
705                                 ob->hitwest = 1;\r
706                         }\r
707                         else\r
708                         {\r
709                                 ob->hiteast = 1;\r
710                         }\r
711                 }\r
712                 else\r
713                 {\r
714                         if (ytry > 0)\r
715                         {\r
716                                 ob->hitnorth = 1;\r
717                         }\r
718                         else\r
719                         {\r
720                                 ob->hitsouth = 1;\r
721                         }\r
722                         MoveObjHoriz(ob, xtry); //redo x movement\r
723                         MoveObjVert(ob, -ytry); //undo y movement\r
724                         if (!CheckPosition(ob))\r
725                         {\r
726                                 MoveObjHoriz(ob, -xtry);        //undo x movement\r
727                                 if (xtry > 0)\r
728                                 {\r
729                                         ob->hitwest = 1;\r
730                                 }\r
731                                 else\r
732                                 {\r
733                                         ob->hiteast = 1;\r
734                                 }\r
735                         }\r
736                 }\r
737         }\r
738 \r
739         ob->xmove = ob->xmove + (ob->x - oldx);\r
740         ob->ymove = ob->ymove + (ob->y - oldy);\r
741 \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
747 }\r
748 \r
749 /*\r
750 ================\r
751 =\r
752 = PushObj\r
753 =\r
754 = Moves the current object xtry/ytry units, clipping to walls\r
755 =\r
756 ================\r
757 */\r
758 \r
759 void PushObj(objtype *ob)\r
760 {\r
761         Uint16 oldx, oldy;\r
762         spritetabletype far *shape;\r
763         \r
764         oldx = ob->x;\r
765         oldy = ob->y;\r
766 \r
767 //\r
768 // move the shape\r
769 //\r
770         ob->x += xtry;\r
771         ob->y += ytry;\r
772 \r
773         ob->needtoreact = true;\r
774 \r
775         if (!ob->shapenum)                              // can't get a hit rect with no shape!\r
776         {\r
777                 return;\r
778         }\r
779 \r
780         shape = &spritetable[ob->shapenum-STARTSPRITES];\r
781 \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
787 \r
788         oldright = ob->right;\r
789         oldtop = ob->top;\r
790         oldleft = ob->left;\r
791         oldbottom = ob->bottom;\r
792         oldmidx = ob->midx;\r
793 \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
799 \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
805 \r
806         if (ob->needtoclip)\r
807         {\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
813 \r
814                 ClipToEnds(ob);\r
815                 ClipToSides(ob);\r
816         }\r
817 \r
818         ob->xmove = ob->xmove + (ob->x - oldx);\r
819         ob->ymove = ob->ymove + (ob->y - oldy);\r
820 }\r
821 \r
822 //==========================================================================\r
823 \r
824 \r
825 /*\r
826 ==================\r
827 =\r
828 = ClipToSpriteSide\r
829 =\r
830 = Clips push to solid\r
831 =\r
832 ==================\r
833 */\r
834 \r
835 void ClipToSpriteSide(objtype *push, objtype *solid)\r
836 {\r
837         Sint16 xmove, leftinto, rightinto;\r
838 \r
839         //\r
840         // amount the push shape can be pushed\r
841         //\r
842         xmove = solid->xmove - push->xmove;\r
843 \r
844         //\r
845         // amount it is inside\r
846         //\r
847         leftinto = solid->right - push->left;\r
848         rightinto = push->right - solid->left;\r
849 \r
850         if (leftinto > 0 && leftinto <= xmove)\r
851         {\r
852                 xtry = leftinto;\r
853                 if (push->state->pushtofloor)\r
854                 {\r
855                         ytry = leftinto+16;\r
856                 }\r
857                 ClipToWalls(push);\r
858                 push->hiteast = 1;\r
859         }\r
860         else if (rightinto > 0 && rightinto <= -xmove)\r
861         {\r
862                 xtry = -rightinto;\r
863                 if (push->state->pushtofloor)\r
864                 {\r
865                         ytry = rightinto+16;\r
866                 }\r
867                 ClipToWalls(push);\r
868                 push->hitwest = 1;\r
869         }\r
870 }\r
871 \r
872 //==========================================================================\r
873 \r
874 \r
875 /*\r
876 ==================\r
877 =\r
878 = ClipToSpriteTop\r
879 =\r
880 = Clips push to solid\r
881 =\r
882 ==================\r
883 */\r
884 \r
885 void ClipToSpriteTop(objtype *push, objtype *solid)\r
886 {\r
887         Sint16 temp, ymove, bottominto;\r
888 \r
889         //\r
890         // amount the push shape can be pushed\r
891         //\r
892         ymove = push->ymove - solid->ymove;\r
893 \r
894         //\r
895         // amount it is inside\r
896         //\r
897         bottominto = push->bottom - solid->top;\r
898 \r
899         if (bottominto >= 0 && bottominto <= ymove)\r
900         {\r
901                 if (push == player)\r
902                 {\r
903                         gamestate.riding = solid;\r
904                 }\r
905                 ytry = -bottominto;\r
906                 temp = push->state->pushtofloor;\r
907                 push->state->pushtofloor = false;\r
908                 ClipToWalls(push);\r
909                 push->state->pushtofloor = temp;\r
910                 if (!push->hitsouth)\r
911                 {\r
912                         push->hitnorth = 25;\r
913                 }\r
914         }\r
915 }\r
916 \r
917 //==========================================================================\r
918 \r
919 \r
920 /*\r
921 ==================\r
922 =\r
923 = ClipToSprite\r
924 =\r
925 = Clips push to solid\r
926 =\r
927 ==================\r
928 */\r
929 \r
930 void ClipToSprite(objtype *push, objtype *solid, boolean squish)\r
931 {\r
932         Sint16 xmove, ymove, leftinto, rightinto, topinto, bottominto;\r
933         \r
934         xmove = solid->xmove - push->xmove;\r
935         xtry = ytry = 0;\r
936 \r
937         //\r
938         // left / right\r
939         //\r
940         leftinto = solid->right - push->left;\r
941         rightinto = push->right - solid->left;\r
942 \r
943         if (leftinto > 0 && xmove+1 >= leftinto)\r
944         {\r
945                 xtry = leftinto;\r
946                 push->xspeed = 0;\r
947                 PushObj(push);\r
948                 if (squish && push->hitwest)\r
949                 {\r
950                         KillKeen();\r
951                 }\r
952                 push->hiteast = 1;\r
953                 return;\r
954         }\r
955         else if (rightinto > 0 && -xmove+1 >= rightinto)\r
956         {\r
957                 xtry = -rightinto;\r
958                 push->xspeed = 0;\r
959                 PushObj(push);\r
960                 if (squish && push->hiteast)\r
961                 {\r
962                         KillKeen();\r
963                 }\r
964                 push->hitwest = 1;\r
965                 return;\r
966         }\r
967 \r
968         //\r
969         // top / bottom\r
970         //\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
975         {\r
976                 if (push == player)\r
977                 {\r
978                         gamestate.riding = solid;\r
979                 }\r
980                 ytry = -bottominto;\r
981                 PushObj(push);\r
982                 if (squish && push->hitsouth)\r
983                 {\r
984                         KillKeen();\r
985                 }\r
986                 if (!push->hitsouth)\r
987                 {\r
988                         push->hitnorth = 25;\r
989                 }\r
990                 return;\r
991         }\r
992         else if (topinto >= 0 && topinto <= ymove)      // BUG: should be 'topinto <= -ymove'\r
993         {\r
994                 ytry = topinto;\r
995                 ClipToWalls(push);\r
996                 if (squish && push->hitnorth)\r
997                 {\r
998                         KillKeen();\r
999                 }\r
1000                 push->hitsouth = 25;\r
1001         }\r
1002 }\r
1003 \r
1004 //==========================================================================\r
1005 \r
1006 \r
1007 /*\r
1008 ==================\r
1009 =\r
1010 = DoActor\r
1011 =\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
1015 =\r
1016 ==================\r
1017 */\r
1018 \r
1019 Sint16 DoActor(objtype *ob, Sint16 numtics)\r
1020 {\r
1021         Sint16 ticcount, usedtics, excesstics;\r
1022         statetype *state;\r
1023         \r
1024         state = ob->state;\r
1025 \r
1026         if (state->progress == think)\r
1027         {\r
1028                 if (state->think)\r
1029                 {\r
1030                         if (ob->nothink)\r
1031                         {\r
1032                                 ob->nothink--;\r
1033                         }\r
1034                         else\r
1035                         {\r
1036                                 state->think(ob);\r
1037                         }\r
1038                 }\r
1039                 return 0;\r
1040         }\r
1041 \r
1042         ticcount = ob->ticcount + numtics;\r
1043 \r
1044         if (state->tictime > ticcount || state->tictime == 0)\r
1045         {\r
1046                 ob->ticcount = ticcount;\r
1047                 if (state->progress == slide || state->progress == slidethink)\r
1048                 {\r
1049                         if (ob->xdir)\r
1050                         {\r
1051                                 xtry += ob->xdir == 1? numtics*state->xmove : -numtics*state->xmove;\r
1052                         }\r
1053                         if (ob->ydir)\r
1054                         {\r
1055                                 ytry += ob->ydir == 1? numtics*state->ymove : -numtics*state->ymove;\r
1056                         }\r
1057                 }\r
1058                 if ((state->progress == slidethink || state->progress == stepthink) && state->think)\r
1059                 {\r
1060                         if (ob->nothink)\r
1061                         {\r
1062                                 ob->nothink--;\r
1063                         }\r
1064                         else\r
1065                         {\r
1066                                 state->think(ob);\r
1067                         }\r
1068                 }\r
1069                 return 0;\r
1070         }\r
1071         else\r
1072         {\r
1073                 usedtics = state->tictime - ob->ticcount;\r
1074                 excesstics = ticcount - state->tictime;\r
1075                 ob->ticcount = 0;\r
1076                 if (state->progress == slide || state->progress == slidethink)\r
1077                 {\r
1078                         if (ob->xdir)\r
1079                         {\r
1080                                 xtry += ob->xdir == 1? usedtics*state->xmove : -usedtics*state->xmove;\r
1081                         }\r
1082                         if (ob->ydir)\r
1083                         {\r
1084                                 ytry += ob->ydir == 1? usedtics*state->ymove : -usedtics*state->ymove;\r
1085                         }\r
1086                 }\r
1087                 else\r
1088                 {\r
1089                         if (ob->xdir)\r
1090                         {\r
1091                                 xtry += ob->xdir == 1? state->xmove : -state->xmove;\r
1092                         }\r
1093                         if (ob->ydir)\r
1094                         {\r
1095                                 ytry += ob->ydir == 1? state->ymove : -state->ymove;\r
1096                         }\r
1097                 }\r
1098 \r
1099                 if (state->think)\r
1100                 {\r
1101                         if (ob->nothink)\r
1102                         {\r
1103                                 ob->nothink--;\r
1104                         }\r
1105                         else\r
1106                         {\r
1107                                 state->think(ob);\r
1108                         }\r
1109                 }\r
1110 \r
1111                 if (state == ob->state)\r
1112                 {\r
1113                         ob->state = state->nextstate;   // go to next state\r
1114                 }\r
1115                 else if (!ob->state)\r
1116                 {\r
1117                         return 0;                       // object removed itself\r
1118                 }\r
1119                 return excesstics;\r
1120         }\r
1121 }\r
1122 \r
1123 //==========================================================================\r
1124 \r
1125 \r
1126 /*\r
1127 ====================\r
1128 =\r
1129 = StateMachine\r
1130 =\r
1131 = Change state and give directions\r
1132 =\r
1133 ====================\r
1134 */\r
1135 \r
1136 void StateMachine(objtype *ob)\r
1137 {\r
1138         Sint16 excesstics, oldshapenum;\r
1139         statetype *state;\r
1140         \r
1141         ob->xmove=ob->ymove=xtry=ytry=0;\r
1142         oldshapenum = ob->shapenum;\r
1143 \r
1144         state = ob->state;\r
1145 \r
1146         excesstics = DoActor(ob, tics);\r
1147         if (ob->state != state)\r
1148         {\r
1149                 ob->ticcount = 0;               // start the new state at 0, then use excess\r
1150                 state = ob->state;\r
1151         }\r
1152 \r
1153         while (excesstics)\r
1154         {\r
1155         //\r
1156         // passed through to next state\r
1157         //\r
1158                 if (!state->skippable && state->tictime <= excesstics)\r
1159                 {\r
1160                         excesstics = DoActor(ob, state->tictime-1);\r
1161                 }\r
1162                 else\r
1163                 {\r
1164                         excesstics = DoActor(ob, excesstics);\r
1165                 }\r
1166                 if (ob->state != state)\r
1167                 {\r
1168                         ob->ticcount = 0;               // start the new state at 0, then use excess\r
1169                         state = ob->state;\r
1170                 }\r
1171         }\r
1172 \r
1173         if (!state)                     // object removed itself\r
1174         {\r
1175                 RemoveObj(ob);\r
1176                 return;\r
1177         }\r
1178 \r
1179         //\r
1180         // if state->rightshapenum == NULL, the state does not have a standard\r
1181         // shape (the think routine should have set it)\r
1182         //\r
1183         if (state->rightshapenum)\r
1184         {\r
1185                 if (ob->xdir > 0)\r
1186                 {\r
1187                         ob->shapenum = state->rightshapenum;\r
1188                 }\r
1189                 else\r
1190                 {\r
1191                         ob->shapenum = state->leftshapenum;\r
1192                 }\r
1193         }\r
1194         if ((Sint16)ob->shapenum == -1)\r
1195         {\r
1196                 ob->shapenum = 0;               // make it invisable this time\r
1197         }\r
1198 \r
1199         if (xtry || ytry || ob->shapenum != oldshapenum || ob->hitnorth == 25)\r
1200         {\r
1201         //\r
1202         // actor moved or changed shape\r
1203         // make sure the movement is within limits (one tile)\r
1204         //\r
1205                 if (ob->needtoclip == cl_fullclip)\r
1206                 {\r
1207                         FullClipToWalls(ob);\r
1208                 }\r
1209                 else\r
1210                 {\r
1211                         ClipToWalls(ob);\r
1212                 }\r
1213         }\r
1214 }\r
1215 \r
1216 //==========================================================================\r
1217 \r
1218 \r
1219 /*\r
1220 ====================\r
1221 =\r
1222 = NewState\r
1223 =\r
1224 ====================\r
1225 */\r
1226 \r
1227 void NewState(objtype *ob, statetype *state)\r
1228 {\r
1229         Sint16 temp;\r
1230         \r
1231         ob->state = state;\r
1232 \r
1233         if (state->rightshapenum)\r
1234         {\r
1235                 if (ob->xdir > 0)\r
1236                 {\r
1237                         ob->shapenum = state->rightshapenum;\r
1238                 }\r
1239                 else\r
1240                 {\r
1241                         ob->shapenum = state->leftshapenum;\r
1242                 }\r
1243         }\r
1244 \r
1245         if ((Sint16)ob->shapenum == -1)\r
1246         {\r
1247                 ob->shapenum = 0;\r
1248         }\r
1249 \r
1250         temp = ob->needtoclip;\r
1251         ob->needtoclip = cl_noclip;\r
1252 \r
1253         xtry = ytry = 0;                                        // no movement\r
1254         ClipToWalls(ob);                                        // just calculate values\r
1255 \r
1256         ob->needtoclip = temp;\r
1257 \r
1258         if (ob->needtoclip == cl_fullclip)\r
1259         {\r
1260                 FullClipToWalls(ob);\r
1261         }\r
1262         else if (ob->needtoclip == cl_midclip)\r
1263         {\r
1264                 ClipToWalls(ob);\r
1265         }\r
1266 }\r
1267 \r
1268 //==========================================================================\r
1269 \r
1270 \r
1271 /*\r
1272 ====================\r
1273 =\r
1274 = ChangeState\r
1275 =\r
1276 ====================\r
1277 */\r
1278 \r
1279 void ChangeState(objtype *ob, statetype *state)\r
1280 {\r
1281         ob->state = state;\r
1282         ob->ticcount = 0;\r
1283         if (state->rightshapenum)\r
1284         {\r
1285                 if (ob->xdir > 0)\r
1286                 {\r
1287                         ob->shapenum = state->rightshapenum;\r
1288                 }\r
1289                 else\r
1290                 {\r
1291                         ob->shapenum = state->leftshapenum;\r
1292                 }\r
1293         }\r
1294 \r
1295         if ((Sint16)ob->shapenum == -1)\r
1296         {\r
1297                 ob->shapenum = 0;\r
1298         }\r
1299 \r
1300         ob->needtoreact = true;                 // it will need to be redrawn this frame\r
1301 \r
1302         xtry = ytry = 0;                                        // no movement\r
1303 \r
1304         if (ob->hitnorth != 25)\r
1305         {\r
1306                 ClipToWalls(ob);\r
1307         }\r
1308 }\r
1309 \r
1310 //==========================================================================\r
1311 \r
1312 \r
1313 /*\r
1314 ====================\r
1315 =\r
1316 = OnScreen\r
1317 =\r
1318 ====================\r
1319 */\r
1320 \r
1321 boolean OnScreen(objtype *ob)\r
1322 {\r
1323         if (ob->tileright < originxtile || ob->tilebottom < originytile\r
1324                 || ob->tileleft > originxtilemax || ob->tiletop > originytilemax)\r
1325         {\r
1326                 return false;\r
1327         }\r
1328         return true;\r
1329 }\r
1330 \r
1331 //==========================================================================\r
1332 \r
1333 \r
1334 /*\r
1335 ====================\r
1336 =\r
1337 = DoGravity\r
1338 =\r
1339 ====================\r
1340 */\r
1341 \r
1342 void DoGravity(objtype *ob)\r
1343 {\r
1344         Sint32 i;\r
1345 //\r
1346 // only accelerate on odd tics, because of limited precision\r
1347 //\r
1348         for (i = lasttimecount-tics; i<lasttimecount; i++)\r
1349         {\r
1350                 if (i&1)\r
1351                 {\r
1352                         if (ob->yspeed < 0 && ob->yspeed >= -4)\r
1353                         {\r
1354                                 ytry += ob->yspeed;\r
1355                                 ob->yspeed = 0;\r
1356                                 return;\r
1357                         }\r
1358                         ob->yspeed += 4;\r
1359                         if (ob->yspeed > 70)\r
1360                         {\r
1361                                 ob->yspeed = 70;\r
1362                         }\r
1363                 }\r
1364                 ytry += ob->yspeed;\r
1365         }\r
1366 }\r
1367 \r
1368 \r
1369 /*\r
1370 ====================\r
1371 =\r
1372 = DoWeakGravity\r
1373 =\r
1374 ====================\r
1375 */\r
1376 \r
1377 void DoWeakGravity(objtype *ob)\r
1378 {\r
1379         Sint32 i;\r
1380 //\r
1381 // only accelerate on odd tics, because of limited precision\r
1382 //\r
1383         for (i = lasttimecount-tics; i<lasttimecount; i++)\r
1384         {\r
1385                 if (i&1)\r
1386                 {\r
1387                         if (ob->yspeed < 0 && ob->yspeed >= -3)\r
1388                         {\r
1389                                 ytry += ob->yspeed;\r
1390                                 ob->yspeed = 0;\r
1391                                 return;\r
1392                         }\r
1393                         ob->yspeed += 3;\r
1394                         if (ob->yspeed > 70)\r
1395                         {\r
1396                                 ob->yspeed = 70;\r
1397                         }\r
1398                 }\r
1399                 ytry += ob->yspeed;\r
1400         }\r
1401 }\r
1402 \r
1403 \r
1404 /*\r
1405 ====================\r
1406 =\r
1407 = DoTinyGravity\r
1408 =\r
1409 ====================\r
1410 */\r
1411 \r
1412 void DoTinyGravity(objtype *ob)\r
1413 {\r
1414         Sint32 i;\r
1415 //\r
1416 // only accelerate every 4 tics, because of limited precision\r
1417 //\r
1418         for (i = lasttimecount-tics; i<lasttimecount; i++)\r
1419         {\r
1420                 if (!i & 3)     //BUG: this is equal to ((!i) & 3), not (!(i & 3))\r
1421                 {\r
1422                         ob->yspeed++;\r
1423                         if (ob->yspeed > 70)\r
1424                         {\r
1425                                 ob->yspeed = 70;\r
1426                         }\r
1427                 }\r
1428                 ytry += ob->yspeed;\r
1429         }\r
1430 }\r
1431 \r
1432 \r
1433 /*\r
1434 ===============\r
1435 =\r
1436 = AccelerateX\r
1437 =\r
1438 ===============\r
1439 */\r
1440 \r
1441 void AccelerateX(objtype *ob, Sint16 dir, Sint16 maxspeed)\r
1442 {\r
1443         Sint32 i;\r
1444         Uint16 oldsign;\r
1445         \r
1446         oldsign = ob->xspeed & 0x8000;\r
1447 //\r
1448 // only accelerate on odd tics, because of limited precision\r
1449 //\r
1450         for (i=lasttimecount-tics; i<lasttimecount; i++)\r
1451         {\r
1452                 if (i & 1)\r
1453                 {\r
1454                         ob->xspeed += dir;\r
1455                         if ((ob->xspeed & 0x8000) != oldsign)\r
1456                         {\r
1457                                 oldsign = ob->xspeed & 0x8000;\r
1458                                 ob->xdir = oldsign? -1 : 1;\r
1459                         }\r
1460                         if (ob->xspeed > maxspeed)\r
1461                         {\r
1462                                 ob->xspeed = maxspeed;\r
1463                         }\r
1464                         else if (ob->xspeed < -maxspeed)\r
1465                         {\r
1466                                 ob->xspeed = -maxspeed;\r
1467                         }\r
1468                 }\r
1469                 xtry += ob->xspeed;\r
1470         }\r
1471 }\r
1472 \r
1473 \r
1474 /*\r
1475 ===============\r
1476 =\r
1477 = AccelerateXv\r
1478 =\r
1479 = Doesn't change object's xdir\r
1480 =\r
1481 ===============\r
1482 */\r
1483 \r
1484 void AccelerateXv(objtype *ob, Sint16 dir, Sint16 maxspeed)\r
1485 {\r
1486         Sint32 i;\r
1487 \r
1488 //\r
1489 // only accelerate on odd tics, because of limited precision\r
1490 //\r
1491         for (i=lasttimecount-tics; i<lasttimecount; i++)\r
1492         {\r
1493                 if (i & 1)\r
1494                 {\r
1495                         ob->xspeed += dir;\r
1496                         if (ob->xspeed > maxspeed)\r
1497                         {\r
1498                                 ob->xspeed = maxspeed;\r
1499                         }\r
1500                         else if (ob->xspeed < -maxspeed)\r
1501                         {\r
1502                                 ob->xspeed = -maxspeed;\r
1503                         }\r
1504                 }\r
1505                 xtry += ob->xspeed;\r
1506         }\r
1507 }\r
1508 \r
1509 \r
1510 /*\r
1511 ===============\r
1512 =\r
1513 = AccelerateY\r
1514 =\r
1515 ===============\r
1516 */\r
1517 \r
1518 void AccelerateY(objtype *ob, Sint16 dir, Sint16 maxspeed)\r
1519 {\r
1520         Sint32 i;\r
1521 \r
1522 //\r
1523 // only accelerate on odd tics, because of limited precision\r
1524 //\r
1525         for (i=lasttimecount-tics; i<lasttimecount; i++)\r
1526         {\r
1527                 if (i & 1)\r
1528                 {\r
1529                         ob->yspeed += dir;\r
1530                         if (ob->yspeed > maxspeed)\r
1531                         {\r
1532                                 ob->yspeed = maxspeed;\r
1533                         }\r
1534                         else if (ob->yspeed < -maxspeed)\r
1535                         {\r
1536                                 ob->yspeed = -maxspeed;\r
1537                         }\r
1538                 }\r
1539                 ytry += ob->yspeed;\r
1540         }\r
1541 }\r
1542 \r
1543 \r
1544 /*\r
1545 ===============\r
1546 =\r
1547 = FrictionX\r
1548 =\r
1549 ===============\r
1550 */\r
1551 \r
1552 void FrictionX(objtype *ob)\r
1553 {\r
1554         Sint16 friction, oldsign;\r
1555         Sint32 i;\r
1556 \r
1557         oldsign = ob->xspeed & 0x8000;\r
1558         if (ob->xspeed > 0)\r
1559         {\r
1560                 friction = -1;\r
1561         }\r
1562         else if (ob->xspeed < 0)\r
1563         {\r
1564                 friction = 1;\r
1565         }\r
1566         else\r
1567         {\r
1568                 friction = 0;\r
1569         }\r
1570 //\r
1571 // only accelerate on odd tics, because of limited precision\r
1572 //\r
1573 \r
1574         for (i=lasttimecount-tics; i<lasttimecount; i++)\r
1575         {\r
1576                 if (i & 1)\r
1577                 {\r
1578                         ob->xspeed += friction;\r
1579                         if ((ob->xspeed & 0x8000) != oldsign)\r
1580                         {\r
1581                                 ob->xspeed = 0;\r
1582                         }\r
1583                 }\r
1584                 xtry += ob->xspeed;\r
1585         }\r
1586 }\r
1587 \r
1588 \r
1589 /*\r
1590 ===============\r
1591 =\r
1592 = FrictionY\r
1593 =\r
1594 ===============\r
1595 */\r
1596 \r
1597 void FrictionY(objtype *ob)\r
1598 {\r
1599         Sint16 friction, oldsign;\r
1600         Sint32 i;\r
1601 \r
1602         if (ob->yspeed > 0)\r
1603         {\r
1604                 friction = -1;\r
1605         }\r
1606         else if (ob->yspeed < 0)\r
1607         {\r
1608                 friction = 1;\r
1609         }\r
1610         else\r
1611         {\r
1612                 friction = 0;\r
1613         }\r
1614 //\r
1615 // only accelerate on odd tics, because of limited precision\r
1616 //\r
1617         for (i=lasttimecount-tics; i<lasttimecount; i++)\r
1618         {\r
1619                 if (i & 1)\r
1620                 {\r
1621                         ob->yspeed += friction;\r
1622                         if ((ob->yspeed & 0x8000) != oldsign)   //BUG: oldsign is not initialized!\r
1623                         {\r
1624                                 ob->yspeed = 0;\r
1625                         }\r
1626                 }\r
1627                 ytry += ob->yspeed;\r
1628         }\r
1629 }\r
1630 \r
1631 //==========================================================================\r
1632 \r
1633 /*\r
1634 ===============\r
1635 =\r
1636 = StunObj\r
1637 =\r
1638 ===============\r
1639 */\r
1640 \r
1641 void StunObj(objtype *ob, objtype *shot, statetype *stunstate)\r
1642 {\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
1648 #ifndef KEEN4\r
1649         ob->yspeed -= 24;\r
1650         if (ob->yspeed < -48)\r
1651                 ob->yspeed = -48;\r
1652 #endif\r
1653 }\r
1654 \r
1655 //==========================================================================\r
1656 \r
1657 /*\r
1658 ===============\r
1659 =\r
1660 = T_Projectile\r
1661 =\r
1662 ===============\r
1663 */\r
1664 \r
1665 void T_Projectile(objtype *ob)\r
1666 {\r
1667         DoGravity(ob);\r
1668         xtry = ob->xspeed*tics;\r
1669 }\r
1670 \r
1671 \r
1672 /*\r
1673 ===============\r
1674 =\r
1675 = T_WeakProjectile\r
1676 =\r
1677 ===============\r
1678 */\r
1679 \r
1680 void T_WeakProjectile(objtype *ob)\r
1681 {\r
1682         DoWeakGravity(ob);\r
1683         xtry = ob->xspeed*tics;\r
1684 }\r
1685 \r
1686 \r
1687 /*\r
1688 ===============\r
1689 =\r
1690 = T_Velocity\r
1691 =\r
1692 ===============\r
1693 */\r
1694 \r
1695 void T_Velocity(objtype *ob)\r
1696 {\r
1697         xtry = ob->xspeed*tics;\r
1698         ytry = ob->yspeed*tics;\r
1699 }\r
1700 \r
1701 \r
1702 /*\r
1703 ===============\r
1704 =\r
1705 = SetReactThink\r
1706 =\r
1707 ===============\r
1708 */\r
1709 \r
1710 void SetReactThink(objtype *ob)\r
1711 {\r
1712         ob->needtoreact = true;\r
1713 }\r
1714 \r
1715 \r
1716 /*\r
1717 ===============\r
1718 =\r
1719 = T_Stunned\r
1720 =\r
1721 ===============\r
1722 */\r
1723 \r
1724 void T_Stunned(objtype *ob)\r
1725 {\r
1726         ob->temp1 = 0;\r
1727         ob->needtoreact = true;\r
1728         if (++ob->temp2 == 3)\r
1729                 ob->temp2 = 0;\r
1730 }\r
1731 \r
1732 \r
1733 /*\r
1734 ===============\r
1735 =\r
1736 = C_Lethal\r
1737 =\r
1738 ===============\r
1739 */\r
1740 \r
1741 void C_Lethal(objtype *ob, objtype *hit)\r
1742 {\r
1743         ob++;                   // shut up compiler\r
1744         if (hit->obclass == keenobj)\r
1745         {\r
1746                 KillKeen();\r
1747         }\r
1748 }\r
1749 \r
1750 \r
1751 /*\r
1752 ===============\r
1753 =\r
1754 = R_Draw\r
1755 =\r
1756 ===============\r
1757 */\r
1758 \r
1759 void R_Draw(objtype *ob)\r
1760 {\r
1761         RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);\r
1762 }\r
1763 \r
1764 \r
1765 /*\r
1766 ===============\r
1767 =\r
1768 = R_Walk\r
1769 =\r
1770 ===============\r
1771 */\r
1772 \r
1773 void R_Walk(objtype *ob)\r
1774 {\r
1775         if (ob->xdir == 1 && ob->hitwest)\r
1776         {\r
1777                 ob->x -= ob->xmove;\r
1778                 ob->xdir = -1;\r
1779                 ob->nothink = US_RndT() >> 5;\r
1780                 ChangeState(ob, ob->state);\r
1781         }\r
1782         else if (ob->xdir == -1 && ob->hiteast)\r
1783         {\r
1784                 ob->x -= ob->xmove;\r
1785                 ob->xdir = 1;\r
1786                 ob->nothink = US_RndT() >> 5;\r
1787                 ChangeState(ob, ob->state);\r
1788         }\r
1789         else if (!ob->hitnorth)\r
1790         {\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
1795         }\r
1796         RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);\r
1797 }\r
1798 \r
1799 \r
1800 /*\r
1801 ===============\r
1802 =\r
1803 = R_WalkNormal\r
1804 =\r
1805 = Actor will not walk onto tiles with special (e.g. deadly) north walls\r
1806 =\r
1807 ===============\r
1808 */\r
1809 \r
1810 void R_WalkNormal(objtype *ob)\r
1811 {\r
1812         if (ob->xdir == 1 && ob->hitwest)\r
1813         {\r
1814                 ob->x -= ob->xmove;\r
1815                 ob->xdir = -1;\r
1816                 ob->nothink = US_RndT() >> 5;\r
1817                 ChangeState(ob, ob->state);\r
1818         }\r
1819         else if (ob->xdir == -1 && ob->hiteast)\r
1820         {\r
1821                 ob->x -= ob->xmove;\r
1822                 ob->xdir = 1;\r
1823                 ob->nothink = US_RndT() >> 5;\r
1824                 ChangeState(ob, ob->state);\r
1825         }\r
1826         else if (!ob->hitnorth || (ob->hitnorth & ~7))\r
1827         {\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
1832         }\r
1833         RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);\r
1834 }\r
1835 \r
1836 \r
1837 /*\r
1838 ===============\r
1839 =\r
1840 = BadState\r
1841 =\r
1842 ===============\r
1843 */\r
1844 \r
1845 void BadState(void)\r
1846 {\r
1847         Quit("Object with bad state!");\r
1848 }\r
1849 \r
1850 \r
1851 /*\r
1852 ===============\r
1853 =\r
1854 = R_Stunned\r
1855 =\r
1856 ===============\r
1857 */\r
1858 \r
1859 void R_Stunned(objtype *ob)\r
1860 {\r
1861         Sint16 starx, stary;\r
1862 \r
1863         if (ob->hitwest || ob->hiteast)\r
1864                 ob->xspeed = 0;\r
1865 \r
1866         if (ob->hitsouth)\r
1867                 ob->yspeed = 0;\r
1868 \r
1869         if (ob->hitnorth)\r
1870         {\r
1871                 ob->xspeed = ob->yspeed = 0;\r
1872                 if (ob->state->nextstate)\r
1873                         ChangeState(ob, ob->state->nextstate);\r
1874         }\r
1875 \r
1876         RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);\r
1877 \r
1878         starx = stary = 0;\r
1879         switch (ob->temp4)\r
1880         {\r
1881 #if defined KEEN4\r
1882         case mimrockobj:\r
1883                 stary = -4*PIXGLOBAL;\r
1884                 break;\r
1885         case eggobj:\r
1886                 starx = 8*PIXGLOBAL;\r
1887                 stary = -8*PIXGLOBAL;\r
1888                 break;\r
1889         case treasureeaterobj:\r
1890                 starx = 8*PIXGLOBAL;\r
1891                 break;\r
1892         case bounderobj:\r
1893                 starx = 4*PIXGLOBAL;\r
1894                 stary = -8*PIXGLOBAL;\r
1895                 break;\r
1896         case wormouthobj:\r
1897                 starx = 4*PIXGLOBAL;\r
1898                 stary = -350;   // -21.875 pixels (this one is a bit strange)\r
1899                 break;\r
1900         case lickobj:\r
1901                 stary = -8*PIXGLOBAL;\r
1902                 break;\r
1903         case inchwormobj:\r
1904                 starx = -4*PIXGLOBAL;\r
1905                 stary = -8*PIXGLOBAL;\r
1906                 break;\r
1907         case slugobj:\r
1908                 stary = -8*PIXGLOBAL;\r
1909                 break;\r
1910 #elif defined KEEN5\r
1911         case sparkyobj:\r
1912                 starx += 4*PIXGLOBAL;\r
1913                 break;\r
1914         case amptonobj:\r
1915                 stary -= 8*PIXGLOBAL;\r
1916                 asm jmp done;           // just to recreate the original code's quirks, feel free to delete this\r
1917                 break;\r
1918         case scottieobj:\r
1919                 stary -= 8*PIXGLOBAL;\r
1920                 break;\r
1921 #elif defined KEEN6\r
1922         case blooguardobj:\r
1923                 starx = 16*PIXGLOBAL;\r
1924                 stary = -4*PIXGLOBAL;\r
1925                 break;\r
1926         case flectobj:\r
1927                 starx = 4*PIXGLOBAL;\r
1928                 stary = -4*PIXGLOBAL;\r
1929                 break;\r
1930         case bloogobj:\r
1931         case nospikeobj:\r
1932                 starx = 8*PIXGLOBAL;\r
1933                 stary = -4*PIXGLOBAL;\r
1934                 break;\r
1935         case bloogletobj:\r
1936         case babobbaobj:\r
1937                 stary = -8*PIXGLOBAL;\r
1938                 break;\r
1939         case fleexobj:\r
1940                 starx = 16*PIXGLOBAL;\r
1941                 stary = 8*PIXGLOBAL;\r
1942                 break;\r
1943         case ceilickobj:\r
1944                 stary = 12*PIXGLOBAL;\r
1945                 break;\r
1946         default:\r
1947                 Quit("No star spec for object!");\r
1948 #endif\r
1949         }\r
1950 done:\r
1951 \r
1952         ob->temp1 = ob->temp1 + tics;\r
1953         if (ob->temp1 > 10)\r
1954         {\r
1955                 ob->temp1 -= 10;\r
1956                 if (++ob->temp2 == 3)\r
1957                         ob->temp2 = 0;\r
1958         }\r
1959 \r
1960         RF_PlaceSprite((void **)&ob->temp3, ob->x+starx, ob->y+stary, ob->temp2+STUNSTARS1SPR, spritedraw, 3);\r
1961 }\r
1962 \r
1963 //==========================================================================\r
1964 \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
1968 #pragma warn +sus