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