]> 4ch.mooo.com Git - 16.git/blob - 16/keen456/KEEN4-6/KEEN5/K5_ACT3.C
extrcted keen code remake
[16.git] / 16 / keen456 / KEEN4-6 / KEEN5 / K5_ACT3.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 K5_ACT3.C\r
25 =========\r
26 \r
27 Contains the following actor types (in this order):\r
28 \r
29 - Shikadi Mine\r
30 - Robo Red\r
31 - Spirogrip\r
32 - Spindred\r
33 - Shikadi Master\r
34 - Shikadi\r
35 - Shockshund\r
36 - Sphereful\r
37 - Scottie\r
38 - QED\r
39 \r
40 */\r
41 \r
42 #include "CK_DEF.H"\r
43 \r
44 /*\r
45 =============================================================================\r
46 \r
47                                                   SHIKADI MINE\r
48 \r
49 temp2 = x position of the "eye", in global units (relative to the mine sprite)\r
50 temp3 = y position of the "eye", in global units (relative to the mine sprite)\r
51 temp4 = sprite pointer for the "eye"\r
52 \r
53 =============================================================================\r
54 */\r
55 \r
56 static Uint16 dopposite[] = {2, 3, 0, 1, 6, 7, 4, 5, 8};\r
57 \r
58 statetype s_mine       = {SHIKADIMINESPR,       SHIKADIMINESPR,       think, false, false,  8, 0, 0, T_Mine, C_Solid, R_Mine, NULL};\r
59 statetype s_minecenter = {SHIKADIMINESPR,       SHIKADIMINESPR,       think, false, false,  0, 0, 0, T_MineCenter, C_Solid, R_Mine, &s_mineshift};\r
60 statetype s_mineshift  = {SHIKADIMINESPR,       SHIKADIMINESPR,       think, false, false,  0, 0, 0, T_MineShift, C_Solid, R_Mine, &s_mine};\r
61 statetype s_mineboom1  = {SHIKADIMINEPULSE1SPR, SHIKADIMINEPULSE1SPR, step,  false, false, 10, 0, 0, NULL, C_Solid, R_Draw, &s_mineboom2};\r
62 statetype s_mineboom2  = {SHIKADIMINEPULSE2SPR, SHIKADIMINEPULSE2SPR, step,  false, false, 10, 0, 0, NULL, C_Solid, R_Draw, &s_mineboom3};\r
63 statetype s_mineboom3  = {SHIKADIMINEPULSE1SPR, SHIKADIMINEPULSE1SPR, step,  false, false, 10, 0, 0, NULL, C_Solid, R_Draw, &s_mineboom4};\r
64 statetype s_mineboom4  = {SHIKADIMINEPULSE2SPR, SHIKADIMINEPULSE2SPR, step,  false, false, 10, 0, 0, T_MineFrag, C_Solid, R_Draw, &s_mineboom5};\r
65 statetype s_mineboom5  = {SHIKADIMINEBOOM1SPR,  SHIKADIMINEBOOM1SPR,  step,  false, false, 20, 0, 0, NULL, C_Spindread, R_Draw, &s_mineboom6};\r
66 statetype s_mineboom6  = {SHIKADIMINEBOOM2SPR,  SHIKADIMINEBOOM2SPR,  step,  false, false, 20, 0, 0, NULL, C_Spindread, R_Draw, NULL};\r
67 statetype s_minepiece  = {SHIKADIMINEPIECESPR,  SHIKADIMINEPIECESPR,  think, false, false,  8, 0, 0, T_Projectile, C_MineFrag, R_Bounce, NULL};\r
68 \r
69 /*\r
70 ===========================\r
71 =\r
72 = SpawnMine\r
73 =\r
74 ===========================\r
75 */\r
76 \r
77 void SpawnMine(Uint16 tileX, Uint16 tileY)\r
78 {\r
79         Sint16 i;\r
80 \r
81         GetNewObj(false);\r
82         new->obclass = mineobj;\r
83         new->active = ac_yes;\r
84         new->needtoclip = cl_noclip;\r
85         new->x = CONVERT_TILE_TO_GLOBAL(tileX);\r
86         new->y = CONVERT_TILE_TO_GLOBAL(tileY) + -(31*PIXGLOBAL + 1);\r
87         new->temp2 = 16*PIXGLOBAL;\r
88         new->temp3 = 13*PIXGLOBAL;\r
89         NewState(new, &s_mineshift);\r
90         new->xspeed = TILEGLOBAL;\r
91 \r
92         for (i=0; i<=3; i++)\r
93         {\r
94                 if (Walk(new, i))\r
95                         break;\r
96         }\r
97 }\r
98 \r
99 /*\r
100 ===========================\r
101 =\r
102 = MinePosCheck\r
103 =\r
104 ===========================\r
105 */\r
106 \r
107 boolean MinePosCheck(Uint16 tileX, Uint16 tileY)\r
108 {\r
109         Uint16 far *map;\r
110         Uint16 x, y, tile;\r
111 \r
112         map = mapsegs[1] + mapbwidthtable[tileY]/2 + tileX;\r
113         for (y=0; y<2; y++)\r
114         {\r
115                 for (x=0; x<3; x++)\r
116                 {\r
117                         tile = *(map + y*mapwidth + x);\r
118                         if (tinf[tile+NORTHWALL] || tinf[tile+EASTWALL] || tinf[tile+SOUTHWALL] || tinf[tile+WESTWALL])\r
119                                 return false;\r
120                 }\r
121         }\r
122         return true;\r
123 }\r
124 \r
125 /*\r
126 ===========================\r
127 =\r
128 = Walk\r
129 =\r
130 ===========================\r
131 */\r
132 \r
133 boolean Walk(objtype *ob, Sint16 dir)\r
134 {\r
135         Uint16 tileX, tileY;\r
136 \r
137         tileX = CONVERT_GLOBAL_TO_TILE(ob->x + xtry);\r
138         tileY = CONVERT_GLOBAL_TO_TILE(ob->y + ytry);\r
139 \r
140         switch (dir)\r
141         {\r
142         case 0:\r
143                 if (MinePosCheck(tileX, tileY-1))\r
144                 {\r
145                         ob->xdir = 0;\r
146                         ob->ydir = -1;\r
147                         return true;\r
148                 }\r
149                 return false;\r
150         case 1:\r
151                 if (MinePosCheck(tileX+1, tileY))\r
152                 {\r
153                         ob->xdir = 1;\r
154                         ob->ydir = 0;\r
155                         return true;\r
156                 }\r
157                 return false;\r
158         case 2:\r
159                 if (MinePosCheck(tileX, tileY+1))\r
160                 {\r
161                         ob->xdir = 0;\r
162                         ob->ydir = 1;\r
163                         return true;\r
164                 }\r
165                 return false;\r
166         case 3:\r
167                 if (MinePosCheck(tileX-1, tileY))\r
168                 {\r
169                         ob->xdir = -1;\r
170                         ob->ydir = 0;\r
171                         return true;\r
172                 }\r
173                 return false;\r
174         default:\r
175                 Quit("Walk: Bad dir");\r
176         }\r
177         return false;\r
178 }\r
179 \r
180 /*\r
181 ===========================\r
182 =\r
183 = ChaseThink\r
184 =\r
185 ===========================\r
186 */\r
187 \r
188 void ChaseThink(objtype *ob)\r
189 {\r
190         Sint16 temp;\r
191         Sint16 xdist, ydist, ydir, xdir;\r
192         Sint16 olddir[2], oppdir;\r
193 \r
194         if (ob->xdir == 1)\r
195         {\r
196                 olddir[0] = 1;\r
197         }\r
198         else if (ob->xdir == -1)\r
199         {\r
200                 olddir[0] = 3;\r
201         }\r
202         else if (ob->ydir == -1)\r
203         {\r
204                 olddir[0] = 0;\r
205         }\r
206         else if (ob->ydir == 1)\r
207         {\r
208                 olddir[0] = 2;\r
209         }\r
210         oppdir = dopposite[olddir[0]];\r
211         xdist = player->x - (ob->x + xtry);\r
212         ydist = player->y - (ob->y + ytry);\r
213         xdir = 8;\r
214         ydir = 8;\r
215         if (xdist > 0)\r
216         {\r
217                 xdir = 1;\r
218         }\r
219         if (xdist < 0)\r
220         {\r
221                 xdir = 3;\r
222         }\r
223         if (ydist > 0)\r
224         {\r
225                 ydir = 2;\r
226         }\r
227         if (ydist < 0)\r
228         {\r
229                 ydir = 0;\r
230         }\r
231         if (abs(ydist) > abs(xdist))\r
232         {\r
233                 temp = xdir;\r
234                 xdir = ydir;\r
235                 ydir = temp;\r
236         }\r
237         if (xdir == oppdir)\r
238         {\r
239                 xdir = 8;\r
240         }\r
241         if (ydir == oppdir)\r
242         {\r
243                 ydir = 8;\r
244         }\r
245         if (ydir != 8 && Walk(ob, ydir))\r
246         {\r
247                 return;\r
248         }\r
249         if (xdir != 8 && Walk(ob, xdir))\r
250         {\r
251                 return;\r
252         }\r
253         if (Walk(ob, olddir[0]))\r
254         {\r
255                 return;\r
256         }\r
257         if (US_RndT() > 0x80)\r
258         {\r
259                 for (temp=0; temp<=3; temp++)\r
260                 {\r
261                         if (temp != oppdir && Walk(ob, temp))\r
262                                 return;\r
263                 }\r
264         }\r
265         else\r
266         {\r
267                 for (temp=3; temp>=0; temp--)\r
268                 {\r
269                         if (temp != oppdir && Walk(ob, temp))\r
270                                 return;\r
271                 }\r
272         }\r
273         Walk(ob, oppdir);\r
274 }\r
275 \r
276 /*\r
277 ===========================\r
278 =\r
279 = T_Mine\r
280 =\r
281 ===========================\r
282 */\r
283 \r
284 void T_Mine(objtype *ob)\r
285 {\r
286         Sint16 oldxdir, oldydir;\r
287         Sint16 xdist, ydist;\r
288         Sint16 speed;\r
289 \r
290         xdist = ob->x - player->x;\r
291         ydist = ob->y - player->y;\r
292         if (xdist <= 2*TILEGLOBAL && xdist >= -5*TILEGLOBAL && ydist <= 3*TILEGLOBAL && ydist >= -5*PIXGLOBAL)\r
293         {\r
294                 SD_PlaySound(SND_MINEEXPLODE);\r
295                 ob->state = &s_mineboom1;\r
296                 RF_RemoveSprite((void**)(&ob->temp4));\r
297         }\r
298         else\r
299         {\r
300                 speed = tics * 10;\r
301                 if (ob->xspeed <= speed)\r
302                 {\r
303                         xtry = ob->xdir * ob->xspeed;\r
304                         ytry = ob->ydir * ob->xspeed;   // yes, this uses xspeed!\r
305                         speed -= ob->xspeed;\r
306                         oldxdir = ob->xdir;\r
307                         oldydir = ob->ydir;\r
308                         ChaseThink(ob);\r
309                         ob->xspeed = TILEGLOBAL;\r
310                         if (ob->xdir != oldxdir || ob->ydir != oldydir)\r
311                         {\r
312                                 ob->state = &s_minecenter;\r
313                                 return;\r
314                         }\r
315                 }\r
316                 ob->xspeed -= speed;\r
317                 xtry += ob->xdir * speed;\r
318                 ytry += ob->ydir * speed;\r
319         }\r
320 }\r
321 \r
322 /*\r
323 ===========================\r
324 =\r
325 = C_Solid\r
326 =\r
327 ===========================\r
328 */\r
329 \r
330 #pragma argsused\r
331 void C_Solid(objtype *ob, objtype *hit)\r
332 {\r
333         if (hit->obclass == stunshotobj)\r
334         {\r
335                 ExplodeShot(hit);\r
336         }\r
337 }\r
338 \r
339 /*\r
340 ===========================\r
341 =\r
342 = C_MineFrag\r
343 =\r
344 ===========================\r
345 */\r
346 \r
347 #pragma argsused\r
348 void C_MineFrag(objtype *ob, objtype *hit)\r
349 {\r
350         if (hit->obclass == stunshotobj)\r
351         {\r
352                 ExplodeShot(hit);\r
353         }\r
354         else if (hit->obclass == keenobj)\r
355         {\r
356                 KillKeen();\r
357         }\r
358         else if (hit->obclass == qedobj)\r
359         {\r
360                 SpawnFuseFlash(hit->tileleft, hit->tiletop);\r
361                 SpawnFuseFlash(hit->tileright, hit->tiletop);\r
362                 SpawnFuseFlash(hit->tileleft, hit->tilebottom);\r
363                 SpawnFuseFlash(hit->tileright, hit->tilebottom);\r
364                 RF_MapToMap(0, 0, 16, 11, 4, 2);\r
365                 RF_MapToMap(4, 0, 16, 13, 4, 2);\r
366                 SpawnDeadMachine();\r
367                 RemoveObj(hit);\r
368         }\r
369 }\r
370 \r
371 /*\r
372 ===========================\r
373 =\r
374 = T_MineCenter\r
375 =\r
376 ===========================\r
377 */\r
378 \r
379 void T_MineCenter(objtype *ob)\r
380 {\r
381         Sint16 px, py, xdist, ydist;\r
382 \r
383         xdist = ob->x - player->x;\r
384         ydist = ob->y - player->y;\r
385 \r
386         if (xdist <= 2*TILEGLOBAL && xdist >= -3*TILEGLOBAL && ydist <= 3*TILEGLOBAL && ydist >= -3*TILEGLOBAL)\r
387         {\r
388                 //BUG? this doesn't play a sound\r
389                 ob->state = &s_mineboom1;\r
390                 RF_RemoveSprite((void**)&ob->temp4);\r
391         }\r
392         else\r
393         {\r
394                 ob->needtoreact = true;\r
395 \r
396                 px = 16*PIXGLOBAL;\r
397                 py = 13*PIXGLOBAL;\r
398 \r
399                 if (ob->temp2 < px)\r
400                 {\r
401                         ob->temp2 = ob->temp2 + tics*4;\r
402                         if (ob->temp2 >= px)\r
403                         {\r
404                                 ob->temp2 = px;\r
405                                 ob->state = ob->state->nextstate;\r
406                         }\r
407                 }\r
408                 else if (ob->temp2 > px)\r
409                 {\r
410                         ob->temp2 = ob->temp2 - tics*4;\r
411                         if (ob->temp2 <= px)\r
412                         {\r
413                                 ob->temp2 = px;\r
414                                 ob->state = ob->state->nextstate;\r
415                         }\r
416                 }\r
417                 if (ob->temp3 < py)\r
418                 {\r
419                         ob->temp3 = ob->temp3 + tics*4;\r
420                         if (ob->temp3 >= py)\r
421                         {\r
422                                 ob->temp3 = py;\r
423                                 ob->state = ob->state->nextstate;\r
424                         }\r
425                 }\r
426                 else if (ob->temp3 > py)\r
427                 {\r
428                         ob->temp3 = ob->temp3 - tics*4;\r
429                         if (ob->temp3 <= py)\r
430                         {\r
431                                 ob->temp3 = py;\r
432                                 ob->state = ob->state->nextstate;\r
433                         }\r
434                 }\r
435         }\r
436 }\r
437 \r
438 /*\r
439 ===========================\r
440 =\r
441 = T_MineShift\r
442 =\r
443 ===========================\r
444 */\r
445 \r
446 void T_MineShift(objtype *ob)\r
447 {\r
448         Sint16 px, py, xdist, ydist;\r
449 \r
450         xdist = ob->x - player->x;\r
451         ydist = ob->y - player->y;\r
452 \r
453         if (xdist <= 2*TILEGLOBAL && xdist >= -3*TILEGLOBAL && ydist <= 3*TILEGLOBAL && ydist >= -3*TILEGLOBAL)\r
454         {\r
455                 //BUG? this doesn't play a sound\r
456                 ob->state = &s_mineboom1;\r
457                 RF_RemoveSprite((void**)&ob->temp4);\r
458         }\r
459         else\r
460         {\r
461                 ob->needtoreact = true;\r
462 \r
463                 switch (ob->xdir)\r
464                 {\r
465                 case -1:\r
466                         px = 8*PIXGLOBAL;\r
467                         break;\r
468                 case 0:\r
469                         px = 16*PIXGLOBAL;\r
470                         break;\r
471                 case 1:\r
472                         px = 24*PIXGLOBAL;\r
473                 }\r
474                 switch (ob->ydir)\r
475                 {\r
476                 case -1:\r
477                         py = 5*PIXGLOBAL;\r
478                         break;\r
479                 case 0:\r
480                         py = 13*PIXGLOBAL;\r
481                         break;\r
482                 case 1:\r
483                         py = 21*PIXGLOBAL;\r
484                 }\r
485 \r
486                 if (ob->temp2 < px)\r
487                 {\r
488                         ob->temp2 = ob->temp2 + tics*4;\r
489                         if (ob->temp2 >= px)\r
490                         {\r
491                                 ob->temp2 = px;\r
492                                 ob->state = ob->state->nextstate;\r
493                         }\r
494                 }\r
495                 else if (ob->temp2 > px)\r
496                 {\r
497                         ob->temp2 = ob->temp2 - tics*4;\r
498                         if (ob->temp2 <= px)\r
499                         {\r
500                                 ob->temp2 = px;\r
501                                 ob->state = ob->state->nextstate;\r
502                         }\r
503                 }\r
504                 if (ob->temp3 < py)\r
505                 {\r
506                         ob->temp3 = ob->temp3 + tics*4;\r
507                         if (ob->temp3 >= py)\r
508                         {\r
509                                 ob->temp3 = py;\r
510                                 ob->state = ob->state->nextstate;\r
511                         }\r
512                 }\r
513                 else if (ob->temp3 > py)\r
514                 {\r
515                         ob->temp3 = ob->temp3 - tics*4;\r
516                         if (ob->temp3 <= py)\r
517                         {\r
518                                 ob->temp3 = py;\r
519                                 ob->state = ob->state->nextstate;\r
520                         }\r
521                 }\r
522         }\r
523 }\r
524 \r
525 /*\r
526 ===========================\r
527 =\r
528 = T_MineFrag\r
529 =\r
530 ===========================\r
531 */\r
532 \r
533 void T_MineFrag(objtype *ob)\r
534 {\r
535         SD_PlaySound(SND_MINEEXPLODE);\r
536 \r
537         GetNewObj(true);\r
538         new->x = ob->x;\r
539         new->y = ob->y;\r
540         new->xspeed = -(US_RndT()>>3);\r
541         new->yspeed = -48;\r
542         NewState(new, &s_minepiece);\r
543 \r
544         GetNewObj(true);\r
545         new->x = ob->x + TILEGLOBAL;\r
546         new->y = ob->y;\r
547         new->xspeed = (US_RndT()>>3);\r
548         new->yspeed = -48;\r
549         NewState(new, &s_minepiece);\r
550 \r
551         GetNewObj(true);\r
552         new->x = ob->x;\r
553         new->y = ob->y;\r
554         new->xspeed = (US_RndT()>>4) + 40;\r
555         new->yspeed = -24;\r
556         NewState(new, &s_minepiece);\r
557 \r
558         GetNewObj(true);\r
559         new->x = ob->x + TILEGLOBAL;\r
560         new->y = ob->y;\r
561         new->xspeed = -40 - (US_RndT()>>4);\r
562         new->yspeed = -24;\r
563         NewState(new, &s_minepiece);\r
564 \r
565         GetNewObj(true);\r
566         new->x = ob->x;\r
567         new->y = ob->y;\r
568         new->xspeed = 24;\r
569         new->yspeed = 16;\r
570         NewState(new, &s_minepiece);\r
571 \r
572         GetNewObj(true);\r
573         new->x = ob->x + TILEGLOBAL;\r
574         new->y = ob->y;\r
575         new->xspeed = 24;\r
576         new->yspeed = 16;\r
577         NewState(new, &s_minepiece);\r
578 }\r
579 \r
580 /*\r
581 ===========================\r
582 =\r
583 = R_Mine\r
584 =\r
585 ===========================\r
586 */\r
587 \r
588 void R_Mine(objtype *ob)\r
589 {\r
590         RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);\r
591         RF_PlaceSprite((void**)&ob->temp4, ob->x+ob->temp2, ob->y+ob->temp3, SHIKADIMINEEYESPR, spritedraw, 2);\r
592 }\r
593 \r
594 /*\r
595 =============================================================================\r
596 \r
597                                                   ROBO RED\r
598 \r
599 temp1 = number of shots to fire\r
600 \r
601 =============================================================================\r
602 */\r
603 \r
604 statetype s_robored      = {ROBOREDLSPR,     ROBOREDRSPR,     step,      false, true,  6, 64, 0, T_RoboRed, C_RoboRed, R_Walk, &s_robored};\r
605 statetype s_roboredfire0 = {ROBOREDLSPR,     ROBOREDRSPR,     step,      true,  true, 40,  0, 0, NULL, C_Spindread, R_Draw, &s_roboredfire1};\r
606 statetype s_roboredfire1 = {ROBOREDLSPR,     ROBOREDRSPR,     step,      true,  true,  4, 64, 0, NULL, C_Spindread, R_Draw, &s_roboredfire2};\r
607 statetype s_roboredfire2 = {ROBOREDLSPR,     ROBOREDRSPR,     step,      false, true,  6,  0, 0, T_RoboShoot, C_Spindread, R_Draw, &s_roboredfire1};\r
608 statetype s_rshot1       = {ROBOSHOT1SPR,    ROBOSHOT1SPR,    stepthink, false, false,  8,  0, 0, T_Velocity, C_RShot, R_RShot, &s_rshot2};\r
609 statetype s_rshot2       = {ROBOSHOT2SPR,    ROBOSHOT2SPR,    stepthink, false, false,  8,  0, 0, T_Velocity, C_RShot, R_RShot, &s_rshot1};\r
610 statetype s_rshothit1    = {ROBOSHOTHIT1SPR, ROBOSHOTHIT1SPR, step,      false, false, 10,  0, 0, NULL, NULL, R_Draw, &s_rshothit2};\r
611 statetype s_rshothit2    = {ROBOSHOTHIT2SPR, ROBOSHOTHIT2SPR, step,      false, false, 10,  0, 0, NULL, NULL, R_Draw, NULL};\r
612 \r
613 /*\r
614 ===========================\r
615 =\r
616 = SpawnRoboRed\r
617 =\r
618 ===========================\r
619 */\r
620 \r
621 void SpawnRoboRed(Uint16 tileX, Uint16 tileY)\r
622 {\r
623         GetNewObj(false);\r
624         new->obclass = roboredobj;\r
625         new->active = ac_yes;\r
626         new->x = CONVERT_TILE_TO_GLOBAL(tileX);\r
627         new->y = CONVERT_TILE_TO_GLOBAL(tileY) + -4*TILEGLOBAL;\r
628         if (US_RndT() < 0x80)\r
629         {\r
630                 new->xdir = 1;\r
631         }\r
632         else\r
633         {\r
634                 new->xdir = -1;\r
635         }\r
636         NewState(new, &s_robored);\r
637 }\r
638 \r
639 /*\r
640 ===========================\r
641 =\r
642 = T_RoboRed\r
643 =\r
644 ===========================\r
645 */\r
646 \r
647 void T_RoboRed(objtype *ob)\r
648 {\r
649         if (!(ob->x & (4*PIXGLOBAL)) && player->bottom > ob->top && player->top < ob->bottom && US_RndT() < 16)\r
650         {\r
651                 if (ob->x > player->x)\r
652                 {\r
653                         ob->xdir = -1;\r
654                 }\r
655                 else\r
656                 {\r
657                         ob->xdir = 1;\r
658                 }\r
659                 ob->temp1 = 10; // shoot 10 times\r
660                 ob->state = &s_roboredfire0;\r
661         }\r
662 }\r
663 \r
664 /*\r
665 ===========================\r
666 =\r
667 = C_RoboRed\r
668 =\r
669 ===========================\r
670 */\r
671 \r
672 void C_RoboRed(objtype *ob, objtype *hit)\r
673 {\r
674         if (hit->obclass == stunshotobj)\r
675         {\r
676                 ExplodeShot(hit);\r
677                 ob->xdir = (player->x > ob->x)? 1 : -1;\r
678                 ob->temp1 = 10; // shoot 10 times\r
679                 ChangeState(ob, &s_roboredfire0);\r
680         }\r
681         else if (hit->obclass == keenobj)\r
682         {\r
683                 KillKeen();\r
684         }\r
685 }\r
686 \r
687 /*\r
688 ===========================\r
689 =\r
690 = T_RoboShoot\r
691 =\r
692 ===========================\r
693 */\r
694 \r
695 void T_RoboShoot(objtype *ob)\r
696 {\r
697         Uint16 x;\r
698 \r
699         if (--ob->temp1 == 0)\r
700         {\r
701                 ob->state = &s_robored;\r
702         }\r
703         if (ob->xdir == 1)\r
704         {\r
705                 x = ob->x + 56*PIXGLOBAL;\r
706         }\r
707         else\r
708         {\r
709                 x = ob->x;\r
710         }\r
711         if (CheckSpawnShot(x, ob->y + 32*PIXGLOBAL, &s_rshot1) != -1)\r
712         {\r
713                 new->xspeed = ob->xdir * 60;\r
714                 if (ob->temp1 & 1)\r
715                 {\r
716                         new->yspeed = 8;\r
717                 }\r
718                 else\r
719                 {\r
720                         new->yspeed = -8;\r
721                 }\r
722                 SD_PlaySound(SND_ENEMYSHOT);\r
723                 xtry = (ob->xdir == 1)? -4*PIXGLOBAL : 4*PIXGLOBAL;\r
724         }\r
725 }\r
726 \r
727 /*\r
728 ===========================\r
729 =\r
730 = C_RShot\r
731 =\r
732 ===========================\r
733 */\r
734 \r
735 void C_RShot(objtype *ob, objtype *hit)\r
736 {\r
737         if (hit->obclass == keenobj)\r
738         {\r
739                 KillKeen();\r
740                 ChangeState(ob, &s_rshothit1);\r
741         }\r
742 }\r
743 \r
744 /*\r
745 ===========================\r
746 =\r
747 = R_RShot\r
748 =\r
749 ===========================\r
750 */\r
751 \r
752 void R_RShot(objtype *ob)\r
753 {\r
754         if (ob->hitnorth || ob->hiteast || ob->hitsouth || ob->hitwest)\r
755         {\r
756                 SD_PlaySound(SND_ENEMYSHOTEXPLODE);\r
757                 ChangeState(ob, &s_rshothit1);\r
758         }\r
759         RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);\r
760 }\r
761 \r
762 /*\r
763 =============================================================================\r
764 \r
765                                                   SPIROGRIP\r
766 \r
767 =============================================================================\r
768 */\r
769 \r
770 statetype s_gripsitd  = {SPIROSITDSPR,   SPIROSITDSPR,   step,  false, false, 150,   0,   0, NULL, C_Spindread, R_Draw, &s_gripjumpd};\r
771 statetype s_gripjumpd = {SPIROSITDSPR,   SPIROSITDSPR,   slide, false, false,  64,   0, -16, NULL, C_Spindread, R_Draw, &s_gripspin7};\r
772 statetype s_gripsitl  = {SPIROSITLSPR,   SPIROSITLSPR,   step,  false, false, 150,   0,   0, NULL, C_Spindread, R_Draw, &s_gripjumpl};\r
773 statetype s_gripjumpl = {SPIROSITLSPR,   SPIROSITLSPR,   slide, false, false,  64,  16,   0, NULL, C_Spindread, R_Draw, &s_gripspin1};\r
774 statetype s_gripsitr  = {SPIROSITRSPR,   SPIROSITRSPR,   step,  false, false, 150,   0,   0, NULL, C_Spindread, R_Draw, &s_gripjumpr};\r
775 statetype s_gripjumpr = {SPIROSITRSPR,   SPIROSITRSPR,   slide, false, false,  64, -16,   0, NULL, C_Spindread, R_Draw, &s_gripspin5};\r
776 statetype s_gripsitu  = {SPIROSITUSPR,   SPIROSITUSPR,   step,  false, false, 150,   0,   0, NULL, C_Spindread, R_Draw, &s_gripjumpu};\r
777 statetype s_gripjumpu = {SPIROSITUSPR,   SPIROSITUSPR,   slide, false, false,  64,   0,  16, NULL, C_Spindread, R_Draw, &s_gripspin3};\r
778 statetype s_gripspin1 = {SPIROSPINULSPR, SPIROSPINULSPR, step,  false, false,   8,   0,   0, NULL, C_Spindread, R_Draw, &s_gripspin2};\r
779 statetype s_gripspin2 = {SPIROSPINUSPR,  SPIROSPINUSPR,  step,  false, false,   8,   0,   0, T_SpiroLaunch, C_Spindread, R_Draw, &s_gripspin3};\r
780 statetype s_gripspin3 = {SPIROSPINURSPR, SPIROSPINURSPR, step,  false, false,   8,   0,   0, NULL, C_Spindread, R_Draw, &s_gripspin4};\r
781 statetype s_gripspin4 = {SPIROSPINRSPR,  SPIROSPINRSPR,  step,  false, false,   8,   0,   0, T_SpiroLaunch, C_Spindread, R_Draw, &s_gripspin5};\r
782 statetype s_gripspin5 = {SPIROSPINDRSPR, SPIROSPINDRSPR, step,  false, false,   8,   0,   0, NULL, C_Spindread, R_Draw, &s_gripspin6};\r
783 statetype s_gripspin6 = {SPIROSPINDSPR,  SPIROSPINDSPR,  step,  false, false,   8,   0,   0, T_SpiroLaunch, C_Spindread, R_Draw, &s_gripspin7};\r
784 statetype s_gripspin7 = {SPIROSPINDLSPR, SPIROSPINDLSPR, step,  false, false,   8,   0,   0, NULL, C_Spindread, R_Draw, &s_gripspin8};\r
785 statetype s_gripspin8 = {SPIROSPINLSPR,  SPIROSPINLSPR,  step,  false, false,   8,   0,   0, T_SpiroLaunch, C_Spindread, R_Draw, &s_gripspin1};\r
786 statetype s_gripflyd  = {SPIROSITDSPR,   SPIROSITDSPR,   slide, false, false,   0,   0,  48, NULL, C_Spindread, R_SpiroFly, &s_gripsitd};\r
787 statetype s_gripflyl  = {SPIROSITLSPR,   SPIROSITLSPR,   slide, false, false,   0, -48,   0, NULL, C_Spindread, R_SpiroFly, &s_gripsitl};\r
788 statetype s_gripflyr  = {SPIROSITRSPR,   SPIROSITRSPR,   slide, false, false,   0,  48,   0, NULL, C_Spindread, R_SpiroFly, &s_gripsitr};\r
789 statetype s_gripflyu  = {SPIROSITUSPR,   SPIROSITUSPR,   slide, false, false,   0,   0, -48, NULL, C_Spindread, R_SpiroFly, &s_gripsitu};\r
790 \r
791 /*\r
792 ===========================\r
793 =\r
794 = SpawnSpirogrip\r
795 =\r
796 ===========================\r
797 */\r
798 \r
799 void SpawnSpirogrip(Uint16 tileX, Uint16 tileY)\r
800 {\r
801         GetNewObj(false);\r
802         new->obclass = spirogripobj;\r
803         new->active = ac_yes;\r
804         new->x = CONVERT_TILE_TO_GLOBAL(tileX);\r
805         new->y = CONVERT_TILE_TO_GLOBAL(tileY) + -1*TILEGLOBAL;\r
806         new->xdir = 1;\r
807         new->ydir = 1;\r
808         NewState(new, &s_gripsitd);\r
809 }\r
810 \r
811 /*\r
812 ===========================\r
813 =\r
814 = T_SpiroLaunch\r
815 =\r
816 ===========================\r
817 */\r
818 \r
819 void T_SpiroLaunch(objtype *ob)\r
820 {\r
821         if (US_RndT() <= 20)\r
822         {\r
823                 SD_PlaySound(SND_SPIROFLY);\r
824                 if (ob->state == &s_gripspin2)\r
825                 {\r
826                         ob->state = &s_gripflyu;\r
827                 }\r
828                 else if (ob->state == &s_gripspin4)\r
829                 {\r
830                         ob->state = &s_gripflyr;\r
831                 }\r
832                 else if (ob->state == &s_gripspin6)\r
833                 {\r
834                         ob->state = &s_gripflyd;\r
835                 }\r
836                 else if (ob->state == &s_gripspin8)\r
837                 {\r
838                         ob->state = &s_gripflyl;\r
839                 }\r
840         }\r
841 }\r
842 \r
843 /*\r
844 ===========================\r
845 =\r
846 = R_SpiroFly\r
847 =\r
848 ===========================\r
849 */\r
850 \r
851 void R_SpiroFly(objtype *ob)\r
852 {\r
853         RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);\r
854         if (ob->hitnorth || ob->hiteast || ob->hitsouth || ob->hitwest)\r
855         {\r
856                 ChangeState(ob, ob->state->nextstate);\r
857                 SD_PlaySound(SND_SPIROGRAB);\r
858         }\r
859 }\r
860 \r
861 /*\r
862 =============================================================================\r
863 \r
864                                                   SPINDRED\r
865 \r
866 temp1 = bounce counter\r
867 \r
868 =============================================================================\r
869 */\r
870 \r
871 statetype s_spindred1     = {SPINDRED1SPR, SPINDRED1SPR, stepthink, false, false, 8, 0, 0, T_Spindread, C_Spindread, R_Spindread, &s_spindred2};\r
872 statetype s_spindred2     = {SPINDRED2SPR, SPINDRED2SPR, stepthink, false, false, 8, 0, 0, T_Spindread, C_Spindread, R_Spindread, &s_spindred3};\r
873 statetype s_spindred3     = {SPINDRED3SPR, SPINDRED3SPR, stepthink, false, false, 8, 0, 0, T_Spindread, C_Spindread, R_Spindread, &s_spindred4};\r
874 statetype s_spindred4     = {SPINDRED4SPR, SPINDRED4SPR, stepthink, false, false, 8, 0, 0, T_Spindread, C_Spindread, R_Spindread, &s_spindred1};\r
875 \r
876 /*\r
877 ===========================\r
878 =\r
879 = SpawnSpindread\r
880 =\r
881 ===========================\r
882 */\r
883 \r
884 void SpawnSpindread(Uint16 tileX, Uint16 tileY)\r
885 {\r
886         GetNewObj(false);\r
887         new->obclass = spindredobj;\r
888         new->active = ac_yes;\r
889         new->priority = 0;\r
890         new->x = CONVERT_TILE_TO_GLOBAL(tileX);\r
891         new->y = CONVERT_TILE_TO_GLOBAL(tileY) + -8*PIXGLOBAL;\r
892         new->ydir = 1;\r
893         NewState(new, &s_spindred1);\r
894 }\r
895 \r
896 /*\r
897 ===========================\r
898 =\r
899 = T_Spindread\r
900 =\r
901 ===========================\r
902 */\r
903 \r
904 void T_Spindread(objtype *ob)\r
905 {\r
906         Sint32 i;\r
907 \r
908         // BUG: this might be executed twice during the same frame if the\r
909         // object's animation/state changed during that frame, causing the\r
910         // object to move at twice the speed during that frame!\r
911         // To avoid that, return if ytry is not 0.\r
912 \r
913         for (i=lasttimecount-tics; i<lasttimecount; i++)\r
914         {\r
915                 if (i & 1)\r
916                 {\r
917                         if (ob->ydir == 1)\r
918                         {\r
919                                 if (ob->yspeed < 0 && ob->yspeed >= -3)\r
920                                 {\r
921                                         ytry += ob->yspeed;\r
922                                         ob->yspeed = 0;\r
923                                         return;\r
924                                 }\r
925                                 else\r
926                                 {\r
927                                         ob->yspeed += 3;\r
928                                         if (ob->yspeed > 70)\r
929                                         {\r
930                                                 ob->yspeed = 70;\r
931                                         }\r
932                                 }\r
933                         }\r
934                         else\r
935                         {\r
936                                 if (ob->yspeed > 0 && ob->yspeed <= 3)\r
937                                 {\r
938                                         ytry += ob->yspeed;\r
939                                         ob->yspeed = 0;\r
940                                         return;\r
941                                 }\r
942                                 else\r
943                                 {\r
944                                         ob->yspeed -= 3;\r
945                                         if (ob->yspeed < -70)\r
946                                         {\r
947                                                 ob->yspeed = -70;\r
948                                         }\r
949                                 }\r
950                         }\r
951                 }\r
952                 ytry += ob->yspeed;\r
953         }\r
954 }\r
955 \r
956 /*\r
957 ===========================\r
958 =\r
959 = C_Spindread\r
960 =\r
961 ===========================\r
962 */\r
963 \r
964 #pragma argsused\r
965 void C_Spindread(objtype *ob, objtype *hit)\r
966 {\r
967         if (hit->obclass == stunshotobj)\r
968         {\r
969                 ExplodeShot(hit);\r
970         }\r
971         else if (hit->obclass == keenobj)\r
972         {\r
973                 KillKeen();\r
974         }\r
975 }\r
976 \r
977 /*\r
978 ===========================\r
979 =\r
980 = R_Spindread\r
981 =\r
982 ===========================\r
983 */\r
984 \r
985 void R_Spindread(objtype *ob)\r
986 {\r
987         if (ob->hitsouth)\r
988         {\r
989                 ob->yspeed = 0;\r
990                 if (ob->ydir == -1)\r
991                 {\r
992                         if (++ob->temp1 == 3)\r
993                         {\r
994                                 ob->temp1 = 0;\r
995                                 ob->yspeed = 68;\r
996                                 ob->ydir = -ob->ydir;\r
997                                 SD_PlaySound(SND_SPINDREDFLIP);\r
998                         }\r
999                         else\r
1000                         {\r
1001                                 SD_PlaySound(SND_SPINDREDBOUNCE);\r
1002                                 ob->yspeed = 40;\r
1003                         }\r
1004                 }\r
1005         }\r
1006         if (ob->hitnorth)\r
1007         {\r
1008                 ob->yspeed = 0;\r
1009                 if (ob->ydir == 1)\r
1010                 {\r
1011                         if (++ob->temp1 == 3)\r
1012                         {\r
1013                                 ob->temp1 = 0;\r
1014                                 ob->yspeed = -68;\r
1015                                 ob->ydir = -ob->ydir;\r
1016                                 SD_PlaySound(SND_BOUNCE);\r
1017                         }\r
1018                         else\r
1019                         {\r
1020                                 SD_PlaySound(SND_SPINDREDBOUNCE);\r
1021                                 ob->yspeed = -40;\r
1022                         }\r
1023                 }\r
1024         }\r
1025         RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);\r
1026 }\r
1027 \r
1028 /*\r
1029 =============================================================================\r
1030 \r
1031                                                   SHIKADI MASTER\r
1032 \r
1033 temp1 = defines next action (0 = shoot, 1 = teleport)\r
1034 \r
1035 =============================================================================\r
1036 */\r
1037 \r
1038 statetype s_master1      = {MASTER1SPR,           MASTER1SPR,           step,      false, false,  8, 0, 0, NULL, C_Master, R_Draw, &s_master2};\r
1039 statetype s_master2      = {MASTER2SPR,           MASTER2SPR,           step,      false, false,  8, 0, 0, NULL, C_Master, R_Draw, &s_master3};\r
1040 statetype s_master3      = {MASTER3SPR,           MASTER3SPR,           step,      false, false,  8, 0, 0, NULL, C_Master, R_Draw, &s_master4};\r
1041 statetype s_master4      = {MASTER4SPR,           MASTER4SPR,           step,      false, false,  8, 0, 0, T_Master, C_Master, R_Draw, &s_master1};\r
1042 statetype s_mastershoot1 = {SHIKMASTERCASTLSPR,   SHIKMASTERCASTRSPR,   step,      false, false, 30, 0, 0, T_MasterShoot, C_Spindread, R_Draw, &s_mastershoot2};\r
1043 statetype s_mastershoot2 = {SHIKMASTERCASTLSPR,   SHIKMASTERCASTRSPR,   step,      false, false,  8, 0, 0, NULL, C_Spindread, R_Draw, &s_master1};\r
1044 statetype s_mastertport1 = {MASTERTELEPORT1SPR,   MASTERTELEPORT1SPR,   step,      false, true,  20, 0, 0, NULL, C_Spindread, R_Draw, &s_mastertport2};\r
1045 statetype s_mastertport2 = {MASTERTELEPORT2SPR,   MASTERTELEPORT2SPR,   step,      false, true,  20, 0, 0, T_MasterTPort, C_Spindread, R_Draw, &s_mastertport3};\r
1046 statetype s_mastertport3 = {MASTERTELEPORT2SPR,   MASTERTELEPORT2SPR,   think,     false, false,  0, 0, 0, T_Projectile, NULL, R_Land, &s_mastertport4};\r
1047 statetype s_mastertport4 = {MASTERTELEPORT2SPR,   MASTERTELEPORT2SPR,   step,      false, false, 60, 0, 0, NULL, C_Spindread, R_Draw, &s_master1};\r
1048 statetype s_mshot1       = {MASTERSHOT4SPR,       MASTERSHOT1SPR,       stepthink, false, false,  8, 0, 0, T_WeakProjectile, C_MShot, R_MShot, &s_mshot2};\r
1049 statetype s_mshot2       = {MASTERSHOT3SPR,       MASTERSHOT2SPR,       stepthink, false, false,  8, 0, 0, T_WeakProjectile, C_MShot, R_MShot, &s_mshot3};\r
1050 statetype s_mshot3       = {MASTERSHOT2SPR,       MASTERSHOT3SPR,       stepthink, false, false,  8, 0, 0, T_WeakProjectile, C_MShot, R_MShot, &s_mshot4};\r
1051 statetype s_mshot4       = {MASTERSHOT1SPR,       MASTERSHOT4SPR,       stepthink, false, false,  8, 0, 0, T_WeakProjectile, C_MShot, R_MShot, &s_mshot1};\r
1052 statetype s_mspray1      = {MASTERFLOORSPARK1SPR, MASTERFLOORSPARK1SPR, stepthink, false, false,  6, 0, 0, T_Projectile, C_MShot, R_MSpray, &s_mspray2};\r
1053 statetype s_mspray2      = {MASTERFLOORSPARK2SPR, MASTERFLOORSPARK2SPR, stepthink, false, false,  6, 0, 0, T_Projectile, C_MShot, R_MSpray, &s_mspray3};\r
1054 statetype s_mspray3      = {MASTERFLOORSPARK3SPR, MASTERFLOORSPARK3SPR, stepthink, false, false,  6, 0, 0, T_Projectile, C_MShot, R_MSpray, &s_mspray4};\r
1055 statetype s_mspray4      = {MASTERFLOORSPARK4SPR, MASTERFLOORSPARK4SPR, stepthink, false, false,  6, 0, 0, T_Projectile, C_MShot, R_MSpray, &s_mspray1};\r
1056 \r
1057 /*\r
1058 ===========================\r
1059 =\r
1060 = SpawnMaster\r
1061 =\r
1062 ===========================\r
1063 */\r
1064 \r
1065 void SpawnMaster(Uint16 tileX, Uint16 tileY)\r
1066 {\r
1067         GetNewObj(false);\r
1068         new->obclass = shikadimasterobj;\r
1069         new->active = ac_yes;\r
1070         new->x = CONVERT_TILE_TO_GLOBAL(tileX);\r
1071         new->y = CONVERT_TILE_TO_GLOBAL(tileY) + -24*PIXGLOBAL;\r
1072         NewState(new, &s_master1);\r
1073 }\r
1074 \r
1075 /*\r
1076 ===========================\r
1077 =\r
1078 = T_Master\r
1079 =\r
1080 ===========================\r
1081 */\r
1082 \r
1083 void T_Master(objtype *ob)\r
1084 {\r
1085         Sint16 randval;\r
1086 \r
1087         randval = US_RndT();\r
1088         if (randval < 0x40)\r
1089         {\r
1090                 if (ob->temp1)\r
1091                 {\r
1092                         ob->state = &s_mastertport1;\r
1093                         ob->temp1 = 0;\r
1094                 }\r
1095                 else\r
1096                 {\r
1097                         ob->temp1 = 1;\r
1098                         if (player->x > ob->x)\r
1099                         {\r
1100                                 ob->xdir = 1;\r
1101                         }\r
1102                         else\r
1103                         {\r
1104                                 ob->xdir = -1;\r
1105                         }\r
1106                         ob->state = &s_mastershoot1;\r
1107                 }\r
1108         }\r
1109 }\r
1110 \r
1111 /*\r
1112 ===========================\r
1113 =\r
1114 = T_MasterShoot\r
1115 =\r
1116 ===========================\r
1117 */\r
1118 \r
1119 void T_MasterShoot(objtype *ob)\r
1120 {\r
1121         Uint16 x;\r
1122 \r
1123         if (ob->xdir == 1)\r
1124         {\r
1125                 x = ob->x + 16*PIXGLOBAL;\r
1126         }\r
1127         else\r
1128         {\r
1129                 x = ob->x;\r
1130         }\r
1131         if (CheckSpawnShot(x, ob->y+8*PIXGLOBAL, &s_mshot1) != -1)\r
1132         {\r
1133                 new->xspeed = ob->xdir * 48;\r
1134                 new->yspeed = -16;\r
1135                 SD_PlaySound(SND_MASTERATTACK);\r
1136         }\r
1137 }\r
1138 \r
1139 /*\r
1140 ===========================\r
1141 =\r
1142 = C_Master\r
1143 =\r
1144 ===========================\r
1145 */\r
1146 \r
1147 void C_Master(objtype *ob, objtype *hit)\r
1148 {\r
1149         if (hit->obclass == stunshotobj)\r
1150         {\r
1151                 ExplodeShot(hit);\r
1152                 ob->xdir = (player->x > ob->x)? 1 : -1;\r
1153                 ob->temp1 = 1;\r
1154                 ChangeState(ob, &s_mastershoot1);\r
1155         }\r
1156         else if (hit->obclass == keenobj)\r
1157         {\r
1158                 KillKeen();\r
1159         }\r
1160 }\r
1161 \r
1162 /*\r
1163 ===========================\r
1164 =\r
1165 = T_MasterTPort\r
1166 =\r
1167 ===========================\r
1168 */\r
1169 \r
1170 void T_MasterTPort(objtype *ob)\r
1171 {\r
1172         Uint16 tile;\r
1173         Sint16 tx, ty, redos, oldx, oldy;\r
1174 \r
1175         oldx = ob->x;\r
1176         oldy = ob->y;\r
1177 \r
1178         GetNewObj(true);\r
1179         new->x = ob->x;\r
1180         new->y = ob->y;\r
1181         new->xspeed = 48;\r
1182         NewState(new, &s_mspray1);      // BUG? new object is not made removable\r
1183 \r
1184         GetNewObj(true);\r
1185         new->x = ob->x;\r
1186         new->y = ob->y;\r
1187         new->xspeed = -48;\r
1188         NewState(new, &s_mspray1);      // BUG? new object is not made removable\r
1189 \r
1190         SD_PlaySound(SND_MASTERBLAST);\r
1191 \r
1192         redos = 0;\r
1193 redo:\r
1194         if (++redos == 10)\r
1195         {\r
1196                 US_RndT();      // call it, but ignore the result\r
1197                 // probably to avoid repeatedly getting the same same "random" values\r
1198                 // due to having an even number of US_RndT() calls in this routine and\r
1199                 // an even number of elements in the random table.\r
1200 \r
1201                 ob->state = &s_master1;\r
1202                 ob->x = oldx - 1;\r
1203                 ob->y = oldy;\r
1204                 xtry = 1;\r
1205                 ytry = 0;\r
1206         }\r
1207         else\r
1208         {\r
1209                 tx = player->tilemidx - 16 + (US_RndT()>>3);\r
1210                 ty = player->tilebottom - 16 + (US_RndT()>>3);\r
1211                 if (tx < 2 || ty < 2 || tx > mapwidth-5 || ty > mapheight-5)\r
1212                         goto redo;\r
1213 \r
1214 \r
1215                 ob->x = CONVERT_TILE_TO_GLOBAL(tx);\r
1216                 ob->y = CONVERT_TILE_TO_GLOBAL(ty);\r
1217                 ob->tileleft = tx-1;\r
1218                 ob->tileright = tx+4;\r
1219                 ob->tiletop = ty-1;\r
1220                 ob->tilebottom = ty+4;\r
1221 \r
1222                 {\r
1223                         Uint16 x, y;\r
1224                         Uint16 far *map;\r
1225                         Uint16 mapdelta;\r
1226 \r
1227                         map = (Uint16 far *)mapsegs[1] + mapbwidthtable[ob->tiletop]/2 + ob->tileleft;\r
1228                         mapdelta = mapwidth - (ob->tileright - ob->tileleft + 1);\r
1229 \r
1230                         for (y = ob->tiletop; ob->tilebottom >= y; y++, map+=mapdelta)\r
1231                         {\r
1232                                 for (x = ob->tileleft; ob->tileright >= x; x++)\r
1233                                 {\r
1234                                         tile = *map++;\r
1235                                         if ((tinf[tile+INTILE] & INTILE_FOREGROUND) || tinf[tile+NORTHWALL] || tinf[tile+EASTWALL]\r
1236                                                 || tinf[tile+SOUTHWALL] || tinf[tile+WESTWALL])\r
1237                                         {\r
1238                                                 goto redo;\r
1239                                         }\r
1240                                 }\r
1241                         }\r
1242                         xtry = ytry = 0;\r
1243                 }\r
1244         }\r
1245 }\r
1246 \r
1247 /*\r
1248 ===========================\r
1249 =\r
1250 = C_MShot\r
1251 =\r
1252 ===========================\r
1253 */\r
1254 \r
1255 void C_MShot(objtype *ob, objtype *hit)\r
1256 {\r
1257         if (hit->obclass == keenobj)\r
1258         {\r
1259                 KillKeen();\r
1260                 RemoveObj(ob);\r
1261         }\r
1262         else if (hit->obclass == stunshotobj)\r
1263         {\r
1264                 ExplodeShot(hit);\r
1265                 RemoveObj(ob);\r
1266         }\r
1267 }\r
1268 \r
1269 /*\r
1270 ===========================\r
1271 =\r
1272 = R_MShot\r
1273 =\r
1274 ===========================\r
1275 */\r
1276 \r
1277 void R_MShot(objtype *ob)\r
1278 {\r
1279         if (ob->hiteast || ob->hitwest)\r
1280         {\r
1281                 ob->xspeed = -ob->xspeed;\r
1282         }\r
1283         if (ob->hitsouth)\r
1284         {\r
1285                 ob->yspeed = 0;\r
1286         }\r
1287         if (ob->hitnorth)\r
1288         {\r
1289                 SD_PlaySound(SND_MASTERATTACK);\r
1290                 ob->xspeed = 48;\r
1291                 ChangeState(ob, &s_mspray1);\r
1292 \r
1293                 GetNewObj(true);\r
1294                 new->x = ob->x;\r
1295                 new->y = ob->y;\r
1296                 new->xspeed = -48;\r
1297                 NewState(new, &s_mspray1);      // BUG? new object is not made removable\r
1298         }\r
1299         RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);\r
1300 }\r
1301 \r
1302 /*\r
1303 ===========================\r
1304 =\r
1305 = R_MSpray\r
1306 =\r
1307 ===========================\r
1308 */\r
1309 \r
1310 void R_MSpray(objtype *ob)\r
1311 {\r
1312         if (ob->hiteast || ob->hitwest)\r
1313         {\r
1314                 RemoveObj(ob);\r
1315         }\r
1316         else\r
1317         {\r
1318                 RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);\r
1319         }\r
1320 }\r
1321 \r
1322 /*\r
1323 =============================================================================\r
1324 \r
1325                                                   SHIKADI\r
1326 \r
1327 temp1 = x tile position of the pole being grabbed (is set but never used)\r
1328 temp2 = health\r
1329 temp3 = flash countdown\r
1330 \r
1331 =============================================================================\r
1332 */\r
1333 \r
1334 statetype s_shikadi1     = {SHIKADI1SPR,      SHIKADI1SPR,      step,  false, true,  8,   0, 0, NULL, C_Shikadi, R_Shikadi, &s_shikadi2};\r
1335 statetype s_shikadi2     = {SHIKADI2SPR,      SHIKADI2SPR,      step,  false, true,  8,   0, 0, NULL, C_Shikadi, R_Shikadi, &s_shikadi3};\r
1336 statetype s_shikadi3     = {SHIKADI3SPR,      SHIKADI3SPR,      step,  false, true,  8,   0, 0, NULL, C_Shikadi, R_Shikadi, &s_shikadi4};\r
1337 statetype s_shikadi4     = {SHIKADI4SPR,      SHIKADI4SPR,      step,  false, true,  8,   0, 0, NULL, C_Shikadi, R_Shikadi, &s_shikadiwalk1};\r
1338 statetype s_shikadiwalk1 = {SHIKADIWALKL1SPR, SHIKADIWALKR1SPR, step,  false, true,  8, 128, 0, T_Shikadi, C_Shikadi, R_Shikadi, &s_shikadiwalk2};\r
1339 statetype s_shikadiwalk2 = {SHIKADIWALKL2SPR, SHIKADIWALKR2SPR, step,  false, true,  8, 128, 0, T_Shikadi, C_Shikadi, R_Shikadi, &s_shikadiwalk3};\r
1340 statetype s_shikadiwalk3 = {SHIKADIWALKL3SPR, SHIKADIWALKR3SPR, step,  false, true,  8, 128, 0, T_Shikadi, C_Shikadi, R_Shikadi, &s_shikadiwalk4};\r
1341 statetype s_shikadiwalk4 = {SHIKADIWALKL4SPR, SHIKADIWALKR4SPR, step,  false, true,  8, 128, 0, T_Shikadi, C_Shikadi, R_Shikadi, &s_shikadiwalk1};\r
1342 statetype s_shikadigrab  = {SHIKADIGRABLSPR,  SHIKADIGRABRSPR,  step,  false, true, 20,   0, 0, T_PoleShock, C_Shikadi, R_Shikadi, &s_shikadigrab2};\r
1343 statetype s_shikadigrab2 = {SHIKADIGRABLSPR,  SHIKADIGRABRSPR,  step,  false, true, 20,   0, 0, NULL, C_Shikadi, R_Shikadi, &s_shikadiwalk1};\r
1344 statetype s_shikadistun  = {SHIKADISTUNSPR,   SHIKADISTUNSPR,   think, false, false, 0,   0, 0, T_Projectile, NULL, R_Stunned, NULL};\r
1345 \r
1346 statetype s_polespark1   = {SHIKADIPOLESPARK1SPR, SHIKADIPOLESPARK1SPR, stepthink, false, false,  0,   0, 0, T_PoleSpark, C_Lethal, R_Draw, &s_polespark2};\r
1347 statetype s_polespark2   = {SHIKADIPOLESPARK1SPR, SHIKADIPOLESPARK1SPR, stepthink, false, false,  0,   0, 0, T_PoleSpark, C_Lethal, R_Draw, &s_polespark1};\r
1348 \r
1349 /*\r
1350 ===========================\r
1351 =\r
1352 = SpawnShikadi\r
1353 =\r
1354 ===========================\r
1355 */\r
1356 \r
1357 void SpawnShikadi(Uint16 tileX, Uint16 tileY)\r
1358 {\r
1359         GetNewObj(false);\r
1360         new->obclass = shikadiobj;\r
1361         new->active = ac_yes;\r
1362         new->x = CONVERT_TILE_TO_GLOBAL(tileX);\r
1363         new->y = CONVERT_TILE_TO_GLOBAL(tileY) + -1*TILEGLOBAL;\r
1364         new->temp2 = 4; // health\r
1365         if (US_RndT() < 0x80)\r
1366         {\r
1367                 new->xdir = 1;\r
1368         }\r
1369         else\r
1370         {\r
1371                 new->xdir = -1;\r
1372         }\r
1373         NewState(new, &s_shikadi1);\r
1374 }\r
1375 \r
1376 /*\r
1377 ===========================\r
1378 =\r
1379 = T_Shikadi\r
1380 =\r
1381 ===========================\r
1382 */\r
1383 \r
1384 void T_Shikadi(objtype *ob)\r
1385 {\r
1386         Uint16 tx, tile;\r
1387 \r
1388         if (player->state->contact == &KeenPosContact\r
1389                 || ob->bottom - player->bottom + TILEGLOBAL <= 2*TILEGLOBAL)\r
1390         {\r
1391                 if (ob->x > player->x + TILEGLOBAL)\r
1392                 {\r
1393                         ob->xdir = -1;\r
1394                 }\r
1395                 else if (ob->x < player->x - TILEGLOBAL)\r
1396                 {\r
1397                         ob->xdir = 1;\r
1398                 }\r
1399                 if (ob->xdir == 1)\r
1400                 {\r
1401                         tx = ob->tileright;\r
1402                 }\r
1403                 else\r
1404                 {\r
1405                         tx = ob->tileleft;\r
1406                 }\r
1407 \r
1408                 if (player->tilemidx != tx)\r
1409                         return;\r
1410         }\r
1411         else\r
1412         {\r
1413                 if (US_RndT() < 0x10)\r
1414                 {\r
1415                         xtry = 0;\r
1416                         ob->state = &s_shikadi1;\r
1417                         return;\r
1418                 }\r
1419                 if ((ob->x & 0xFF) || !OnScreen(ob))\r
1420                         return;\r
1421 \r
1422                 if (ob->xdir == 1)\r
1423                 {\r
1424                         tx = ob->tileright;\r
1425                 }\r
1426                 else\r
1427                 {\r
1428                         tx = ob->tileleft;\r
1429                 }\r
1430         }\r
1431 \r
1432         tile = *(mapsegs[1]+mapbwidthtable[ob->tiletop]/2 + tx);\r
1433         if (tinf[tile+INTILE] == INTILE_POLE)\r
1434         {\r
1435                 ob->temp1 = tx;\r
1436                 ob->state = &s_shikadigrab;\r
1437                 xtry = 0;\r
1438                 SD_PlaySound(SND_SHIKADIATTACK);\r
1439         }\r
1440 }\r
1441 \r
1442 /*\r
1443 ===========================\r
1444 =\r
1445 = C_Shikadi\r
1446 =\r
1447 ===========================\r
1448 */\r
1449 \r
1450 void C_Shikadi(objtype *ob, objtype *hit)\r
1451 {\r
1452         if (hit->obclass == keenobj)\r
1453         {\r
1454                 KillKeen();\r
1455         }\r
1456         if (hit->obclass == stunshotobj)\r
1457         {\r
1458                 if (--ob->temp2 == 0)   // handle health\r
1459                 {\r
1460                         ob->xspeed = 0;\r
1461                         ob->yspeed = 0;\r
1462                         StunObj(ob, hit, &s_shikadistun);\r
1463                 }\r
1464                 else\r
1465                 {\r
1466                         ob->temp3 = 2;  // draw white two times\r
1467                         ob->needtoreact = true;\r
1468                         ExplodeShot(hit);\r
1469                 }\r
1470         }\r
1471 }\r
1472 \r
1473 /*\r
1474 ===========================\r
1475 =\r
1476 = T_PoleShock\r
1477 =\r
1478 ===========================\r
1479 */\r
1480 \r
1481 void T_PoleShock(objtype *ob)\r
1482 {\r
1483         Uint16 x;\r
1484 \r
1485         ob->nothink = 2;\r
1486         if (ob->xdir == 1)\r
1487         {\r
1488                 x = CONVERT_TILE_TO_GLOBAL(ob->tileright);\r
1489         }\r
1490         else\r
1491         {\r
1492                 x = CONVERT_TILE_TO_GLOBAL(ob->tileleft);\r
1493         }\r
1494 \r
1495         GetNewObj(true);\r
1496         new->x = x;\r
1497         new->y = ob->y + 8*PIXGLOBAL;\r
1498         new->obclass = mshotobj;\r
1499         new->active = ac_removable;\r
1500         new->needtoclip = cl_noclip;\r
1501         NewState(new, &s_polespark1);\r
1502         if (ob->y < player->y)\r
1503         {\r
1504                 new->ydir = 1;\r
1505         }\r
1506         else\r
1507         {\r
1508                 new->ydir = -1;\r
1509         }\r
1510         SD_PlaySound(SND_SHIKADIATTACK);\r
1511 }\r
1512 \r
1513 /*\r
1514 ===========================\r
1515 =\r
1516 = T_PoleSpark\r
1517 =\r
1518 ===========================\r
1519 */\r
1520 \r
1521 void T_PoleSpark(objtype *ob)\r
1522 {\r
1523         Uint16 tile;\r
1524 \r
1525         if (ytry == 0)\r
1526         {\r
1527                 ytry = ob->ydir * 48;\r
1528                 tile = *(mapsegs[1]+mapbwidthtable[ob->tiletop]/2 + ob->tilemidx);\r
1529                 if (tinf[tile+INTILE] != INTILE_POLE)\r
1530                 {\r
1531                         ob->state = NULL;\r
1532                 }\r
1533         }\r
1534 }\r
1535 \r
1536 /*\r
1537 ===========================\r
1538 =\r
1539 = R_Shikadi\r
1540 =\r
1541 ===========================\r
1542 */\r
1543 \r
1544 void R_Shikadi(objtype *ob)\r
1545 {\r
1546         if (ob->xdir == 1 && ob->hitwest)\r
1547         {\r
1548                 ob->x -= ob->xmove;\r
1549                 ob->xdir = -1;\r
1550                 ob->nothink = US_RndT() >> 5;\r
1551                 ChangeState(ob, ob->state);\r
1552         }\r
1553         else if (ob->xdir == -1 && ob->hiteast)\r
1554         {\r
1555                 ob->x -= ob->xmove;\r
1556                 ob->xdir = 1;\r
1557                 ob->nothink = US_RndT() >> 5;\r
1558                 ChangeState(ob, ob->state);\r
1559         }\r
1560         else if (!ob->hitnorth)\r
1561         {\r
1562                 ob->x -= ob->xmove;\r
1563                 ob->xdir = -ob->xdir;\r
1564                 ob->nothink = US_RndT() >> 5;\r
1565                 ChangeState(ob, ob->state);\r
1566         }\r
1567         if (ob->temp3)\r
1568         {\r
1569                 ob->temp3--;\r
1570                 RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, maskdraw, ob->priority);\r
1571         }\r
1572         else\r
1573         {\r
1574                 RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);\r
1575         }\r
1576 }\r
1577 \r
1578 /*\r
1579 =============================================================================\r
1580 \r
1581                                                   PET (a.k.a. SHOCKSHUND)\r
1582 \r
1583 temp1 = countdown for sit animation\r
1584 temp2 = health\r
1585 temp3 = flash countdown\r
1586 \r
1587 =============================================================================\r
1588 */\r
1589 \r
1590 statetype s_petsit1   = {PETSIT1SPR,      PETSIT1SPR,      step,      false, true,   8,   0, 0, NULL, C_Pet, R_Pet, &s_petsit2};\r
1591 statetype s_petsit2   = {PETSIT2SPR,      PETSIT2SPR,      step,      false, true,   8,   0, 0, T_PetSit, C_Pet, R_Pet, &s_petsit1};\r
1592 statetype s_petbark1  = {PETBARKL1SPR,    PETBARKR1SPR,    step,      false, true,   8,   0, 0, NULL, C_Pet, R_Pet, &s_petbark2};\r
1593 statetype s_petbark2  = {PETBARKL2SPR,    PETBARKR2SPR,    step,      false, true,   8,   0, 0, T_PetBark, C_Pet, R_Pet, &s_pet1};\r
1594 statetype s_pet1      = {PETRUNL1SPR,     PETRUNR1SPR,     step,      false, true,   8, 128, 0, NULL, C_Pet, R_Pet, &s_pet2};\r
1595 statetype s_pet2      = {PETRUNL2SPR,     PETRUNR2SPR,     step,      false, true,   8, 128, 0, NULL, C_Pet, R_Pet, &s_pet3};\r
1596 statetype s_pet3      = {PETRUNL3SPR,     PETRUNR3SPR,     step,      false, true,   8, 128, 0, NULL, C_Pet, R_Pet, &s_pet4};\r
1597 statetype s_pet4      = {PETRUNL4SPR,     PETRUNR4SPR,     step,      false, true,   8, 128, 0, T_Pet, C_Pet, R_Pet, &s_pet1};\r
1598 statetype s_petjump   = {PETJUMPLSPR,     PETJUMPRSPR,     think,     false, false,  8, 128, 0, T_Projectile, C_Pet, R_PetJump, &s_pet2};\r
1599 statetype s_pshot1    = {PETSPARK1SPR,    PETSPARK1SPR,    stepthink, false, false,  8,   0, 0, T_Velocity, C_PShot, R_PShot, &s_pshot2};\r
1600 statetype s_pshot2    = {PETSPARK2SPR,    PETSPARK2SPR,    stepthink, false, false,  8,   0, 0, T_Velocity, C_PShot, R_PShot, &s_pshot1};\r
1601 statetype s_pshothot1 = {PETSPARKHIT1SPR, PETSPARKHIT1SPR, step,      false, false, 10,   0, 0, NULL, NULL, R_Draw, &s_pshothot2};\r
1602 statetype s_pshothot2 = {PETSPARKHIT2SPR, PETSPARKHIT2SPR, step,      false, false, 10,   0, 0, NULL, NULL, R_Draw, NULL};\r
1603 statetype s_petstun   = {PETSTUNSPR,      PETSTUNSPR,      think,     false, false,  0,   0, 0, T_Projectile, NULL, R_Stunned, NULL};\r
1604 \r
1605 /*\r
1606 ===========================\r
1607 =\r
1608 = SpawnPet\r
1609 =\r
1610 ===========================\r
1611 */\r
1612 \r
1613 void SpawnPet(Uint16 tileX, Uint16 tileY)\r
1614 {\r
1615         GetNewObj(false);\r
1616         new->obclass = petobj;\r
1617         new->active = ac_yes;\r
1618         new->x = CONVERT_TILE_TO_GLOBAL(tileX);\r
1619         new->y = CONVERT_TILE_TO_GLOBAL(tileY) + -8*PIXGLOBAL;\r
1620         new->temp2 = 2; // health\r
1621         new->xdir = new->ydir = 1;\r
1622         NewState(new, &s_pet1);\r
1623 }\r
1624 \r
1625 /*\r
1626 ===========================\r
1627 =\r
1628 = T_Pet\r
1629 =\r
1630 ===========================\r
1631 */\r
1632 \r
1633 void T_Pet(objtype *ob)\r
1634 {\r
1635         if (ob->x > player->x)\r
1636         {\r
1637                 ob->xdir = -1;\r
1638         }\r
1639         else\r
1640         {\r
1641                 ob->xdir = 1;\r
1642         }\r
1643         if (US_RndT() < 0x10)\r
1644         {\r
1645                 xtry = 0;\r
1646                 ob->state = &s_petsit1;\r
1647                 ob->temp1 = 10; // repeat animation 10 times;\r
1648         }\r
1649         else\r
1650         {\r
1651                 if (ob->bottom != player->bottom)\r
1652                 {\r
1653                         ob->state = &s_petjump;\r
1654                         if (ob->xdir == 1)\r
1655                         {\r
1656                                 ob->xspeed = 40;\r
1657                         }\r
1658                         else\r
1659                         {\r
1660                                 ob->xspeed = -40;\r
1661                         }\r
1662                         ob->yspeed = -40;\r
1663                 }\r
1664                 if (US_RndT() < 0x80)\r
1665                 {\r
1666                         xtry = 0;\r
1667                         ob->state = &s_petbark1;\r
1668                 }\r
1669         }\r
1670 }\r
1671 \r
1672 /*\r
1673 ===========================\r
1674 =\r
1675 = T_PetSit\r
1676 =\r
1677 ===========================\r
1678 */\r
1679 \r
1680 void T_PetSit(objtype *ob)\r
1681 {\r
1682         if (--ob->temp1 == 0)\r
1683         {\r
1684                 ob->state = &s_pet1;\r
1685         }\r
1686 }\r
1687 \r
1688 /*\r
1689 ===========================\r
1690 =\r
1691 = T_PetBark\r
1692 =\r
1693 ===========================\r
1694 */\r
1695 \r
1696 void T_PetBark(objtype *ob)\r
1697 {\r
1698         Uint16 x;\r
1699 \r
1700         if (ob->xdir == 1)\r
1701         {\r
1702                 x = ob->x + 7*PIXGLOBAL;\r
1703         }\r
1704         else\r
1705         {\r
1706                 x = ob->x;\r
1707         }\r
1708         if (CheckSpawnShot(x, ob->y+4*PIXGLOBAL, &s_pshot1) != -1)\r
1709         {\r
1710                 new->xspeed = ob->xdir * 60;\r
1711                 new->xdir = ob->xdir;\r
1712                 SD_PlaySound(SND_SHOCKSHUNDBARK);\r
1713         }\r
1714 }\r
1715 \r
1716 /*\r
1717 ===========================\r
1718 =\r
1719 = C_Pet\r
1720 =\r
1721 ===========================\r
1722 */\r
1723 \r
1724 void C_Pet(objtype *ob, objtype *hit)\r
1725 {\r
1726         if (hit->obclass == keenobj)\r
1727         {\r
1728                 KillKeen();\r
1729         }\r
1730         if (hit->obclass == stunshotobj)        // no 'else if' in the original code!\r
1731         {\r
1732                 if (--ob->temp2 == 0)   // handle health\r
1733                 {\r
1734                         ob->xspeed = 0;\r
1735                         ob->yspeed = 0;\r
1736                         StunObj(ob, hit, &s_petstun);\r
1737                 }\r
1738                 else\r
1739                 {\r
1740                         ob->temp3 = 2;  // draw white two times\r
1741                         ob->needtoreact = true;\r
1742                         ExplodeShot(hit);\r
1743                 }\r
1744         }\r
1745 }\r
1746 \r
1747 /*\r
1748 ===========================\r
1749 =\r
1750 = R_Pet\r
1751 =\r
1752 ===========================\r
1753 */\r
1754 \r
1755 void R_Pet(objtype *ob)\r
1756 {\r
1757         if (ob->xdir == 1 && ob->hitwest)\r
1758         {\r
1759                 ob->x -= ob->xmove;\r
1760                 ob->xdir = -1;\r
1761                 ob->nothink = US_RndT() >> 5;\r
1762                 ChangeState(ob, ob->state);\r
1763         }\r
1764         else if (ob->xdir == -1 && ob->hiteast)\r
1765         {\r
1766                 ob->x -= ob->xmove;\r
1767                 ob->xdir = 1;\r
1768                 ob->nothink = US_RndT() >> 5;\r
1769                 ChangeState(ob, ob->state);\r
1770         }\r
1771         else if (!ob->hitnorth)\r
1772         {\r
1773                 if ((ob->xdir == 1 && player->x > ob->x) || (ob->xdir == -1 && player->x < ob->x))\r
1774                 {\r
1775                         ChangeState(ob, &s_petjump);\r
1776                         if (ob->xdir == 1)\r
1777                         {\r
1778                                 ob->xspeed = 40;\r
1779                         }\r
1780                         else\r
1781                         {\r
1782                                 ob->xspeed = -40;\r
1783                         }\r
1784                         ob->yspeed = -40;\r
1785                 }\r
1786                 else\r
1787                 {\r
1788                         ob->x -= ob->xmove*2;\r
1789                         ob->xdir = -ob->xdir;\r
1790                         ob->nothink = US_RndT() >> 5;\r
1791                         ChangeState(ob, ob->state);\r
1792                 }\r
1793         }\r
1794         if (ob->temp3)\r
1795         {\r
1796                 ob->temp3--;\r
1797                 RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, maskdraw, ob->priority);\r
1798         }\r
1799         else\r
1800         {\r
1801                 RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);\r
1802         }\r
1803 }\r
1804 \r
1805 /*\r
1806 ===========================\r
1807 =\r
1808 = R_PetJump\r
1809 =\r
1810 ===========================\r
1811 */\r
1812 \r
1813 void R_PetJump(objtype *ob)\r
1814 {\r
1815         if (ob->hitnorth)\r
1816         {\r
1817                 ob->nothink = US_RndT() >> 5;\r
1818                 ChangeState(ob, &s_pet1);\r
1819         }\r
1820         if (ob->temp3)\r
1821         {\r
1822                 ob->temp3--;\r
1823                 RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, maskdraw, ob->priority);\r
1824         }\r
1825         else\r
1826         {\r
1827                 RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);\r
1828         }\r
1829 }\r
1830 \r
1831 /*\r
1832 ===========================\r
1833 =\r
1834 = C_PShot\r
1835 =\r
1836 ===========================\r
1837 */\r
1838 \r
1839 void C_PShot(objtype *ob, objtype *hit)\r
1840 {\r
1841         if (hit->obclass == keenobj)\r
1842         {\r
1843                 KillKeen();\r
1844                 ChangeState(ob, &s_pshothot1);\r
1845         }\r
1846         else if (hit->obclass == stunshotobj)\r
1847         {\r
1848                 ExplodeShot(hit);\r
1849                 ChangeState(ob, &s_pshothot1);\r
1850         }\r
1851 }\r
1852 \r
1853 /*\r
1854 ===========================\r
1855 =\r
1856 = R_PShot\r
1857 =\r
1858 ===========================\r
1859 */\r
1860 \r
1861 void R_PShot(objtype *ob)\r
1862 {\r
1863         if (ob->hitnorth || ob->hiteast || ob->hitsouth || ob->hitwest)\r
1864         {\r
1865                 SD_PlaySound(SND_SHOCKBALLEXPLODE);\r
1866                 ChangeState(ob, &s_pshothot1);\r
1867         }\r
1868         RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);\r
1869 }\r
1870 \r
1871 /*\r
1872 =============================================================================\r
1873 \r
1874                                                   SPHEREFUL\r
1875 \r
1876 temp1 ... temp4 = sprite pointers for the guards circling around the sphere\r
1877 \r
1878 =============================================================================\r
1879 */\r
1880 \r
1881 statetype s_sphereful1    = {SPHEREFUL1SPR, SPHEREFUL1SPR, stepthink, false, false, 6, 0, 0, T_Sphereful, C_Spindread, R_Sphereful, &s_sphereful2};\r
1882 statetype s_sphereful2    = {SPHEREFUL2SPR, SPHEREFUL2SPR, stepthink, false, false, 6, 0, 0, T_Sphereful, C_Spindread, R_Sphereful, &s_sphereful3};\r
1883 statetype s_sphereful3    = {SPHEREFUL3SPR, SPHEREFUL3SPR, stepthink, false, false, 6, 0, 0, T_Sphereful, C_Spindread, R_Sphereful, &s_sphereful4};\r
1884 statetype s_sphereful4    = {SPHEREFUL4SPR, SPHEREFUL4SPR, stepthink, false, false, 6, 0, 0, T_Sphereful, C_Spindread, R_Sphereful, &s_sphereful1};\r
1885 \r
1886 /*\r
1887 ===========================\r
1888 =\r
1889 = SpawnSphereful\r
1890 =\r
1891 ===========================\r
1892 */\r
1893 \r
1894 void SpawnSphereful(Uint16 tileX, Uint16 tileY)\r
1895 {\r
1896         GetNewObj(false);\r
1897         new->obclass = spherefulobj;\r
1898         new->needtoclip = cl_fullclip;\r
1899         new->active = ac_yes;\r
1900         new->x = CONVERT_TILE_TO_GLOBAL(tileX);\r
1901         new->y = CONVERT_TILE_TO_GLOBAL(tileY) + -1*TILEGLOBAL;\r
1902         new->priority = 1;\r
1903         NewState(new, &s_sphereful1);\r
1904 }\r
1905 \r
1906 /*\r
1907 ===========================\r
1908 =\r
1909 = T_Sphereful\r
1910 =\r
1911 ===========================\r
1912 */\r
1913 \r
1914 void T_Sphereful(objtype *ob)\r
1915 {\r
1916         Sint32 i;\r
1917         ob->needtoreact = true;\r
1918 \r
1919         //\r
1920         // this code could be executed twice during the same frame because the\r
1921         // object's animation/state changed during that frame, so don't update\r
1922         // anything if we already have movement for the current frame i.e. the\r
1923         // update code has already been executed this frame\r
1924         //\r
1925         if (xtry == 0 && ytry == 0)\r
1926         {\r
1927                 for (i=lasttimecount-tics; i<lasttimecount; i++)\r
1928                 {\r
1929                         if (!(i & 0xF))\r
1930                         {\r
1931                                 if (ob->yspeed < 8)\r
1932                                         ob->yspeed++;\r
1933 \r
1934                                 if (ob->x < player->x && ob->xspeed < 8)\r
1935                                         ob->xspeed++;\r
1936 \r
1937                                 if (ob->x > player->x && ob->xspeed > -8)\r
1938                                         ob->xspeed--;\r
1939                         }\r
1940                         ytry += ob->yspeed;\r
1941                         xtry += ob->xspeed;\r
1942                 }\r
1943         }\r
1944 }\r
1945 \r
1946 /*\r
1947 ===========================\r
1948 =\r
1949 = R_Sphereful\r
1950 =\r
1951 ===========================\r
1952 */\r
1953 \r
1954 void R_Sphereful(objtype *ob)\r
1955 {\r
1956         static Uint16 circle[16] = {\r
1957                 384, 369, 328, 265, 192, 119,  58,  15,\r
1958                   0,  15,  58, 119, 192, 265, 328, 369\r
1959         };\r
1960 \r
1961         Uint16 index, shapenum, priority;\r
1962 \r
1963         if (ob->hitwest || ob->hiteast)\r
1964         {\r
1965                 ob->xspeed = -ob->xspeed;\r
1966                 SD_PlaySound(SND_SPHEREFULBOUNCE);\r
1967         }\r
1968         if (ob->hitsouth)\r
1969         {\r
1970                 ob->yspeed = -ob->yspeed;\r
1971                 SD_PlaySound(SND_SPHEREFULBOUNCE);\r
1972         }\r
1973 \r
1974         if (ob->hitnorth)\r
1975         {\r
1976                 ob->yspeed = -ob->yspeed;\r
1977                 if (player->y < ob->y)\r
1978                 {\r
1979                         ob->yspeed--;\r
1980                 }\r
1981                 else\r
1982                 {\r
1983                         ob->yspeed++;\r
1984                 }\r
1985                 if (ob->yspeed > -4)\r
1986                 {\r
1987                         ob->yspeed = -4;\r
1988                 }\r
1989                 else if (ob->yspeed < -12)\r
1990                 {\r
1991                         ob->yspeed = -12;\r
1992                 }\r
1993                 SD_PlaySound(SND_BOUNCE);\r
1994         }\r
1995         RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);\r
1996 \r
1997         index = ((Uint16)lasttimecount / 8) & 0xF;\r
1998         shapenum = index / 4 + SPHEREGUARD1SPR;\r
1999         if (index >= 8)\r
2000         {\r
2001                 priority = 2;\r
2002         }\r
2003         else\r
2004         {\r
2005                 priority = 0;\r
2006         }\r
2007         RF_PlaceSprite((void**)&ob->temp1, ob->x+circle[index], ob->y+circle[index], shapenum, spritedraw, priority);\r
2008         RF_PlaceSprite((void**)&ob->temp2, ob->x+24*PIXGLOBAL-circle[index], ob->y+circle[index], shapenum, spritedraw, priority);\r
2009 \r
2010         index += 8;\r
2011         index &= 0xF;\r
2012         if (index >= 8)\r
2013         {\r
2014                 priority = 2;\r
2015         }\r
2016         else\r
2017         {\r
2018                 priority = 0;\r
2019         }\r
2020         RF_PlaceSprite((void**)&ob->temp3, ob->x+circle[index], ob->y+circle[index], shapenum, spritedraw, priority);\r
2021         RF_PlaceSprite((void**)&ob->temp4, ob->x+24*PIXGLOBAL-circle[index], ob->y+circle[index], shapenum, spritedraw, priority);\r
2022 }\r
2023 \r
2024 /*\r
2025 =============================================================================\r
2026 \r
2027                                                   SCOTTIE\r
2028 \r
2029 =============================================================================\r
2030 */\r
2031 \r
2032 statetype s_scottie1    = {SCOTTIEWALKL1SPR, SCOTTIEWALKR1SPR, step,  false, true,  8, 128, 0, T_Scottie, C_Scottie, R_Walk, &s_scottie2};\r
2033 statetype s_scottie2    = {SCOTTIEWALKL2SPR, SCOTTIEWALKR2SPR, step,  false, true,  8, 128, 0, T_Scottie, C_Scottie, R_Walk, &s_scottie3};\r
2034 statetype s_scottie3    = {SCOTTIEWALKL3SPR, SCOTTIEWALKR3SPR, step,  false, true,  8, 128, 0, T_Scottie, C_Scottie, R_Walk, &s_scottie4};\r
2035 statetype s_scottie4    = {SCOTTIEWALKL4SPR, SCOTTIEWALKR4SPR, step,  false, true,  8, 128, 0, T_Scottie, C_Scottie, R_Walk, &s_scottie1};\r
2036 statetype s_scottieface = {SCOTTIEFACESPR,   SCOTTIEFACESPR,   step,  false, true, 30,   0, 0, NULL, C_Scottie, R_Walk, &s_scottie1};\r
2037 statetype s_scottiestun = {SCOTTIESTUNSPR,   SCOTTIESTUNSPR,   think, false, false, 0,   0, 0, T_Projectile, NULL, R_Stunned, NULL};\r
2038 \r
2039 /*\r
2040 ===========================\r
2041 =\r
2042 = SpawnScottie\r
2043 =\r
2044 ===========================\r
2045 */\r
2046 \r
2047 void SpawnScottie(Uint16 tileX, Uint16 tileY)\r
2048 {\r
2049         GetNewObj(false);\r
2050         new->obclass = scottieobj;\r
2051         new->active = ac_yes;\r
2052         new->x = CONVERT_TILE_TO_GLOBAL(tileX);\r
2053         new->y = CONVERT_TILE_TO_GLOBAL(tileY) + -8*PIXGLOBAL;\r
2054         if (US_RndT() < 0x80)\r
2055         {\r
2056                 new->xdir = 1;\r
2057         }\r
2058         else\r
2059         {\r
2060                 new->xdir = -1;\r
2061         }\r
2062         NewState(new, &s_scottie1);\r
2063 }\r
2064 \r
2065 /*\r
2066 ===========================\r
2067 =\r
2068 = T_Scottie\r
2069 =\r
2070 ===========================\r
2071 */\r
2072 \r
2073 void T_Scottie(objtype *ob)\r
2074 {\r
2075         if (US_RndT() < 0x10)\r
2076         {\r
2077                 xtry = 0;\r
2078                 if (US_RndT() < 0x80)\r
2079                 {\r
2080                         ob->xdir = 1;\r
2081                 }\r
2082                 else\r
2083                 {\r
2084                         ob->xdir = -1;\r
2085                 }\r
2086                 ob->state = &s_scottieface;\r
2087         }\r
2088 }\r
2089 \r
2090 /*\r
2091 ===========================\r
2092 =\r
2093 = C_Scottie\r
2094 =\r
2095 ===========================\r
2096 */\r
2097 \r
2098 void C_Scottie(objtype *ob, objtype *hit)\r
2099 {\r
2100         if (hit->obclass == keenobj && hit->state->contact)\r
2101         {\r
2102                 ClipToSpriteSide(hit, ob);\r
2103         }\r
2104         else if (hit->obclass == stunshotobj)\r
2105         {\r
2106                 StunObj(ob, hit, &s_scottiestun);\r
2107         }\r
2108 }\r
2109 \r
2110 /*\r
2111 =============================================================================\r
2112 \r
2113                                                   QED\r
2114 \r
2115 =============================================================================\r
2116 */\r
2117 \r
2118 statetype s_qed = {-1, -1, step, false, true, 8, 128, 0, NULL, NULL, NULL, &s_qed};\r
2119 \r
2120 /*\r
2121 ===========================\r
2122 =\r
2123 = SpawnQed\r
2124 =\r
2125 ===========================\r
2126 */\r
2127 \r
2128 void SpawnQed(Uint16 tileX, Uint16 tileY)\r
2129 {\r
2130         GetNewObj(false);\r
2131         new->obclass = qedobj;\r
2132         new->active = ac_yes;\r
2133         new->tileleft = tileX;\r
2134         new->tiletop = tileY;\r
2135         new->tileright = new->tileleft + 1;\r
2136         new->tilebottom = new->tiletop + 1;\r
2137         new->x = new->left = CONVERT_TILE_TO_GLOBAL(tileX) + -1*PIXGLOBAL;\r
2138         new->y = new->top = CONVERT_TILE_TO_GLOBAL(tileY) + -1*PIXGLOBAL;\r
2139         new->right = new->left + 34*PIXGLOBAL;\r
2140         new->bottom = new->top + 34*PIXGLOBAL;\r
2141         NewState(new, &s_qed);\r
2142 }