Subversion Repositories Games.Rick Dangerous

Rev

Details | Last modification | View Log | RSS feed

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