Compare commits

...

4 Commits

  1. 29
      .clang-format
  2. 5
      AUTHORS
  3. 41
      Makefile
  4. 26
      Makefile.osx
  5. 9
      Makefile.sdldemoswin32
  6. 472
      draw.c
  7. 980
      game.c
  8. 872
      gfx.c
  9. 69
      gfx.h
  10. 61
      list.h
  11. 392
      matcher.c
  12. 402
      menu.c
  13. 75
      meson.build
  14. 190
      sound.c
  15. 0
      src/SDLMain.h
  16. 197
      src/announce.c
  17. 2
      src/announce.h
  18. 34
      src/bomber.c
  19. 75
      src/bomber.h
  20. 482
      src/draw.c
  21. 17
      src/draw.h
  22. 951
      src/game.c
  23. 14
      src/game.h
  24. 923
      src/gfx.c
  25. 69
      src/gfx.h
  26. 34
      src/list.c
  27. 63
      src/list.h
  28. 357
      src/matcher.c
  29. 387
      src/menu.c
  30. 0
      src/menu.h
  31. 363
      src/network.c
  32. 14
      src/network.h
  33. 179
      src/sound.c
  34. 0
      src/sound.h
  35. 73
      src/utils.c
  36. 12
      src/utils.h

29
.clang-format

@ -0,0 +1,29 @@
---
BasedOnStyle: LLVM
IndentWidth: 4
TabWidth: 4
---
Language: Cpp
AccessModifierOffset: -4
AlignAfterOpenBracket: AlwaysBreak
AlignOperands: false
AlignTrailingComments: false
AllowAllParametersOfDeclarationOnNextLine: false
AllowShortBlocksOnASingleLine: true
AllowShortFunctionsOnASingleLine: Empty
AllowShortIfStatementsOnASingleLine: true
AllowShortLoopsOnASingleLine: true
AlwaysBreakTemplateDeclarations: true
BinPackArguments: false
BinPackParameters: false
BreakConstructorInitializersBeforeComma: true
ColumnLimit: 160
ConstructorInitializerAllOnOneLineOrOnePerLine: true
ConstructorInitializerIndentWidth: 0
MaxEmptyLinesToKeep: 2
NamespaceIndentation: All
PointerAlignment: Left
SpaceAfterCStyleCast: true
Standard: Cpp11
UseTab: Always
...

5
AUTHORS

@ -1,7 +1,6 @@
David Ashley
dashxdr@gmail.com
http://www.xdr.com/dash
http://www.linuxmotors.com
https://www.linuxmotors.com/linux/SDL_bomber/
Stefan Bühler
http://stbuehler.de/
https://stbuehler.de/

41
Makefile

