]> 4ch.mooo.com Git - 16.git/blob - 16/keen456/KEEN4-6/CK_KEEN2.C
extrcted keen code remake
[16.git] / 16 / keen456 / KEEN4-6 / CK_KEEN2.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 /*\r
24 CK_KEEN2.C\r
25 ==========\r
26 \r
27 Contains the following actor types (in this order):\r
28 \r
29 - Score Box & Demo sprites\r
30 - Keen (world map)\r
31 - Flags (world map)\r
32 - Neural Stunner Shots\r
33 - Gem Door Opener\r
34 - Card Door Opener (Keen 5 only)\r
35 \r
36 */\r
37 \r
38 #include "CK_DEF.H"\r
39 \r
40 Direction opposite[8] = {dir_South, dir_SouthWest, dir_West, dir_NorthWest, dir_North, dir_NorthEast, dir_East, dir_SouthEast};\r
41 \r
42 /*\r
43 =============================================================================\r
44 \r
45                                                  SCORE BOX ROUTINES\r
46 \r
47 =============================================================================\r
48 */\r
49 \r
50 statetype s_score         = {  0,   0, think, false, false, 0, 0, 0, NULL, NULL, NULL, NULL};\r
51 statetype s_demo          = {DEMOPLAQUESPR, DEMOPLAQUESPR, think, false, false, 0, 0, 0, NULL, NULL, NULL, NULL};\r
52 \r
53 /*\r
54 ======================\r
55 =\r
56 = SpawnScore\r
57 =\r
58 ======================\r
59 */\r
60 \r
61 void SpawnScore(void)\r
62 {\r
63         scoreobj->obclass = inertobj;\r
64         scoreobj->priority = 3;\r
65         scoreobj->active = ac_allways;\r
66         scoreobj->needtoclip = cl_noclip;\r
67         scoreobj->temp2 = -1;\r
68         scoreobj->temp1 = -1;\r
69         scoreobj->temp3 = -1;\r
70         scoreobj->temp4 = -1;\r
71         if (scorescreenkludge)\r
72         {\r
73                 scoreobj->state = &sc_deadstate;\r
74         }\r
75         else if (!DemoMode)\r
76         {\r
77                 NewState(scoreobj, &s_score);\r
78         }\r
79         else\r
80         {\r
81                 NewState(scoreobj, &s_demo);\r
82                 CA_MarkGrChunk(DEMOPLAQUESPR);\r
83         }\r
84 }\r
85 \r
86 \r
87 // Taken from Keen Dreams: MemDrawChar and ShiftScore\r
88 \r
89 /*\r
90 ======================\r
91 =\r
92 = MemDrawChar\r
93 =\r
94 ======================\r
95 */\r
96 \r
97 #if GRMODE == EGAGR\r
98 \r
99 void MemDrawChar(Sint16 char8, Uint8 far *dest, Uint16 width, Uint16 planesize)\r
100 {\r
101         Uint16 source = (Uint16)grsegs[STARTTILE8];     // Note: this differs from Keen Dreams source\r
102 \r
103 asm     mov     si,[char8]\r
104 asm     shl     si,1\r
105 asm     shl     si,1\r
106 asm     shl     si,1\r
107 asm     shl     si,1\r
108 asm     shl     si,1            // index into char 8 segment\r
109 \r
110 asm     mov     ds,[WORD PTR source]    // Note: this differs from Keen Dreams source\r
111 asm     mov     es,[WORD PTR dest+2]\r
112 \r
113 asm     mov     cx,4            // draw four planes\r
114 asm     mov     bx,[width]\r
115 asm     dec     bx\r
116 \r
117 planeloop:\r
118 \r
119 asm     mov     di,[WORD PTR dest]\r
120 \r
121 asm     movsb\r
122 asm     add     di,bx\r
123 asm     movsb\r
124 asm     add     di,bx\r
125 asm     movsb\r
126 asm     add     di,bx\r
127 asm     movsb\r
128 asm     add     di,bx\r
129 asm     movsb\r
130 asm     add     di,bx\r
131 asm     movsb\r
132 asm     add     di,bx\r
133 asm     movsb\r
134 asm     add     di,bx\r
135 asm     movsb\r
136 \r
137 asm     mov     ax,[planesize]\r
138 asm     add     [WORD PTR dest],ax\r
139 \r
140 asm     loop    planeloop\r
141 \r
142 asm     mov     ax,ss\r
143 asm     mov     ds,ax\r
144 }\r
145 \r
146 #elif GRMODE == CGAGR\r
147 \r
148 void MemDrawChar (int char8,byte far *dest,unsigned width,unsigned planesize)\r
149 {\r
150 asm     mov     si,[char8]\r
151 asm     shl     si,1\r
152 asm     shl     si,1\r
153 asm     shl     si,1\r
154 asm     shl     si,1            // index into char 8 segment\r
155 \r
156 asm     mov     ds,[WORD PTR grsegs+STARTTILE8*2]\r
157 asm     mov     es,[WORD PTR dest+2]\r
158 \r
159 asm     mov     bx,[width]\r
160 asm     sub     bx,2\r
161 \r
162 asm     mov     di,[WORD PTR dest]\r
163 \r
164 asm     movsw\r
165 asm     add     di,bx\r
166 asm     movsw\r
167 asm     add     di,bx\r
168 asm     movsw\r
169 asm     add     di,bx\r
170 asm     movsw\r
171 asm     add     di,bx\r
172 asm     movsw\r
173 asm     add     di,bx\r
174 asm     movsw\r
175 asm     add     di,bx\r
176 asm     movsw\r
177 asm     add     di,bx\r
178 asm     movsw\r
179 \r
180 asm     mov     ax,ss\r
181 asm     mov     ds,ax\r
182 \r
183         planesize++;            // shut the compiler up\r
184 }\r
185 #endif\r
186 \r
187 /*\r
188 ====================\r
189 =\r
190 = ShiftScore\r
191 =\r
192 ====================\r
193 */\r
194 #if GRMODE == EGAGR\r
195 void ShiftScore (void)\r
196 {\r
197         spritetabletype far *spr;\r
198         spritetype _seg *dest;\r
199 \r
200         spr = &spritetable[SCOREBOXSPR-STARTSPRITES];\r
201         dest = (spritetype _seg *)grsegs[SCOREBOXSPR];\r
202 \r
203         CAL_ShiftSprite (FP_SEG(dest),dest->sourceoffset[0],\r
204                 dest->sourceoffset[1],spr->width,spr->height,2);\r
205 \r
206         CAL_ShiftSprite (FP_SEG(dest),dest->sourceoffset[0],\r
207                 dest->sourceoffset[2],spr->width,spr->height,4);\r
208 \r
209         CAL_ShiftSprite (FP_SEG(dest),dest->sourceoffset[0],\r
210                 dest->sourceoffset[3],spr->width,spr->height,6);\r
211 }\r
212 #endif\r
213 \r
214 /*\r
215 ===============\r
216 =\r
217 = UpdateScore\r
218 =\r
219 ===============\r
220 */\r
221 \r
222 void UpdateScore(objtype *ob)\r
223 {\r
224         char            str[10],*ch;\r
225         spritetype      _seg    *block;\r
226         Uint8           far *dest;\r
227         Uint16  i, length, width, planesize, number;\r
228         boolean changed;\r
229 \r
230         if (scorescreenkludge)\r
231                 return;\r
232 \r
233         if (DemoMode)\r
234         {\r
235                 DrawDemoPlaque(ob);\r
236                 return;\r
237         }\r
238 \r
239         if (!showscorebox)\r
240                 return;\r
241 \r
242         changed = false;\r
243 \r
244 //code below is a combination of ScoreThink and ScoreReact from Keen Dreams with minor changes\r
245 \r
246 //\r
247 // score changed\r
248 //\r
249         if ((gamestate.score>>16) != ob->temp1\r
250                 || (Uint16)gamestate.score != ob->temp2 )\r
251         {\r
252                 block = (spritetype _seg *)grsegs[SCOREBOXSPR];\r
253                 width = block->width[0];\r
254                 planesize = block->planesize[0];\r
255                 dest = (Uint8 far *)grsegs[SCOREBOXSPR]+block->sourceoffset[0]\r
256                         + planesize + width*4;\r
257 \r
258                 ltoa (gamestate.score,str,10);\r
259 \r
260                 // erase leading spaces\r
261                 length = strlen(str);\r
262                 for (i=9;i>length;i--)\r
263                         MemDrawChar (41,dest+=CHARWIDTH,width,planesize);\r
264 \r
265                 // draw digits\r
266                 ch = str;\r
267                 while (*ch)\r
268                         MemDrawChar (*ch++ - '0'+42,dest+=CHARWIDTH,width,planesize);\r
269 \r
270 #if GRMODE == EGAGR\r
271                 ShiftScore ();\r
272 #endif\r
273                 ob->needtoreact = true;\r
274                 ob->temp1 = gamestate.score>>16;\r
275                 ob->temp2 = gamestate.score;\r
276 \r
277                 changed = true;\r
278         }\r
279 \r
280 //\r
281 // ammo changed\r
282 //\r
283         number = gamestate.ammo;\r
284         if (number != ob->temp3)\r
285         {\r
286                 block = (spritetype _seg *)grsegs[SCOREBOXSPR];\r
287                 width = block->width[0];\r
288                 planesize = block->planesize[0];\r
289                 dest = (byte far *)grsegs[SCOREBOXSPR]+block->sourceoffset[0]\r
290                         + planesize + width*20 + 7*CHARWIDTH;\r
291 \r
292                 if (number > 99)\r
293                         strcpy (str,"99");\r
294                 else\r
295                         ltoa (number,str,10);\r
296 \r
297                 // erase leading spaces\r
298                 length = strlen(str);\r
299                 for (i=2;i>length;i--)\r
300                         MemDrawChar (41,dest+=CHARWIDTH,width,planesize);\r
301 \r
302                 // draw digits\r
303                 ch = str;\r
304                 while (*ch)\r
305                         MemDrawChar (*ch++ - '0'+42,dest+=CHARWIDTH,width,planesize);\r
306 \r
307 #if GRMODE == EGAGR\r
308                 ShiftScore ();\r
309 #endif\r
310                 ob->needtoreact = true;\r
311                 ob->temp3 = number;\r
312 \r
313                 changed = true;\r
314         }\r
315 \r
316 //\r
317 // lives changed\r
318 //\r
319         if (gamestate.lives != ob->temp4)\r
320         {\r
321                 block = (spritetype _seg *)grsegs[SCOREBOXSPR];\r
322                 width = block->width[0];\r
323                 planesize = block->planesize[0];\r
324                 dest = (byte far *)grsegs[SCOREBOXSPR]+block->sourceoffset[0]\r
325                         + planesize + width*20 + 2*CHARWIDTH;\r
326 \r
327                 if (gamestate.lives > 99)\r
328                         strcpy (str,"99");\r
329                 else\r
330                         ltoa (gamestate.lives,str,10);\r
331 \r
332                 // erase leading spaces\r
333                 length = strlen(str);\r
334                 for (i=2;i>length;i--)\r
335                         MemDrawChar (41,dest+=CHARWIDTH,width,planesize);\r
336 \r
337                 // draw digits\r
338                 ch = str;\r
339                 while (*ch)\r
340                         MemDrawChar (*ch++ - '0'+42,dest+=CHARWIDTH,width,planesize);\r
341 \r
342 #if GRMODE == EGAGR\r
343                 ShiftScore ();\r
344 #endif\r
345                 ob->needtoreact = true;\r
346                 ob->temp4 = gamestate.lives;\r
347 \r
348                 changed = true;\r
349         }\r
350 \r
351 /*\r
352 Note:\r
353 -----\r
354 \r
355 It would be more efficient to use\r
356 \r
357         if (changed)\r
358                 ShiftScore();\r
359 \r
360 here instead of the individual ShiftScore() calls above. Because if the player\r
361 gains a life by collecting points items, both the score and lives numbers need\r
362 to be updated, which means the sprite would be shifted twice. And if the player\r
363 fires a shot during the same frame, the ammo number also needs to be updated,\r
364 leading to up to three shifts in one frame.\r
365 */\r
366 \r
367         if (ob->x != originxglobal || ob->y != originyglobal)\r
368         {\r
369                 ob->x = originxglobal;\r
370                 ob->y = originyglobal;\r
371                 changed = true;\r
372         }\r
373 \r
374         if (changed)\r
375 #if GRMODE == EGAGR\r
376                 RF_PlaceSprite(&ob->sprite, ob->x+4*PIXGLOBAL, ob->y+4*PIXGLOBAL, SCOREBOXSPR, spritedraw, 3);\r
377 #elif GRMODE == CGAGR\r
378                 RF_PlaceSprite(&ob->sprite, ob->x+8*PIXGLOBAL, ob->y+8*PIXGLOBAL, SCOREBOXSPR, spritedraw, 3);\r
379 #endif\r
380 }\r
381 \r
382 /*\r
383 ===============\r
384 =\r
385 = DrawDemoPlaque\r
386 =\r
387 ===============\r
388 */\r
389 \r
390 void DrawDemoPlaque(objtype *ob)\r
391 {\r
392         if (ob->x != originxglobal || ob->y != originyglobal)\r
393         {\r
394                 ob->x = originxglobal;\r
395                 ob->y = originyglobal;\r
396                 RF_PlaceSprite(&ob->sprite, ob->x + 160*PIXGLOBAL - 32*PIXGLOBAL, ob->y + 8*PIXGLOBAL, DEMOPLAQUESPR, spritedraw, 3);\r
397         }\r
398 }\r
399 \r
400 \r
401 /*\r
402 =============================================================================\r
403 \r
404                                                            MINI KEEN\r
405 \r
406 player->temp1 = dir\r
407 player->temp2 = animation stage\r
408 \r
409 =============================================================================\r
410 */\r
411 \r
412 #ifdef KEEN4\r
413 statetype s_keenonfoot1 = {WOLRDKEENRIDE1SPR, WOLRDKEENRIDE1SPR, stepthink, false, false, 30, 0, 0, T_FootFly, NULL, R_Draw, &s_keenonfoot2};\r
414 statetype s_keenonfoot2 = {WOLRDKEENRIDE2SPR, WOLRDKEENRIDE2SPR, stepthink, false, false, 30, 0, 0, T_FootFly, NULL, R_Draw, &s_keenonfoot1};\r
415 statetype s_worldswim = {0, 0, slide, true, false, 6, 16, 16, T_KeenWorldSwim, NULL, R_Draw, &s_worldswim};\r
416 #endif\r
417 \r
418 #ifdef KEEN5\r
419 statetype s_worldelevate = {-1, -1, think, true, false, 6, 16, 16, T_Elevate, NULL, R_Draw, NULL};\r
420 #endif\r
421 \r
422 statetype s_worldkeen     = {0, 0, stepthink, false, false, 360, 0, 0, T_KeenWorld, NULL, R_Draw, &s_worldkeenwave1};\r
423 \r
424 statetype s_worldkeenwave1 = {WORLDKEENWAVE1SPR, WORLDKEENWAVE1SPR, stepthink, false, false, 20, 0, 0, T_KeenWorld, NULL, R_Draw, &s_worldkeenwave2};\r
425 statetype s_worldkeenwave2 = {WORLDKEENWAVE2SPR, WORLDKEENWAVE2SPR, stepthink, false, false, 20, 0, 0, T_KeenWorld, NULL, R_Draw, &s_worldkeenwave3};\r
426 statetype s_worldkeenwave3 = {WORLDKEENWAVE1SPR, WORLDKEENWAVE1SPR, stepthink, false, false, 20, 0, 0, T_KeenWorld, NULL, R_Draw, &s_worldkeenwave4};\r
427 statetype s_worldkeenwave4 = {WORLDKEENWAVE2SPR, WORLDKEENWAVE2SPR, stepthink, false, false, 20, 0, 0, T_KeenWorld, NULL, R_Draw, &s_worldkeenwave5};\r
428 statetype s_worldkeenwave5 = {WORLDKEENWAVE1SPR, WORLDKEENWAVE1SPR, stepthink, false, false, 20, 0, 0, T_KeenWorldWalk, NULL, R_Draw, &s_worldkeen};\r
429 \r
430 statetype s_worldkeenwalk = {0, 0, slide, true, false, 4, 24, 24, T_KeenWorldWalk, NULL, R_Draw, &s_worldkeenwalk};\r
431 \r
432 Sint16 worldshapes[8] = {WORLDKEENU1SPR-1, WORLDKEENUR1SPR-1, WORLDKEENR1SPR-1, WORLDKEENDR1SPR-1, WORLDKEEND1SPR-1, WORLDKEENDL1SPR-1, WORLDKEENL1SPR-1, WORLDKEENUL1SPR-1};   //-1 to everything because worldanims values are 1-based\r
433 Sint16 worldanims[4] = {2, 3, 1, 3};\r
434 #ifdef KEEN4\r
435 Sint16 swimshapes[8] = {WORLDKEENSWIMU1SPR, WORLDKEENSWIMUR1SPR, WORLDKEENSWIMR1SPR, WORLDKEENSWIMDR1SPR, WORLDKEENSWIMD1SPR, WORLDKEENSWIMDL1SPR, WORLDKEENSWIML1SPR, WORLDKEENSWIMUL1SPR};\r
436 #endif\r
437 #ifndef KEEN6\r
438 Sint16 tiledir[4] = {dir_South, dir_West, dir_North, dir_East};\r
439 #endif\r
440 \r
441 /*\r
442 ======================\r
443 =\r
444 = SpawnWorldKeen\r
445 =\r
446 ======================\r
447 */\r
448 \r
449 void SpawnWorldKeen(Sint16 x, Sint16 y)\r
450 {\r
451 #ifdef KEEN4\r
452         if (playstate == ex_foot)\r
453         {\r
454                 player->needtoclip = cl_noclip;\r
455                 player->obclass = keenobj;\r
456                 player->x = gamestate.worldx;\r
457                 player->y = gamestate.worldy;\r
458                 player->active = ac_allways;\r
459                 player->priority = 3;\r
460                 player->xdir = 0;\r
461                 player->ydir = 0;\r
462                 if (gamestate.worldx < 20*TILEGLOBAL)\r
463                 {\r
464                         player->temp1 = 280;\r
465                         player->xspeed = (30*TILEGLOBAL - player->x)/280 + 1;\r
466                         player->yspeed = (55*TILEGLOBAL - player->y)/280 + 1;\r
467                 }\r
468                 else\r
469                 {\r
470                         player->temp1 = 140;\r
471                         player->xspeed = (Sint16)(16*TILEGLOBAL - player->x)/140 + 1;\r
472                         player->yspeed = (Sint16)(47*TILEGLOBAL - player->y)/140 + 1;\r
473                 }\r
474                 NewState(player, &s_keenonfoot1);\r
475                 return;\r
476         }\r
477 #endif\r
478 \r
479         player->obclass = keenobj;\r
480         if (gamestate.worldx == 0)\r
481         {\r
482                 player->x = CONVERT_TILE_TO_GLOBAL(x);\r
483                 player->y = CONVERT_TILE_TO_GLOBAL(y);\r
484         }\r
485         else\r
486         {\r
487                 player->x = gamestate.worldx;\r
488                 player->y = gamestate.worldy;\r
489         }\r
490         player->active = ac_allways;\r
491         player->priority = 1;\r
492         player->xdir = 0;\r
493         player->ydir = 0;\r
494         player->temp1 = dir_West;\r
495         player->temp2 = 3;\r
496         player->temp3 = 0;\r
497         player->shapenum = WORLDKEENL3SPR;\r
498         NewState(player, &s_worldkeen);\r
499 }\r
500 \r
501 #ifdef KEEN5\r
502 /*\r
503 ======================\r
504 =\r
505 = SpawnWorldKeenPort\r
506 =\r
507 ======================\r
508 */\r
509 \r
510 void SpawnWorldKeenPort(Uint16 tileX, Uint16 tileY)\r
511 {\r
512         player->obclass = keenobj;\r
513         player->x = CONVERT_TILE_TO_GLOBAL(tileX);\r
514         player->y = CONVERT_TILE_TO_GLOBAL(tileY);\r
515         player->active = ac_allways;\r
516         player->priority = 1;\r
517         player->xdir = 0;\r
518         player->ydir = 0;\r
519         player->temp1 = dir_West;\r
520         player->temp2 = 3;\r
521         player->temp3 = 0;\r
522         player->shapenum = WORLDKEENL3SPR;\r
523         NewState(player, &s_worldkeen);\r
524 }\r
525 #endif\r
526 \r
527 \r
528 /*\r
529 ======================\r
530 =\r
531 = CheckEnterLevel\r
532 =\r
533 ======================\r
534 */\r
535 \r
536 void CheckEnterLevel(objtype *ob)\r
537 {\r
538         Uint16 x, y, info;\r
539 \r
540         for (y = ob->tiletop; y <= ob->tilebottom; y++)\r
541         {\r
542                 for (x = ob->tileleft; x <= ob->tileright; x++)\r
543                 {\r
544                         info = *(mapsegs[2]+mapbwidthtable[y]/2 + x);\r
545                         if (info > 0xC000 && info <= (0xC000 + 18))\r
546                         {\r
547                                 gamestate.worldx = ob->x;\r
548                                 gamestate.worldy = ob->y;\r
549                                 gamestate.mapon = info - 0xC000;\r
550                                 playstate = ex_completed;\r
551                                 SD_PlaySound(SND_ENTERLEVEL);\r
552                         }\r
553                 }\r
554         }\r
555 }\r
556 \r
557 /*\r
558 ======================\r
559 =\r
560 = T_KeenWorld\r
561 =\r
562 ======================\r
563 */\r
564 \r
565 void T_KeenWorld(objtype *ob)\r
566 {\r
567         if (c.dir != dir_None)\r
568         {\r
569                 ob->state = &s_worldkeenwalk;\r
570                 ob->temp2 = 0;\r
571                 T_KeenWorldWalk(ob);\r
572         }\r
573         if (jumpbutton || pogobutton || firebutton)\r
574         {\r
575                 CheckEnterLevel(ob);\r
576         }\r
577 }\r
578 \r
579 /*\r
580 ======================\r
581 =\r
582 = T_KeenWorldWalk\r
583 =\r
584 ======================\r
585 */\r
586 \r
587 void T_KeenWorldWalk(objtype *ob)\r
588 {\r
589         if (ob->temp3)\r
590         {\r
591                 ob->temp3 -= 4;\r
592                 if (ob->temp3 < 0)\r
593                         ob->temp3 = 0;\r
594         }\r
595         else\r
596         {\r
597                 ob->xdir = c.xaxis;\r
598                 ob->ydir = c.yaxis;\r
599                 if (pogobutton || firebutton || jumpbutton)\r
600                 {\r
601                         CheckEnterLevel(ob);\r
602                 }\r
603                 if (c.dir == dir_None)\r
604                 {\r
605                         ob->state = &s_worldkeen;\r
606                         ob->shapenum = worldshapes[ob->temp1] + 3;\r
607                         return;\r
608                 }\r
609                 ob->temp1 = c.dir;\r
610         }\r
611         if (++ob->temp2 == 4)\r
612                 ob->temp2 = 0;\r
613         ob->shapenum = worldshapes[ob->temp1] + worldanims[ob->temp2];\r
614 \r
615         if (ob->temp2 == 1)\r
616         {\r
617                 SD_PlaySound(SND_WORLDWALK1);\r
618         }\r
619         else if (ob->temp2 == 3)\r
620         {\r
621                 SD_PlaySound(SND_WORLDWALK2);\r
622         }\r
623 }\r
624 \r
625 #ifdef KEEN4\r
626 /*\r
627 ======================\r
628 =\r
629 = T_FootFly\r
630 =\r
631 ======================\r
632 */\r
633 \r
634 void T_FootFly(objtype *ob)\r
635 {\r
636         ob->temp1 = ob->temp1 - tics;\r
637         xtry = ob->xspeed * tics;\r
638         ytry = ob->yspeed * tics;\r
639         if (ob->temp1 <= 0)\r
640         {\r
641                 xtry -= ob->xspeed * -ob->temp1;\r
642                 ytry -= ob->yspeed * -ob->temp1;\r
643                 ob->priority = 1;\r
644                 ob->temp1 = dir_West;\r
645                 ob->temp2 = 3;\r
646                 ob->temp3 = 0;\r
647                 player->xdir = 0;\r
648                 player->ydir = 0;\r
649                 ob->state = &s_worldkeen;\r
650                 ob->shapenum = WORLDKEENL3SPR;\r
651                 ob->needtoclip = cl_midclip;\r
652         }\r
653 }\r
654 \r
655 /*\r
656 ======================\r
657 =\r
658 = T_KeenWorldSwim\r
659 =\r
660 ======================\r
661 */\r
662 \r
663 void T_KeenWorldSwim(objtype *ob)\r
664 {\r
665         if (ob->temp3)\r
666         {\r
667                 ob->temp3 -= 6;\r
668                 if (ob->temp3 < 0)\r
669                         ob->temp3 = 0;\r
670         }\r
671         else\r
672         {\r
673                 ob->xdir = c.xaxis;\r
674                 ob->ydir = c.yaxis;\r
675                 if (c.xaxis || c.yaxis)\r
676                         ob->temp1 = c.dir;\r
677         }\r
678         ob->shapenum = swimshapes[ob->temp1] + ob->temp2;\r
679         if (++ob->temp2 == 2)\r
680                 ob->temp2 = 0;\r
681 \r
682         if (ob->temp2 == 0)\r
683         {\r
684                 SD_PlaySound(SND_SWIM1);\r
685         }\r
686         else\r
687         {\r
688                 SD_PlaySound(SND_SWIM2);\r
689         }\r
690 }\r
691 \r
692 #else   // NOT Keen 4 (i.e. Keen 5 & 6):\r
693 \r
694 /*\r
695 ======================\r
696 =\r
697 = Teleport\r
698 =\r
699 ======================\r
700 */\r
701 \r
702 void Teleport(Uint16 tileX, Uint16 tileY)\r
703 {\r
704         Uint16 tile, globalx, globaly, duration, move;\r
705         objtype *o;\r
706         objtype *ob = player;\r
707 \r
708         //\r
709         // enter the teleporter\r
710         //\r
711         SD_PlaySound(SND_TELEPORT);\r
712         globalx = CONVERT_TILE_TO_GLOBAL(tileX);\r
713         globaly = CONVERT_TILE_TO_GLOBAL(tileY);\r
714 \r
715 #ifdef KEEN6Ev15\r
716         // We need to make the compiler "forget" that duration starts at 0\r
717         // to make sure the while-loop check is performed when entering the\r
718         // loop. Can't change compiler settings since we do need that loop\r
719         // optimization for the for-loop at the end of this routine.\r
720         if (true)\r
721                 duration = 0;\r
722 #else\r
723         duration = 0;\r
724 #endif\r
725 \r
726         while (duration < 130)\r
727         {\r
728                 RF_Refresh();\r
729                 move = tics*2;\r
730                 duration += tics;\r
731 \r
732                 if (ob->x == globalx && ob->y == globaly)\r
733                         break;\r
734 \r
735                 if (ob->y < globaly)\r
736                 {\r
737                         ob->y += move;\r
738                         if (ob->y > globaly)\r
739                                 ob->y = globaly;\r
740                 }\r
741                 else if (ob->y > globaly)\r
742                 {\r
743                         ob->y -= move;\r
744                         if (ob->y < globaly)\r
745                                 ob->y = globaly;\r
746                 }\r
747 \r
748                 if (ob->x < globalx)\r
749                 {\r
750                         ob->x += move;\r
751                         if (ob->x > globalx)\r
752                                 ob->x = globalx;\r
753                 }\r
754                 else if (ob->x > globalx)\r
755                 {\r
756                         ob->x -= move;\r
757                         if (ob->x < globalx)\r
758                                 ob->x = globalx;\r
759                 }\r
760 \r
761                 ob->shapenum = ((TimeCount >> 3) % 3) + WORLDKEENU1SPR;\r
762                 RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);\r
763 \r
764                 tile = ((TimeCount >> 2) & TELEPORERTILEMASK) + TELEPORTERTILE1;\r
765                 RF_MemToMap(&tile, 1, tileX, tileY, 1, 1);\r
766         }\r
767 \r
768         tile = TELEPORTERTILE2;\r
769         RF_MemToMap(&tile, 1, tileX, tileY, 1, 1);\r
770 \r
771         //\r
772         // teleport to new location\r
773         //\r
774         tile = *(mapsegs[2]+mapbwidthtable[tileY]/2 + tileX);\r
775         tileX = tile >> 8;\r
776         tileY = tile & 0x7F;    // BUG? y coordinate is limited to 1..127\r
777         ob->x = CONVERT_TILE_TO_GLOBAL(tileX);\r
778         ob->y = CONVERT_TILE_TO_GLOBAL(tileY);\r
779         ob->xdir = 0;\r
780         ob->ydir = 1;\r
781         ob->temp1 = dir_South;\r
782         NewState(ob, ob->state);\r
783         CenterActor(ob);\r
784 \r
785         //\r
786         // draw flags/signs for new location\r
787         //\r
788         for (o=player->next; o; o=o->next)\r
789         {\r
790                 if (!o->active && o->obclass == flagobj\r
791                         && o->tileright >= originxtile-1 && o->tileleft <= originxtilemax+1\r
792                         && o->tiletop <= originytilemax+1 && o->tilebottom >= originytile-1)\r
793                 {\r
794                         o->needtoreact = true;\r
795                         o->active = ac_yes;\r
796                         RF_PlaceSprite(&o->sprite, o->x, o->y, o->shapenum, spritedraw, o->priority);\r
797                 }\r
798         }\r
799         UpdateScore(scoreobj);\r
800         RF_Refresh();\r
801         RF_Refresh();\r
802 \r
803         //\r
804         // leave teleporter\r
805         //\r
806         SD_PlaySound(SND_TELEPORT);\r
807 \r
808         for (duration = 0; duration < 90; )\r
809         {\r
810                 RF_Refresh();\r
811                 duration += tics;\r
812                 ob->y += tics*2 + tics;\r
813 \r
814                 ob->shapenum = ((TimeCount >> 3) % 3) + WORLDKEEND1SPR;\r
815                 RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);\r
816 \r
817                 tile = ((TimeCount >> 2) & TELEPORERTILEMASK) + TELEPORTERTILE3;\r
818                 RF_MemToMap(&tile, 1, tileX, tileY, 1, 1);\r
819         }\r
820 \r
821         tile = TELEPORTERTILE4;\r
822         RF_MemToMap(&tile, 1, tileX, tileY, 1, 1);\r
823         xtry = ytry = 0;\r
824         ClipToWalls(ob);\r
825 }\r
826 \r
827 #ifdef KEEN5\r
828 \r
829 /*\r
830 ======================\r
831 =\r
832 = T_Elevate\r
833 =\r
834 ======================\r
835 */\r
836 \r
837 void T_Elevate(objtype *ob)\r
838 {\r
839         Sint16 i, x, y, tx, ty;\r
840         Uint16 tiles[2][2];\r
841 \r
842         ytry = ob->ydir * 64 * tics;\r
843         if (ob->x != ob->temp2)\r
844         {\r
845                 xtry = ob->xdir * 12 * tics;\r
846                 if ( (ob->xdir == 1 && ob->x + xtry > ob->temp2)\r
847                         || (ob->xdir == -1 && ob->x + xtry < ob->temp2) )\r
848                 {\r
849                         xtry = ob->temp2 - ob->x;\r
850                 }\r
851         }\r
852 \r
853         //\r
854         // Keen has no sprite in this state, so we need to update the hitbox manually\r
855         // to avoid issues (the screen scrolling routines use left/right/top/bottom)\r
856         //\r
857         ob->left = ob->x + xtry;\r
858         ob->right = ob->left + (TILEGLOBAL-1);\r
859         ob->top = ob->y + ytry;\r
860         ob->bottom = ob->top + (TILEGLOBAL-1);\r
861 \r
862         if (ob->ydir == 1)\r
863         {\r
864                 if (ob->y + ytry < ob->temp1)\r
865                         return;\r
866         }\r
867         else\r
868         {\r
869                 if (ob->y + ytry > ob->temp1)\r
870                         return;\r
871         }\r
872 \r
873         //\r
874         // the invisible Keen has arrived at its destination\r
875         //\r
876         ytry = 0;\r
877         xtry = 0;\r
878         ob->x = ob->temp2;\r
879         ob->y = ob->temp1;\r
880         ob->priority = 1;\r
881         ob->temp1 = 4;\r
882         ob->temp2 = 3;\r
883         ob->temp3 = 0;\r
884         player->xdir = 0;\r
885         player->ydir = 0;\r
886         ob->state = &s_worldkeen;\r
887         ob->shapenum = WORLDKEEND3SPR;\r
888         ob->needtoclip = cl_midclip;\r
889         tx = CONVERT_GLOBAL_TO_TILE(ob->x);\r
890         ty = CONVERT_GLOBAL_TO_TILE(ob->y);\r
891         WorldScrollScreen(ob);\r
892         UpdateScore(scoreobj);\r
893         RF_Refresh();\r
894         RF_Refresh();\r
895 \r
896         ob->y -= TILEGLOBAL;\r
897         RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);\r
898 \r
899         //\r
900         // open the elevator door\r
901         //\r
902         SD_PlaySound(SND_ELEVATORDOOR);\r
903         for (i=0; i<=5; i++)\r
904         {\r
905                 for (y=0; y<2; y++)\r
906                 {\r
907                         for (x=0; x<2; x++)\r
908                         {\r
909                                 tiles[y][x] = *(mapsegs[1]+mapbwidthtable[y]/2 + i*2 + x);\r
910                         }\r
911                 }\r
912                 RF_MemToMap(&tiles[0][0], 1, tx, ty-2, 2, 2);\r
913                 RF_Refresh();\r
914                 VW_WaitVBL(8);\r
915         }\r
916 \r
917         //\r
918         // make Keen walk out of the elevator\r
919         //\r
920         for (y=0; y<32; y++)\r
921         {\r
922                 ob->y += 8;     // move half a pixel every frame for 32 frames -> move down 16 pixels total\r
923                 ob->shapenum = (y / 4) % 3 + WORLDKEEND1SPR;\r
924                 RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);\r
925                 RF_Refresh();\r
926         }\r
927         ob->needtoclip = cl_midclip;    // redundant, but doesn't do any harm\r
928 }\r
929 \r
930 /*\r
931 ======================\r
932 =\r
933 = Elevator\r
934 =\r
935 ======================\r
936 */\r
937 \r
938 void Elevator(Uint16 tileX, Uint16 tileY, Sint16 dir)\r
939 {\r
940         Uint16 info, globalx, globaly, duration, move;\r
941         Sint16 x, y, i;\r
942         Uint16 tiles[2][2];\r
943         objtype *ob = player;\r
944 \r
945         globalx = CONVERT_TILE_TO_GLOBAL(tileX);\r
946         globaly = CONVERT_TILE_TO_GLOBAL(tileY);\r
947 \r
948         //\r
949         // make Keen walk into the elevator\r
950         //\r
951         for (duration = 0; duration < 130; )\r
952         {\r
953                 CalcBounds(ob);\r
954                 WorldScrollScreen(ob);\r
955                 UpdateScore(scoreobj);\r
956                 RF_Refresh();\r
957 \r
958                 move = tics * 2;\r
959                 duration += tics;\r
960 \r
961                 if (ob->x == globalx && ob->y == globaly)\r
962                         break;\r
963 \r
964                 if (ob->y < globaly)\r
965                 {\r
966                         ob->y += move;\r
967                         if (ob->y > globaly)\r
968                                 ob->y = globaly;\r
969                 }\r
970                 else if (ob->y > globaly)\r
971                 {\r
972                         ob->y -= move;\r
973                         if (ob->y < globaly)\r
974                                 ob->y = globaly;\r
975                 }\r
976 \r
977                 if (ob->x < globalx)\r
978                 {\r
979                         ob->x += move;\r
980                         if (ob->x > globalx)\r
981                                 ob->x = globalx;\r
982                 }\r
983                 else if (ob->x > globalx)\r
984                 {\r
985                         ob->x -= move;\r
986                         if (ob->x < globalx)\r
987                                 ob->x = globalx;\r
988                 }\r
989 \r
990                 ob->shapenum = ((duration / 8) % 3) + WORLDKEENU1SPR;\r
991                 RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);\r
992         }\r
993 \r
994         //\r
995         // close the elevator door\r
996         //\r
997         SD_PlaySound(SND_ELEVATORDOOR);\r
998         for (i=5; i >= 0; i--)\r
999         {\r
1000                 for (y=0; y<2; y++)\r
1001                 {\r
1002                         for (x=0; x<2; x++)\r
1003                         {\r
1004                                 tiles[y][x] = *(mapsegs[1]+mapbwidthtable[y]/2 + i*2 + x);\r
1005                         }\r
1006                 }\r
1007                 RF_MemToMap(&tiles[0][0], 1, tileX+dir, tileY-1, 2, 2);\r
1008                 RF_Refresh();\r
1009                 VW_WaitVBL(8);\r
1010         }\r
1011 \r
1012         //\r
1013         // make Keen invisible (and not clipping) and send him to the destination\r
1014         //\r
1015         RF_RemoveSprite(&ob->sprite);\r
1016         info = *(mapsegs[2] + mapbwidthtable[tileY]/2 + tileX);\r
1017         ob->temp2 = CONVERT_TILE_TO_GLOBAL(info >> 8);\r
1018         ob->temp1 = CONVERT_TILE_TO_GLOBAL((info & 0x7F) + 1);  // BUG? y coordinate is limited to 1..127\r
1019         if (ob->temp1 < ob->y)\r
1020         {\r
1021                 ob->ydir = -1;\r
1022         }\r
1023         else\r
1024         {\r
1025                 ob->ydir = 1;\r
1026         }\r
1027         if (ob->temp2 < ob->x)\r
1028         {\r
1029                 ob->xdir = -1;\r
1030         }\r
1031         else\r
1032         {\r
1033                 ob->xdir = 1;\r
1034         }\r
1035         ob->needtoclip = cl_noclip;\r
1036         ob->state = &s_worldelevate;\r
1037 }\r
1038 \r
1039 #endif  //ifdef KEEN5\r
1040 \r
1041 #endif  //ifdef KEEN4 ... else ...\r
1042 \r
1043 /*\r
1044 ======================\r
1045 =\r
1046 = CheckWorldInTiles\r
1047 =\r
1048 ======================\r
1049 */\r
1050 \r
1051 void CheckWorldInTiles(objtype *ob)\r
1052 {\r
1053         Uint16 tx, ty, intile;\r
1054 \r
1055         if (ob->temp3)\r
1056                 return;\r
1057 \r
1058         tx = ob->tilemidx;\r
1059         ty = CONVERT_GLOBAL_TO_TILE(ob->top + (ob->bottom-ob->top)/2);\r
1060         intile = tinf[INTILE + *(mapsegs[1]+mapbwidthtable[ty]/2+tx)];\r
1061 #if defined KEEN4\r
1062         if (intile == INTILE_SHORESOUTH || intile == INTILE_SHORENORTH\r
1063                 || intile == INTILE_SHOREEAST || intile == INTILE_SHOREWEST)\r
1064         {\r
1065                 if (!gamestate.wetsuit)\r
1066                 {\r
1067                         SD_PlaySound(SND_NOWAY);\r
1068                         CantSwim();\r
1069                         RF_ForceRefresh();\r
1070                         xtry = -ob->xmove;\r
1071                         ytry = -ob->ymove;\r
1072                         ob->xdir = ob->ydir = 0;\r
1073                         ClipToWalls(ob);\r
1074                 }\r
1075                 else\r
1076                 {\r
1077                         ob->temp1 = tiledir[intile-INTILE_SHORESOUTH];\r
1078                         if (ob->state == &s_worldswim)\r
1079                         {\r
1080                                 ob->temp1 = opposite[ob->temp1];\r
1081                         }\r
1082                         switch (ob->temp1)\r
1083                         {\r
1084                         case dir_North:\r
1085                                 ob->xdir = 0;\r
1086                                 ob->ydir = -1;\r
1087                                 break;\r
1088                         case dir_East:\r
1089                                 ob->xdir = 1;\r
1090                                 ob->ydir = 0;\r
1091                                 break;\r
1092                         case dir_South:\r
1093                                 ob->xdir = 0;\r
1094                                 ob->ydir = 1;\r
1095                                 break;\r
1096                         case dir_West:\r
1097                                 ob->xdir = -1;\r
1098                                 ob->ydir = 0;\r
1099                                 break;\r
1100                         }\r
1101                         ob->temp2 = 0;\r
1102                         ob->temp3 = 18;\r
1103                         if (ob->state == &s_worldswim)\r
1104                         {\r
1105                                 ChangeState(ob, &s_worldkeenwalk);\r
1106                         }\r
1107                         else\r
1108                         {\r
1109                                 ChangeState(ob, &s_worldswim);\r
1110                         }\r
1111                 }\r
1112         }\r
1113 #elif defined KEEN5\r
1114         switch (intile)\r
1115         {\r
1116         case INTILE_TELEPORT:\r
1117                 Teleport(tx, ty);\r
1118                 break;\r
1119         case INTILE_ELEVATORLEFT:\r
1120                 Elevator(tx, ty, 0);\r
1121                 break;\r
1122         case INTILE_ELEVATORRIGHT:\r
1123                 Elevator(tx, ty, -1);\r
1124                 break;\r
1125         }\r
1126 #elif defined KEEN6\r
1127         switch (intile)\r
1128         {\r
1129         case INTILE_TELEPORT:\r
1130                 Teleport(tx, ty);\r
1131                 break;\r
1132         }\r
1133 #endif\r
1134 }\r
1135 \r
1136 /*\r
1137 =============================================================================\r
1138 \r
1139                                                            FLAGS\r
1140 \r
1141 temp1 = x destination for the thrown flag\r
1142 temp2 = y destination for the thrown flag\r
1143 temp3 = amount of time passed since flag was thrown (in tics)\r
1144 \r
1145 =============================================================================\r
1146 */\r
1147 \r
1148 statetype s_flagwave1     = {FLAGFLAP1SPR, FLAGFLAP1SPR, step, false, false, 10, 0, 0, NULL, NULL, R_Draw, &s_flagwave2};\r
1149 statetype s_flagwave2     = {FLAGFLAP2SPR, FLAGFLAP2SPR, step, false, false, 10, 0, 0, NULL, NULL, R_Draw, &s_flagwave3};\r
1150 statetype s_flagwave3     = {FLAGFLAP3SPR, FLAGFLAP3SPR, step, false, false, 10, 0, 0, NULL, NULL, R_Draw, &s_flagwave4};\r
1151 statetype s_flagwave4     = {FLAGFLAP4SPR, FLAGFLAP4SPR, step, false, false, 10, 0, 0, NULL, NULL, R_Draw, &s_flagwave1};\r
1152 \r
1153 #ifndef KEEN5\r
1154 statetype s_throwflag0    = {FLAGFLIP1SPR, FLAGFLIP1SPR, think, false, false, 6, 0, 0, TossThink, NULL, R_Draw, &s_throwflag1};\r
1155 statetype s_throwflag1    = {FLAGFLIP1SPR, FLAGFLIP1SPR, stepthink, false, false, 12, 0, 0, PathThink, NULL, R_Draw, &s_throwflag2};\r
1156 statetype s_throwflag2    = {FLAGFLIP2SPR, FLAGFLIP2SPR, stepthink, false, false, 12, 0, 0, PathThink, NULL, R_Draw, &s_throwflag3};\r
1157 statetype s_throwflag3    = {FLAGFLIP3SPR, FLAGFLIP3SPR, stepthink, false, false, 12, 0, 0, PathThink, NULL, R_Draw, &s_throwflag4};\r
1158 statetype s_throwflag4    = {FLAGFLIP4SPR, FLAGFLIP4SPR, stepthink, false, false, 12, 0, 0, PathThink, NULL, R_Draw, &s_throwflag5};\r
1159 statetype s_throwflag5    = {FLAGFALL1SPR, FLAGFALL1SPR, stepthink, false, false, 12, 0, 0, PathThink, NULL, R_Draw, &s_throwflag6};\r
1160 statetype s_throwflag6    = {FLAGFALL1SPR, FLAGFALL1SPR, stepthink, true, false, 1, 0, 0, FlagAlign, NULL, R_Draw, &s_flagwave1};\r
1161 #endif\r
1162 \r
1163 Sint16 flagx, flagy;\r
1164 Point flagpath[30];\r
1165 \r
1166 /*\r
1167 ======================\r
1168 =\r
1169 = SpawnFlag\r
1170 =\r
1171 ======================\r
1172 */\r
1173 \r
1174 void SpawnFlag(Sint16 x, Sint16 y)\r
1175 {\r
1176         GetNewObj(false);\r
1177         new->needtoclip = cl_noclip;\r
1178         new->priority = 3;\r
1179         new->obclass = flagobj;\r
1180         new->active = ac_yes;\r
1181 #if defined KEEN4\r
1182         new->x = CONVERT_TILE_TO_GLOBAL(x) + 6*PIXGLOBAL;\r
1183         new->y = CONVERT_TILE_TO_GLOBAL(y) + -30*PIXGLOBAL;\r
1184 #elif defined KEEN5\r
1185         new->x = CONVERT_TILE_TO_GLOBAL(x) + -5*PIXGLOBAL;\r
1186         new->y = CONVERT_TILE_TO_GLOBAL(y) + -30*PIXGLOBAL;\r
1187 #elif defined KEEN6\r
1188         new->x = CONVERT_TILE_TO_GLOBAL(x) + 6*PIXGLOBAL;               // useless!\r
1189         new->y = CONVERT_TILE_TO_GLOBAL(y) + -30*PIXGLOBAL;     // useless!\r
1190 #if GRMODE == CGAGR\r
1191         new->x = CONVERT_TILE_TO_GLOBAL(x) + 10*PIXGLOBAL;\r
1192 #else\r
1193         new->x = CONVERT_TILE_TO_GLOBAL(x) + 14*PIXGLOBAL;\r
1194 #endif\r
1195         new->y = CONVERT_TILE_TO_GLOBAL(y) + -26*PIXGLOBAL;\r
1196         {\r
1197                 Uint16 tile = *(mapsegs[1]+mapbwidthtable[y]/2 + x) + 1;\r
1198                 RF_MemToMap(&tile, 1, x, y, 1, 1);\r
1199         }\r
1200 #endif\r
1201         new->ticcount = US_RndT() / 16;\r
1202         NewState(new, &s_flagwave1);\r
1203 }\r
1204 \r
1205 #ifndef KEEN5\r
1206 /*\r
1207 ======================\r
1208 =\r
1209 = SpawnThrowFlag\r
1210 =\r
1211 ======================\r
1212 */\r
1213 \r
1214 void SpawnThrowFlag(Sint16 x, Sint16 y)\r
1215 {\r
1216         Sint16 i;\r
1217         Sint32 xdist, ydist;\r
1218 \r
1219         GetNewObj(false);\r
1220         new->needtoclip = cl_noclip;\r
1221         new->priority = 3;\r
1222         new->obclass = flagobj;\r
1223         new->active = ac_allways;\r
1224         new->x = gamestate.worldx - 16*PIXGLOBAL;\r
1225         new->y = gamestate.worldy - 16*PIXGLOBAL;\r
1226 #if defined KEEN4\r
1227         new->temp1 = CONVERT_TILE_TO_GLOBAL(x) + 6*PIXGLOBAL;\r
1228         new->temp2 = CONVERT_TILE_TO_GLOBAL(y) + -38*PIXGLOBAL;\r
1229 #elif defined KEEN6\r
1230         flagx = x;\r
1231         flagy = y;\r
1232 #if GRMODE == CGAGR\r
1233         new->temp1 = CONVERT_TILE_TO_GLOBAL(x) + 10*PIXGLOBAL;\r
1234 #else\r
1235         new->temp1 = CONVERT_TILE_TO_GLOBAL(x) + 14*PIXGLOBAL;\r
1236 #endif\r
1237         new->temp2 = CONVERT_TILE_TO_GLOBAL(y) + -34*PIXGLOBAL;\r
1238 #endif\r
1239         xdist = (Sint32)new->temp1 - (Sint32)new->x;\r
1240         ydist = (Sint32)new->temp2 - (Sint32)new->y;\r
1241         for (i = 0; i < 30; i++)\r
1242         {\r
1243                 flagpath[i].x = new->x + (xdist * min(i, 24))/24;\r
1244                 flagpath[i].y = new->y + (ydist * i)/30;\r
1245                 if (i < 10)\r
1246                 {\r
1247                         flagpath[i].y -= i*3*PIXGLOBAL;\r
1248                 }\r
1249                 else if (i < 15)\r
1250                 {\r
1251                         flagpath[i].y -= i*PIXGLOBAL + 20*PIXGLOBAL;\r
1252                 }\r
1253                 else if (i < 20)\r
1254                 {\r
1255                         flagpath[i].y -= (20-i)*PIXGLOBAL + 30*PIXGLOBAL;\r
1256                 }\r
1257                 else\r
1258                 {\r
1259                         flagpath[i].y -= (29-i)*3*PIXGLOBAL;\r
1260                 }\r
1261         }\r
1262         NewState(new, &s_throwflag0);\r
1263 }\r
1264 \r
1265 /*\r
1266 ======================\r
1267 =\r
1268 = TossThink\r
1269 =\r
1270 ======================\r
1271 */\r
1272 \r
1273 void TossThink(objtype *ob)\r
1274 {\r
1275         if (screenfaded)\r
1276                 return;\r
1277 \r
1278         SD_StopSound();\r
1279         SD_PlaySound(SND_FLAGSPIN);\r
1280         ob->state = ob->state->nextstate;\r
1281 }\r
1282 \r
1283 /*\r
1284 ======================\r
1285 =\r
1286 = PathThink\r
1287 =\r
1288 ======================\r
1289 */\r
1290 \r
1291 void PathThink(objtype *ob)\r
1292 {\r
1293         ob->temp3 = ob->temp3 + tics;\r
1294         if (ob->temp3 > 58)\r
1295                 ob->temp3 = 58;\r
1296 \r
1297         ob->x = flagpath[ob->temp3/2].x;\r
1298         ob->y = flagpath[ob->temp3/2].y;\r
1299         ob->needtoreact = true;\r
1300         if (ob->temp1 == 0)\r
1301         {\r
1302                 SD_PlaySound(SND_FLAGSPIN);\r
1303         }\r
1304 }\r
1305 \r
1306 /*\r
1307 ======================\r
1308 =\r
1309 = FlagAlign\r
1310 =\r
1311 ======================\r
1312 */\r
1313 \r
1314 void FlagAlign(objtype *ob)\r
1315 {\r
1316         ob->x = ob->temp1;\r
1317         ob->y = ob->temp2 + 8*PIXGLOBAL;\r
1318         SD_PlaySound(SND_FLAGLAND);\r
1319 #ifdef KEEN6\r
1320         {\r
1321                 Uint16 tile = *(mapsegs[1]+mapbwidthtable[flagy]/2 + flagx) + 1;\r
1322                 RF_MemToMap(&tile, 1, flagx, flagy, 1, 1);\r
1323         }\r
1324 #endif\r
1325 }\r
1326 #endif\r
1327 \r
1328 /*\r
1329 =============================================================================\r
1330 \r
1331                                                  NEURAL STUNNER\r
1332 \r
1333 =============================================================================\r
1334 */\r
1335 statetype s_stunray1 = {STUN1SPR, STUN1SPR, slide, false, false, 6, 64, 64, T_Shot, NULL, R_Shot, &s_stunray2};\r
1336 statetype s_stunray2 = {STUN2SPR, STUN2SPR, slide, false, false, 6, 64, 64, T_Shot, NULL, R_Shot, &s_stunray3};\r
1337 statetype s_stunray3 = {STUN3SPR, STUN3SPR, slide, false, false, 6, 64, 64, T_Shot, NULL, R_Shot, &s_stunray4};\r
1338 statetype s_stunray4 = {STUN4SPR, STUN4SPR, slide, false, false, 6, 64, 64, T_Shot, NULL, R_Shot, &s_stunray1};\r
1339 \r
1340 statetype s_stunhit  = {STUNHIT1SPR, STUNHIT1SPR, step, false, false, 12, 0, 0, NULL, NULL, R_Draw, &s_stunhit2};\r
1341 statetype s_stunhit2 = {STUNHIT2SPR, STUNHIT2SPR, step, false, false, 12, 0, 0, NULL, NULL, R_Draw, NULL};\r
1342 \r
1343 /*\r
1344 ======================\r
1345 =\r
1346 = SpawnShot\r
1347 =\r
1348 ======================\r
1349 */\r
1350 \r
1351 void SpawnShot(Uint16 x, Uint16 y, Direction dir)\r
1352 {\r
1353         if (!gamestate.ammo)\r
1354         {\r
1355                 SD_PlaySound(SND_USESWITCH);\r
1356                 return;\r
1357         }\r
1358 \r
1359         gamestate.ammo--;\r
1360         GetNewObj(true);\r
1361         new->x = x;\r
1362         new->y = y;\r
1363         new->priority = 0;\r
1364         new->obclass = stunshotobj;\r
1365         new->active = ac_allways;\r
1366         SD_PlaySound(SND_KEENFIRE);\r
1367         switch (dir)\r
1368         {\r
1369         case dir_North:\r
1370                 new->xdir = 0;\r
1371                 new->ydir = -1;\r
1372                 break;\r
1373         case dir_East:\r
1374                 new->xdir = 1;\r
1375                 new->ydir = 0;\r
1376                 break;\r
1377         case dir_South:\r
1378                 new->xdir = 0;\r
1379                 new->ydir = 1;\r
1380                 break;\r
1381         case dir_West:\r
1382                 new->xdir = -1;\r
1383                 new->ydir = 0;\r
1384                 break;\r
1385         default:\r
1386                 Quit("SpawnShot: Bad dir!");\r
1387                 break;\r
1388         }\r
1389         NewState(new, &s_stunray1);\r
1390 \r
1391 #ifdef KEEN6\r
1392         {\r
1393                 objtype *ob;\r
1394 \r
1395                 for (ob=player->next; ob; ob=ob->next)\r
1396                 {\r
1397                         if (ob->active\r
1398                                 && new->right > ob->left && new->left < ob->right\r
1399                                 && new->top < ob->bottom && new->bottom > ob->top\r
1400                                 && ob->state->contact)\r
1401                         {\r
1402                                 ob->state->contact(ob, new);\r
1403                                 return;\r
1404                         }\r
1405                 }\r
1406         }\r
1407 #endif\r
1408 }\r
1409 \r
1410 /*\r
1411 ======================\r
1412 =\r
1413 = ExplodeShot\r
1414 =\r
1415 ======================\r
1416 */\r
1417 \r
1418 void ExplodeShot(objtype *ob)\r
1419 {\r
1420         ob->obclass = inertobj;\r
1421         ChangeState(ob, &s_stunhit);\r
1422         SD_PlaySound(SND_SHOTEXPLODE);\r
1423 }\r
1424 \r
1425 /*\r
1426 ======================\r
1427 =\r
1428 = T_Shot\r
1429 =\r
1430 ======================\r
1431 */\r
1432 \r
1433 void T_Shot(objtype *ob)\r
1434 {\r
1435         objtype *ob2;\r
1436 \r
1437         if (ob->tileright >= originxtile && ob->tilebottom >= originytile\r
1438                 && ob->tileleft <= originxtilemax && ob->tiletop <= originytilemax)\r
1439         {\r
1440                 //object is visible, so do nothing\r
1441                 return;\r
1442         }\r
1443 \r
1444         if (ob->tileright+10 < originxtile\r
1445                 || ob->tileleft-10 > originxtilemax\r
1446                 || ob->tilebottom+6 < originytile\r
1447                 || ob->tiletop-6 > originytilemax)\r
1448         {\r
1449                 //shot is off-screen by more than half a screen, so remove it\r
1450                 RemoveObj(ob);\r
1451                 return;\r
1452         }\r
1453 \r
1454         //check for collisions with INACTIVE objects\r
1455         for (ob2 = player->next; ob2; ob2 = ob2->next)\r
1456         {\r
1457                 if (!ob2->active && ob->right > ob2->left && ob->left < ob2->right\r
1458                         && ob->top < ob2->bottom && ob->bottom > ob2->top)\r
1459                 {\r
1460                         if (ob2->state->contact)\r
1461                         {\r
1462                                 ob2->state->contact(ob2, ob);\r
1463                                 ob2->needtoreact = true;\r
1464                                 ob2->active = ac_yes;\r
1465                         }\r
1466 \r
1467                         if (ob->obclass == nothing)     //BUG: obclass is 'inertobj' for the exploded shot\r
1468                                 break;\r
1469                 }\r
1470         }\r
1471 }\r
1472 \r
1473 /*\r
1474 ======================\r
1475 =\r
1476 = R_Shot\r
1477 =\r
1478 ======================\r
1479 */\r
1480 \r
1481 void R_Shot(objtype *ob)\r
1482 {\r
1483         Uint16 tile;\r
1484 \r
1485         if (ob->hitnorth == 1 && ob->tileleft != ob->tileright)\r
1486         {\r
1487                 tile = *(mapsegs[1]+mapbwidthtable[ob->tiletop-1]/2+ob->tileright);\r
1488                 if (tinf[NORTHWALL+tile] == 17)\r
1489                 {\r
1490                         ob->hitnorth = 17;\r
1491                         ob->x += 0x100 - (ob->x & 0xFF);\r
1492                 }\r
1493         }\r
1494         else if (ob->hitnorth == 17 && ob->tileleft != ob->tileright)\r
1495         {\r
1496                 ob->x &= 0xFF00;\r
1497         }\r
1498         if (ob->hitsouth == 1 && ob->tileleft != ob->tileright)\r
1499         {\r
1500                 tile = *(mapsegs[1]+mapbwidthtable[ob->tilebottom+1]/2+ob->tileright);\r
1501                 if (tinf[SOUTHWALL+tile] == 17)\r
1502                 {\r
1503                         ob->hitsouth = 17;\r
1504                         ob->x += 0x100 - (ob->x & 0xFF);\r
1505                 }\r
1506         }\r
1507         else if (ob->hitsouth == 17 && ob->tileleft != ob->tileright)\r
1508         {\r
1509                 ob->x &= 0xFF00;\r
1510         }\r
1511         if (ob->hitnorth == 17 || ob->hitsouth == 17)\r
1512         {\r
1513                 ytry = ob->state->ymove * tics * ob->ydir;\r
1514                 ob->y += ytry;\r
1515                 ob->top += ytry;\r
1516                 ob->bottom += ytry;\r
1517                 ob->tiletop = CONVERT_GLOBAL_TO_TILE(ob->top);\r
1518                 ob->tilebottom = CONVERT_GLOBAL_TO_TILE(ob->bottom);\r
1519         }\r
1520         else if (ob->hitnorth || ob->hitsouth || ob->hiteast || ob->hitwest)\r
1521         {\r
1522                 ExplodeShot(ob);\r
1523         }\r
1524         RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);\r
1525 }\r
1526 \r
1527 /*\r
1528 =============================================================================\r
1529 \r
1530                                                  DOOR\r
1531 \r
1532 temp1 = height of the door's main section (identical tiles!), in tiles\r
1533         DoorOpen changes two more tiles at the bottom end of the door\r
1534                   (total door height in tiles is temp1 + 2)\r
1535 \r
1536 =============================================================================\r
1537 */\r
1538 \r
1539 statetype s_door1    = {0, 0, step, false, false, 10, 0, 0, DoorOpen, NULL, NULL, &s_door2};\r
1540 statetype s_door2    = {0, 0, step, false, false, 10, 0, 0, DoorOpen, NULL, NULL, &s_door3};\r
1541 statetype s_door3    = {0, 0, step, false, false, 10, 0, 0, DoorOpen, NULL, NULL, NULL};\r
1542 \r
1543 /*\r
1544 ======================\r
1545 =\r
1546 = DoorOpen\r
1547 =\r
1548 ======================\r
1549 */\r
1550 \r
1551 void DoorOpen(objtype *ob)\r
1552 {\r
1553         Sint16 i;\r
1554         Uint16 far *map;\r
1555         Uint16 tiles[50];\r
1556 \r
1557         map = mapsegs[1] + mapbwidthtable[ob->y]/2 + ob->x;\r
1558         for (i=0; i < ob->temp1+2; i++, map+=mapwidth)\r
1559         {\r
1560                 tiles[i] = *map + 1;\r
1561         }\r
1562         RF_MemToMap(tiles, 1, ob->x, ob->y, 1, ob->temp1+2);\r
1563 }\r
1564 \r
1565 #ifdef KEEN5\r
1566 /*\r
1567 =============================================================================\r
1568 \r
1569                                                  CARD DOOR\r
1570 \r
1571 temp1 = frame counter\r
1572 \r
1573 =============================================================================\r
1574 */\r
1575 statetype s_carddoor    = {0, 0, step, false, false, 15, 0, 0, CardDoorOpen, NULL, NULL, &s_carddoor};\r
1576 \r
1577 /*\r
1578 ======================\r
1579 =\r
1580 = CardDoorOpen\r
1581 =\r
1582 ======================\r
1583 */\r
1584 \r
1585 void CardDoorOpen(objtype *ob)\r
1586 {\r
1587         Sint16 x, y;\r
1588         Uint16 far *map;\r
1589         Uint16 tiles[16], *tileptr;\r
1590 \r
1591         tileptr = tiles;\r
1592         map = mapsegs[1] + mapbwidthtable[ob->y]/2 + ob->x;\r
1593         for (y=0; y<4; y++, map+=mapwidth)\r
1594         {\r
1595                 for (x=0; x<4; x++)\r
1596                 {\r
1597                         *tileptr++ = map[x]-4;\r
1598                 }\r
1599         }\r
1600         RF_MemToMap(tiles, 1, ob->x, ob->y, 4, 4);\r
1601 \r
1602         if (++ob->temp1 == 3)\r
1603                 ob->state = NULL;\r
1604 }\r
1605 \r
1606 #endif