952 lines
20 KiB
C
952 lines
20 KiB
C
|
|
#include "game.h"
|
|
#include "bomber.h"
|
|
#include "draw.h"
|
|
#include "gfx.h"
|
|
#include "list.h"
|
|
#include "menu.h"
|
|
#include "network.h"
|
|
#include "sound.h"
|
|
#include "utils.h"
|
|
|
|
static int gameframe;
|
|
|
|
static listhead activebombs;
|
|
static listhead activedecays;
|
|
static listhead activebonus;
|
|
static listhead activegeneric;
|
|
static listhead detonatebombs;
|
|
static listhead activeflames;
|
|
static listhead activeplayers;
|
|
static listhead allplayers;
|
|
|
|
figure walking[MAXSETS][NUMWALKFRAMES];
|
|
solid background, backgroundoriginal;
|
|
|
|
/* The playfield array, contains FIELD_* equates */
|
|
unsigned char field[32][32];
|
|
void* info[32][32];
|
|
|
|
volatile char exitflag = 0;
|
|
static int framecount = 0;
|
|
|
|
char playername[16];
|
|
|
|
static int mycount;
|
|
|
|
static int bonustotal;
|
|
static const int bonuschances[] = {TILE_BOMB, 20, TILE_FLAME, 20, TILE_TRIGGER, 2, TILE_GOLDFLAME, 2, TILE_SKATES, 20, TILE_TURTLE, 5, TILE_NONE, 160};
|
|
|
|
static GameOptions gameoptions;
|
|
|
|
static const unsigned char playerpositions[MAXNETNODES * 3] = {
|
|
/* color, x, y */
|
|
2, 0, 0, 3, 14, 10, 4, 14, 0, 5, 0, 10, 1, 6, 0, 6, 8, 10, 7, 0, 6, 8, 14, 4,
|
|
};
|
|
|
|
static void resetplayer(player* pl, int color, int x, int y) {
|
|
pl->speed = SPEEDSTART;
|
|
pl->flags = 0;
|
|
pl->flamelength = gameoptions.flames + 1;
|
|
pl->bombsavailable = gameoptions.bombs + 1;
|
|
|
|
pl->color = color;
|
|
|
|
pl->xpos = arraytoscreenx(x);
|
|
pl->ypos = arraytoscreeny(y);
|
|
|
|
field[y][x] = FIELD_EMPTY;
|
|
if (x) field[y][x - 1] = FIELD_EMPTY;
|
|
if (y) field[y - 1][x] = FIELD_EMPTY;
|
|
if (x < arraynumx - 1) field[y][x + 1] = FIELD_EMPTY;
|
|
if (y < arraynumy - 1) field[y + 1][x] = FIELD_EMPTY;
|
|
|
|
addtail(&activeplayers, pl);
|
|
}
|
|
|
|
static void initplayer(int color, int x, int y, int controller) {
|
|
player* pl;
|
|
|
|
pl = allocentry(player);
|
|
if (!pl) nomem("Couldn't get player structure (allocentry())");
|
|
|
|
list_add_tail(&allplayers, &pl->list_all_players);
|
|
|
|
pl->controller = controller;
|
|
pl->fixx = -4;
|
|
pl->fixy = -40;
|
|
pl->kills = pl->deaths = 0;
|
|
|
|
resetplayer(pl, color, x, y);
|
|
}
|
|
|
|
static void initplayers(void) {
|
|
int i;
|
|
const unsigned char* p;
|
|
int c, x, y;
|
|
|
|
if (NETWORK_NONE == network) {
|
|
initplayer(2, 0, 0, -1);
|
|
return;
|
|
}
|
|
p = playerpositions;
|
|
for (i = 0; i < MAXNETNODES; ++i) {
|
|
if (!netnodes[i].used) continue;
|
|
c = *p++;
|
|
x = *p++;
|
|
y = *p++;
|
|
initplayer(c, x, y, i);
|
|
}
|
|
}
|
|
|
|
static void resetplayers(void) {
|
|
const unsigned char* p;
|
|
int c, x, y;
|
|
player* pl;
|
|
|
|
p = playerpositions;
|
|
list_for_each_entry(pl, &allplayers, list_all_players) {
|
|
c = *p++;
|
|
x = *p++;
|
|
y = *p++;
|
|
resetplayer(pl, c, x, y);
|
|
}
|
|
}
|
|
|
|
static void firstzero(void) {
|
|
alloc_things();
|
|
list_init_head(&activebombs);
|
|
list_init_head(&detonatebombs);
|
|
list_init_head(&activeflames);
|
|
list_init_head(&activedecays);
|
|
list_init_head(&activebonus);
|
|
list_init_head(&activeplayers);
|
|
list_init_head(&activegeneric);
|
|
list_init_head(&allplayers);
|
|
|
|
mycount = mydatacount = 0;
|
|
memset(latestactions, 0, sizeof(latestactions));
|
|
memset(latestcounts, 0, sizeof(latestcounts));
|
|
memset(actionblock, 0, sizeof(actionblock));
|
|
actioncount = 0;
|
|
}
|
|
|
|
static void initgame() {
|
|
int i, j;
|
|
int x, y;
|
|
int bl;
|
|
const int* p;
|
|
int comp;
|
|
|
|
if (network != NETWORK_SLAVE) set_game_options(&configopts);
|
|
gameframe = 0;
|
|
|
|
things_list_clear(&activebombs);
|
|
things_list_clear(&detonatebombs);
|
|
things_list_clear(&activeflames);
|
|
things_list_clear(&activedecays);
|
|
things_list_clear(&activebonus);
|
|
list_init_head(&activeplayers);
|
|
things_list_clear(&activegeneric);
|
|
|
|
p = bonuschances;
|
|
bonustotal = 0;
|
|
for (;;) {
|
|
i = *p++;
|
|
if (i == TILE_NONE) break;
|
|
bonustotal += *p++;
|
|
}
|
|
bonustotal += 64 * (3 - gameoptions.generosity);
|
|
memset(field, 0, sizeof(field));
|
|
comp = gameoptions.density;
|
|
for (j = 0; j < arraynumy; ++j)
|
|
for (i = 0; i < arraynumx; ++i) {
|
|
/* if((i&j)&1) {
|
|
field[j][i]=FIELD_BORDER;
|
|
} else*/
|
|
field[j][i] = (myrand() & 3) >= comp ? FIELD_BRICK : FIELD_EMPTY;
|
|
}
|
|
|
|
solidcopyany(&backgroundoriginal, &background, 0, 0, IXSIZE, IYSIZE);
|
|
|
|
resetplayers();
|
|
|
|
for (j = 0; j < arraynumy; ++j) {
|
|
y = arraystarty + j * arrayspacey;
|
|
for (i = 0; i < arraynumx; ++i) {
|
|
x = arraystartx + i * arrayspacex;
|
|
bl = field[j][i];
|
|
if (bl == FIELD_BORDER)
|
|
bl = 2;
|
|
else if (bl == FIELD_BRICK)
|
|
bl = 1;
|
|
else
|
|
continue;
|
|
drawfigureany(x, y, blocks + bl, &background);
|
|
}
|
|
}
|
|
solidcopy(&background, 0, 0, IXSIZE, IYSIZE);
|
|
copyup();
|
|
}
|
|
|
|
static void addflame(player* owner, int px, int py) {
|
|
flame *fl, *fl2;
|
|
|
|
fl = allocentry(flame);
|
|
if (!fl) return;
|
|
addtail(&activeflames, fl);
|
|
field[py][px] = FIELD_FLAME;
|
|
info[py][px] = fl;
|
|
fl->px = px;
|
|
fl->py = py;
|
|
fl->xpos = arraytoscreenx(px);
|
|
fl->ypos = arraytoscreeny(py);
|
|
fl->owner = owner;
|
|
if (px && field[py][px - 1] == FIELD_FLAME) {
|
|
fl2 = info[py][px - 1];
|
|
fl->lurd |= FL_LEFT;
|
|
fl2->lurd |= FL_RIGHT;
|
|
}
|
|
if (py && field[py - 1][px] == FIELD_FLAME) {
|
|
fl2 = info[py - 1][px];
|
|
fl->lurd |= FL_UP;
|
|
fl2->lurd |= FL_DOWN;
|
|
}
|
|
if (px < arraynumx - 1 && field[py][px + 1] == FIELD_FLAME) {
|
|
fl2 = info[py][px + 1];
|
|
fl->lurd |= FL_RIGHT;
|
|
fl2->lurd |= FL_LEFT;
|
|
}
|
|
if (py < arraynumy - 1 && field[py + 1][px] == FIELD_FLAME) {
|
|
fl2 = info[py + 1][px];
|
|
fl->lurd |= FL_DOWN;
|
|
fl2->lurd |= FL_UP;
|
|
}
|
|
}
|
|
|
|
static void dropbomb(player* pl, int px, int py, int type) {
|
|
bomb* bmb;
|
|
|
|
if (field[py][px] != FIELD_EMPTY) return;
|
|
bmb = allocentry(bomb);
|
|
if (!bmb) return;
|
|
playsound(3);
|
|
addtail(&activebombs, bmb);
|
|
|
|
--(pl->bombsavailable);
|
|
field[py][px] = FIELD_BOMB;
|
|
info[py][px] = bmb;
|
|
bmb->type = type;
|
|
bmb->px = px;
|
|
bmb->py = py;
|
|
bmb->xpos = arraytoscreenx(px);
|
|
bmb->ypos = arraytoscreeny(py);
|
|
bmb->power = pl->flamelength;
|
|
bmb->owner = pl;
|
|
}
|
|
|
|
static void adddecay(int px, int py) {
|
|
brickdecay* bd;
|
|
int xpos, ypos;
|
|
|
|
bd = allocentry(brickdecay);
|
|
if (!bd) return;
|
|
field[py][px] = FIELD_EXPLODING;
|
|
bd->xpos = arraytoscreenx(px);
|
|
bd->ypos = arraytoscreeny(py);
|
|
bd->px = px;
|
|
bd->py = py;
|
|
addtail(&activedecays, bd);
|
|
xpos = tovideox(bd->xpos);
|
|
ypos = tovideoy(bd->ypos);
|
|
solidcopyany(&backgroundoriginal, &background, xpos, ypos, arrayspacex, arrayspacey);
|
|
solidcopy(&background, xpos, ypos, arrayspacex, arrayspacey);
|
|
}
|
|
|
|
static void addbonus(int px, int py, int type) {
|
|
bonustile* bonus;
|
|
|
|
bonus = allocentry(bonustile);
|
|
if (!bonus) return;
|
|
addtail(&activebonus, bonus);
|
|
bonus->px = px;
|
|
bonus->py = py;
|
|
bonus->xpos = arraytoscreenx(px);
|
|
bonus->ypos = arraytoscreeny(py);
|
|
bonus->type = type;
|
|
field[py][px] = FIELD_BONUS;
|
|
info[py][px] = bonus;
|
|
}
|
|
|
|
static void trybonus(int px, int py) {
|
|
int i = 0, r;
|
|
const int* p;
|
|
|
|
if (field[py][px] != FIELD_EMPTY) return;
|
|
p = bonuschances;
|
|
r = myrand() % bonustotal;
|
|
while (r >= 0) {
|
|
i = *p++;
|
|
r -= *p++;
|
|
}
|
|
if (i == TILE_NONE) return;
|
|
addbonus(px, py, i);
|
|
}
|
|
|
|
static void deletebonus(bonustile* bonus) {
|
|
int px, py;
|
|
|
|
px = bonus->px;
|
|
py = bonus->py;
|
|
field[py][px] = 0;
|
|
info[py][px] = 0;
|
|
list_del(&bonus->list);
|
|
freeentry(bonus);
|
|
}
|
|
|
|
static void adddetonate(bomb* bmb) {
|
|
if (bmb->type == BOMB_OFF) return;
|
|
|
|
bmb->type = BOMB_OFF;
|
|
field[bmb->py][bmb->px] = FIELD_EXPLODING;
|
|
info[bmb->py][bmb->px] = 0;
|
|
|
|
removeitem(bmb);
|
|
addtail(&detonatebombs, bmb);
|
|
}
|
|
|
|
static void processbombs() {
|
|
bomb *bmb, *next;
|
|
|
|
list_for_each_entry_safe(bmb, next, &activebombs, list) {
|
|
++(bmb->figcount);
|
|
++bmb->timer;
|
|
switch (bmb->type) {
|
|
case BOMB_NORMAL:
|
|
if (bmb->timer == BOMBLIFE) adddetonate(bmb);
|
|
break;
|
|
case BOMB_CONTROLLED:
|
|
if (bmb->timer == BOMB_CONTROLLED_LIFE) {
|
|
bmb->timer = 0;
|
|
bmb->type = BOMB_NORMAL;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void flameshaft(player* owner, int px, int py, int dx, int dy, int power) {
|
|
int there;
|
|
bomb* bmb;
|
|
|
|
while (power--) {
|
|
px += dx;
|
|
py += dy;
|
|
if (px < 0 || py < 0 || px >= arraynumx || py >= arraynumy) break;
|
|
there = field[py][px];
|
|
switch (there) {
|
|
case FIELD_BOMB:
|
|
bmb = info[py][px];
|
|
adddetonate(bmb);
|
|
break;
|
|
case FIELD_EMPTY:
|
|
addflame(owner, px, py);
|
|
break;
|
|
case FIELD_BRICK:
|
|
adddecay(px, py);
|
|
power = 0;
|
|
break;
|
|
case FIELD_BONUS:
|
|
deletebonus(info[py][px]);
|
|
power = 0;
|
|
break;
|
|
case FIELD_BORDER:
|
|
case FIELD_EXPLODING:
|
|
default:
|
|
power = 0;
|
|
case FIELD_FLAME:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void detonatebomb(bomb* bmb) {
|
|
int px, py;
|
|
int power;
|
|
player* owner;
|
|
|
|
++(bmb->owner->bombsavailable);
|
|
px = bmb->px;
|
|
py = bmb->py;
|
|
power = bmb->power;
|
|
owner = bmb->owner;
|
|
list_del(&bmb->list);
|
|
freeentry(bmb);
|
|
addflame(owner, px, py);
|
|
flameshaft(owner, px, py, -1, 0, power);
|
|
flameshaft(owner, px, py, 0, -1, power);
|
|
flameshaft(owner, px, py, 1, 0, power);
|
|
flameshaft(owner, px, py, 0, 1, power);
|
|
}
|
|
|
|
static void dodetonations(void) {
|
|
int i = 0;
|
|
bomb* bmb;
|
|
|
|
while (!list_empty(&detonatebombs)) {
|
|
bmb = (bomb*) detonatebombs.next;
|
|
++i;
|
|
detonatebomb(bmb);
|
|
}
|
|
if (i) playsound((myrand() & 1) ? 0 : 4);
|
|
}
|
|
|
|
static void processflames(void) {
|
|
flame *fl, *next;
|
|
|
|
list_for_each_entry_safe(fl, next, &activeflames, list) {
|
|
++(fl->timer);
|
|
|
|
if (fl->timer == FLAMELIFE) {
|
|
field[fl->py][fl->px] = FIELD_EMPTY;
|
|
info[fl->py][fl->px] = 0;
|
|
list_del(&fl->list);
|
|
freeentry(fl);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void processdecays() {
|
|
brickdecay *bd, *next;
|
|
|
|
list_for_each_entry_safe(bd, next, &activedecays, list) {
|
|
++(bd->timer);
|
|
if (bd->timer == DECAYLIFE) {
|
|
field[bd->py][bd->px] = FIELD_EMPTY;
|
|
trybonus(bd->px, bd->py);
|
|
list_del(&bd->list);
|
|
freeentry(bd);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void drawbombs(void) {
|
|
int j;
|
|
bomb* bmb;
|
|
struct figure* figtab;
|
|
int color;
|
|
int xpos, ypos;
|
|
|
|
list_for_each_entry(bmb, &activebombs, list) {
|
|
color = bmb->owner->color;
|
|
figtab = (bmb->type == BOMB_NORMAL) ? bombs1[color] : bombs2[color];
|
|
j = bmb->figcount % (NUMBOMBFRAMES << 1);
|
|
if (j >= NUMBOMBFRAMES) j = (NUMBOMBFRAMES << 1) - j - 1;
|
|
xpos = tovideox(bmb->xpos);
|
|
ypos = tovideoy(bmb->ypos) - 3;
|
|
|
|
addsprite(xpos, ypos, figtab + j);
|
|
}
|
|
}
|
|
|
|
static void drawflames(void) {
|
|
flame* fl;
|
|
int xpos, ypos;
|
|
/* no player specific flame sprites yet */
|
|
/* int color; */
|
|
int fig;
|
|
|
|
list_for_each_entry(fl, &activeflames, list) {
|
|
/* color=fl->owner->color; */
|
|
xpos = tovideox(fl->xpos);
|
|
ypos = tovideoy(fl->ypos);
|
|
fig = (fl->timer * 10) / FLAMELIFE;
|
|
if (fig >= 5) fig = 9 - fig;
|
|
fig += 5 * fl->lurd;
|
|
addsprite(xpos, ypos, flamefigs[0 /* color */] + fig);
|
|
}
|
|
}
|
|
|
|
static void drawdecays() {
|
|
brickdecay* bd;
|
|
|
|
list_for_each_entry(bd, &activedecays, list) {
|
|
addsprite(tovideox(bd->xpos), tovideoy(bd->ypos), blocksx + (bd->timer * 9) / DECAYLIFE);
|
|
}
|
|
}
|
|
|
|
static void drawbonus() {
|
|
bonustile* bonus;
|
|
|
|
list_for_each_entry(bonus, &activebonus, list) {
|
|
addsprite(tovideox(bonus->xpos), tovideoy(bonus->ypos), tiles + bonus->type);
|
|
}
|
|
}
|
|
|
|
static void drawplayers() {
|
|
player* pl;
|
|
int xpos, ypos;
|
|
|
|
list_for_each_entry(pl, &activeplayers, list) {
|
|
if (!(pl->flags & FLG_DEAD)) {
|
|
if (!pl->figure) pl->figure = walking[pl->color] + 30;
|
|
xpos = tovideox(pl->xpos) + pl->fixx;
|
|
ypos = tovideoy(pl->ypos) + pl->fixy;
|
|
addsprite(xpos, ypos, pl->figure);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void detonatecontrolled(player* pl) {
|
|
bomb *bmb, *next;
|
|
|
|
list_for_each_entry_safe(bmb, next, &activebombs, list) {
|
|
if (bmb->owner == pl && bmb->type == BOMB_CONTROLLED) adddetonate(bmb);
|
|
}
|
|
}
|
|
|
|
static void playonce(generic* gen) {
|
|
if (gen->timer == gen->data1) {
|
|
list_del(&gen->list);
|
|
freeentry(gen);
|
|
}
|
|
}
|
|
|
|
static void drawgeneric(generic* gen) {
|
|
addsprite(gen->xpos, gen->ypos, ((figure*) (gen->ptr1)) + gen->timer);
|
|
}
|
|
|
|
static void queuesequence(int xpos, int ypos, figure* fig, int count) {
|
|
generic* gen;
|
|
|
|
gen = allocentry(generic);
|
|
if (!gen) return;
|
|
gen->xpos = xpos;
|
|
gen->ypos = ypos;
|
|
gen->data1 = count;
|
|
gen->process = playonce;
|
|
gen->draw = drawgeneric;
|
|
gen->ptr1 = fig;
|
|
addtail(&activegeneric, gen);
|
|
}
|
|
|
|
static void adddeath(player* pl) {
|
|
int xpos, ypos;
|
|
|
|
xpos = tovideox(pl->xpos) + pl->fixx - 10;
|
|
ypos = tovideoy(pl->ypos) + pl->fixy;
|
|
queuesequence(xpos, ypos, death, NUMDEATHFRAMES);
|
|
}
|
|
|
|
static void killplayer(player* pl) {
|
|
pl->deaths++;
|
|
pl->flags |= FLG_DEAD;
|
|
playsound(2);
|
|
adddeath(pl);
|
|
detonatecontrolled(pl);
|
|
}
|
|
|
|
static void processgenerics(void) {
|
|
generic *gen, *next;
|
|
|
|
list_for_each_entry_safe(gen, next, &activegeneric, list) {
|
|
++(gen->timer);
|
|
gen->process(gen);
|
|
}
|
|
}
|
|
|
|
static void drawgenerics(void) {
|
|
generic* gen;
|
|
|
|
list_for_each_entry(gen, &activegeneric, list) {
|
|
gen->draw(gen);
|
|
}
|
|
}
|
|
|
|
static void drawstats(void) {
|
|
player* pl;
|
|
int p = 0;
|
|
const char* n;
|
|
char buf[17];
|
|
|
|
solidcopy(&background, 0, 0, IXSIZE, arraystarty);
|
|
list_for_each_entry(pl, &allplayers, list_all_players) {
|
|
if (pl->controller >= 0) {
|
|
n = netnodes[pl->controller].name;
|
|
} else {
|
|
n = playername;
|
|
}
|
|
snprintf(buf, sizeof(buf), "%-8.8s %2i/%2i", n, pl->kills % 100, pl->deaths % 100);
|
|
drawstring(11 + (p / 2) * (15 * fontxsize + 7), 11 + (p % 2) * (fontysize + 2), buf);
|
|
p++;
|
|
}
|
|
}
|
|
|
|
static int centerxchange(player* pl) {
|
|
int speed;
|
|
int val;
|
|
int line;
|
|
int max;
|
|
|
|
max = arrayspacex << FRACTION;
|
|
speed = pl->speed;
|
|
val = pl->xpos + (max << 2);
|
|
val %= max;
|
|
line = max >> 1;
|
|
if (val < line) {
|
|
if (val - speed < 0)
|
|
return -val;
|
|
else
|
|
return -speed;
|
|
} else if (val >= line) {
|
|
if (val + speed > max)
|
|
return max - val;
|
|
else
|
|
return speed;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void centerx(player* pl) {
|
|
pl->xpos += centerxchange(pl);
|
|
}
|
|
|
|
static int centerychange(player* pl) {
|
|
int speed;
|
|
int val;
|
|
int line;
|
|
int max;
|
|
|
|
max = arrayspacey << FRACTION;
|
|
speed = pl->speed;
|
|
val = pl->ypos + (max << 2);
|
|
val %= max;
|
|
line = max >> 1;
|
|
if (val < line) {
|
|
if (val - speed < 0)
|
|
return -val;
|
|
else
|
|
return -speed;
|
|
} else if (val >= line) {
|
|
if (val + speed > max)
|
|
return max - val;
|
|
else
|
|
return speed;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void centery(player* pl) {
|
|
pl->ypos += centerychange(pl);
|
|
}
|
|
|
|
#define SGN(x) ((x) == 0 ? 0 : ((x) < 0 ? -1 : 1))
|
|
|
|
static void trymove(player* pl, int dx, int dy) {
|
|
int wx, wy;
|
|
int sx, sy;
|
|
int there;
|
|
int px, py;
|
|
static int depth = 0;
|
|
int tx, ty;
|
|
|
|
++depth;
|
|
sx = (dx * (arrayspacex + 1)) << (FRACTION - 1);
|
|
sy = (dy * (arrayspacey + 1)) << (FRACTION - 1);
|
|
|
|
wx = screentoarrayx(pl->xpos + sx);
|
|
wy = screentoarrayy(pl->ypos + sy);
|
|
px = screentoarrayx(pl->xpos);
|
|
py = screentoarrayy(pl->ypos);
|
|
|
|
if (wx < 0 || wx >= arraynumx || wy < 0 || wy >= arraynumy) {
|
|
--depth;
|
|
return;
|
|
}
|
|
there = field[wy][wx];
|
|
if ((px != wx || py != wy) && (there == FIELD_BRICK || there == FIELD_BOMB || there == FIELD_BORDER)) {
|
|
if (dx && !dy) {
|
|
ty = centerychange(pl);
|
|
if (ty && depth == 1) trymove(pl, 0, -SGN(ty));
|
|
|
|
} else if (dy && !dx) {
|
|
tx = centerxchange(pl);
|
|
if (tx && depth == 1) trymove(pl, -SGN(tx), 0);
|
|
}
|
|
|
|
} else {
|
|
pl->xpos += dx * pl->speed;
|
|
pl->ypos += dy * pl->speed;
|
|
if (dx && !dy) centery(pl);
|
|
if (dy && !dx) centerx(pl);
|
|
}
|
|
--depth;
|
|
}
|
|
|
|
|
|
static void applybonus(player* pl, bonustile* bonus) {
|
|
int type;
|
|
int maxflame;
|
|
|
|
maxflame = arraynumx > arraynumy ? arraynumx : arraynumy;
|
|
type = bonus->type;
|
|
deletebonus(bonus);
|
|
switch (type) {
|
|
case TILE_BOMB:
|
|
if (pl->bombsavailable < 9) ++(pl->bombsavailable);
|
|
break;
|
|
case TILE_FLAME:
|
|
if (pl->flamelength < maxflame) ++(pl->flamelength);
|
|
break;
|
|
case TILE_GOLDFLAME:
|
|
pl->flamelength = maxflame;
|
|
break;
|
|
case TILE_TRIGGER:
|
|
pl->flags |= FLG_CONTROL;
|
|
break;
|
|
case TILE_SKATES:
|
|
if (pl->speed < SPEEDSTART) {
|
|
pl->speed = SPEEDSTART;
|
|
} else {
|
|
pl->speed += SPEEDDELTA;
|
|
}
|
|
if (pl->speed > SPEEDMAX) pl->speed = SPEEDMAX;
|
|
break;
|
|
case TILE_TURTLE:
|
|
pl->speed = SPEEDTURTLE;
|
|
pl->speedturtle_timeout = SPEEDTURTLE_TIMEOUT;
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void doplayer(player* pl) {
|
|
/* int last; */
|
|
int color;
|
|
/* int speed; */
|
|
int px, py;
|
|
int there;
|
|
int flags;
|
|
int what;
|
|
|
|
if (pl->controller == -1)
|
|
what = myaction;
|
|
else
|
|
what = actions[pl->controller];
|
|
|
|
flags = pl->flags;
|
|
if (flags & FLG_DEAD) return;
|
|
color = pl->color;
|
|
/* last=pl->doing; */
|
|
pl->doing = what;
|
|
/* speed=pl->speed; */
|
|
px = screentoarrayx(pl->xpos);
|
|
py = screentoarrayy(pl->ypos);
|
|
there = field[py][px];
|
|
|
|
if (what == ACT_QUIT) {
|
|
killplayer(pl);
|
|
list_del(&pl->list_all_players);
|
|
return;
|
|
}
|
|
|
|
if (there == FIELD_BONUS) {
|
|
playsound((myrand() & 1) ? 1 : 5);
|
|
applybonus(pl, info[py][px]);
|
|
} else if (there == FIELD_FLAME) {
|
|
flame* fl = info[py][px];
|
|
if (fl->owner == pl) {
|
|
pl->kills--;
|
|
} else {
|
|
fl->owner->kills++;
|
|
}
|
|
killplayer(pl);
|
|
return;
|
|
}
|
|
|
|
// if(what&ACT_TURBO) speed<<=2;
|
|
if (what & ACT_PRIMARY) {
|
|
if (there == FIELD_EMPTY && pl->bombsavailable) dropbomb(pl, px, py, (flags & FLG_CONTROL) ? BOMB_CONTROLLED : BOMB_NORMAL);
|
|
}
|
|
if (what & ACT_SECONDARY && (flags & FLG_CONTROL)) detonatecontrolled(pl);
|
|
|
|
switch (what & ACT_MASK) {
|
|
case ACT_UP:
|
|
trymove(pl, 0, -1);
|
|
pl->figcount = (pl->figcount + 1) % 15;
|
|
pl->figure = walking[color] + pl->figcount + 15;
|
|
break;
|
|
case ACT_DOWN:
|
|
trymove(pl, 0, 1);
|
|
pl->figcount = (pl->figcount + 1) % 15;
|
|
pl->figure = walking[color] + pl->figcount + 30;
|
|
break;
|
|
case ACT_LEFT:
|
|
trymove(pl, -1, 0);
|
|
pl->figcount = (pl->figcount + 1) % 15;
|
|
pl->figure = walking[color] + pl->figcount + 45;
|
|
break;
|
|
case ACT_RIGHT:
|
|
trymove(pl, 1, 0);
|
|
pl->figcount = (pl->figcount + 1) % 15;
|
|
pl->figure = walking[color] + pl->figcount;
|
|
break;
|
|
case ACT_NONE:
|
|
break;
|
|
}
|
|
|
|
if (pl->speedturtle_timeout > 0) {
|
|
pl->speedturtle_timeout--;
|
|
if (0 == pl->speedturtle_timeout) {
|
|
if (pl->speed < SPEEDSTART) pl->speed = SPEEDSTART;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void processplayers(void) {
|
|
player *pl, *next;
|
|
|
|
list_for_each_entry_safe(pl, next, &activeplayers, list) {
|
|
doplayer(pl);
|
|
}
|
|
}
|
|
|
|
static void processquits(void) {
|
|
int i;
|
|
|
|
if (network != NETWORK_SLAVE) return;
|
|
for (i = 0; i < MAXNETNODES; ++i) {
|
|
if (netnodes[i].used && actions[i] == ACT_QUIT) netnodes[i].used = 0;
|
|
}
|
|
}
|
|
|
|
static int getaction(void) {
|
|
int what;
|
|
|
|
what = ACT_NONE;
|
|
if (checkpressed(MYLEFT))
|
|
what = ACT_LEFT;
|
|
else if (checkpressed(MYRIGHT))
|
|
what = ACT_RIGHT;
|
|
else if (checkpressed(MYDOWN))
|
|
what = ACT_DOWN;
|
|
else if (checkpressed(MYUP))
|
|
what = ACT_UP;
|
|
else if (checkdown(13))
|
|
what = ACT_ENTER;
|
|
else if (checkdown(0x1b))
|
|
what = ACT_QUIT;
|
|
|
|
if (checkdown(' ')) what |= ACT_PRIMARY;
|
|
if (checkdown('b')) what |= ACT_SECONDARY;
|
|
|
|
return what;
|
|
}
|
|
|
|
static int iterate(void) {
|
|
int i;
|
|
int gountil; /* destination tick */
|
|
static int deathcount = 0;
|
|
|
|
mypause();
|
|
scaninput();
|
|
|
|
erasesprites();
|
|
clearspritelist();
|
|
gfxunlock();
|
|
|
|
myaction = getaction();
|
|
if (NETWORK_NONE == network && myaction == ACT_QUIT) return CODE_QUIT;
|
|
if (NETWORK_NONE == network) {
|
|
gountil = mycount + 1; /* single step in singly player mode */
|
|
} else {
|
|
gountil = networktraffic(); /* as master single step, as slave as many as we can do */
|
|
}
|
|
|
|
while (mycount < gountil) { /* simulate ticks up to gountil */
|
|
++mycount;
|
|
if (NETWORK_NONE != network) {
|
|
i = gountil - mycount;
|
|
if (i >= ACTIONHIST) // too far behind
|
|
goto leave_game;
|
|
memcpy(actions, actionblock + i * MAXNETNODES, MAXNETNODES);
|
|
if (actions[myslot] == ACT_QUIT) return CODE_QUIT;
|
|
}
|
|
processbombs();
|
|
dodetonations();
|
|
processdecays();
|
|
processflames();
|
|
processgenerics();
|
|
processquits();
|
|
processplayers();
|
|
}
|
|
|
|
/*
|
|
if(!(rand()&127))
|
|
{
|
|
i=gtime();
|
|
while(gtime()-i<100);
|
|
}
|
|
*/
|
|
|
|
drawbombs();
|
|
drawbonus();
|
|
drawgenerics();
|
|
drawdecays();
|
|
drawflames();
|
|
drawplayers();
|
|
drawstats();
|
|
plotsprites();
|
|
copyup();
|
|
if (list_empty(&activegeneric)) {
|
|
player* pl;
|
|
|
|
int deadplayers = 0;
|
|
i = 0;
|
|
|
|
list_for_each_entry(pl, &activeplayers, list) {
|
|
if (!(pl->flags & FLG_DEAD))
|
|
++i;
|
|
else
|
|
deadplayers++;
|
|
}
|
|
if (deadplayers > 0 && (!i || (NETWORK_NONE != network && i == 1))) {
|
|
++deathcount;
|
|
if (deathcount == 25) return CODE_ALLDEAD;
|
|
} else
|
|
deathcount = 0;
|
|
}
|
|
return CODE_CONT;
|
|
|
|
leave_game: /* client disconnect/timeout: send ACT_QUIT to master */
|
|
myaction = ACT_QUIT;
|
|
networktraffic();
|
|
return CODE_QUIT;
|
|
}
|
|
|
|
void set_game_options(GameOptions* options) {
|
|
gameoptions = *options;
|
|
}
|
|
|
|
void run_single_player(void) {
|
|
int code;
|
|
network = NETWORK_NONE;
|
|
|
|
firstzero();
|
|
initplayers();
|
|
do {
|
|
initgame();
|
|
while (!(code = iterate()) && !exitflag) ++framecount;
|
|
} while (code != CODE_QUIT && !exitflag);
|
|
}
|
|
|
|
void run_network_game(void) {
|
|
int code;
|
|
|
|
firstzero();
|
|
initplayers();
|
|
do {
|
|
initgame();
|
|
while (!(code = iterate()) && !exitflag) ++framecount;
|
|
} while (code != CODE_QUIT && !exitflag);
|
|
}
|