1 /*
2  * xrick/src/ents.c
3  *
4  * Copyright (C) 1998-2002 BigOrno (bigorno@bigorno.net). All rights reserved.
5  *
6  * The use and distribution terms for this software are contained in the file
7  * named README, which can be found in the root of this distribution. By
8  * using this software in any fashion, you are agreeing to be bound by the
9  * terms of this license.
10  *
11  * You must not remove this notice, or any other, from this software.
12  */
13 
14 #include <stdlib.h>
15 
16 #include "system.h"
17 #include "config.h"
18 #include "game.h"
19 #include "ents.h"
20 #include "debug.h"
21 
22 #include "e_bullet.h"
23 #include "e_bomb.h"
24 #include "e_rick.h"
25 #include "e_them.h"
26 #include "e_bonus.h"
27 #include "e_box.h"
28 #include "e_sbonus.h"
29 #include "rects.h"
30 #include "maps.h"
31 #include "draw.h"
32 
33 /*
34  * global vars
35  */
36 ent_t ent_ents[ENT_ENTSNUM + 1];
37 rect_t *ent_rects = NULL;
38 
39 
40 /*
41  * prototypes
42  */
43 static void ent_addrect(S16, S16, U16, U16);
44 static U8 ent_creat1(U8 *);
45 static U8 ent_creat2(U8 *, U16);
46 
47 
48 /*
49  * Reset entities
50  *
51  * ASM 2520
52  */
53 void
ent_reset(void)54 ent_reset(void)
55 {
56   U8 i;
57 
58   E_RICK_STRST(E_RICK_STSTOP);
59   e_bomb_lethal = FALSE;
60 
61   ent_ents[0].n = 0;
62   for (i = 2; ent_ents[i].n != 0xff; i++)
63     ent_ents[i].n = 0;
64 }
65 
66 
67 /*
68  * Create an entity on slots 4 to 8 by using the first slot available.
69  * Entities of type e_them on slots 4 to 8, when lethal, can kill
70  * other e_them (on slots 4 to C) as well as rick.
71  *
72  * ASM 209C
73  *
74  * e: anything, CHANGED to the allocated entity number.
75  * return: TRUE/OK FALSE/not
76  */
77 static U8
ent_creat1(U8 * e)78 ent_creat1(U8 *e)
79 {
80   /* look for a slot */
81   for (*e = 0x04; *e < 0x09; (*e)++)
82     if (ent_ents[*e].n == 0) {  /* if slot available, use it */
83       ent_ents[*e].c1 = 0;
84       return TRUE;
85     }
86 
87   return FALSE;
88 }
89 
90 
91 /*
92  * Create an entity on slots 9 to C by using the first slot available.
93  * Entities of type e_them on slots 9 to C can kill rick when lethal,
94  * but they can never kill other e_them.
95  *
96  * ASM 20BC
97  *
98  * e: anything, CHANGED to the allocated entity number.
99  * m: number of the mark triggering the creation of the entity.
100  * ret: TRUE/OK FALSE/not
101  */
102 static U8
ent_creat2(U8 * e,U16 m)103 ent_creat2(U8 *e, U16 m)
104 {
105   /* make sure the entity created by this mark is not active already */
106   for (*e = 0x09; *e < 0x0c; (*e)++)
107     if (ent_ents[*e].n != 0 && ent_ents[*e].mark == m)
108       return FALSE;
109 
110   /* look for a slot */
111   for (*e = 0x09; *e < 0x0c; (*e)++)
112     if (ent_ents[*e].n == 0) {  /* if slot available, use it */
113       ent_ents[*e].c1 = 2;
114       return TRUE;
115     }
116 
117   return FALSE;
118 }
119 
120 
121 /*
122  * Process marks that are within the visible portion of the map,
123  * and create the corresponding entities.
124  *
125  * absolute map coordinate means that they are not relative to
126  * map_frow, as any other coordinates are.
127  *
128  * ASM 1F40
129  *
130  * frow: first visible row of the map -- absolute map coordinate
131  * lrow: last visible row of the map -- absolute map coordinate
132  */
133 void
ent_actvis(U8 frow,U8 lrow)134 ent_actvis(U8 frow, U8 lrow)
135 {
136 	U16 m;
137 	U8 e;
138 	U16 y;
139 
140 	/*
141 	* go through the list and find the first mark that
142 	* is visible, i.e. which has a row greater than the
143 	* first row (marks being ordered by row number).
144 	*/
145 	for (m = map_submaps[game_submap].mark;
146 		map_marks[m].row != 0xff && map_marks[m].row < frow;
147 		m++);
148 
149 	if (map_marks[m].row == 0xff)  /* none found */
150 		return;
151 
152 	/*
153 	* go through the list and process all marks that are
154 	* visible, i.e. which have a row lower than the last
155 	* row (marks still being ordered by row number).
156 	*/
157 	for (;
158 		map_marks[m].row != 0xff && map_marks[m].row < lrow;
159 		m++) {
160 
161 		/* ignore marks that are not active */
162 		if (map_marks[m].ent & MAP_MARK_NACT)
163 			continue;
164 
165 		/*
166 		 * allocate a slot to the new entity
167 		 *
168 		 * slot type
169 		 *  0   available for e_them (lethal to other e_them, and stops entities
170 		 *      i.e. entities can't move over them. E.g. moving blocks. But they
171 		 *      can move over entities and kill them!).
172 		 *  1   xrick
173 		 *  2   bullet
174 		 *  3   bomb
175 		 * 4-8  available for e_them, e_box, e_bonus or e_sbonus (lethal to
176 		 *      other e_them, identified by their number being >= 0x10)
177 		 * 9-C  available for e_them, e_box, e_bonus or e_sbonus (not lethal to
178 		 *      other e_them, identified by their number being < 0x10)
179 		 *
180 		 * the type of an entity is determined by its .n as detailed below.
181 		 *
182 		 * 1               xrick
183 		 * 2               bullet
184 		 * 3               bomb
185 		 * 4, 7, a, d      e_them, type 1a
186 		 * 5, 8, b, e      e_them, type 1b
187 		 * 6, 9, c, f      e_them, type 2
188 		 * 10, 11          box
189 		 * 12, 13, 14, 15  bonus
190 		 * 16, 17          speed bonus
191 		 * >17             e_them, type 3
192 		 * 47              zombie
193 		 */
194 
195 		if (!(map_marks[m].flags & ENT_FLG_STOPRICK)) {
196 			if (map_marks[m].ent >= 0x10) {
197 				/* boxes, bonuses and type 3 e_them go to slot 4-8 */
198 				/* (c1 set to 0 -> all type 3 e_them are sleeping) */
199 				if (!ent_creat1(&e)) continue;
200 			}
201 			else {
202 				/* type 1 and 2 e_them go to slot 9-c */
203 				/* (c1 set to 2) */
204 				if (!ent_creat2(&e, m)) continue;
205 			}
206 		}
207 		else {
208 			/* entities stopping rick (e.g. blocks) go to slot 0 */
209 			if (ent_ents[0].n) continue;
210 			e = 0;
211 			ent_ents[0].c1 = 0;
212 		}
213 
214     /*
215      * initialize the entity
216      */
217     ent_ents[e].mark = m;
218     ent_ents[e].flags = map_marks[m].flags;
219     ent_ents[e].n = map_marks[m].ent;
220 
221     /*
222      * if entity is to be already running (i.e. not asleep and waiting
223      * for some trigger to move), then use LETHALR i.e. restart flag, right
224      * from the beginning
225      */
226     if (ent_ents[e].flags & ENT_FLG_LETHALR)
227       ent_ents[e].n |= ENT_LETHAL;
228 
229     ent_ents[e].x = map_marks[m].xy & 0xf8;
230 
231     y = (map_marks[m].xy & 0x07) + (map_marks[m].row & 0xf8) - map_frow;
232     y <<= 3;
233     if (!(ent_ents[e].flags & ENT_FLG_STOPRICK))
234       y += 3;
235     ent_ents[e].y = y;
236 
237     ent_ents[e].xsave = ent_ents[e].x;
238     ent_ents[e].ysave = ent_ents[e].y;
239 
240     /*ent_ents[e].w0C = 0;*/  /* in ASM code but never used */
241 
242     ent_ents[e].w = ent_entdata[map_marks[m].ent].w;
243     ent_ents[e].h = ent_entdata[map_marks[m].ent].h;
244     ent_ents[e].sprbase = ent_entdata[map_marks[m].ent].spr;
245     ent_ents[e].sprite = (U8)ent_entdata[map_marks[m].ent].spr;
246     ent_ents[e].step_no_i = ent_entdata[map_marks[m].ent].sni;
247     ent_ents[e].trigsnd = (U8)ent_entdata[map_marks[m].ent].snd;
248 
249     /*
250      * FIXME what is this? when all trigger flags are up, then
251      * use .sni for sprbase. Why? What is the point? (This is
252      * for type 1 and 2 e_them, ...)
253      *
254      * This also means that as long as sprite has not been
255      * recalculated, a wrong value is used. This is normal, see
256      * what happens to the falling guy on the right on submap 3:
257      * it changes when hitting the ground.
258      */
259 #define ENT_FLG_TRIGGERS \
260 (ENT_FLG_TRIGBOMB|ENT_FLG_TRIGBULLET|ENT_FLG_TRIGSTOP|ENT_FLG_TRIGRICK)
261     if ((ent_ents[e].flags & ENT_FLG_TRIGGERS) == ENT_FLG_TRIGGERS
262 	&& e >= 0x09)
263       ent_ents[e].sprbase = (U8)(ent_entdata[map_marks[m].ent].sni & 0x00ff);
264 #undef ENT_FLG_TRIGGERS
265 
266     ent_ents[e].trig_x = map_marks[m].lt & 0xf8;
267     ent_ents[e].latency = (map_marks[m].lt & 0x07) << 5;  /* <<5 eq *32 */
268 
269     ent_ents[e].trig_y = 3 + 8 * ((map_marks[m].row & 0xf8) - map_frow +
270 				  (map_marks[m].lt & 0x07));
271 
272     ent_ents[e].c2 = 0;
273     ent_ents[e].offsy = 0;
274     ent_ents[e].ylow = 0;
275 
276     ent_ents[e].front = FALSE;
277 
278   }
279 }
280 
281 
282 /*
283  * Add a tile-aligned rectangle containing the given rectangle (indicated
284  * by its MAP coordinates) to the list of rectangles. Clip the rectangle
285  * so it fits into the display zone.
286  */
287 static void
ent_addrect(S16 x,S16 y,U16 width,U16 height)288 ent_addrect(S16 x, S16 y, U16 width, U16 height)
289 {
290   S16 x0, y0;
291   U16 w0, h0;
292 
293   /*sys_printf("rect %#04x,%#04x %#04x %#04x ", x, y, width, height);*/
294 
295   /* align to tiles */
296   x0 = x & 0xfff8;
297   y0 = y & 0xfff8;
298   w0 = width;
299   h0 = height;
300   if (x - x0) w0 = (w0 + (x - x0)) | 0x0007;
301   if (y - y0) h0 = (h0 + (y - y0)) | 0x0007;
302 
303   /* clip */
304   if (draw_clipms(&x0, &y0, &w0, &h0)) {  /* do not add if fully clipped */
305     /*sys_printf("-> [clipped]\n");*/
306     return;
307   }
308 
309   /*sys_printf("-> %#04x,%#04x %#04x %#04x\n", x0, y0, w0, h0);*/
310 
311 #ifdef GFXST
312   y0 += 8;
313 #endif
314 
315   /* get to screen */
316   x0 -= DRAW_XYMAP_SCRLEFT;
317   y0 -= DRAW_XYMAP_SCRTOP;
318 
319   /* add rectangle to the list */
320   ent_rects = rects_new(x0, y0, w0, h0, ent_rects);
321 }
322 
323 
324 /*
325  * Draw all entities onto the frame buffer.
326  *
327  * ASM 07a4
328  *
329  * NOTE This may need to be part of draw.c. Also needs better comments,
330  * NOTE and probably better rectangles management.
331  */
332 void
ent_draw(void)333 ent_draw(void)
334 {
335   U8 i;
336 #ifdef ENABLE_CHEATS
337   static U8 ch3 = FALSE;
338 #endif
339   S16 dx, dy;
340 
341   draw_tilesBank = map_tilesBank;
342 
343   /* reset rectangles list */
344   rects_free(ent_rects);
345   ent_rects = NULL;
346 
347   /*sys_printf("\n");*/
348 
349   /*
350    * background loop : erase all entities that were visible
351    */
352   for (i = 0; ent_ents[i].n != 0xff; i++) {
353 #ifdef ENABLE_CHEATS
354     if (ent_ents[i].prev_n && (ch3 || ent_ents[i].prev_s))
355 #else
356     if (ent_ents[i].prev_n && ent_ents[i].prev_s)
357 #endif
358       /* if entity was active, then erase it (redraw the map) */
359       draw_spriteBackground(ent_ents[i].prev_x, ent_ents[i].prev_y);
360   }
361 
362   /*
363    * foreground loop : draw all entities that are visible
364    */
365   for (i = 0; ent_ents[i].n != 0xff; i++) {
366     /*
367      * If entity is active now, draw the sprite. If entity was
368      * not active before, add a rectangle for the sprite.
369      */
370 #ifdef ENABLE_CHEATS
371     if (ent_ents[i].n && (game_cheat3 || ent_ents[i].sprite))
372 #else
373     if (ent_ents[i].n && ent_ents[i].sprite)
374 #endif
375       /* If entitiy is active, draw the sprite. */
376       draw_sprite2(ent_ents[i].sprite,
377 		   ent_ents[i].x, ent_ents[i].y,
378 		   ent_ents[i].front);
379   }
380 
381   /*
382    * rectangles loop : figure out which parts of the screen have been
383    * impacted and need to be refreshed, then save state
384    */
385   for (i = 0; ent_ents[i].n != 0xff; i++) {
386 #ifdef ENABLE_CHEATS
387     if (ent_ents[i].prev_n && (ch3 || ent_ents[i].prev_s)) {
388 #else
389     if (ent_ents[i].prev_n && ent_ents[i].prev_s) {
390 #endif
391       /* (1) if entity was active and has been drawn ... */
392 #ifdef ENABLE_CHEATS
393       if (ent_ents[i].n && (game_cheat3 || ent_ents[i].sprite)) {
394 #else
395       if (ent_ents[i].n && ent_ents[i].sprite) {
396 #endif
397 	/* (1.1) ... and is still active now and still needs to be drawn, */
398 	/*       then check if rectangles intersect */
399 	dx = abs(ent_ents[i].x - ent_ents[i].prev_x);
400 	dy = abs(ent_ents[i].y - ent_ents[i].prev_y);
401 	if (dx < 0x20 && dy < 0x16) {
402 	  /* (1.1.1) if they do, then create one rectangle */
403 	  ent_addrect((ent_ents[i].prev_x < ent_ents[i].x)
404 		      ? ent_ents[i].prev_x : ent_ents[i].x,
405 		      (ent_ents[i].prev_y < ent_ents[i].y)
406 		      ? ent_ents[i].prev_y : ent_ents[i].y,
407 		      dx + 0x20, dy + 0x15);
408 	}
409 	else {
410 	  /* (1.1.2) else, create two rectangles */
411 	  ent_addrect(ent_ents[i].x, ent_ents[i].y, 0x20, 0x15);
412 	  ent_addrect(ent_ents[i].prev_x, ent_ents[i].prev_y, 0x20, 0x15);
413 	}
414       }
415       else
416 	/* (1.2) ... and is not active anymore or does not need to be drawn */
417 	/*       then create one single rectangle */
418 	ent_addrect(ent_ents[i].prev_x, ent_ents[i].prev_y, 0x20, 0x15);
419     }
420 #ifdef ENABLE_CHEATS
421     else if (ent_ents[i].n && (game_cheat3 || ent_ents[i].sprite)) {
422 #else
423     else if (ent_ents[i].n && ent_ents[i].sprite) {
424 #endif
425       /* (2) if entity is active and needs to be drawn, */
426       /*     then create one rectangle */
427       ent_addrect(ent_ents[i].x, ent_ents[i].y, 0x20, 0x15);
428     }
429 
430     /* save state */
431     ent_ents[i].prev_x = ent_ents[i].x;
432     ent_ents[i].prev_y = ent_ents[i].y;
433     ent_ents[i].prev_n = ent_ents[i].n;
434     ent_ents[i].prev_s = ent_ents[i].sprite;
435   }
436 
437 #ifdef ENABLE_CHEATS
438   ch3 = game_cheat3;
439 #endif
440 }
441 
442 
443 /*
444  * Clear entities previous state
445  *
446  */
447 void
448 ent_clprev(void)
449 {
450   U8 i;
451 
452   for (i = 0; ent_ents[i].n != 0xff; i++)
453     ent_ents[i].prev_n = 0;
454 }
455 
456 /*
457  * Table containing entity action function pointers.
458  */
459 void (*ent_actf[])(U8) = {
460   NULL,        /* 00 - zero means that the slot is free */
461   e_rick_action,   /* 01 - 12CA */
462   e_bullet_action,  /* 02 - 1883 */
463   e_bomb_action,  /* 03 - 18CA */
464   e_them_t1a_action,  /* 04 - 2452 */
465   e_them_t1b_action,  /* 05 - 21CA */
466   e_them_t2_action,  /* 06 - 2718 */
467   e_them_t1a_action,  /* 07 - 2452 */
468   e_them_t1b_action,  /* 08 - 21CA */
469   e_them_t2_action,  /* 09 - 2718 */
470   e_them_t1a_action,  /* 0A - 2452 */
471   e_them_t1b_action,  /* 0B - 21CA */
472   e_them_t2_action,  /* 0C - 2718 */
473   e_them_t1a_action,  /* 0D - 2452 */
474   e_them_t1b_action,  /* 0E - 21CA */
475   e_them_t2_action,  /* 0F - 2718 */
476   e_box_action,  /* 10 - 245A */
477   e_box_action,  /* 11 - 245A */
478   e_bonus_action,  /* 12 - 242C */
479   e_bonus_action,  /* 13 - 242C */
480   e_bonus_action,  /* 14 - 242C */
481   e_bonus_action,  /* 15 - 242C */
482   e_sbonus_start,  /* 16 - 2182 */
483   e_sbonus_stop  /* 17 - 2143 */
484 };
485 
486 
487 /*
488  * Run entities action function
489  *
490  */
491 void
492 ent_action(void)
493 {
494   U8 i, k;
495 
496   IFDEBUG_ENTS(
497     sys_printf("xrick/ents: --------- action ----------------\n");
498     for (i = 0; ent_ents[i].n != 0xff; i++)
499       if (ent_ents[i].n) {
500 	sys_printf("xrick/ents: slot %#04x, entity %#04x", i, ent_ents[i].n);
501 	sys_printf(" (%#06x, %#06x), sprite %#04x.\n",
502 		   ent_ents[i].x, ent_ents[i].y, ent_ents[i].sprite);
503       }
504     );
505 
506   for (i = 0; ent_ents[i].n != 0xff; i++) {
507     if (ent_ents[i].n) {
508       k = ent_ents[i].n & 0x7f;
509       if (k == 0x47)
510 	e_them_z_action(i);
511       else if (k >= 0x18)
512         e_them_t3_action(i);
513       else
514 	ent_actf[k](i);
515     }
516   }
517 }
518 
519 
520 /* eof */
521