1
2 // handle carets; a simplified type of objects used for visual effects.
3 // carets have no interaction with real objects, and are always drawn
4 // on top of all other objects and in front even of map foreground tiles.
5
6 #include "nx.h"
7 #include <math.h>
8 #include "common/llist.h"
9 #include "caret.fdh"
10
11 Caret *firstcaret = NULL;
12 Caret *lastcaret = NULL;
13 static int _effecttype = EFFECT_NONE;
14
15
init(void)16 bool Carets::init(void)
17 {
18 firstcaret = NULL;
19 lastcaret = NULL;
20 return 0;
21 }
22
close(void)23 void Carets::close(void)
24 {
25 Carets::DestroyAll();
26 }
27
28 /*
29 void c------------------------------() {}
30 */
31
CreateCaret(int x,int y,int sprite,void (* ontick)(Caret * c),int xinertia,int yinertia)32 Caret *CreateCaret(int x, int y, int sprite, void (*ontick)(Caret *c), \
33 int xinertia, int yinertia)
34 {
35 Caret *c = new Caret;
36 memset(c, 0, sizeof(Caret));
37
38 c->x = x;
39 c->y = y;
40 c->xinertia = xinertia;
41 c->yinertia = yinertia;
42 c->sprite = sprite;
43 c->OnTick = ontick;
44 c->effecttype = _effecttype;
45
46 LL_ADD_END(c, prev, next, firstcaret, lastcaret);
47 return c;
48 }
49
Delete()50 void Caret::Delete()
51 {
52 this->deleted = true;
53 }
54
Destroy()55 void Caret::Destroy()
56 {
57 LL_REMOVE(this, prev, next, firstcaret, lastcaret);
58 delete this;
59 }
60
MoveAtDir(int dir,int speed)61 void Caret::MoveAtDir(int dir, int speed)
62 {
63 this->xinertia = 0;
64 this->yinertia = 0;
65
66 switch(dir)
67 {
68 case LEFT: this->xinertia = -speed; break;
69 case RIGHT: this->xinertia = speed; break;
70 case UP: this->yinertia = -speed; break;
71 case DOWN: this->yinertia = speed; break;
72 }
73 }
74
75 /*
76 void c------------------------------() {}
77 */
78
DrawAll(void)79 void Carets::DrawAll(void)
80 {
81 Caret *c = firstcaret;
82 Caret *next;
83 int scr_x, scr_y;
84
85 while(c)
86 {
87 next = c->next;
88
89 if (c->deleted)
90 {
91 c->Destroy();
92 }
93 else
94 {
95 // do caret ai
96 (*c->OnTick)(c);
97
98 // move caret
99 c->x += c->xinertia;
100 c->y += c->yinertia;
101
102 // get caret's onscreen position
103 // since caret's are all short-lived we just assume it's still onscreen
104 // and let SDL's clipping handle it if not.
105 if (!c->invisible && !c->deleted) // must check deleted again in case handler_function set it
106 {
107 scr_x = (c->x >> CSF) - (map.displayed_xscroll >> CSF);
108 scr_y = (c->y >> CSF) - (map.displayed_yscroll >> CSF);
109 scr_x -= sprites[c->sprite].frame[c->frame].dir[0].drawpoint.x;
110 scr_y -= sprites[c->sprite].frame[c->frame].dir[0].drawpoint.y;
111
112 draw_sprite(scr_x, scr_y, c->sprite, c->frame, RIGHT);
113 }
114 }
115
116 c = next;
117 }
118 }
119
CountByEffectType(int type)120 int Carets::CountByEffectType(int type)
121 {
122 int count = 0;
123 Caret *c = firstcaret;
124 while(c)
125 {
126 if (c->effecttype == type) count++;
127 c = c->next;
128 }
129
130 return count;
131 }
132
DeleteByEffectType(int type)133 int Carets::DeleteByEffectType(int type)
134 {
135 int count = 0;
136 Caret *c = firstcaret;
137 while(c)
138 {
139 if (c->effecttype == type) c->Delete();
140 c = c->next;
141 }
142
143 return count;
144 }
145
DestroyAll(void)146 void Carets::DestroyAll(void)
147 {
148 while(firstcaret)
149 firstcaret->Destroy();
150 }
151
152 /*
153 void c------------------------------() {}
154 */
155
156 // generates a caret-based effect at x, y. Most sprites used for carets have the
157 // drawpoint at their center so the effect is generally centered at that position.
158 //
159 // an effect can be just a convenience function for creating a caret
160 // with a particular sprite/ai combo, or it can produce a group of carets
161 // which are always seen together (e.g. bonkplus or bloodspatter).
effect(int x,int y,int effectno)162 Caret *effect(int x, int y, int effectno)
163 {
164 Caret *c;
165 int i;
166
167 // tell CreateCaret what kind of effect we're spawning
168 _effecttype = effectno;
169
170 switch(effectno)
171 {
172 case EFFECT_STARSOLID: c = CreateCaret(x, y, SPR_STAR_SOLID, caret_animate3); break;
173 case EFFECT_STARPOOF: c = CreateCaret(x, y, SPR_STAR_POOF, caret_animate3); break;
174 case EFFECT_FISHY: c = CreateCaret(x, y, SPR_FISHY, caret_fishy); break;
175 case EFFECT_BOOMFLASH: c = CreateCaret(x, y, SPR_BOOMFLASH, caret_animate3); break;
176 case EFFECT_BUBBLE_BURST: c = CreateCaret(x, y, SPR_BUBBLE_BURST, caret_animate3); break;
177 case EFFECT_SPUR_HIT: c = CreateCaret(x, y, SPR_SPUR_HIT, caret_spur_hit); break;
178 case EFFECT_ZZZZ: c = CreateCaret(x, y, SPR_ZZZZ, caret_zzzz); break;
179 case EFFECT_LEVELUP: c = CreateCaret(x, y, SPR_LEVELUP, caret_playertext); break;
180 case EFFECT_LEVELDOWN: c = CreateCaret(x, y, SPR_LEVELDOWN, caret_playertext); break;
181 case EFFECT_BONUSFLASH: c = CreateCaret(x, y, SPR_SMOKE_CLOUD, caret_bonusflash); break;
182 case EFFECT_HEY: c = CreateCaret(x, y, SPR_HEY, caret_hey); break;
183 case EFFECT_EMPTY: c = CreateCaret(x, y, SPR_EMPTY, caret_playertext); break;
184 case EFFECT_SMOKETRAIL: c = CreateCaret(x, y, SPR_SMOKETRAIL, caret_animate2); break;
185
186 case EFFECT_SMOKETRAIL_SLOW:
187 c = CreateCaret(x, y, SPR_SMOKETRAIL, caret_animate3);
188 break;
189
190 case EFFECT_GUNFISH_BUBBLE:
191 {
192 c = CreateCaret(x-(3<<CSF), y-(3<<CSF), SPR_GUNFISH_BUBBLE, caret_gunfish_bubble); break;
193 }
194 break;
195
196 case EFFECT_LAVA_SPLASH:
197 {
198 c = CreateCaret(x-(3<<CSF), y-(3<<CSF), SPR_LAVA_DRIP_SPLASH, caret_gunfish_bubble); break;
199 }
200 break;
201
202 case EFFECT_GHOST_SPARKLE:
203 {
204 c = CreateCaret(x, y, SPR_GHOST_SPARKLE, caret_ghost_sparkle);
205 c->yinertia = random(-0x600, -0x200);
206 }
207 break;
208
209 // "blood" spatters from shot hitting enemy
210 case EFFECT_BLOODSPLATTER:
211 {
212 for(i=0;i<3;i++)
213 {
214 c = CreateCaret(x, y, SPR_BLOODHIT, caret_animate3);
215 vector_from_angle(random(0, 255), (2<<CSF), &c->xinertia, &c->yinertia);
216 }
217 }
218 break;
219
220 // two little blinky stars when player bonks his head on the ceiling
221 case EFFECT_BONKPLUS:
222 {
223 for(i=0;i<2;i++)
224 {
225 c = CreateCaret(x, y, SPR_BONKHEADPLUS, caret_bonkplus);
226
227 c->xinertia = random(-0x600, 0x600);
228 c->yinertia = random(-0x200, 0x200);
229 //uint8_t angle = random(-14, 14);
230 //if (random(0, 1)) angle += 128;
231 //vector_from_angle(angle, random(0x200, 0x384), &c->xinertia, &c->yinertia);
232 }
233 }
234 break;
235
236 case EFFECT_QMARK:
237 {
238 // only 1 question mark is ever shown at a time
239 DeleteEffectsOfType(EFFECT_QMARK);
240 c = CreateCaret(x, y, SPR_QMARK, caret_qmark);
241 }
242 break;
243
244 default:
245 NX_ERR("effect: invalid effect type %d\n", effectno);
246 return NULL;
247 }
248
249 _effecttype = EFFECT_NONE;
250 return c;
251 }
252
253 /*
254 void c------------------------------() {}
255 */
256
caret_animate1(Caret * c)257 void caret_animate1(Caret *c)
258 {
259 c->animdie(0);
260 }
261
caret_animate2(Caret * c)262 void caret_animate2(Caret *c)
263 {
264 c->animdie(1);
265 }
266
caret_animate3(Caret * c)267 void caret_animate3(Caret *c)
268 {
269 c->animdie(2);
270 }
271
anim(int speed)272 void Caret::anim(int speed)
273 {
274 Caret * const &c = this;
275
276 if (++c->animtimer > speed)
277 {
278 c->animtimer = 0;
279
280 if (++c->frame >= sprites[c->sprite].nframes)
281 c->frame = 0;
282 }
283 }
284
animdie(int speed)285 void Caret::animdie(int speed)
286 {
287 Caret * const &c = this;
288
289 if (++c->animtimer > speed)
290 {
291 c->animtimer = 0;
292
293 if (++c->frame >= sprites[c->sprite].nframes)
294 c->Delete();
295 }
296 }
297
298 /*
299 void c------------------------------() {}
300 */
301
302 // flickers rapidly and decels at exponential speed.
303 // used for the "bonkplus" effect when you bonk your head
caret_bonkplus(Caret * c)304 void caret_bonkplus(Caret *c)
305 {
306 c->xinertia *= 4; c->xinertia /= 5;
307 c->yinertia *= 4; c->yinertia /= 5;
308
309 c->invisible = (++c->timer & 2);
310
311 if (c->timer > 20)
312 c->Delete();
313 }
314
315
caret_fishy(Caret * c)316 void caret_fishy(Caret *c)
317 {
318 c->yinertia -= 16;
319 c->animdie(4);
320 }
321
322
caret_spur_hit(Caret * c)323 void caret_spur_hit(Caret *c)
324 {
325 c->timer++;
326 c->frame = (c->timer / 2) % 3;
327
328 if (c->timer > 24)
329 c->Delete();
330 }
331
332
333 // "Level Up", "Level Down", and "Empty" texts
caret_playertext(Caret * c)334 void caret_playertext(Caret *c)
335 {
336 int spd, stop;
337
338 c->anim(1);
339
340 // "EMPTY" text goes twice as fast as "Level" text
341 if (c->sprite == SPR_EMPTY)
342 {
343 spd = 2;
344 stop = 18;
345 }
346 else
347 {
348 spd = 1;
349 stop = 20;
350 }
351
352 c->timer += spd;
353 if (c->timer < 80)
354 {
355 if (c->timer < stop)
356 {
357 c->y -= (spd << CSF);
358 }
359 }
360 else
361 {
362 c->Delete();
363 }
364 }
365
366
367 // ? effect when you press down with no object around to activate
caret_qmark(Caret * c)368 void caret_qmark(Caret *c)
369 {
370 if (++c->timer < 40)
371 {
372 if (c->timer < 7)
373 {
374 c->y -= (3 << CSF);
375 }
376 }
377 else
378 {
379 c->Delete();
380 }
381 }
382
383
caret_bonusflash(Caret * c)384 void caret_bonusflash(Caret *c)
385 {
386 if (++c->timer == 4)
387 c->Delete();
388 }
389
390
caret_hey(Caret * c)391 void caret_hey(Caret *c)
392 {
393 if (++c->timer > 30) c->Delete();
394 if (c->timer < 5) c->y -= (1<<CSF);
395 }
396
397
caret_gunfish_bubble(Caret * c)398 void caret_gunfish_bubble(Caret *c)
399 {
400 c->animdie(5);
401
402 c->yinertia += 0x40;
403 if (c->yinertia >= 0x5ff) c->yinertia = 0x5ff;
404 }
405
406
caret_ghost_sparkle(Caret * c)407 void caret_ghost_sparkle(Caret *c)
408 {
409 c->invisible = (++c->timer & 2);
410
411 if (c->timer > 20)
412 c->Delete();
413 }
414
415
caret_zzzz(Caret * c)416 void caret_zzzz(Caret *c)
417 {
418 c->animdie(5);
419
420 c->x += 0x80;
421 c->y -= 0x80;
422 }
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440