@ -1,41 +0,0 @@
DBG = -g
CC = gcc
WARNFLAGS = -Wall -Wmissing-declarations -Wdeclaration-after-statement -Wcast-align -Winline -Wsign-compare -Wnested-externs -Wpointer-arith -Wformat-security
override CFLAGS += -D_REENTRANT $(shell sdl-config --cflags) $(DBG) $(WARNFLAGS)
.PHONY: all clean install
all: sdlbomber
sdlbomber: announce.o bomber.o draw.o game.o gfx.o sound.o list.o network.o menu.o utils.o
gcc $(LDFLAGS) -o $@ $^ $(shell sdl-config --libs) -lavahi-common -lavahi-client $(DBG)
matcher: matcher.c
announce.o: announce.c announce.h network.h
bomber.o: bomber.c announce.h bomber.h draw.h game.h gfx.h list.h menu.h network.h sound.h utils.h
draw.o: draw.c draw.h bomber.h gfx.h
game.o: game.c announce.h bomber.h draw.h game.h gfx.h list.h menu.h network.h sound.h utils.h
gfx.o: gfx.c gfx.h bomber.h
list.o: list.c bomber.h list.h utils.h
menu.o: menu.c announce.h bomber.h draw.h game.h gfx.h list.h menu.h network.h sound.h utils.h
network.o: network.c announce.h bomber.h draw.h game.h menu.h network.h utils.h
sound.o: sound.c sound.h
utils.o: utils.c bomber.h utils.h gfx.h
clean:
rm -f *.o matcher sdlbomber
install: sdlbomber
echo "Installing into $(DESTDIR)"
mkdir -p "$(DESTDIR)/usr/bin/" "$(DESTDIR)/usr/share/sdlbomber/"
install -m 0755 sdlbomber "$(DESTDIR)/usr/bin/"
install -m 0644 data/*.pcx data/*.raw "$(DESTDIR)/usr/share/sdlbomber/"

26
Makefile.osx

@ -1,26 +0,0 @@
#DBG += -g
#DBG += -pg
CC = gcc
CFLAGS = -O2 -Wall -I/Library/Frameworks/SDL.framework/Headers $(DBG)
LDFLAGS += -framework SDL -framework Cocoa -o $@ -lavahi-client
all: sdlbomber
sdlbomber: gfx.o bomber.o sound.o SDLMain.o announce.o
SDLMain.o: SDLmain.m
bomber.o: bomber.c bomber.h gfx.h sound.h announce.h
gfx.o: gfx.c bomber.h gfx.h
sound.o: sound.c bomber.h sound.h
announce.o: announce.c announce.h
clean:
rm -f *.o sdlbomber matcher
test: all
./sdlbomber

9
Makefile.sdldemoswin32

@ -1,9 +0,0 @@
# Makefile for bomber
#
TARGET = sdlbomber
USEINET = true
include ../GNUmake
$(TARGET): bomber.o gfx.o sound.o announce.o

472
draw.c

@ -1,472 +0,0 @@
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <netdb.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <signal.h>
#include <fcntl.h>
#include <stdarg.h>
#include "bomber.h"
#include "draw.h"
#include "game.h"
#include "gfx.h"
#include "utils.h"
#ifndef DATADIR
#define DATADIR "data"
#endif
#define NUMCHARACTERS 50
static figure font[NUMCHARACTERS];
static figure bigfont[NUMCHARACTERS];
gfxset gfxsets[NUMGFX];
static char walkingname[256];
static char colorsetname[256];
static char backgroundname[256];
static char blocksname[256];
static char blocksxname[256];
static char bombs1name[256];
static char bombs2name[256];
static char flamesname[256];
static char tilesname[256];
static char deathname[256];
static char fontname[256];
static char bigfontname[256];
figure blocks[3];
figure blocksx[9];
figure bombs1[MAXSETS][NUMBOMBFRAMES];
figure bombs2[MAXSETS][NUMBOMBFRAMES];
figure flamefigs[MAXSETS][NUMFLAMEFRAMES];
figure tiles[15];
figure death[NUMDEATHFRAMES];
int fontxsize,fontysize;
int bigfontxsize,bigfontysize,bigfontyspace;
static int textx,texty;
static const unsigned char *remapstring = (const unsigned char*) "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ.:!?\177/\\*-,>< =";
static char asciiremap[256];
/* On screen array variables */
int arraynumx=15;
int arraynumy=11;
int arraystartx=20;
int arraystarty=70;
int arrayspacex=40;
int arrayspacey=36;
static sprite sprites[MAXSPRITES];
static int spritesused=0;
#define IBUFFLEN 1024
int ileft=0,ihand=0,byteswide;
unsigned char ibuff[IBUFFLEN],*itake;
static void freegfxset(gfxset *gs) {
if(gs->gs_pic) free(gs->gs_pic);
gs->gs_pic=0;
}
static int myci() {
if (!ileft) {
ileft=read(ihand,ibuff,IBUFFLEN);
if(!ileft) return -1;
itake=ibuff;
}
ileft--;
return *itake++;
}
static int dopcxreal(char *name,gfxset *gs) {
int xs,ys;
int i,j,k;
int totalsize;
int width,height;
unsigned char *bm,*lp;
char tname[256];
memset(gs,0,sizeof(gfxset));
ileft=0;
snprintf(tname,sizeof(tname),DATADIR "/%s",name);
ihand=open(tname,O_RDONLY);
if(ihand<0) {
char tname2[260];
snprintf(tname2,sizeof(tname2),"%s.pcx",tname);
ihand=open(tname2,O_RDONLY);
if(ihand<0)
return 1;
}
if(myci()!=10) {close(ihand);return 2;} // 10=zsoft .pcx
if(myci()!=5) {close(ihand);return 3;} // version 3.0
if(myci()!=1) {close(ihand);return 4;} //encoding method
if(myci()!=8) {close(ihand);return 5;} //bpp
xs=myci();
xs|=myci()<<8;
ys=myci();
ys|=myci()<<8;
width=myci();
width|=myci()<<8;
height=myci();
height|=myci()<<8;
width=width+1-xs;
height=height+1-ys;
for(i=0;i<48+4;++i) myci();
myci();
if(myci()!=1) {close(ihand);return 6;} // # of planes
byteswide=myci();
byteswide|=myci()<<8;
i=myci();
i|=myci()<<8;
// if(i!=1) {close(ihand);return 7;} // 1=color/bw,2=grey
for(i=0;i<58;++i) myci();
totalsize=height*byteswide;
bm=malloc(totalsize+1);
if(!bm) {close(ihand);return 8;} // no memory
gs->gs_pic=bm;
gs->gs_xsize=width;
gs->gs_ysize=height;
while(height--) {
lp=bm;
i=byteswide;
while(i>0) {
j=myci();
if(j<0xc0) {
*lp++=j;
--i;
} else {
j&=0x3f;
k=myci();
while(j-- && i) {
*lp++=k;
--i;
}
}
}
bm+=width;
}
lseek(ihand,-0x300,SEEK_END);
read(ihand,gs->gs_colormap,0x300);
close(ihand);
return 0;
}
static int dopcx(char *name,gfxset *gs) {
int err;
err=dopcxreal(name,gs);
if(err)
printf("Error loading \"%s\":code %d\n",name,err);
return err;
}
static void getgroup(char *name,gfxset *colorgs,figure *fig,int count) {
int err;
int i;
gfxset gs;
err=dopcx(name,&gs);
if(err) exit(1000+err);
createinout(&gs);
for(i=0;i<MAXSETS;++i,fig+=count,++colorgs) {
if(!colorgs->gs_pic) continue;
memmove(gs.gs_colormap,colorgs->gs_colormap,
sizeof(gs.gs_colormap));
createinout(&gs);
gfxfetch(&gs,fig,count);
}
freegfxset(&gs);
}
static void getsingle(char *name,figure *fig,int count) {
gfxset gs;
int err;
err=dopcx(name,&gs);
if(err) exit(1000+err);
createinout(&gs);
gfxfetch(&gs,fig,count);
freegfxset(&gs);
}
static void texthome(void) {
textx=texty=10;
}
void drawstring(int xpos, int ypos, const char *str) {
char ch;
while((ch=*str++)) {
drawfigure(xpos,ypos,font+asciiremap[toupper(ch)]);
xpos+=fontxsize;
}
}
void drawbigstring(int xpos, int ypos, const char *str) {
char ch;
while('\0' != (ch=*str++)) {
drawfigure(xpos,ypos,bigfont+asciiremap[toupper(ch)]);
xpos+=bigfontxsize;
}
}
void centerbig(int y, const char *str) {
int w;
w=strlen(str)*bigfontxsize;
drawbigstring((IXSIZE-w)>>1,y,str);
}
// static void scrprintf(char *str,...) {
// char output[256],*p,*p2;
// va_list ap;
//
// va_start(ap, str);
//
// vsprintf(output,str,ap);
// p=output;
// for(;;) {
// p2=p;
// while(*p2 && *p2!='\n') ++p2;
// if(*p2) {
// *p2=0;
// drawstring(textx,texty,p);
// texty+=fontysize;
// textx=10;
// p=p2+1;
// } else {
// drawstring(textx,texty,p);
// textx+=fontxsize*(p2-p);
// break;
// }
// }
// copyup();
// }
static void bigscrprintf(char *str,...) {
char output[256],*p,*p2;
va_list ap;
va_start(ap, str);
vsnprintf(output,sizeof(output),str,ap);
p=output;
for(;;) {
p2=p;
while(*p2 && *p2!='\n') ++p2;
if(*p2) {
*p2=0;
drawbigstring(textx,texty,p);
texty+=bigfontysize;
textx=10;
p=p2+1;
} else {
drawbigstring(textx,texty,p);
textx+=bigfontxsize*(p2-p);
break;
}
}
copyup();
}
void addsprite(int x,int y,figure *fig) {
sprite *sp;
if(spritesused==MAXSPRITES) return;
sp=sprites+spritesused++;
sp->flags=0;
sp->xpos=x;
sp->ypos=y;
sp->fig=fig;
}
void plotsprites(void) {
int i;
sprite *sp;
sp=sprites;
for(i=0;i<spritesused;++i) {
drawfigure(sp->xpos,sp->ypos,sp->fig);
++sp;
}
}
void erasesprites(void) {
int i;
sprite *sp;
figure *fig;
sp=sprites;
for(i=0;i<spritesused;++i) {
fig=sp->fig;
solidcopy(&background,
sp->xpos+fig->xdelta,sp->ypos+fig->ydelta,
fig->xsize,fig->ysize);
++sp;
}
}
void clearsprites(void) {
int i;
sprite *sp;
figure *fig;
sp=sprites;
for(i=0;i<spritesused;++i) {
fig=sp->fig;
clearrect(sp->xpos+fig->xdelta,sp->ypos+fig->ydelta,
fig->xsize,fig->ysize);
++sp;
}
}
void clearspritelist(void) {
spritesused=0;
}
int tovideox(int x) {
return (x>>FRACTION)+arraystartx;
}
int tovideoy(int y) {
return (y>>FRACTION)+arraystarty;
}
int screentoarrayx(int x) {
x+=arrayspacex << (FRACTION+2);
return ((x>>FRACTION)+(arrayspacex>>1))/arrayspacex-4;
}
int screentoarrayy(int y) {
y+=arrayspacey << (FRACTION+2);
return ((y>>FRACTION)+(arrayspacey>>1))/arrayspacey-4;
}
int arraytoscreenx(int x) {
return arrayspacex*x<<FRACTION;
}
int arraytoscreeny(int y) {
return arrayspacey*y<<FRACTION;
}
static void loadfonts(void) {
int i,j;
const unsigned char *p;
getsingle(fontname,font,NUMCHARACTERS);
getsingle(bigfontname,bigfont,NUMCHARACTERS);
fontxsize=8;
fontysize=12;
bigfontxsize=16;
bigfontysize=24;
bigfontyspace=32;
p=remapstring;
j=0;while(*p && *p!=' ') ++p,++j;
memset(asciiremap,j,sizeof(asciiremap));
p=remapstring;
i=0;
while(*p && i<NUMCHARACTERS)
asciiremap[(int)*p++]=i++;
}
void loadgfx() {
gfxset *gs;
gfxset *colorgs;
int err;
int i;
char name[267];
strcpy(walkingname,"walk");
strcpy(colorsetname,"pal");
strcpy(backgroundname,"field0");
strcpy(blocksname,"blocks3");
strcpy(blocksxname,"blocks3x");
strcpy(bombs1name,"bomb1");
strcpy(bombs2name,"bomb2");
strcpy(flamesname,"flames");
strcpy(tilesname,"tiles");
strcpy(deathname,"death1");
strcpy(fontname,"font");
strcpy(bigfontname,"bigfont");
gs=malloc((MAXSETS+1)*sizeof(gfxset));
if(!gs)
nomem("loadgfx");
colorgs=gs+1;
for(i=0;i<MAXSETS;++i) {
snprintf(name,sizeof(name),"%s%d",colorsetname,i);
err=dopcx(name,colorgs+i);
if(err) continue;
}
loadfonts();
texthome();
bigscrprintf("Loading graphics...\n");
err=dopcx(backgroundname,gs);
if(err) exit(1000+err);
createinout(gs);
solidfetch(gs,&background);
solidfetch(gs,&backgroundoriginal);
freegfxset(gs);
bigscrprintf("Loading blocks\n");
getsingle(blocksname,blocks,3);
bigscrprintf("Loading block explosions\n");
getsingle(blocksxname,blocksx,9);
bigscrprintf("Loading walking figures\n");
getgroup(walkingname,colorgs,walking[0],NUMWALKFRAMES);
bigscrprintf("Loading normal bombs\n");
getgroup(bombs1name,colorgs,bombs1[0],NUMBOMBFRAMES);
bigscrprintf("Loading controlled bombs\n");
getgroup(bombs2name,colorgs,bombs2[0],NUMBOMBFRAMES);
bigscrprintf("Loading flames\n");
// getgroup(flamesname,colorgs,flamefigs[0],NUMFLAMEFRAMES);
getsingle(flamesname,flamefigs[0],NUMFLAMEFRAMES);
bigscrprintf("Loading bonus tiles\n");
getsingle(tilesname,tiles,15);
bigscrprintf("Loading death sequence\n");
getsingle(deathname,death,NUMDEATHFRAMES);
for(i=0;i<MAXSETS;++i)
freegfxset(colorgs+i);
free(gs);
bigscrprintf("Done loading graphics\n");
}
void failure(char *str,...) {
char output[256];
va_list ap;
int len;
long now;
va_start(ap, str);
len = vsnprintf(output, sizeof(output), str, ap);
if (len >= 256) len = 255; /* truncated string */
clear();
drawbigstring((IXSIZE - len*bigfontxsize) / 2, (IYSIZE-bigfontysize) / 2, output);
copyup();
now = longtime();
while (!exitflag && longtime()-now < 3) {
scaninput();
if (anydown()) {
takedown();
return;
}
}
}

980
game.c

@ -1,980 +0,0 @@
#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