]> 4ch.mooo.com Git - 16.git/blob - 16/keen456/KEEN4-6/KEEN4/K4_ACT2.C
41b0ad950cfea21cb137f7358648e0fd4ed4d413
[16.git] / 16 / keen456 / KEEN4-6 / KEEN4 / K4_ACT2.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 K4_ACT2.C\r
25 =========\r
26 \r
27 Contains the following actor types (in this order):\r
28 \r
29 - Wormouth\r
30 - Thundercloud & Lightning\r
31 - Berkeloid & Fireball\r
32 - Inchworm & Foot (in-level)\r
33 - Bounder\r
34 - Lick\r
35 - Platform\r
36 - Dropping Platform\r
37 \r
38 */\r
39 \r
40 #include "CK_DEF.H"\r
41 \r
42 /*\r
43 =============================================================================\r
44 \r
45                                                   WORMOUTH\r
46 \r
47 =============================================================================\r
48 */\r
49 \r
50 statetype s_worm      = {WORMOUTHSPR,       WORMOUTHSPR,       slide, true,  true,  4, 16, 0, T_Worm,          NULL,                  R_Walk,    &s_worm};\r
51 statetype s_wormpeek1 = {WORMOUTHPEEKL1SPR, WORMOUTHPEEKL1SPR, step,  false, false, 20,  0, 0, NULL,                   C_Worm,   R_Draw,    &s_wormpeek2};\r
52 statetype s_wormpeek2 = {WORMOUTHPEEKL2SPR, WORMOUTHPEEKL2SPR, step,  false, false,  8,  0, 0, NULL,                   C_Worm,   R_Draw,    &s_wormpeek3};\r
53 statetype s_wormpeek3 = {WORMOUTHPEEKL1SPR, WORMOUTHPEEKL1SPR, step,  false, false, 20,  0, 0, T_WormLookLeft,  C_Worm,   R_Draw,    &s_wormpeek4};\r
54 statetype s_wormpeek4 = {WORMOUTHSPR,       WORMOUTHSPR,       step,  false, false,  8,  0, 0, NULL,                   C_Worm,   R_Draw,    &s_wormpeek5};\r
55 statetype s_wormpeek5 = {WORMOUTHPEEKR1SPR, WORMOUTHPEEKR1SPR, step,  false, false, 20,  0, 0, NULL,                   C_Worm,   R_Draw,    &s_wormpeek6};\r
56 statetype s_wormpeek6 = {WORMOUTHPEEKR2SPR, WORMOUTHPEEKR2SPR, step,  false, false,  8,  0, 0, NULL,                   C_Worm,   R_Draw,    &s_wormpeek7};\r
57 statetype s_wormpeek7 = {WORMOUTHPEEKR1SPR, WORMOUTHPEEKR1SPR, step,  false, false, 20,  0, 0, T_WormLookRight, C_Worm,   R_Draw,    &s_wormpeek8};\r
58 statetype s_wormpeek8 = {WORMOUTHSPR,       WORMOUTHSPR,       step,  false, false,  8,  0, 0, T_WormLook,      NULL,                  R_Draw,    &s_worm};\r
59 statetype s_wormbite1 = {WORMOUTHBITEL1SPR, WORMOUTHBITER1SPR, step,  false, false,  8,  0, 0, NULL,                   C_WormKill, R_Draw,    &s_wormbite2};\r
60 statetype s_wormbite2 = {WORMOUTHBITEL2SPR, WORMOUTHBITER2SPR, step,  false, false,  8,  0, 0, NULL,                   C_WormKill, R_Draw,    &s_wormbite3};\r
61 statetype s_wormbite3 = {WORMOUTHBITEL3SPR, WORMOUTHBITER3SPR, step,  false, false, 16,  0, 0, NULL,                   C_WormKill, R_Draw,    &s_wormbite4};\r
62 statetype s_wormbite4 = {WORMOUTHBITEL2SPR, WORMOUTHBITER2SPR, step,  false, false,  8,  0, 0, NULL,                   C_WormKill, R_Draw,    &s_wormbite5};\r
63 statetype s_wormbite5 = {WORMOUTHBITEL1SPR, WORMOUTHBITER1SPR, step,  false, false,  8,  0, 0, NULL,                   C_WormKill, R_Draw,    &s_worm};\r
64 statetype s_wormstun  = {WORMOUTHSTUNSPR,   WORMOUTHSTUNSPR,   think, false, false, 10,  0, 0, T_Projectile,       NULL,                  R_Stunned, NULL};\r
65 \r
66 /*\r
67 ===========================\r
68 =\r
69 = SpawnWormMouth\r
70 =\r
71 ===========================\r
72 */\r
73 \r
74 void SpawnWormMouth(Sint16 x, Sint16 y)\r
75 {\r
76         GetNewObj(false);\r
77         new->obclass = wormouthobj;\r
78         new->active = ac_yes;\r
79         new->priority = 0;\r
80         new->x = CONVERT_TILE_TO_GLOBAL(x);\r
81         new->y = CONVERT_TILE_TO_GLOBAL(y) + 0x8F;\r
82         if (US_RndT() < 0x80)\r
83         {\r
84                 new->xdir = 1;\r
85         }\r
86         else\r
87         {\r
88                 new->xdir = -1;\r
89         }\r
90         new->ydir = 1;\r
91         NewState(new, &s_worm);\r
92 }\r
93 \r
94 /*\r
95 ===========================\r
96 =\r
97 = T_WormLookRight\r
98 =\r
99 ===========================\r
100 */\r
101 \r
102 void T_WormLookRight(objtype *ob)\r
103 {\r
104         if (player->x > ob->x)\r
105         {\r
106                 ob->xdir = 1;\r
107                 ob->state = &s_worm;\r
108         }\r
109 }\r
110 \r
111 /*\r
112 ===========================\r
113 =\r
114 = T_WormLook\r
115 =\r
116 ===========================\r
117 */\r
118 \r
119 void T_WormLook(objtype *ob)\r
120 {\r
121         if (player->x > ob->x)\r
122         {\r
123                 ob->xdir = 1;\r
124         }\r
125         else\r
126         {\r
127                 ob->xdir = -1;\r
128         }\r
129 }\r
130 \r
131 /*\r
132 ===========================\r
133 =\r
134 = T_WormLookLeft\r
135 =\r
136 ===========================\r
137 */\r
138 \r
139 void T_WormLookLeft(objtype *ob)\r
140 {\r
141         if (player->x < ob->x)\r
142         {\r
143                 ob->xdir = -1;\r
144                 ob->state = &s_worm;\r
145         }\r
146 }\r
147 \r
148 /*\r
149 ===========================\r
150 =\r
151 = T_Worm\r
152 =\r
153 ===========================\r
154 */\r
155 \r
156 void T_Worm(objtype *ob)\r
157 {\r
158         Sint16 xdist, ydist;\r
159 \r
160         xdist = player->x - ob->x;\r
161         ydist = player->bottom - ob->bottom;\r
162         if ((xdist < -3*TILEGLOBAL || xdist > 3*TILEGLOBAL) && US_RndT() < 6)\r
163         {\r
164                 ob->state = &s_wormpeek1;\r
165         }\r
166         else if (ydist >= -TILEGLOBAL && ydist <= TILEGLOBAL)\r
167         {\r
168                 if (ob->xdir == 1 && xdist > 8*PIXGLOBAL && xdist < 24*PIXGLOBAL\r
169                         || ob->xdir == -1 && xdist < -8*PIXGLOBAL && xdist > -32*PIXGLOBAL)\r
170                 {\r
171                         SD_PlaySound(SND_WORMOUTHATTACK);\r
172                         ob->state = &s_wormbite1;\r
173                 }\r
174         }\r
175 }\r
176 \r
177 /*\r
178 ===========================\r
179 =\r
180 = C_Worm\r
181 =\r
182 ===========================\r
183 */\r
184 \r
185 void C_Worm(objtype *ob, objtype *hit)\r
186 {\r
187         if (hit->obclass == stunshotobj)\r
188         {\r
189                 StunObj(ob, hit, &s_wormstun);\r
190         }\r
191 }\r
192 \r
193 /*\r
194 ===========================\r
195 =\r
196 = C_WormKill\r
197 =\r
198 ===========================\r
199 */\r
200 \r
201 void C_WormKill(objtype *ob, objtype *hit)\r
202 {\r
203         if (hit->obclass == keenobj)\r
204         {\r
205                 if (!(ob->xdir == 1 && ob->x > hit->x || ob->xdir == -1 && ob->x < hit->x))\r
206                 {\r
207                         KillKeen();\r
208                 }\r
209                 return;\r
210         }\r
211         else\r
212         {\r
213                 C_Worm(ob, hit);\r
214         }\r
215 }\r
216 \r
217 /*\r
218 =============================================================================\r
219 \r
220                                                   THUNDERCLOUD\r
221 \r
222 =============================================================================\r
223 */\r
224 \r
225 statetype s_cloudsleep   = {CLOUDSPR,       CLOUDSPR,       think,     false, false,  20, 0, 0, NULL,         C_CloudSleep, R_Draw,  NULL};\r
226 statetype s_cloudwake    = {CLOUDACTIVESPR, CLOUDACTIVESPR, step,      false, false, 100, 0, 0, NULL,         NULL,         R_Cloud, &s_cloud};\r
227 statetype s_cloud        = {CLOUDACTIVESPR, CLOUDACTIVESPR, think,     false, false,  20, 0, 0, T_Cloud,      NULL,         R_Cloud, NULL};\r
228 statetype s_cloudalign   = {CLOUDACTIVESPR, CLOUDACTIVESPR, think,     false, false,  20, 0, 0, T_CloudAlign, NULL,         R_Cloud, NULL};\r
229 statetype s_cloudcharge  = {CLOUDACTIVESPR, CLOUDACTIVESPR, stepthink, false, false,  60, 0, 0, T_Velocity,   NULL,         R_Cloud, &s_cloud};\r
230 statetype s_cloudattack1 = {CLOUDCHARGESPR, CLOUDCHARGESPR, step,      false, false,  10, 0, 0, NULL,         NULL,         R_Draw,  &s_cloudattack2};\r
231 statetype s_cloudattack2 = {CLOUDACTIVESPR, CLOUDACTIVESPR, step,      false, false,  10, 0, 0, NULL,         NULL,         R_Draw,  &s_cloudattack3};\r
232 statetype s_cloudattack3 = {CLOUDCHARGESPR, CLOUDCHARGESPR, step,      false, false,  10, 0, 0, NULL,         NULL,         R_Draw,  &s_cloudattack4};\r
233 statetype s_cloudattack4 = {CLOUDACTIVESPR, CLOUDACTIVESPR, step,      false, false,  10, 0, 0, T_CloudShoot, NULL,         R_Draw,  &s_cloudattack5};\r
234 statetype s_cloudattack5 = {CLOUDCHARGESPR, CLOUDCHARGESPR, step,      false, false,  10, 0, 0, NULL,         NULL,         R_Draw,  &s_cloudattack6};\r
235 statetype s_cloudattack6 = {CLOUDACTIVESPR, CLOUDACTIVESPR, step,      false, false,  10, 0, 0, NULL,         NULL,         R_Draw,  &s_cloudattack7};\r
236 statetype s_cloudattack7 = {CLOUDCHARGESPR, CLOUDCHARGESPR, step,      false, false,  10, 0, 0, NULL,         NULL,         R_Draw,  &s_cloudattack8};\r
237 statetype s_cloudattack8 = {CLOUDACTIVESPR, CLOUDACTIVESPR, step,      false, false,  10, 0, 0, NULL,         NULL,         R_Draw,  &s_cloudattack9};\r
238 statetype s_cloudattack9 = {CLOUDCHARGESPR, CLOUDCHARGESPR, step,      false, false,  48, 0, 0, NULL,         NULL,         R_Draw,  &s_cloudcharge};\r
239 \r
240 statetype s_bolt1 = {BOLT1SPR, BOLT1SPR, step, false, false, 8, 0, 0, NULL, C_Lethal, R_Draw, &s_bolt2};\r
241 statetype s_bolt2 = {BOLT2SPR, BOLT2SPR, step, false, false, 8, 0, 0, NULL, C_Lethal, R_Draw, &s_bolt3};\r
242 statetype s_bolt3 = {BOLT1SPR, BOLT1SPR, step, false, false, 8, 0, 0, NULL, C_Lethal, R_Draw, &s_bolt4};\r
243 statetype s_bolt4 = {BOLT2SPR, BOLT2SPR, step, false, false, 8, 0, 0, NULL, C_Lethal, R_Draw, &s_bolt5};\r
244 statetype s_bolt5 = {BOLT1SPR, BOLT1SPR, step, false, false, 8, 0, 0, NULL, C_Lethal, R_Draw, &s_bolt6};\r
245 statetype s_bolt6 = {BOLT2SPR, BOLT2SPR, step, false, false, 8, 0, 0, NULL, C_Lethal, R_Draw, NULL};\r
246 \r
247 /*\r
248 ===========================\r
249 =\r
250 = SpawnCloudster\r
251 =\r
252 ===========================\r
253 */\r
254 \r
255 void SpawnCloudster(Sint16 x, Sint16 y)\r
256 {\r
257         GetNewObj(false);\r
258         new->obclass = thundercloudobj;\r
259         new->active = ac_yes;\r
260         new->priority = 2;\r
261         new->x = CONVERT_TILE_TO_GLOBAL(x);\r
262         new->y = CONVERT_TILE_TO_GLOBAL(y);\r
263         new->ydir = new->xdir = 1;\r
264         NewState(new, &s_cloudsleep);\r
265 }\r
266 \r
267 /*\r
268 ===========================\r
269 =\r
270 = T_Cloud\r
271 =\r
272 ===========================\r
273 */\r
274 \r
275 void T_Cloud(objtype *ob)\r
276 {\r
277         if (US_RndT() < tics)\r
278                 ob->xdir = ob->x > player->x? -1 : 1;\r
279 \r
280         AccelerateX(ob, ob->xdir, 10);\r
281         if (player->bottom < ob->bottom || (Sint16)(player->top - ob->bottom) > 64*PIXGLOBAL)\r
282                 return;\r
283         if (ob->left < player->right && ob->right > player->left)\r
284         {\r
285                 ob->state = &s_cloudalign;\r
286         }\r
287 }\r
288 \r
289 /*\r
290 ===========================\r
291 =\r
292 = T_CloudAlign\r
293 =\r
294 ===========================\r
295 */\r
296 \r
297 void T_CloudAlign(objtype *ob)\r
298 {\r
299         AccelerateX(ob, ob->xdir, 10);\r
300         if (xtry < 0 && (Sint16)((ob->x & 0x7F) + xtry) <= 0)\r
301         {\r
302                 xtry = -(ob->x & 0x7F);\r
303                 ob->state = &s_cloudattack1;\r
304         }\r
305         if (xtry > 0 && (ob->x & 0x7F) + xtry >= 8*PIXGLOBAL)\r
306         {\r
307                 xtry = 8*PIXGLOBAL - (ob->x & 0x7F);\r
308                 ob->state = &s_cloudattack1;\r
309         }\r
310 }\r
311 \r
312 /*\r
313 ===========================\r
314 =\r
315 = R_Cloud\r
316 =\r
317 ===========================\r
318 */\r
319 \r
320 void R_Cloud(objtype *ob)\r
321 {\r
322         if (ob->hitwest)\r
323         {\r
324                 ob->xspeed = 0;\r
325                 ob->xdir = -1;\r
326         }\r
327         else if (ob->hiteast)\r
328         {\r
329                 ob->xspeed = 0;\r
330                 ob->xdir = 1;\r
331         }\r
332         RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);\r
333 }\r
334 \r
335 /*\r
336 ===========================\r
337 =\r
338 = T_CloudShoot\r
339 =\r
340 ===========================\r
341 */\r
342 \r
343 void T_CloudShoot(objtype *ob)\r
344 {\r
345         GetNewObj(true);\r
346         new->obclass = lightningobj;\r
347         new->active = ac_removable;\r
348         new->needtoclip = cl_noclip;\r
349         new->x = ob->x + TILEGLOBAL;\r
350         new->y = ob->y + TILEGLOBAL;\r
351         NewState(new, &s_bolt1);\r
352         SD_PlaySound(SND_THUNDER);\r
353 }\r
354 \r
355 /*\r
356 ===========================\r
357 =\r
358 = C_CloudSleep\r
359 =\r
360 ===========================\r
361 */\r
362 \r
363 void C_CloudSleep(objtype *ob, objtype *hit)\r
364 {\r
365         if (hit->obclass == keenobj)\r
366         {\r
367                 ChangeState(ob, &s_cloudwake);\r
368         }\r
369 }\r
370 \r
371 /*\r
372 =============================================================================\r
373 \r
374                                                   BERKELOID\r
375 \r
376 temp1 = float offset, in global units (added to ob->y when drawing the sprite)\r
377 temp2 = float speed, in global units per tic (added to temp1 for every tic)\r
378 \r
379 =============================================================================\r
380 */\r
381 \r
382 statetype s_berkefloat1  = {BERKEWALKL1SPR,  BERKEWALKR1SPR,  slide, false, true,  6, 8, 0, BerkeThink, C_Berke, BerkeWalkReact, &s_berkefloat2};\r
383 statetype s_berkefloat2  = {BERKEWALKL2SPR,  BERKEWALKR2SPR,  slide, false, true,  6, 8, 0, BerkeThink, C_Berke, BerkeWalkReact, &s_berkefloat3};\r
384 statetype s_berkefloat3  = {BERKEWALKL3SPR,  BERKEWALKR3SPR,  slide, false, true,  6, 8, 0, BerkeThink, C_Berke, BerkeWalkReact, &s_berkefloat4};\r
385 statetype s_berkefloat4  = {BERKEWALKL4SPR,  BERKEWALKR4SPR,  slide, false, true,  6, 8, 0, BerkeThink, C_Berke, BerkeWalkReact, &s_berkefloat1};\r
386 statetype s_berkethrow1  = {BERKETHROWL1SPR, BERKETHROWR1SPR, step,  false, false, 6, 0, 0, SetReactThink, C_Berke, BerkeDrawReact, &s_berkethrow2};\r
387 statetype s_berkethrow2  = {BERKETHROWL1SPR, BERKETHROWR1SPR, step,  false, false, 6, 0, 0, SetReactThink, C_Berke, BerkeDrawReact, &s_berkethrow3};\r
388 statetype s_berkethrow3  = {BERKETHROWL1SPR, BERKETHROWR1SPR, step,  false, false, 6, 0, 0, SetReactThink, C_Berke, BerkeDrawReact, &s_berkethrow4};\r
389 statetype s_berkethrow4  = {BERKETHROWL1SPR, BERKETHROWR1SPR, step,  false, false, 6, 0, 0, SetReactThink, C_Berke, BerkeDrawReact, &s_berkethrow5};\r
390 statetype s_berkethrow5  = {BERKETHROWL1SPR, BERKETHROWR1SPR, step,  false, false, 6, 0, 0, SetReactThink, C_Berke, BerkeDrawReact, &s_berkethrow6};\r
391 statetype s_berkethrow6  = {BERKETHROWL1SPR, BERKETHROWR1SPR, step,  false, false, 6, 0, 0, BerkeThrowThink, C_Berke, BerkeDrawReact, &s_berkethrow7};\r
392 statetype s_berkethrow7  = {BERKETHROWL2SPR, BERKETHROWR2SPR, step,  false, false, 6, 0, 0, SetReactThink, C_Berke, BerkeDrawReact, &s_berkethrow8};\r
393 statetype s_berkethrow8  = {BERKETHROWL2SPR, BERKETHROWR2SPR, step,  false, false, 6, 0, 0, SetReactThink, C_Berke, BerkeDrawReact, &s_berkethrow9};\r
394 statetype s_berkethrow9  = {BERKETHROWL2SPR, BERKETHROWR2SPR, step,  false, false, 6, 0, 0, SetReactThink, C_Berke, BerkeDrawReact, &s_berkethrow10};\r
395 statetype s_berkethrow10 = {BERKETHROWL2SPR, BERKETHROWR2SPR, step,  false, false, 6, 0, 0, SetReactThink, C_Berke, BerkeDrawReact, &s_berkethrow11};\r
396 statetype s_berkethrow11 = {BERKETHROWL2SPR, BERKETHROWR2SPR, step,  false, false, 6, 0, 0, SetReactThink, C_Berke, BerkeDrawReact, &s_berkethrow12};\r
397 statetype s_berkethrow12 = {BERKETHROWL2SPR, BERKETHROWR2SPR, step,  false, false, 6, 0, 0, BerkeThrowDone, C_Berke, BerkeDrawReact, &s_berkefloat1};\r
398 \r
399 statetype s_fire1     = {FIREBALL1SPR, FIREBALL1SPR, stepthink, false, false, 6, 0, 0, T_WeakProjectile, C_Lethal, FireReact, &s_fire2};\r
400 statetype s_fire2     = {FIREBALL2SPR, FIREBALL2SPR, stepthink, false, false, 6, 0, 0, T_WeakProjectile, C_Lethal, FireReact, &s_fire1};\r
401 statetype s_fireland1 = {FIREBALL1SPR, FIREBALL1SPR, step, false, false,  6, 0, 0, NULL, C_Berke, R_Draw, &s_fireland2};\r
402 statetype s_fireland2 = {FIREBALL3SPR, FIREBALL3SPR, step, false, false, 12, 0, 0, NULL, C_Berke, R_Draw, &s_fireland3};\r
403 statetype s_fireland3 = {FIREBALL4SPR, FIREBALL4SPR, step, false, false, 12, 0, 0, NULL, C_Berke, R_Draw, &s_fireland4};\r
404 statetype s_fireland4 = {FIREBALL3SPR, FIREBALL3SPR, step, false, false, 12, 0, 0, NULL, C_Berke, R_Draw, &s_fireland5};\r
405 statetype s_fireland5 = {FIREBALL4SPR, FIREBALL4SPR, step, false, false, 12, 0, 0, NULL, C_Berke, R_Draw, &s_fireland6};\r
406 statetype s_fireland6 = {FIREBALL1SPR, FIREBALL1SPR, step, false, false, 12, 0, 0, NULL, C_Berke, R_Draw, &s_fireland7};\r
407 statetype s_fireland7 = {FIREBALL2SPR, FIREBALL2SPR, step, false, false, 12, 0, 0, NULL, C_Berke, R_Draw, &s_fireland8};\r
408 statetype s_fireland8 = {FIREBALL1SPR, FIREBALL1SPR, step, false, false, 12, 0, 0, NULL, C_Berke, R_Draw, &s_fireland9};\r
409 statetype s_fireland9 = {FIREBALL2SPR, FIREBALL2SPR, step, false, false, 12, 0, 0, NULL, C_Berke, R_Draw, NULL};\r
410 \r
411 /*\r
412 ===========================\r
413 =\r
414 = SpawnBerkeloid\r
415 =\r
416 ===========================\r
417 */\r
418 \r
419 void SpawnBerkeloid(Sint16 x, Sint16 y)\r
420 {\r
421         GetNewObj(false);\r
422         new->obclass = berkeloidobj;\r
423         new->active = ac_yes;\r
424         new->priority = 2;\r
425         new->x = CONVERT_TILE_TO_GLOBAL(x);\r
426         new->y = CONVERT_TILE_TO_GLOBAL(y) + -2*TILEGLOBAL;\r
427         if (US_RndT() < 0x80)\r
428         {\r
429                 new->xdir = 1;\r
430         }\r
431         else\r
432         {\r
433                 new->xdir = -1;\r
434         }\r
435         new->ydir = 1;\r
436         new->temp2 = 8;\r
437         NewState(new, &s_berkefloat1);\r
438 }\r
439 \r
440 \r
441 /*\r
442 ===========================\r
443 =\r
444 = BerkeThink\r
445 =\r
446 ===========================\r
447 */\r
448 \r
449 void BerkeThink(objtype *ob)\r
450 {\r
451         Sint16 xdist, ydist;\r
452 \r
453         if (US_RndT() < 0x20)\r
454                 ob->xdir = player->x < ob->x? -1 : 1;\r
455 \r
456         if (US_RndT() < 8)\r
457         {\r
458                 // BUG: might be about to move off a ledge, causing it to get stuck\r
459                 // after throwing (the throw states don't use BerkeWalkReact)!\r
460                 // To avoid that, set xtry to 0 here.\r
461 \r
462                 ob->state = &s_berkethrow1;\r
463                 // BUG? this doesn't play the attack sound\r
464         }\r
465         else if (US_RndT() <= 0x40)\r
466         {\r
467                 xdist = player->x - ob->x;\r
468                 ydist = player->y - ob->y;\r
469                 if (ydist >= -TILEGLOBAL && ydist <= TILEGLOBAL\r
470                         && (ob->xdir == 1 && xdist > 0 || ob->xdir == -1 && xdist < 0))\r
471                 {\r
472                         // BUG: might be about to move off a ledge, causing it to get stuck\r
473                         // after throwing (the throw states don't use BerkeWalkReact)!\r
474                         // To avoid that, set xtry to 0 here.\r
475 \r
476                         ob->state = &s_berkethrow1;\r
477                         SD_PlaySound(SND_BERKELOIDATTACK);\r
478                 }\r
479         }\r
480 }\r
481 \r
482 /*\r
483 ===========================\r
484 =\r
485 = BerkeThrowThink\r
486 =\r
487 ===========================\r
488 */\r
489 \r
490 void BerkeThrowThink(objtype *ob)\r
491 {\r
492         GetNewObj(true);\r
493         new->active = ac_removable;\r
494         new->obclass = berkeloidobj;\r
495         new->y = ob->y + 8*PIXGLOBAL;\r
496         new->yspeed = -8;\r
497         if (ob->xdir == 1)\r
498         {\r
499                 new->xspeed = 48;\r
500                 new->x = ob->x + 32*PIXGLOBAL;\r
501                 new->xdir = 1;\r
502         }\r
503         else\r
504         {\r
505                 new->xspeed = -48;\r
506                 new->x = ob->x - 16*PIXGLOBAL;\r
507                 new->xdir = -1;\r
508         }\r
509         NewState(new, &s_fire1);\r
510         ob->needtoreact = true;\r
511 }\r
512 \r
513 /*\r
514 ===========================\r
515 =\r
516 = BerkeThrowDone\r
517 =\r
518 ===========================\r
519 */\r
520 \r
521 void BerkeThrowDone(objtype *ob)\r
522 {\r
523         ob->nothink = 4;\r
524         ob->needtoreact = true;\r
525 }\r
526 \r
527 /*\r
528 ===========================\r
529 =\r
530 = C_Berke\r
531 =\r
532 ===========================\r
533 */\r
534 \r
535 void C_Berke(objtype *ob, objtype *hit)\r
536 {\r
537         ob++;                   // shut up compiler\r
538         if (hit->obclass == stunshotobj)\r
539         {\r
540                 ExplodeShot(hit);\r
541         }\r
542         else if (hit->obclass == keenobj)\r
543         {\r
544                 KillKeen();\r
545         }\r
546 }\r
547 \r
548 /*\r
549 ===========================\r
550 =\r
551 = FireReact\r
552 =\r
553 ===========================\r
554 */\r
555 \r
556 void FireReact(objtype *ob)\r
557 {\r
558         if (ob->hiteast || ob->hitwest)\r
559                 ob->xspeed = 0;\r
560 \r
561         if (ob->hitnorth)\r
562         {\r
563                 SD_PlaySound(SND_FIREBALLLAND);\r
564                 ChangeState(ob, &s_fireland1);\r
565         }\r
566         RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);\r
567 }\r
568 \r
569 /*\r
570 ===========================\r
571 =\r
572 = BerkeDrawReact\r
573 =\r
574 ===========================\r
575 */\r
576 \r
577 void BerkeDrawReact(objtype *ob)\r
578 {\r
579         //float up & down:\r
580         ob->temp1 = ob->temp1 + ob->temp2 * tics;\r
581         if (ob->temp1 > 0)\r
582         {\r
583                 ob->temp1 = 0;\r
584                 ob->temp2 = -8;\r
585         }\r
586         else if (ob->temp1 < -TILEGLOBAL)\r
587         {\r
588                 ob->temp1 = -TILEGLOBAL;\r
589                 ob->temp2 = 8;\r
590         }\r
591 \r
592         RF_PlaceSprite(&ob->sprite, ob->x, ob->y+ob->temp1, ob->shapenum, spritedraw, 0);\r
593 }\r
594 \r
595 /*\r
596 ===========================\r
597 =\r
598 = BerkeWalkReact\r
599 =\r
600 ===========================\r
601 */\r
602 \r
603 void BerkeWalkReact(objtype *ob)\r
604 {\r
605         if (ob->xdir == 1 && ob->hitwest)\r
606         {\r
607                 ob->x -= ob->xmove;\r
608                 ob->xdir = -1;\r
609                 ob->nothink = US_RndT() >> 5;\r
610                 ChangeState(ob, ob->state);\r
611         }\r
612         else if (ob->xdir == -1 && ob->hiteast)\r
613         {\r
614                 ob->x -= ob->xmove;\r
615                 ob->xdir = 1;\r
616                 ob->nothink = US_RndT() >> 5;\r
617                 ChangeState(ob, ob->state);\r
618         }\r
619         else if (!ob->hitnorth)\r
620         {\r
621                 ob->x -= ob->xmove*2;\r
622                 ob->xdir = -ob->xdir;\r
623                 ob->nothink = US_RndT() >> 5;\r
624                 ChangeState(ob, ob->state);\r
625         }\r
626         BerkeDrawReact(ob);\r
627 }\r
628 \r
629 /*\r
630 =============================================================================\r
631 \r
632                                                   INCHWORM & FOOT\r
633 \r
634 temp1 = last lasttimecount (for resetting the touch counter after each frame)\r
635 temp2 = touch counter\r
636 \r
637 =============================================================================\r
638 */\r
639 \r
640 statetype s_footsmoke1 = {SMOKE1SPR, SMOKE1SPR, step, false, false, 12, 0, 0, NULL, NULL, R_Draw, &s_footsmoke2};\r
641 statetype s_footsmoke2 = {SMOKE2SPR, SMOKE2SPR, step, false, false, 12, 0, 0, NULL, NULL, R_Draw, &s_footsmoke3};\r
642 statetype s_footsmoke3 = {SMOKE3SPR, SMOKE3SPR, step, false, false, 12, 0, 0, NULL, NULL, R_Draw, &s_footsmoke4};\r
643 statetype s_footsmoke4 = {SMOKE4SPR, SMOKE4SPR, step, false, false, 12, 0, 0, NULL, NULL, R_Draw, NULL};\r
644 statetype s_inch1      = {INCHWORML1SPR, INCHWORMR1SPR, step, false, true, 30, 128, 0, InchThink, InchContact, R_Walk, &s_inch2};\r
645 statetype s_inch2      = {INCHWORML2SPR, INCHWORMR2SPR, step, false, true, 30, 128, 0, InchThink, InchContact, R_Walk, &s_inch1};\r
646 statetype s_footchange = { -1,  -1, step, false, false, 48, 0, 0, NULL, NULL, R_Draw, &s_footwait};     //never used!\r
647 statetype s_footwait   = {FOOTSPR, FOOTSPR, think, false, false, 30000, 0, 0, NULL, FootContact, R_Draw, NULL};\r
648 \r
649 /*\r
650 ===========================\r
651 =\r
652 = SpawnInchworm\r
653 =\r
654 ===========================\r
655 */\r
656 \r
657 void SpawnInchworm(Sint16 x, Sint16 y)\r
658 {\r
659         GetNewObj(false);\r
660         new->obclass = inchwormobj;\r
661         new->active = ac_yes;\r
662         new->priority = 2;\r
663         new->x = CONVERT_TILE_TO_GLOBAL(x);\r
664         new->y = CONVERT_TILE_TO_GLOBAL(y);\r
665         if (US_RndT() < 0x80)\r
666         {\r
667                 new->xdir = 1;\r
668         }\r
669         else\r
670         {\r
671                 new->xdir = -1;\r
672         }\r
673         new->ydir = 1;\r
674         NewState(new, &s_inch1);\r
675         new->ticcount = US_RndT() / 32;\r
676 }\r
677 \r
678 /*\r
679 ===========================\r
680 =\r
681 = SpawnFoot\r
682 =\r
683 ===========================\r
684 */\r
685 \r
686 void SpawnFoot(Sint16 x, Sint16 y)\r
687 {\r
688         GetNewObj(false);\r
689         new->obclass = footobj;\r
690         new->active = ac_yes;\r
691         new->priority = 0;\r
692         new->x = CONVERT_TILE_TO_GLOBAL(x);\r
693         new->y = CONVERT_TILE_TO_GLOBAL(y-3);\r
694         NewState(new, &s_footwait);\r
695 }\r
696 \r
697 /*\r
698 ===========================\r
699 =\r
700 = InchThink\r
701 =\r
702 ===========================\r
703 */\r
704 \r
705 void InchThink(objtype *ob)\r
706 {\r
707         if (ob->x > player->x)\r
708         {\r
709                 ob->xdir = -1;\r
710         }\r
711         else\r
712         {\r
713                 ob->xdir = 1;\r
714         }\r
715 }\r
716 \r
717 /*\r
718 ===========================\r
719 =\r
720 = InchContact\r
721 =\r
722 ===========================\r
723 */\r
724 \r
725 void InchContact(objtype *ob, objtype *hit)\r
726 {\r
727         objtype *ob2;\r
728 \r
729         if (hit->obclass != inchwormobj)\r
730                 return;\r
731 \r
732         if (ob->temp1 != (Sint16)lasttimecount)\r
733         {\r
734                 ob->temp1 = (Sint16)lasttimecount;\r
735                 ob->temp2 = 0;\r
736         }\r
737 \r
738         if (++ob->temp2 != 11)  //11 instead of 12 since the object can't contact itself\r
739                 return;\r
740 \r
741         //change current inchworm into a foot:\r
742         SD_PlaySound(SND_MAKEFOOT);\r
743         ob->y -= 5*TILEGLOBAL;\r
744         ob->obclass = footobj;\r
745         ChangeState(ob, &s_footwait);\r
746 \r
747         //Note: It would make more sense to remove the remaining inchworm BEFORE\r
748         //spawning the smoke, just in case there are not enough free spots in the\r
749         //objarray to spawn the smoke. The game won't crash either way, though.\r
750 \r
751         //spawn smoke:\r
752         GetNewObj(true);\r
753         new->x = ob->x -  8*PIXGLOBAL;\r
754         new->y = ob->y + 16*PIXGLOBAL;\r
755         new->priority = 3;\r
756         NewState(new, &s_footsmoke1);\r
757 \r
758         GetNewObj(true);\r
759         new->x = ob->x + 16*PIXGLOBAL;\r
760         new->y = ob->y + 24*PIXGLOBAL;\r
761         new->priority = 3;\r
762         NewState(new, &s_footsmoke1);\r
763 \r
764         GetNewObj(true);\r
765         new->x = ob->x + 40*PIXGLOBAL;\r
766         new->y = ob->y + 16*PIXGLOBAL;\r
767         new->priority = 3;\r
768         NewState(new, &s_footsmoke1);\r
769 \r
770         GetNewObj(true);\r
771         new->x = ob->x;\r
772         new->y = ob->y - 8*PIXGLOBAL;\r
773         new->priority = 3;\r
774         NewState(new, &s_footsmoke1);\r
775 \r
776         //remove ALL inchworm from the level:\r
777         for (ob2 = player->next; ob2; ob2=ob2->next)\r
778         {\r
779                 if (ob2->obclass == inchwormobj)\r
780                         RemoveObj(ob2);\r
781         }\r
782 }\r
783 \r
784 /*\r
785 ===========================\r
786 =\r
787 = FootContact\r
788 =\r
789 ===========================\r
790 */\r
791 \r
792 void FootContact(objtype *ob, objtype *hit)     //completely useless\r
793 {\r
794         ob++;                   // shut up compiler\r
795         hit++;                  // shut up compiler\r
796 }\r
797 \r
798 /*\r
799 =============================================================================\r
800 \r
801                                                   BOUNDER\r
802 \r
803 temp1 = if non-zero, pick a new (random) direction when hitting the ground\r
804         Makes the Bounder jump straight up at least once after having jumped\r
805                   left/right (unless Keen is riding the Bounder)\r
806 temp2 = jump counter to make the Bounder jump straight up at least twice\r
807         after Keen has fallen/jumped off\r
808 \r
809 =============================================================================\r
810 */\r
811 \r
812 statetype s_bounderup1   = {BOUNDERC1SPR,   BOUNDERC1SPR,   stepthink, false, false, 20, 0, 0, T_Projectile, C_Bounder, R_Bounder, &s_bounderup2};\r
813 statetype s_bounderup2   = {BOUNDERC2SPR,   BOUNDERC2SPR,   stepthink, false, false, 20, 0, 0, T_Projectile, C_Bounder, R_Bounder, &s_bounderup1};\r
814 statetype s_bounderside1 = {BOUNDERL1SPR,   BOUNDERR1SPR,   stepthink, false, false, 20, 0, 0, T_Projectile, C_Bounder, R_Bounder, &s_bounderside2};\r
815 statetype s_bounderside2 = {BOUNDERL2SPR,   BOUNDERR2SPR,   stepthink, false, false, 20, 0, 0, T_Projectile, C_Bounder, R_Bounder, &s_bounderside1};\r
816 statetype s_bounderstun  = {BOUNDERC1SPR,   BOUNDERC1SPR,   think,     false, false,  0, 0, 0, T_Projectile, NULL, R_Stunned, &s_bounderstun2};\r
817 statetype s_bounderstun2 = {BOUNDERSTUNSPR, BOUNDERSTUNSPR, think,     false, false,  0, 0, 0, T_Projectile, NULL, R_Stunned, NULL};\r
818 \r
819 /*\r
820 ===========================\r
821 =\r
822 = SpawnBounder\r
823 =\r
824 ===========================\r
825 */\r
826 \r
827 void SpawnBounder(Sint16 x, Sint16 y)\r
828 {\r
829         GetNewObj(false);\r
830         new->obclass = bounderobj;\r
831         new->active = ac_yes;\r
832         new->priority = 0;\r
833         new->x = CONVERT_TILE_TO_GLOBAL(x);\r
834         new->y = CONVERT_TILE_TO_GLOBAL(y) + -8*PIXGLOBAL;\r
835         new->ydir = 1;\r
836         new->xdir = 0;\r
837         NewState(new, &s_bounderup1);\r
838 }\r
839 \r
840 /*\r
841 ===========================\r
842 =\r
843 = C_Bounder\r
844 =\r
845 ===========================\r
846 */\r
847 \r
848 void C_Bounder(objtype *ob, objtype *hit)\r
849 {\r
850         if (hit->obclass == stunshotobj)\r
851         {\r
852                 //basically StunObj(), but in different order:\r
853                 ob->temp1 = 0;\r
854                 ob->temp2 = 0;\r
855                 ob->temp3 = 0;\r
856                 ob->temp4 = ob->obclass;\r
857                 ExplodeShot(hit);\r
858                 ChangeState(ob, &s_bounderstun);\r
859                 ob->obclass = stunnedobj;\r
860 \r
861                 ob->yspeed -= 32;\r
862         }\r
863 }\r
864 \r
865 /*\r
866 ===========================\r
867 =\r
868 = R_Bounder\r
869 =\r
870 ===========================\r
871 */\r
872 \r
873 void R_Bounder(objtype *ob)\r
874 {\r
875         Sint16 randnum;\r
876 \r
877         if (ob->hitsouth)\r
878                 ob->yspeed = 0;\r
879 \r
880         if (ob->hitnorth)\r
881         {\r
882                 ob->temp2++;\r
883                 if (OnScreen(ob))\r
884                         SD_PlaySound(SND_BOUNCE2);\r
885 \r
886                 ob->yspeed = -50;\r
887                 if (gamestate.riding == ob)\r
888                 {\r
889                         ob->temp2 = 0;\r
890                         if (player->left < ob->left-4*PIXGLOBAL)\r
891                         {\r
892                                 ob->xdir = -1;\r
893                         }\r
894                         else if (player->right > ob->right+4*PIXGLOBAL)\r
895                         {\r
896                                 ob->xdir = 1;\r
897                         }\r
898                         else\r
899                         {\r
900                                 ob->xdir = 0;\r
901                         }\r
902                         ob->xspeed = ob->xdir * 24;\r
903                 }\r
904                 else if (ob->temp2 <= 2 || ob->temp1 == 0)\r
905                 {\r
906                         ob->temp1 = 1;\r
907                         ob->xdir = ob->xspeed = 0;\r
908                         ChangeState(ob, &s_bounderup1);\r
909                 }\r
910                 else\r
911                 {\r
912                         ob->temp1 = 0;\r
913                         randnum = US_RndT();\r
914                         if (randnum < 100)\r
915                         {\r
916                                 ob->xdir = -1;\r
917                         }\r
918                         else if (randnum < 200)\r
919                         {\r
920                                 ob->xdir = 1;\r
921                         }\r
922                         else\r
923                         {\r
924                                 ob->xdir = 0;\r
925                         }\r
926                         ob->xspeed = ob->xdir * 24;\r
927                 }\r
928 \r
929                 if (ob->xdir)\r
930                 {\r
931                         ChangeState(ob, &s_bounderside1);\r
932                 }\r
933                 else\r
934                 {\r
935                         ChangeState(ob, &s_bounderup1);\r
936                 }\r
937         }\r
938 \r
939         if (ob->hiteast || ob->hitwest)\r
940         {\r
941                 ob->xdir = -ob->xdir;\r
942                 ob->xspeed = -ob->xspeed;\r
943         }\r
944 \r
945         RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);\r
946 }\r
947 \r
948 /*\r
949 =============================================================================\r
950 \r
951                                                   LICK\r
952 \r
953 =============================================================================\r
954 */\r
955 \r
956 statetype s_lick1     = {LICKMOVEL1SPR,   LICKMOVER1SPR,   step,  false, false, 10, 0, 0, LickJumpThink, LickContact, R_Draw, &s_lick2};\r
957 statetype s_lick2     = {LICKMOVEL2SPR,   LICKMOVER2SPR,   think, false, false,  0, 0, 0, T_Projectile, LickContact, LickAirReact, &s_lick3};\r
958 statetype s_lick3     = {LICKMOVEL3SPR,   LICKMOVER3SPR,   think, false, false,  0, 0, 0, T_Projectile, LickContact, LickAirReact, NULL};\r
959 statetype s_lick4     = {LICKMOVEL4SPR,   LICKMOVER4SPR,   step,  false, false, 10, 0, 0, NULL, LickContact, R_Draw, &s_lick1};\r
960 statetype s_licklick1 = {LICKATTACKL1SPR, LICKATTACKR1SPR, step,  true,  false,  4, 0, 0, NULL, LickKillContact, R_Draw, &s_licklick2};\r
961 statetype s_licklick2 = {LICKATTACKL2SPR, LICKATTACKR2SPR, step,  true,  false,  4, 0, 0, NULL, LickKillContact, R_Draw, &s_licklick3};\r
962 statetype s_licklick3 = {LICKATTACKL3SPR, LICKATTACKR3SPR, step,  true,  false,  4, 0, 0, NULL, LickKillContact, R_Draw, &s_licklick4};\r
963 statetype s_licklick4 = {LICKATTACKL2SPR, LICKATTACKR2SPR, step,  true,  false,  4, 0, 0, NULL, LickKillContact, R_Draw, &s_licklick5};\r
964 statetype s_licklick5 = {LICKATTACKL1SPR, LICKATTACKR1SPR, step,  true,  false,  4, 0, 0, NULL, LickKillContact, R_Draw, &s_licklick6};\r
965 statetype s_licklick6 = {LICKATTACKL2SPR, LICKATTACKR2SPR, step,  true,  false,  4, 0, 0, NULL, LickKillContact, R_Draw, &s_licklick7};\r
966 statetype s_licklick7 = {LICKATTACKL3SPR, LICKATTACKR3SPR, step,  true,  false,  4, 0, 0, NULL, LickKillContact, R_Draw, &s_licklick8};\r
967 statetype s_licklick8 = {LICKATTACKL2SPR, LICKATTACKR2SPR, step,  true,  false,  4, 0, 0, NULL, LickKillContact, R_Draw, &s_lick3};\r
968 statetype s_lickstun  = {LICKSTUNSPR,     LICKSTUNSPR,     think, false, false,  0, 0, 0, T_Projectile, NULL, R_Stunned, &s_lickstun2};\r
969 statetype s_lickstun2 = {LICKSTUNSPR,     LICKSTUNSPR,     think, false, false,  0, 0, 0, T_Projectile, NULL, R_Stunned, NULL};\r
970 \r
971 /*\r
972 ===========================\r
973 =\r
974 = SpawnLick\r
975 =\r
976 ===========================\r
977 */\r
978 \r
979 void SpawnLick(Sint16 x, Sint16 y)\r
980 {\r
981         GetNewObj(false);\r
982         new->obclass = lickobj;\r
983         new->active = ac_yes;\r
984         new->priority = 2;\r
985         new->x = CONVERT_TILE_TO_GLOBAL(x);\r
986         new->y = CONVERT_TILE_TO_GLOBAL(y);\r
987         if (US_RndT() < 0x80)\r
988         {\r
989                 new->xdir = 1;\r
990         }\r
991         else\r
992         {\r
993                 new->xdir = -1;\r
994         }\r
995         new->ydir = 1;\r
996         new->nothink = US_RndT() / 64;\r
997         NewState(new, &s_lick3);\r
998 }\r
999 \r
1000 /*\r
1001 ===========================\r
1002 =\r
1003 = LickJumpThink\r
1004 =\r
1005 ===========================\r
1006 */\r
1007 \r
1008 void LickJumpThink(objtype *ob)\r
1009 {\r
1010         Sint16 xdist, ydist;\r
1011 \r
1012         if (ob->x > player->x)\r
1013         {\r
1014                 ob->xdir = -1;\r
1015         }\r
1016         else\r
1017         {\r
1018                 ob->xdir = 1;\r
1019         }\r
1020 \r
1021         xdist = player->x - ob->x;\r
1022         ydist = player->y - ob->y;\r
1023 \r
1024         if (ydist >= -TILEGLOBAL && ydist <= TILEGLOBAL && \r
1025                 ( ob->xdir == 1 && xdist > -2*PIXGLOBAL && xdist < 24*PIXGLOBAL\r
1026                  || ob->xdir == -1 && xdist < 2*PIXGLOBAL && xdist > -32*PIXGLOBAL ) )\r
1027         {\r
1028                 SD_PlaySound(SND_LICKATTACK);\r
1029                 ob->state = &s_licklick1;\r
1030         }\r
1031         else if (abs(xdist) > 3*TILEGLOBAL)\r
1032         {\r
1033                 ob->xspeed = ob->xdir * 32;\r
1034                 ob->yspeed = -32;\r
1035         }\r
1036         else\r
1037         {\r
1038                 ob->xspeed = (ob->xdir * 32)/2;\r
1039                 ob->yspeed = -16;\r
1040         }\r
1041 }\r
1042 \r
1043 /*\r
1044 ===========================\r
1045 =\r
1046 = LickContact\r
1047 =\r
1048 ===========================\r
1049 */\r
1050 \r
1051 void LickContact(objtype *ob, objtype *hit)\r
1052 {\r
1053         if (hit->obclass == stunshotobj)\r
1054         {\r
1055                 StunObj(ob, hit, &s_lickstun);\r
1056                 ob->yspeed -= 16;\r
1057         }\r
1058 }\r
1059 \r
1060 /*\r
1061 ===========================\r
1062 =\r
1063 = LickKillContact\r
1064 =\r
1065 ===========================\r
1066 */\r
1067 \r
1068 void LickKillContact(objtype *ob, objtype *hit)\r
1069 {\r
1070         if (hit->obclass == keenobj)\r
1071         {\r
1072                 if (ob->xdir == 1 && player->x > ob->x \r
1073                         || ob->xdir == -1 && player->x < ob->x)\r
1074                 {\r
1075                         KillKeen();\r
1076                 }\r
1077         }\r
1078         else\r
1079         {\r
1080                 LickContact(ob, hit);\r
1081         }\r
1082 }\r
1083 \r
1084 /*\r
1085 ===========================\r
1086 =\r
1087 = LickAirReact\r
1088 =\r
1089 ===========================\r
1090 */\r
1091 \r
1092 void LickAirReact(objtype *ob)\r
1093 {\r
1094         if (ob->hitnorth)\r
1095                 ChangeState(ob, &s_lick4);\r
1096 \r
1097         RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);\r
1098 }\r
1099 \r
1100 /*\r
1101 =============================================================================\r
1102 \r
1103                                                   PLATFORM\r
1104 \r
1105 temp2 = additional sprite pointer for thruster sprites\r
1106 temp3 = additional sprite pointer for thruster sprites\r
1107 \r
1108 =============================================================================\r
1109 */\r
1110 \r
1111 statetype s_platform = {PLATFORMSPR, PLATFORMSPR, think, false, false, 0, 0, 0, T_Platform, NULL, R_Platform, NULL};\r
1112 \r
1113 /*\r
1114 ===========================\r
1115 =\r
1116 = SpawnPlatform\r
1117 =\r
1118 ===========================\r
1119 */\r
1120 \r
1121 void SpawnPlatform(Sint16 x, Sint16 y, Sint16 dir)\r
1122 {\r
1123         GetNewObj(false);\r
1124         new->obclass = platformobj;\r
1125         new->active = ac_allways;\r
1126         new->priority = 0;\r
1127         new->x = CONVERT_TILE_TO_GLOBAL(x);\r
1128         new->y = CONVERT_TILE_TO_GLOBAL(y);\r
1129         switch (dir)\r
1130         {\r
1131         case 0:\r
1132                 new->xdir = 0;\r
1133                 new->ydir = -1;\r
1134                 break;\r
1135         case 1:\r
1136                 new->xdir = 1;\r
1137                 new->ydir = 0;\r
1138                 break;\r
1139         case 2:\r
1140                 new->xdir = 0;\r
1141                 new->ydir = 1;\r
1142                 break;\r
1143         case 3:\r
1144                 new->xdir = -1;\r
1145                 new->ydir = 0;\r
1146                 break;\r
1147         }\r
1148         NewState(new, &s_platform);\r
1149 }\r
1150 \r
1151 /*\r
1152 ===========================\r
1153 =\r
1154 = T_Platform\r
1155 =\r
1156 ===========================\r
1157 */\r
1158 \r
1159 void T_Platform(objtype *ob)\r
1160 {\r
1161         Uint16 newpos, newtile;\r
1162 \r
1163         xtry = ob->xdir * 12 * tics;\r
1164         ytry = ob->ydir * 12 * tics;\r
1165 \r
1166         if (ob->xdir == 1)\r
1167         {\r
1168                 newpos = ob->right + xtry;\r
1169                 newtile = CONVERT_GLOBAL_TO_TILE(newpos);\r
1170                 if (ob->tileright != newtile)\r
1171                 {\r
1172                         if (*(mapsegs[2]+mapbwidthtable[ob->tiletop]/2+newtile) == 31)\r
1173                         {\r
1174                                 ob->xdir = -1;\r
1175                                 xtry = xtry - (newpos & 0xFF);\r
1176                         }\r
1177                 }\r
1178         }\r
1179         else if (ob->xdir == -1)\r
1180         {\r
1181                 newpos = ob->left + xtry;\r
1182                 newtile = CONVERT_GLOBAL_TO_TILE(newpos);\r
1183                 if (ob->tileleft != newtile)\r
1184                 {\r
1185                         if (*(mapsegs[2]+mapbwidthtable[ob->tiletop]/2+newtile) == 31)\r
1186                         {\r
1187                                 ob->xdir = 1;\r
1188                                 xtry = xtry + (0x100 - (newpos & 0xFF));\r
1189                         }\r
1190                 }\r
1191         }\r
1192         else if (ob->ydir == 1)\r
1193         {\r
1194                 newpos = ob->bottom + ytry;\r
1195                 newtile = CONVERT_GLOBAL_TO_TILE(newpos);\r
1196                 if (ob->tilebottom != newtile)\r
1197                 {\r
1198                         if (*(mapsegs[2]+mapbwidthtable[newtile]/2+ob->tileleft) == 31)\r
1199                         {\r
1200                                 if (*(mapsegs[2]+mapbwidthtable[newtile-2]/2+ob->tileleft) == 31)\r
1201                                 {\r
1202                                         ytry = 0;\r
1203                                         ob->needtoreact = true;\r
1204                                 }\r
1205                                 else\r
1206                                 {\r
1207                                         ob->ydir = -1;\r
1208                                         ytry = ytry - (newpos & 0xFF);\r
1209                                 }\r
1210                         }\r
1211                 }\r
1212         }\r
1213         else if (ob->ydir == -1)\r
1214         {\r
1215                 newpos = ob->top + ytry;\r
1216                 newtile = CONVERT_GLOBAL_TO_TILE(newpos);\r
1217                 if (ob->tiletop != newtile)\r
1218                 {\r
1219                         if (*(mapsegs[2]+mapbwidthtable[newtile]/2+ob->tileleft) == 31)\r
1220                         {\r
1221                                 if (*(mapsegs[2]+mapbwidthtable[newtile+2]/2+ob->tileleft) == 31)\r
1222                                 {\r
1223                                         ytry = 0;\r
1224                                         ob->needtoreact = true;\r
1225                                 }\r
1226                                 else\r
1227                                 {\r
1228                                         ob->ydir = 1;\r
1229                                         ytry = ytry + (0x100 - (newpos & 0xFF));\r
1230                                 }\r
1231                         }\r
1232                 }\r
1233         }\r
1234 }\r
1235 \r
1236 /*\r
1237 ===========================\r
1238 =\r
1239 = R_Platform\r
1240 =\r
1241 ===========================\r
1242 */\r
1243 \r
1244 void R_Platform(objtype *ob)\r
1245 {\r
1246         Uint16 frame;\r
1247 \r
1248         //place platform sprite:\r
1249         RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);\r
1250         \r
1251         //place (or remove) thruster sprites:\r
1252         frame = (lasttimecount >> 2) & 1;\r
1253         if (ob->xdir == 1)\r
1254         {\r
1255                 RF_PlaceSprite((void**)&ob->temp2, ob->x-1*PIXGLOBAL, ob->y+3*PIXGLOBAL, frame+PLATSIDETHRUST1SPR, spritedraw, 0);\r
1256                 if (ob->temp3)\r
1257                         RF_RemoveSprite((void**)&ob->temp3);\r
1258         }\r
1259         else if (ob->xdir == -1)\r
1260         {\r
1261                 if (ob->temp2)\r
1262                         RF_RemoveSprite((void**)&ob->temp2);\r
1263                 RF_PlaceSprite((void**)&ob->temp3, ob->x+48*PIXGLOBAL, ob->y+5*PIXGLOBAL, frame+PLATSIDETHRUST1SPR, spritedraw, 1);\r
1264         }\r
1265         else if (ob->ydir == -1)\r
1266         {\r
1267                 RF_PlaceSprite((void**)&ob->temp2, ob->x+2*PIXGLOBAL, ob->y+9*PIXGLOBAL, frame+PLATLTHRUST1SPR, spritedraw, 0);\r
1268                 RF_PlaceSprite((void**)&ob->temp3, ob->x+46*PIXGLOBAL, ob->y+8*PIXGLOBAL, frame+PLATRTHRUST1SPR, spritedraw, 0);\r
1269         }\r
1270         else if (ob->ydir == 1)\r
1271         {\r
1272                 if (frame)\r
1273                 {\r
1274                         RF_PlaceSprite((void**)&ob->temp2, ob->x+2*PIXGLOBAL, ob->y+9*PIXGLOBAL, frame+PLATLTHRUST1SPR, spritedraw, 0);\r
1275                         RF_PlaceSprite((void**)&ob->temp3, ob->x+46*PIXGLOBAL, ob->y+8*PIXGLOBAL, frame+PLATRTHRUST1SPR, spritedraw, 0);\r
1276                 }\r
1277                 else\r
1278                 {\r
1279                         if (ob->temp2)\r
1280                                 RF_RemoveSprite((void**)&ob->temp2);\r
1281                         if (ob->temp3)\r
1282                                 RF_RemoveSprite((void**)&ob->temp3);\r
1283                 }\r
1284         }\r
1285 }\r
1286 \r
1287 /*\r
1288 =============================================================================\r
1289 \r
1290                                                   DROPPING PLATFORM\r
1291 \r
1292 temp1 = initial y position\r
1293 \r
1294 =============================================================================\r
1295 */\r
1296 \r
1297 statetype s_dropplatsit  = {PLATFORMSPR, PLATFORMSPR, think,      false, false, 0, 0,   0, T_DropPlatSit, NULL, R_Draw, NULL};\r
1298 statetype s_dropplatfall = {PLATFORMSPR, PLATFORMSPR, think,      false, false, 0, 0,   0, T_DropPlatFall, NULL, R_Draw, NULL};\r
1299 statetype s_dropplatrise = {PLATFORMSPR, PLATFORMSPR, slidethink, false, false, 0, 0, -32, T_DropPlatRise, NULL, R_Draw, NULL};\r
1300 \r
1301 /*\r
1302 ===========================\r
1303 =\r
1304 = SpawnDropPlat\r
1305 =\r
1306 ===========================\r
1307 */\r
1308 \r
1309 void SpawnDropPlat(Sint16 x, Sint16 y)\r
1310 {\r
1311         GetNewObj(false);\r
1312         new->obclass = platformobj;\r
1313         new->active = ac_allways;\r
1314         new->priority = 0;\r
1315         new->x = CONVERT_TILE_TO_GLOBAL(x);\r
1316         new->y = new->temp1 = CONVERT_TILE_TO_GLOBAL(y);\r
1317         new->xdir = 0;\r
1318         new->ydir = 1;\r
1319         new->needtoclip = cl_noclip;\r
1320         NewState(new, &s_dropplatsit);\r
1321 }\r
1322 \r
1323 /*\r
1324 ===========================\r
1325 =\r
1326 = T_DropPlatSit\r
1327 =\r
1328 ===========================\r
1329 */\r
1330 \r
1331 void T_DropPlatSit(objtype *ob)\r
1332 {\r
1333         if (gamestate.riding == ob)\r
1334         {\r
1335                 ytry = tics * 16;\r
1336                 ob->yspeed = 0;\r
1337                 if (ob->y + ytry - ob->temp1 >= 8*PIXGLOBAL)\r
1338                         ob->state = &s_dropplatfall;\r
1339         }\r
1340 }\r
1341 \r
1342 /*\r
1343 ===========================\r
1344 =\r
1345 = T_DropPlatFall\r
1346 =\r
1347 ===========================\r
1348 */\r
1349 \r
1350 void T_DropPlatFall(objtype *ob)\r
1351 {\r
1352         Uint16 newy, ty;\r
1353 \r
1354         DoGravity(ob);\r
1355 \r
1356         if (ytry >= 15*PIXGLOBAL)\r
1357                 ytry = 15*PIXGLOBAL;\r
1358 \r
1359         newy = ob->bottom + ytry;\r
1360         ty = CONVERT_GLOBAL_TO_TILE(newy);\r
1361         if (ob->tilebottom != ty)\r
1362         {\r
1363                 if (*(mapsegs[2]+mapbwidthtable[ty]/2+ob->tileleft) == 31)\r
1364                 {\r
1365                         ytry = 0xFF - (ob->bottom & 0xFF);\r
1366                         if (gamestate.riding != ob)\r
1367                                 ob->state = &s_dropplatrise;\r
1368                 }\r
1369         }\r
1370 }\r
1371 \r
1372 /*\r
1373 ===========================\r
1374 =\r
1375 = T_DropPlatRise\r
1376 =\r
1377 ===========================\r
1378 */\r
1379 \r
1380 void T_DropPlatRise(objtype *ob)\r
1381 {\r
1382         if (gamestate.riding == ob)\r
1383         {\r
1384                 ob->yspeed = 0;\r
1385                 ob->state = &s_dropplatfall;\r
1386         }\r
1387         else if (ob->y <= ob->temp1)\r
1388         {\r
1389                 ytry = ob->temp1 - ob->y;\r
1390                 ob->state = &s_dropplatsit;\r
1391         }\r
1392 }\r