#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 list activebombs; static list activedecays; static list activebonus; static list activegeneric; static bomb *detonated[MAXBOMBSDETONATED]; static int detonateput=0; static int detonatetake=0; static list activeflames; static list activeplayers; #define REGISTERLEN (1+4+4+4+16+1) figure walking[MAXSETS][NUMWALKFRAMES]; solid background,backgroundoriginal; /* The playfield array, contains FIELD_* equates */ unsigned char field[32][32]; void *info[32][32]; int gamemode = 0; char exitflag = 0; static int framecount = 0; char playername[16]; static int gountil, mycount; static int bonustotal; static const int bonuschances[]= { TILE_BOMB,20, TILE_FLAME,20, TILE_CONTROL,2, TILE_GOLDFLAME,2, TILE_SKATES,20, TILE_TURTLE,5, TILE_NONE,160 }; static GameOptions gameoptions; static const unsigned char playerpositions[] = { /* 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); memset(pl,0,sizeof(player)); 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; bmb=activebombs.next; while(bmb) { switch(bmb->type) { case BOMB_NORMAL: ++bmb->timer; if(bmb->timer==BOMBLIFE) adddetonate(bmb); ++(bmb->figcount); break; case BOMB_CONTROLLED: ++(bmb->figcount); break; } bmb=bmb->next; } } 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(&activebonus,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(&activebombs,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,*fl2; fl=activeflames.next; while(fl) { ++(fl->timer); fl=fl->next; } fl=activeflames.next; while(fl) { if(fl->timer==FLAMELIFE) { field[fl->py][fl->px]=FIELD_EMPTY; info[fl->py][fl->px]=0; fl2=fl; fl=fl->next; delink(&activeflames,fl2); } else fl=fl->next; } } static void processdecays() { brickdecay *bd,*bd2; bd=activedecays.next; while(bd) { ++(bd->timer); if(bd->timer==DECAYLIFE) { field[bd->py][bd->px]=FIELD_EMPTY; trybonus(bd->px,bd->py); bd2=bd; bd=bd->next; delink(&activedecays,bd2); } else bd=bd->next; } } static void drawbombs(void) { int j; bomb *bmb; struct figure *figtab; int color; int xpos,ypos; bmb=activebombs.next; while(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); bmb=bmb->next; } } static void drawflames(void) { flame *fl; int xpos,ypos; int color; int fig; fl=activeflames.next; while(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); fl=fl->next; } } static void drawdecays() { brickdecay *bd; bd=activedecays.next; while(bd) { addsprite(tovideox(bd->xpos),tovideoy(bd->ypos), blocksx+(bd->timer*9)/DECAYLIFE); bd=bd->next; } } static void drawbonus() { bonustile *bonus; bonus=activebonus.next; while(bonus) { addsprite(tovideox(bonus->xpos),tovideoy(bonus->ypos), tiles+bonus->type); bonus=bonus->next; } } static void drawplayers() { player *pl; int xpos,ypos; pl=activeplayers.next; while(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); } pl=pl->next; } } static void detonatecontrolled(player *pl) { bomb *bmb; bmb=activebombs.next; while(bmb) { if(bmb->owner==pl && bmb->type==BOMB_CONTROLLED) adddetonate(bmb); bmb=bmb->next; } } static void playonce(generic *gen) { if(gen->timer==gen->data1) delink(&activegeneric,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=activegeneric.next; while(gen) { gen2=gen; gen=gen->next; ++(gen2->timer); gen2->process(gen2); } } static void drawgenerics(void) { generic *gen; gen=activegeneric.next; while(gen) { gen->draw(gen); gen=gen->next; } } 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: ++(pl->bombsavailable); break; case TILE_FLAME: if(pl->flamelengthflamelength); break; case TILE_GOLDFLAME: pl->flamelength=maxflame; break; case TILE_CONTROL: 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=activeplayers.next; while(pl) { pl2=pl; pl=pl->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; } int iterate(void) { int i; static int deathcount=0; mypause(); scaninput(); erasesprites(); clearspritelist(); gfxunlock(); myaction=getaction(); if(!network && myaction==ACT_QUIT) return CODE_QUIT; i=networktraffic(); if(i<0) gountil=mycount+1; else gountil=i; while(mycount=ACTIONHIST) // too far behind return CODE_QUIT; 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) { player *pl; int deadplayers = 0; pl=activeplayers.next; i=0; while(pl) { if(!(pl->flags & FLG_DEAD)) ++i; else deadplayers++; pl=pl->next; } if (deadplayers > 0 && (!i || (network && i==1))) { ++deathcount; if(deathcount==25) return CODE_ALLDEAD; } else deathcount=0; } return CODE_CONT; } void set_game_options(GameOptions *options) { gameoptions = *options; }