mirror of
https://github.com/dwsJason/xrick2gs.git
synced 2024-09-12 06:55:40 +00:00
521 lines
13 KiB
C
521 lines
13 KiB
C
/*
|
|
* xrick/src/ents.c
|
|
*
|
|
* Copyright (C) 1998-2002 BigOrno (bigorno@bigorno.net). All rights reserved.
|
|
*
|
|
* The use and distribution terms for this software are contained in the file
|
|
* named README, which can be found in the root of this distribution. By
|
|
* using this software in any fashion, you are agreeing to be bound by the
|
|
* terms of this license.
|
|
*
|
|
* You must not remove this notice, or any other, from this software.
|
|
*/
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include "system.h"
|
|
#include "config.h"
|
|
#include "game.h"
|
|
#include "ents.h"
|
|
#include "debug.h"
|
|
|
|
#include "e_bullet.h"
|
|
#include "e_bomb.h"
|
|
#include "e_rick.h"
|
|
#include "e_them.h"
|
|
#include "e_bonus.h"
|
|
#include "e_box.h"
|
|
#include "e_sbonus.h"
|
|
#include "rects.h"
|
|
#include "maps.h"
|
|
#include "draw.h"
|
|
|
|
/*
|
|
* global vars
|
|
*/
|
|
ent_t ent_ents[ENT_ENTSNUM + 1];
|
|
rect_t *ent_rects = NULL;
|
|
|
|
|
|
/*
|
|
* prototypes
|
|
*/
|
|
static void ent_addrect(S16, S16, U16, U16);
|
|
static U8 ent_creat1(U8 *);
|
|
static U8 ent_creat2(U8 *, U16);
|
|
|
|
|
|
/*
|
|
* Reset entities
|
|
*
|
|
* ASM 2520
|
|
*/
|
|
void
|
|
ent_reset(void)
|
|
{
|
|
U8 i;
|
|
|
|
E_RICK_STRST(E_RICK_STSTOP);
|
|
e_bomb_lethal = FALSE;
|
|
|
|
ent_ents[0].n = 0;
|
|
for (i = 2; ent_ents[i].n != 0xff; i++)
|
|
ent_ents[i].n = 0;
|
|
}
|
|
|
|
|
|
/*
|
|
* Create an entity on slots 4 to 8 by using the first slot available.
|
|
* Entities of type e_them on slots 4 to 8, when lethal, can kill
|
|
* other e_them (on slots 4 to C) as well as rick.
|
|
*
|
|
* ASM 209C
|
|
*
|
|
* e: anything, CHANGED to the allocated entity number.
|
|
* return: TRUE/OK FALSE/not
|
|
*/
|
|
static U8
|
|
ent_creat1(U8 *e)
|
|
{
|
|
/* look for a slot */
|
|
for (*e = 0x04; *e < 0x09; (*e)++)
|
|
if (ent_ents[*e].n == 0) { /* if slot available, use it */
|
|
ent_ents[*e].c1 = 0;
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
/*
|
|
* Create an entity on slots 9 to C by using the first slot available.
|
|
* Entities of type e_them on slots 9 to C can kill rick when lethal,
|
|
* but they can never kill other e_them.
|
|
*
|
|
* ASM 20BC
|
|
*
|
|
* e: anything, CHANGED to the allocated entity number.
|
|
* m: number of the mark triggering the creation of the entity.
|
|
* ret: TRUE/OK FALSE/not
|
|
*/
|
|
static U8
|
|
ent_creat2(U8 *e, U16 m)
|
|
{
|
|
/* make sure the entity created by this mark is not active already */
|
|
for (*e = 0x09; *e < 0x0c; (*e)++)
|
|
if (ent_ents[*e].n != 0 && ent_ents[*e].mark == m)
|
|
return FALSE;
|
|
|
|
/* look for a slot */
|
|
for (*e = 0x09; *e < 0x0c; (*e)++)
|
|
if (ent_ents[*e].n == 0) { /* if slot available, use it */
|
|
ent_ents[*e].c1 = 2;
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
/*
|
|
* Process marks that are within the visible portion of the map,
|
|
* and create the corresponding entities.
|
|
*
|
|
* absolute map coordinate means that they are not relative to
|
|
* map_frow, as any other coordinates are.
|
|
*
|
|
* ASM 1F40
|
|
*
|
|
* frow: first visible row of the map -- absolute map coordinate
|
|
* lrow: last visible row of the map -- absolute map coordinate
|
|
*/
|
|
void
|
|
ent_actvis(U8 frow, U8 lrow)
|
|
{
|
|
U16 m;
|
|
U8 e;
|
|
U16 y;
|
|
|
|
/*
|
|
* go through the list and find the first mark that
|
|
* is visible, i.e. which has a row greater than the
|
|
* first row (marks being ordered by row number).
|
|
*/
|
|
for (m = map_submaps[game_submap].mark;
|
|
map_marks[m].row != 0xff && map_marks[m].row < frow;
|
|
m++);
|
|
|
|
if (map_marks[m].row == 0xff) /* none found */
|
|
return;
|
|
|
|
/*
|
|
* go through the list and process all marks that are
|
|
* visible, i.e. which have a row lower than the last
|
|
* row (marks still being ordered by row number).
|
|
*/
|
|
for (;
|
|
map_marks[m].row != 0xff && map_marks[m].row < lrow;
|
|
m++) {
|
|
|
|
/* ignore marks that are not active */
|
|
if (map_marks[m].ent & MAP_MARK_NACT)
|
|
continue;
|
|
|
|
/*
|
|
* allocate a slot to the new entity
|
|
*
|
|
* slot type
|
|
* 0 available for e_them (lethal to other e_them, and stops entities
|
|
* i.e. entities can't move over them. E.g. moving blocks. But they
|
|
* can move over entities and kill them!).
|
|
* 1 xrick
|
|
* 2 bullet
|
|
* 3 bomb
|
|
* 4-8 available for e_them, e_box, e_bonus or e_sbonus (lethal to
|
|
* other e_them, identified by their number being >= 0x10)
|
|
* 9-C available for e_them, e_box, e_bonus or e_sbonus (not lethal to
|
|
* other e_them, identified by their number being < 0x10)
|
|
*
|
|
* the type of an entity is determined by its .n as detailed below.
|
|
*
|
|
* 1 xrick
|
|
* 2 bullet
|
|
* 3 bomb
|
|
* 4, 7, a, d e_them, type 1a
|
|
* 5, 8, b, e e_them, type 1b
|
|
* 6, 9, c, f e_them, type 2
|
|
* 10, 11 box
|
|
* 12, 13, 14, 15 bonus
|
|
* 16, 17 speed bonus
|
|
* >17 e_them, type 3
|
|
* 47 zombie
|
|
*/
|
|
|
|
if (!(map_marks[m].flags & ENT_FLG_STOPRICK)) {
|
|
if (map_marks[m].ent >= 0x10) {
|
|
/* boxes, bonuses and type 3 e_them go to slot 4-8 */
|
|
/* (c1 set to 0 -> all type 3 e_them are sleeping) */
|
|
if (!ent_creat1(&e)) continue;
|
|
}
|
|
else {
|
|
/* type 1 and 2 e_them go to slot 9-c */
|
|
/* (c1 set to 2) */
|
|
if (!ent_creat2(&e, m)) continue;
|
|
}
|
|
}
|
|
else {
|
|
/* entities stopping rick (e.g. blocks) go to slot 0 */
|
|
if (ent_ents[0].n) continue;
|
|
e = 0;
|
|
ent_ents[0].c1 = 0;
|
|
}
|
|
|
|
/*
|
|
* initialize the entity
|
|
*/
|
|
ent_ents[e].mark = m;
|
|
ent_ents[e].flags = map_marks[m].flags;
|
|
ent_ents[e].n = map_marks[m].ent;
|
|
|
|
/*
|
|
* if entity is to be already running (i.e. not asleep and waiting
|
|
* for some trigger to move), then use LETHALR i.e. restart flag, right
|
|
* from the beginning
|
|
*/
|
|
if (ent_ents[e].flags & ENT_FLG_LETHALR)
|
|
ent_ents[e].n |= ENT_LETHAL;
|
|
|
|
ent_ents[e].x = map_marks[m].xy & 0xf8;
|
|
|
|
y = (map_marks[m].xy & 0x07) + (map_marks[m].row & 0xf8) - map_frow;
|
|
y <<= 3;
|
|
if (!(ent_ents[e].flags & ENT_FLG_STOPRICK))
|
|
y += 3;
|
|
ent_ents[e].y = y;
|
|
|
|
ent_ents[e].xsave = ent_ents[e].x;
|
|
ent_ents[e].ysave = ent_ents[e].y;
|
|
|
|
/*ent_ents[e].w0C = 0;*/ /* in ASM code but never used */
|
|
|
|
ent_ents[e].w = ent_entdata[map_marks[m].ent].w;
|
|
ent_ents[e].h = ent_entdata[map_marks[m].ent].h;
|
|
ent_ents[e].sprbase = ent_entdata[map_marks[m].ent].spr;
|
|
ent_ents[e].sprite = (U8)ent_entdata[map_marks[m].ent].spr;
|
|
ent_ents[e].step_no_i = ent_entdata[map_marks[m].ent].sni;
|
|
ent_ents[e].trigsnd = (U8)ent_entdata[map_marks[m].ent].snd;
|
|
|
|
/*
|
|
* FIXME what is this? when all trigger flags are up, then
|
|
* use .sni for sprbase. Why? What is the point? (This is
|
|
* for type 1 and 2 e_them, ...)
|
|
*
|
|
* This also means that as long as sprite has not been
|
|
* recalculated, a wrong value is used. This is normal, see
|
|
* what happens to the falling guy on the right on submap 3:
|
|
* it changes when hitting the ground.
|
|
*/
|
|
#define ENT_FLG_TRIGGERS \
|
|
(ENT_FLG_TRIGBOMB|ENT_FLG_TRIGBULLET|ENT_FLG_TRIGSTOP|ENT_FLG_TRIGRICK)
|
|
if ((ent_ents[e].flags & ENT_FLG_TRIGGERS) == ENT_FLG_TRIGGERS
|
|
&& e >= 0x09)
|
|
ent_ents[e].sprbase = (U8)(ent_entdata[map_marks[m].ent].sni & 0x00ff);
|
|
#undef ENT_FLG_TRIGGERS
|
|
|
|
ent_ents[e].trig_x = map_marks[m].lt & 0xf8;
|
|
ent_ents[e].latency = (map_marks[m].lt & 0x07) << 5; /* <<5 eq *32 */
|
|
|
|
ent_ents[e].trig_y = 3 + 8 * ((map_marks[m].row & 0xf8) - map_frow +
|
|
(map_marks[m].lt & 0x07));
|
|
|
|
ent_ents[e].c2 = 0;
|
|
ent_ents[e].offsy = 0;
|
|
ent_ents[e].ylow = 0;
|
|
|
|
ent_ents[e].front = FALSE;
|
|
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Add a tile-aligned rectangle containing the given rectangle (indicated
|
|
* by its MAP coordinates) to the list of rectangles. Clip the rectangle
|
|
* so it fits into the display zone.
|
|
*/
|
|
static void
|
|
ent_addrect(S16 x, S16 y, U16 width, U16 height)
|
|
{
|
|
S16 x0, y0;
|
|
U16 w0, h0;
|
|
|
|
/*sys_printf("rect %#04x,%#04x %#04x %#04x ", x, y, width, height);*/
|
|
|
|
/* align to tiles */
|
|
x0 = x & 0xfff8;
|
|
y0 = y & 0xfff8;
|
|
w0 = width;
|
|
h0 = height;
|
|
if (x - x0) w0 = (w0 + (x - x0)) | 0x0007;
|
|
if (y - y0) h0 = (h0 + (y - y0)) | 0x0007;
|
|
|
|
/* clip */
|
|
if (draw_clipms(&x0, &y0, &w0, &h0)) { /* do not add if fully clipped */
|
|
/*sys_printf("-> [clipped]\n");*/
|
|
return;
|
|
}
|
|
|
|
/*sys_printf("-> %#04x,%#04x %#04x %#04x\n", x0, y0, w0, h0);*/
|
|
|
|
#ifdef GFXST
|
|
y0 += 8;
|
|
#endif
|
|
|
|
/* get to screen */
|
|
x0 -= DRAW_XYMAP_SCRLEFT;
|
|
y0 -= DRAW_XYMAP_SCRTOP;
|
|
|
|
/* add rectangle to the list */
|
|
ent_rects = rects_new(x0, y0, w0, h0, ent_rects);
|
|
}
|
|
|
|
|
|
/*
|
|
* Draw all entities onto the frame buffer.
|
|
*
|
|
* ASM 07a4
|
|
*
|
|
* NOTE This may need to be part of draw.c. Also needs better comments,
|
|
* NOTE and probably better rectangles management.
|
|
*/
|
|
void
|
|
ent_draw(void)
|
|
{
|
|
U8 i;
|
|
#ifdef ENABLE_CHEATS
|
|
static U8 ch3 = FALSE;
|
|
#endif
|
|
S16 dx, dy;
|
|
|
|
draw_tilesBank = map_tilesBank;
|
|
|
|
/* reset rectangles list */
|
|
rects_free(ent_rects);
|
|
ent_rects = NULL;
|
|
|
|
/*sys_printf("\n");*/
|
|
|
|
/*
|
|
* background loop : erase all entities that were visible
|
|
*/
|
|
for (i = 0; ent_ents[i].n != 0xff; i++) {
|
|
#ifdef ENABLE_CHEATS
|
|
if (ent_ents[i].prev_n && (ch3 || ent_ents[i].prev_s))
|
|
#else
|
|
if (ent_ents[i].prev_n && ent_ents[i].prev_s)
|
|
#endif
|
|
/* if entity was active, then erase it (redraw the map) */
|
|
draw_spriteBackground(ent_ents[i].prev_x, ent_ents[i].prev_y);
|
|
}
|
|
|
|
/*
|
|
* foreground loop : draw all entities that are visible
|
|
*/
|
|
for (i = 0; ent_ents[i].n != 0xff; i++) {
|
|
/*
|
|
* If entity is active now, draw the sprite. If entity was
|
|
* not active before, add a rectangle for the sprite.
|
|
*/
|
|
#ifdef ENABLE_CHEATS
|
|
if (ent_ents[i].n && (game_cheat3 || ent_ents[i].sprite))
|
|
#else
|
|
if (ent_ents[i].n && ent_ents[i].sprite)
|
|
#endif
|
|
/* If entitiy is active, draw the sprite. */
|
|
draw_sprite2(ent_ents[i].sprite,
|
|
ent_ents[i].x, ent_ents[i].y,
|
|
ent_ents[i].front);
|
|
}
|
|
|
|
/*
|
|
* rectangles loop : figure out which parts of the screen have been
|
|
* impacted and need to be refreshed, then save state
|
|
*/
|
|
for (i = 0; ent_ents[i].n != 0xff; i++) {
|
|
#ifdef ENABLE_CHEATS
|
|
if (ent_ents[i].prev_n && (ch3 || ent_ents[i].prev_s)) {
|
|
#else
|
|
if (ent_ents[i].prev_n && ent_ents[i].prev_s) {
|
|
#endif
|
|
/* (1) if entity was active and has been drawn ... */
|
|
#ifdef ENABLE_CHEATS
|
|
if (ent_ents[i].n && (game_cheat3 || ent_ents[i].sprite)) {
|
|
#else
|
|
if (ent_ents[i].n && ent_ents[i].sprite) {
|
|
#endif
|
|
/* (1.1) ... and is still active now and still needs to be drawn, */
|
|
/* then check if rectangles intersect */
|
|
dx = abs(ent_ents[i].x - ent_ents[i].prev_x);
|
|
dy = abs(ent_ents[i].y - ent_ents[i].prev_y);
|
|
if (dx < 0x20 && dy < 0x16) {
|
|
/* (1.1.1) if they do, then create one rectangle */
|
|
ent_addrect((ent_ents[i].prev_x < ent_ents[i].x)
|
|
? ent_ents[i].prev_x : ent_ents[i].x,
|
|
(ent_ents[i].prev_y < ent_ents[i].y)
|
|
? ent_ents[i].prev_y : ent_ents[i].y,
|
|
dx + 0x20, dy + 0x15);
|
|
}
|
|
else {
|
|
/* (1.1.2) else, create two rectangles */
|
|
ent_addrect(ent_ents[i].x, ent_ents[i].y, 0x20, 0x15);
|
|
ent_addrect(ent_ents[i].prev_x, ent_ents[i].prev_y, 0x20, 0x15);
|
|
}
|
|
}
|
|
else
|
|
/* (1.2) ... and is not active anymore or does not need to be drawn */
|
|
/* then create one single rectangle */
|
|
ent_addrect(ent_ents[i].prev_x, ent_ents[i].prev_y, 0x20, 0x15);
|
|
}
|
|
#ifdef ENABLE_CHEATS
|
|
else if (ent_ents[i].n && (game_cheat3 || ent_ents[i].sprite)) {
|
|
#else
|
|
else if (ent_ents[i].n && ent_ents[i].sprite) {
|
|
#endif
|
|
/* (2) if entity is active and needs to be drawn, */
|
|
/* then create one rectangle */
|
|
ent_addrect(ent_ents[i].x, ent_ents[i].y, 0x20, 0x15);
|
|
}
|
|
|
|
/* save state */
|
|
ent_ents[i].prev_x = ent_ents[i].x;
|
|
ent_ents[i].prev_y = ent_ents[i].y;
|
|
ent_ents[i].prev_n = ent_ents[i].n;
|
|
ent_ents[i].prev_s = ent_ents[i].sprite;
|
|
}
|
|
|
|
#ifdef ENABLE_CHEATS
|
|
ch3 = game_cheat3;
|
|
#endif
|
|
}
|
|
|
|
|
|
/*
|
|
* Clear entities previous state
|
|
*
|
|
*/
|
|
void
|
|
ent_clprev(void)
|
|
{
|
|
U8 i;
|
|
|
|
for (i = 0; ent_ents[i].n != 0xff; i++)
|
|
ent_ents[i].prev_n = 0;
|
|
}
|
|
|
|
/*
|
|
* Table containing entity action function pointers.
|
|
*/
|
|
void (*ent_actf[])(U8) = {
|
|
NULL, /* 00 - zero means that the slot is free */
|
|
e_rick_action, /* 01 - 12CA */
|
|
e_bullet_action, /* 02 - 1883 */
|
|
e_bomb_action, /* 03 - 18CA */
|
|
e_them_t1a_action, /* 04 - 2452 */
|
|
e_them_t1b_action, /* 05 - 21CA */
|
|
e_them_t2_action, /* 06 - 2718 */
|
|
e_them_t1a_action, /* 07 - 2452 */
|
|
e_them_t1b_action, /* 08 - 21CA */
|
|
e_them_t2_action, /* 09 - 2718 */
|
|
e_them_t1a_action, /* 0A - 2452 */
|
|
e_them_t1b_action, /* 0B - 21CA */
|
|
e_them_t2_action, /* 0C - 2718 */
|
|
e_them_t1a_action, /* 0D - 2452 */
|
|
e_them_t1b_action, /* 0E - 21CA */
|
|
e_them_t2_action, /* 0F - 2718 */
|
|
e_box_action, /* 10 - 245A */
|
|
e_box_action, /* 11 - 245A */
|
|
e_bonus_action, /* 12 - 242C */
|
|
e_bonus_action, /* 13 - 242C */
|
|
e_bonus_action, /* 14 - 242C */
|
|
e_bonus_action, /* 15 - 242C */
|
|
e_sbonus_start, /* 16 - 2182 */
|
|
e_sbonus_stop /* 17 - 2143 */
|
|
};
|
|
|
|
|
|
/*
|
|
* Run entities action function
|
|
*
|
|
*/
|
|
void
|
|
ent_action(void)
|
|
{
|
|
U8 i, k;
|
|
|
|
IFDEBUG_ENTS(
|
|
sys_printf("xrick/ents: --------- action ----------------\n");
|
|
for (i = 0; ent_ents[i].n != 0xff; i++)
|
|
if (ent_ents[i].n) {
|
|
sys_printf("xrick/ents: slot %#04x, entity %#04x", i, ent_ents[i].n);
|
|
sys_printf(" (%#06x, %#06x), sprite %#04x.\n",
|
|
ent_ents[i].x, ent_ents[i].y, ent_ents[i].sprite);
|
|
}
|
|
);
|
|
|
|
for (i = 0; ent_ents[i].n != 0xff; i++) {
|
|
if (ent_ents[i].n) {
|
|
k = ent_ents[i].n & 0x7f;
|
|
if (k == 0x47)
|
|
e_them_z_action(i);
|
|
else if (k >= 0x18)
|
|
e_them_t3_action(i);
|
|
else
|
|
ent_actf[k](i);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/* eof */
|