981 lines
19 KiB
C
981 lines
19 KiB
C
|
|
#include "bomber.h"
|
|
#include "game.h"
|
|
#include "gfx.h"
|
|
#include "network.h"
|
|
#include "draw.h"
|
|
#include "utils.h"
|
|
#include "sound.h"
|
|
#include "menu.h"
|
|
#include "list.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[16];
|
|
|
|
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);
|
|
}
|