#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 bomb *detonated[MAXBOMBSDETONATED]; static int detonateput=0; static int detonatetake=0; static listhead activeflames; static listhead activeplayers; figure walking[MAXSETS][NUMWALKFRAMES]; solid background, backgroundoriginal; /* The playfield array, contains FIELD_* equates */ unsigned char field[32][32]; void *info[32][32]; 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 initplayer(int color,int x,int y,int controller) { player *pl; pl=allocentry(); if(!pl) nomem("Couldn't get player structure (allocentry())"); addtail(&activeplayers,pl); pl->xpos=arraytoscreenx(x); pl->ypos=arraytoscreeny(y); pl->color=color; pl->speed=SPEEDSTART; pl->flags=0; pl->fixx=-4; pl->fixy=-40; pl->flamelength=gameoptions.flames+1; pl->bombsavailable=gameoptions.bombs+1; pl->controller=controller; field[y][x]=FIELD_EMPTY; if(x) field[y][x-1]=FIELD_EMPTY; if(y) field[y-1][x]=FIELD_EMPTY; if(x=comp ? FIELD_BRICK : FIELD_EMPTY; } solidcopyany(&backgroundoriginal,&background,0,0,IXSIZE,IYSIZE); initplayers(); for(j=0;jpx=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(pxlurd|=FL_RIGHT; fl2->lurd|=FL_LEFT; } if(pylurd|=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(); 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 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; detonated[detonateput++]=bmb; detonateput%=MAXBOMBSDETONATED; } static void processbombs() { bomb *bmb; foreach_list_fast(&activebombs, bmb) { switch(bmb->type) { case BOMB_NORMAL: ++bmb->timer; if(bmb->timer==BOMBLIFE) adddetonate(bmb); ++(bmb->figcount); break; case BOMB_CONTROLLED: ++(bmb->figcount); break; } } } static void adddecay(int px,int py) { brickdecay *bd; int xpos,ypos; bd=allocentry(); 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(); 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; delink(bonus); } 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; delink(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; while(detonatetake!=detonateput) { ++i; detonatebomb(detonated[detonatetake]); detonatetake=(detonatetake+1) % MAXBOMBSDETONATED; } if(i) playsound((myrand()&1) ? 0 : 4); } static void processflames(void) { flame *fl, *next; foreach_list_safe(&activeflames, fl, next) { ++(fl->timer); if(fl->timer==FLAMELIFE) { field[fl->py][fl->px]=FIELD_EMPTY; info[fl->py][fl->px]=0; delink(fl); } } } static void processdecays() { brickdecay *bd, *next; foreach_list_safe(&activedecays, bd, next) { ++(bd->timer); if(bd->timer == DECAYLIFE) { field[bd->py][bd->px] = FIELD_EMPTY; trybonus(bd->px, bd->py); delink(bd); } } } static void drawbombs(void) { int j; bomb *bmb; struct figure *figtab; int color; int xpos,ypos; foreach_list_fast(&activebombs, bmb) { 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; int color; int fig; foreach_list_fast(&activeflames, fl) { 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; foreach_list_fast(&activedecays, bd) { addsprite(tovideox(bd->xpos),tovideoy(bd->ypos), blocksx+(bd->timer*9)/DECAYLIFE); } } static void drawbonus() { bonustile *bonus; foreach_list_fast(&activebonus, bonus) { addsprite(tovideox(bonus->xpos),tovideoy(bonus->ypos), tiles+bonus->type); } } static void drawplayers() { player *pl; int xpos,ypos; foreach_list_fast(&activeplayers, pl) { 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; foreach_list_fast(&activebombs, bmb) { if(bmb->owner==pl && bmb->type==BOMB_CONTROLLED) adddetonate(bmb); } } static void playonce(generic *gen) { if(gen->timer==gen->data1) delink(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(); 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->flags|=FLG_DEAD; playsound(2); adddeath(pl); detonatecontrolled(pl); } static void processgenerics(void) { generic *gen,*gen2; gen = (generic*) activegeneric.next; while ((listhead*) gen != &activegeneric) { gen2 = gen; gen = (generic*) gen->list.next; ++(gen2->timer); gen2->process(gen2); } } static void drawgenerics(void) { generic *gen; foreach_list_fast(&activegeneric, gen) { gen->draw(gen); } } static int centerxchange(player *pl) { int speed; int val; int line; int max; max=arrayspacex<speed; val=pl->xpos+(max<<2); val%=max; line=max>>1; 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<speed; val=pl->ypos+(max<<2); val%=max; line=max>>1; 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->flamelengthflamelength); 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); return; } if(there==FIELD_BONUS) { playsound((myrand()&1) ? 1 : 5); applybonus(pl,info[py][px]); } else if(there==FIELD_FLAME) { 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, *pl2; pl = (player*) activeplayers.next; while ((listhead*) pl != &activeplayers) { pl2 = pl; pl = (player*) pl->list.next; doplayer(pl2); } } 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(); plotsprites(); copyup(); if (activegeneric.next == &activegeneric) { player *pl; int deadplayers = 0; i = 0; foreach_list_fast(&activeplayers, pl) { 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(); do { initgame(); while(!(code=iterate()) && !exitflag) ++framecount; } while (code != CODE_QUIT && !exitflag); } void run_network_game(void) { int code; firstzero(); do { initgame(); while (!(code=iterate()) && !exitflag) ++framecount; } while (code != CODE_QUIT && !exitflag); }