Compare commits

...

27 Commits

Author SHA1 Message Date
Stefan Bühler 56458af5fc add clang-format config and format everything 2021-09-15 19:20:26 +02:00
Stefan Bühler 187ec1a68e use c17 std, fix warnings / errors 2021-09-15 18:52:43 +02:00
Stefan Bühler 7e9fbaf5d2 move code files to src/, move to meson build system 2021-09-15 16:58:28 +02:00
Stefan Bühler eb78414668 update author links 2021-09-15 16:39:51 +02:00
Stefan Bühler 7d143c4940 fix maxnetnodesconst linker error - no need for typename, certainly no need as variable 2021-09-15 15:13:59 +02:00
Stefan Bühler 38de80b9f1 use s*n*printf instead of sprintf 2021-09-15 15:12:45 +02:00
Stefan Bühler fc3baeef51 fix some warnings about buffer sizes 2021-09-15 15:07:35 +02:00
Stefan Bühler 03ef972059 use $LDFLAGS in Makefile 2021-09-15 15:03:27 +02:00
Stefan Bühler ed516154d8 fix some warnings 2017-06-12 12:47:01 +02:00
Stefan Bühler e567abcc93 Network version 1.0.9 2009-09-01 09:39:15 +02:00
Stefan Bühler ccffdfef2e Add option to disable sound 2009-08-28 22:06:06 +02:00
Stefan Bühler fe507df7f0 Don't drop "action" keys in network 2009-08-28 21:50:52 +02:00
Stefan Bühler 7db7728f3b Network version 1.0.8 2009-08-19 13:17:07 +02:00
Stefan Bühler 41b98c0c01 Fix stats handling 2009-08-19 13:16:28 +02:00
Stefan Bühler 3da47935a4 Network version 1.0.7 2009-08-18 14:06:26 +02:00
Stefan Bühler 3f6d769ebe New double-linked list implementation, add death stats (kills always 0) 2009-08-18 14:05:12 +02:00
Stefan Bühler 5e908b9936 Redo some list foreach loops 2009-08-11 19:13:18 +02:00
Stefan Bühler 8a8afdf56e remove limit of detonating bombs 2009-08-11 19:09:59 +02:00
Stefan Bühler e280118e3d Limit lifetime of controlled bombs 2009-08-11 18:20:48 +02:00
Stefan Bühler de5183b818 Improve foreach_list* macros 2009-08-11 17:46:32 +02:00
Stefan Bühler 4c516766ba List rewrite 2009-08-11 17:19:43 +02:00
Stefan Bühler f00652cac7 Network protocl 1.0.6 2009-08-10 22:42:54 +02:00
Stefan Bühler 1b3b3b3eb8 Remove global var "gountil" 2009-08-10 22:06:54 +02:00
Stefan Bühler 8b614a8728 Restrict players to 10 bombs, use enum for tile types 2009-08-10 22:00:27 +02:00
Stefan Bühler 9b60fa91f0 More fixes for network code (slaves don't block anymore) 2009-08-10 21:44:41 +02:00
Stefan Bühler 36a55544f9 Remove usage of some temporary vars 2009-08-10 21:14:18 +02:00
Stefan Bühler 9aee736978 Display failure messages 2009-08-10 00:02:13 +02:00
38 changed files with 4597 additions and 4448 deletions

29
.clang-format Normal file
View File

@ -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
...

View File

@ -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/

View File

@ -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 -O2 $(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 -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 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/"

View File

@ -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

View File

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

448
draw.c
View File

@ -1,448 +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 char *remapstring="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;
sprintf(tname,DATADIR "/%s",name);
ihand=open(tname,O_RDONLY);
if(ihand<0) {
char tname2[256];
sprintf(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;
}
// static void drawstring(int xpos,int ypos,char *str) {
// char ch;
//
// while((ch=*str++)) {
// drawfigure(xpos,ypos,font+asciiremap[toupper(ch)]);
// xpos+=fontxsize;
// }
// }
void drawbigstring(int xpos,int ypos,char *str) {
char ch;
while('\0' != (ch=*str++)) {
drawfigure(xpos,ypos,bigfont+asciiremap[toupper(ch)]);
xpos+=bigfontxsize;
}
}
void centerbig(int y,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);
vsprintf(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;
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<40)
asciiremap[(int)*p++]=i++;
}
void loadgfx() {
gfxset *gs;
gfxset *colorgs;
int err;
int i;
char name[256];
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) {
sprintf(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");
}

943
game.c
View File

@ -1,943 +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 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<arraynumx-1) field[y][x+1]=FIELD_EMPTY;
if(y<arraynumy-1) field[y+1][x]=FIELD_EMPTY;
}
static void initplayers(void) {
int i;
const unsigned char *p;
int c,x,y;
if(!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 firstzero(void) {
gountil=mycount=mydatacount=0;
memset(latestactions,0,sizeof(latestactions));
memset(latestcounts,0,sizeof(latestcounts));
actionput=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;
allocthings();
initheader(&activebombs);
initheader(&activeflames);
initheader(&activedecays);
initheader(&activebonus);
initheader(&activeplayers);
initheader(&activegeneric);
detonateput=detonatetake=0;
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);
initplayers();
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();
}
void run_single_player(void) {
int code;
network=0;
firstzero();
do {
initgame();
while(!(code=iterate())) ++framecount;
} while (code != CODE_QUIT);
gamemode=0;
}
void run_network_game(void) {
int code;
firstzero();
do {
initgame();
while(!(code=iterate())) ++framecount;
} while (code != CODE_QUIT);
network = 0;
gamemode = 0;
}
static void addflame(player *owner,int px,int py) {
flame *fl,*fl2;
fl=allocentry();
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();
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<<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:
++(pl->bombsavailable);
break;
case TILE_FLAME:
if(pl->flamelength<maxflame)
++(pl->flamelength);
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<gountil) {
++mycount;
if(network) {
i=gountil-mycount;
if(i>=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;
}

872
gfx.c
View File

@ -1,872 +0,0 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "bomber.h"
#include "gfx.h"
#define MAXCOLORS 256
int usedcolors=0;
SDL_Surface *thescreen;
SDL_Color themap[256];
uchar *videomem;
int stride;
int mousex,mousey,mouseb;
uchar mustlock=0,locked=0;
uchar *block64;
int buttonstate=0,buttondown=0;
int mxpos,mypos;
int pressedcodes[KEYMAX],downcodes[KEYMAX],numpressed,numdown;
void dumpgfx()
{
usedcolors = 0;
}
static int bestmatch(int red,int green,int blue)
{
int i;
int bestcolor,bestdelta=0;
int rdelta,gdelta,bdelta;
int delta;
i=0;
bestcolor=-1;
while(i<usedcolors)
{
rdelta=themap[i].r;
rdelta-=red;
rdelta*=rdelta;
gdelta=themap[i].g;
gdelta-=green;
gdelta*=gdelta;
bdelta=themap[i].b;
bdelta-=blue;
bdelta*=bdelta;
delta=rdelta+gdelta+bdelta;
if(bestcolor<0 || delta<bestdelta)
{
bestdelta=delta;
bestcolor=i;
}
i++;
}
return bestcolor;
}
static void updatemap(void)
{
SDL_SetColors(thescreen, themap, 0, 256);
}
void createinout(gfxset *gs)
{
uchar *p;
int i,j,counts[256];
uchar red,green,blue;
int cnt;
p=gs->gs_pic;
for(i=0;i<256;i++) counts[i]=0;
i=gs->gs_xsize*gs->gs_ysize;
while(i--)
counts[*p++]++;
cnt=0;
gs->gs_inout[0]=0;
for(i=1;i<256;i++)
{
if(counts[i])
{
cnt++;
p=gs->gs_colormap+i+i+i;
red=*p++;
green=*p++;
blue=*p++;
for(j=0;j<usedcolors;j++)
{
if(red==themap[j].r &&
green==themap[j].g && blue==themap[j].b)
{
gs->gs_inout[i]=j;
break;
}
}
if(j==usedcolors)
{
if(usedcolors<MAXCOLORS)
{
themap[j].r=red;
themap[j].g=green;
themap[j].b=blue;
gs->gs_inout[i]=usedcolors;
++usedcolors;
} else
gs->gs_inout[i]=bestmatch(red,green,blue);
}
}
}
updatemap();
}
static uchar *compressfig(uchar *put,gfxset *gs,
int sourcex,int sourcey,int sizex,int sizey)
{
int j,gswidth;
uchar *p, *p2,pixel,*map1;
int dx,dy;
gswidth=gs->gs_xsize;
map1=gs->gs_inout;
p=gs->gs_pic+sourcex+gswidth*sourcey;
for(dy=0;dy<sizey;dy++)
{
p2=p;
j=0;
for(dx=0;dx<=sizex;dx++)
{
if(dx<sizex) pixel=*p2;
else pixel=0;
++p2;
if(pixel)
++j;
else if(j)
{
*put++=j;
*put++=dx-j;
*put++=dy;
p2-=j+1;
while(j)
*put++=map1[*p2++],--j;
++p2;
}
}
p+=gswidth;
}
*put++=0;
return put;
}
static void gfxfetchsingle(figure *fig,gfxset *gs,int sourcex,int sourcey,int sizex,int sizey)
{
uchar *p,*p2;
int dx,dy;
uchar *map1;
int gswidth;
int minx,miny,maxx,maxy;
int tx,ty;
map1=gs->gs_inout;
gswidth=gs->gs_xsize;
p=gs->gs_pic+sourcex+gswidth*sourcey;
minx=miny=maxx=maxy=-1;
for(dy=0;dy<sizey;dy++)
{
p2=p;
ty=sourcey+dy;
for(dx=0;dx<sizex;dx++)
{
if(!*p2++) continue;
if(miny==-1 || ty<miny) miny=ty;
if(maxy==-1 || ty>maxy) maxy=ty;
tx=sourcex+dx;
if(minx==-1 || tx<minx) minx=tx;
if(maxx==-1 || tx>maxx) maxx=tx;
}
p+=gswidth;
}
if(minx==-1)
{
minx=maxx=sourcex;
miny=maxy=sourcey;
}
fig->xdelta=minx-sourcex;
fig->ydelta=miny-sourcey;
sourcex=minx;
sourcey=miny;
fig->xsize=sizex=maxx-minx+1;
fig->ysize=sizey=maxy-miny+1;
p=compressfig(block64,gs,sourcex,sourcey,sizex,sizey);
fig->graphics=malloc(p-block64);
if(fig->graphics)
memcpy(fig->graphics,block64,p-block64);
}
//(gfxset *gs,figure *fig,int sourcex,int sourcey,int sizex,int sizey)
void gfxfetch(gfxset *gs,figure *fig,int num)
{
int x,y;
int xsize,ysize;
int fxsize,fysize;
unsigned char *p,*p2;
xsize=gs->gs_xsize;
ysize=gs->gs_ysize;
p2=p=gs->gs_pic+xsize+1;
fxsize=2;
while(*p++==0) ++fxsize;
fysize=2;
while(*p2==0) ++fysize,p2+=xsize;
x=fxsize;
y=0;
while(num--)
{
gfxfetchsingle(fig,gs,x,y,fxsize,fysize);
x+=fxsize;
if(x>xsize-fxsize)
{
x=0;
y+=fysize;
if(y>ysize-fysize)
y=0;
}
fig++;
}
}
void solidfetch(gfxset *gs,solid *dest)
{
int xsize,ysize;
int i;
unsigned char *p,*map;
uchar *gfx;
memset(dest,0,sizeof(solid));
xsize=gs->gs_xsize;
ysize=gs->gs_ysize;
i=xsize*ysize;
gfx=dest->graphics=malloc(i);
if(!gfx) return;
dest->xsize=xsize;
dest->ysize=ysize;
map=gs->gs_inout;
memcpy(gfx,gs->gs_pic,i);
p=gfx;
while(i--)
{
if(*p) *p=map[*p];
++p;
}
}
void solidcopyany(solid *src,solid *dest,int destx,int desty,int sizex,int sizey)
{
int xmax,ymax;
int j;
uchar *p1,*p2;
int w;
xmax=src->xsize;
ymax=src->ysize;
if(destx>=xmax || desty>=ymax || destx+sizex<=0 || desty+sizey<=0)
return;
if(destx<0)
{
sizex+=destx;
destx=0;
}
if(desty<0)
{
sizey+=desty;
desty=0;
}
if(destx+sizex>xmax)
sizex=xmax-destx;
if(desty+sizey>ymax)
sizey=ymax-desty;
if(dest)
{
w=dest->xsize;
p1=dest->graphics+desty*w+destx;
} else
{
gfxlock();
w=stride;
p1=videomem+desty*stride+destx;
}
p2=src->graphics+desty*xmax+destx;
for(j=0;j<sizey;++j)
{
memcpy(p1,p2,sizex);
p1+=w;
p2+=xmax;
}
}
void solidcopy(solid *solid,int destx,int desty,int sizex,int sizey)
{
solidcopyany(solid,0,destx,desty,sizex,sizey);
}
void drawfigureany(int x,int y,figure *fig,solid *dest)
{
int run;
int dx,dy;
int xsize,ysize;
int clipx,clipy,w;
unsigned char *pc,*p,*p2,*take;
take=fig->graphics;
if(dest)
{
w=clipx=dest->xsize;
clipy=dest->ysize;
pc=dest->graphics;
} else
{
gfxlock();
w=stride;
clipx=IXSIZE;
clipy=IYSIZE;
pc=videomem;
}
dx=fig->xdelta;
dy=fig->ydelta;
xsize=fig->xsize;
ysize=fig->ysize;
x+=dx;
y+=dy;
if(x>=0 && y>=0 && x<=clipx-xsize && y<=clipy-ysize)
{
while((run=*take++))
{
dx=*((signed char *)take); ++take;
dy=*((signed char *)take); ++take;
p=pc+w*(y+dy)+x+dx;
while(run--)
*p++=*take++;
}
} else
{
while((run=*take++))
{
dx=*((signed char *)take); ++take;
dy=*((signed char *)take); ++take;
dx+=x;
dy+=y;
p2=take;
take+=run;
if(dy<0 || dy>=clipy) continue;
if(dx>=clipx) continue;
if(dx<0)
{
p2-=dx;
run+=dx;
dx=0;
} else if(dx+run>clipx)
run=clipx-dx;
p=pc+w*dy+dx;
if(run)
memcpy(p,p2,run);
}
}
}
void drawfigure(int destx,int desty,figure *fig)
{
drawfigureany(destx,desty,fig,0);
}
void copyup()
{
gfxunlock();
SDL_UpdateRect(thescreen, 0, 0, 0, 0);
needwhole=0;
}
void copyupxy(int x,int y)
{
gfxunlock();
SDL_UpdateRect(thescreen,x,y,24,24);
}
void copyupxysize(int x,int y,int xsize,int ysize)
{
gfxunlock();
SDL_UpdateRect(thescreen,x,y,xsize,ysize);
}
// static void set_color(int color, int red, int green, int blue) {
// themap[color].r=red;
// themap[color].g=green;
// themap[color].b=blue;
// }
void opengfx(int argc, char **argv) {
unsigned long videoflags;
themap[0].r=0;
themap[0].g=0;
themap[0].b=0;
usedcolors=1;
block64=malloc(65536);
if(!block64)
{
printf("Couldn't allocate block64\n");
exit(50);
}
if ( SDL_Init(SDL_INIT_VIDEO|SDL_INIT_TIMER|SDL_INIT_AUDIO) < 0 )
{
fprintf(stderr, "Couldn't initialize SDL: %s\n",SDL_GetError());
exit(1);
}
videoflags = SDL_SWSURFACE|SDL_HWPALETTE; //|SDL_FULLSCREEN;
thescreen = SDL_SetVideoMode(IXSIZE, IYSIZE, 8, videoflags);
if ( thescreen == NULL )
{
fprintf(stderr, "Couldn't set display mode: %s\n",
SDL_GetError());
SDL_Quit();
exit(5);
}
stride=thescreen->pitch;
videomem=thescreen->pixels;
mustlock=SDL_MUSTLOCK(thescreen);
locked=0;
SDL_ShowCursor(0);
}
void closegfx(void)
{
SDL_Quit();
}
int checkpressed(int code)
{
int *p,i;
i=numpressed;
p=pressedcodes;
while(i--)
if(*p++==code) return 1;
return 0;
}
int checkdown(int code)
{
int *p,i;
i=numdown;
p=downcodes;
while(i--)
if(*p++==code) return 1;
return 0;
}
int checkbutton(int button)
{
return buttonstate & (1<<button);
}
int checkbuttondown(int button)
{
return buttondown & (1<<button);
}
int anydown()
{
return numdown;
}
int takedown(void)
{
int res=0;
if(numdown)
{
res=*downcodes;
--numdown;
if(numdown)
memmove(downcodes,downcodes+1,numdown*sizeof(int));
}
return res;
}
int firstdown(vopid)
{
return *downcodes;
}
#define ENDMARK 0xaabacada
int sdlinoutnormal[]={
SDLK_0,'0',
SDLK_1,'1',
SDLK_2,'2',
SDLK_3,'3',
SDLK_4,'4',
SDLK_5,'5',
SDLK_6,'6',
SDLK_7,'7',
SDLK_8,'8',
SDLK_9,'9',
SDLK_a,'a',
SDLK_b,'b',
SDLK_c,'c',
SDLK_d,'d',
SDLK_e,'e',
SDLK_f,'f',
SDLK_g,'g',
SDLK_h,'h',
SDLK_i,'i',
SDLK_j,'j',
SDLK_k,'k',
SDLK_l,'l',
SDLK_m,'m',
SDLK_n,'n',
SDLK_o,'o',
SDLK_p,'p',
SDLK_q,'q',
SDLK_r,'r',
SDLK_s,'s',
SDLK_t,'t',
SDLK_u,'u',
SDLK_v,'v',
SDLK_w,'w',
SDLK_x,'x',
SDLK_y,'y',
SDLK_z,'z',
SDLK_MINUS,'-',
SDLK_EQUALS,'=',
SDLK_LEFTBRACKET,'[',
SDLK_RIGHTBRACKET,']',
SDLK_SEMICOLON,';',
SDLK_QUOTE,'\'',
SDLK_SLASH,'/',
SDLK_PERIOD,'.',
SDLK_COMMA,',',
SDLK_BACKQUOTE,'`',
SDLK_BACKSPACE,8,
SDLK_TAB,9,
SDLK_DELETE,MYDELETE,
SDLK_RETURN,13,
SDLK_F1,MYF1,
SDLK_F2,MYF2,
SDLK_F3,MYF3,
SDLK_F4,MYF4,
SDLK_F5,MYF5,
SDLK_F6,MYF6,
SDLK_F7,MYF7,
SDLK_F8,MYF8,
SDLK_F9,MYF9,
SDLK_F10,MYF10,
SDLK_ESCAPE,0x1b,
SDLK_LEFT,MYLEFT,
SDLK_RIGHT,MYRIGHT,
SDLK_UP,MYUP,
SDLK_DOWN,MYDOWN,
SDLK_PAGEUP,MYPAGEUP,
SDLK_PAGEDOWN,MYPAGEDOWN,
SDLK_SPACE,' ',
SDLK_HOME,MYHOME,
SDLK_END,MYEND,
SDLK_LALT,MYALTL,
SDLK_RALT,MYALTR,
ENDMARK
};
int sdlinoutshifted[]={
SDLK_0,')',
SDLK_1,'!',
SDLK_2,'@',
SDLK_3,'#',
SDLK_4,'$',
SDLK_5,'%',
SDLK_6,'^',
SDLK_7,'&',
SDLK_8,'*',
SDLK_9,'(',
SDLK_a,'A',
SDLK_b,'B',
SDLK_c,'C',
SDLK_d,'D',
SDLK_e,'E',
SDLK_f,'F',
SDLK_g,'G',
SDLK_h,'H',
SDLK_i,'I',
SDLK_j,'J',
SDLK_k,'K',
SDLK_l,'L',
SDLK_m,'M',
SDLK_n,'N',
SDLK_o,'O',
SDLK_p,'P',
SDLK_q,'Q',
SDLK_r,'R',
SDLK_s,'S',
SDLK_t,'T',
SDLK_u,'U',
SDLK_v,'V',
SDLK_w,'W',
SDLK_x,'X',
SDLK_y,'Y',
SDLK_z,'Z',
SDLK_MINUS,'_',
SDLK_EQUALS,'+',
SDLK_LEFTBRACKET,'{',
SDLK_RIGHTBRACKET,'}',
SDLK_SEMICOLON,':',
SDLK_QUOTE,'"',
SDLK_SLASH,'?',
SDLK_PERIOD,'>',
SDLK_COMMA,'<',
SDLK_BACKQUOTE,'~',
SDLK_BACKSPACE,8,
SDLK_TAB,9,
SDLK_DELETE,MYDELETE,
SDLK_RETURN,13,
SDLK_F1,MYF1+MYSHIFTED,
SDLK_F2,MYF2+MYSHIFTED,
SDLK_F3,MYF3+MYSHIFTED,
SDLK_F4,MYF4+MYSHIFTED,
SDLK_F5,MYF5+MYSHIFTED,
SDLK_F6,MYF6+MYSHIFTED,
SDLK_F7,MYF7+MYSHIFTED,
SDLK_F8,MYF8+MYSHIFTED,
SDLK_F9,MYF9+MYSHIFTED,
SDLK_F10,MYF10+MYSHIFTED,
SDLK_ESCAPE,0x1b,
SDLK_LEFT,MYLEFT+MYSHIFTED,
SDLK_RIGHT,MYRIGHT+MYSHIFTED,
SDLK_UP,MYUP+MYSHIFTED,
SDLK_DOWN,MYDOWN+MYSHIFTED,
SDLK_PAGEUP,MYPAGEUP,
SDLK_PAGEDOWN,MYPAGEDOWN,
SDLK_SPACE,' ',
SDLK_HOME,MYHOME,
SDLK_END,MYEND,
SDLK_LALT,MYALTL,
SDLK_RALT,MYALTR,
ENDMARK
};
static int looklist(int code,int *list)
{
while((unsigned int)*list!=ENDMARK) {
if(*list==code)
return list[1];
list+=2;
}
return -1;
}
static int mapkey(int code,int qual)
{
if(qual & KMOD_SHIFT)
code=looklist(code,sdlinoutshifted);
else
code=looklist(code,sdlinoutnormal);
if(code<0) return -1;
if(qual & KMOD_ALT)
code|=MYALTED;
return code;
}
static void markkey(int code,int status)
{
int i;
int *ip;
if(status)
{
if(numdown<KEYMAX)
downcodes[numdown++]=code;
ip=pressedcodes;
i=numpressed;
while(i)
if(*ip++==code) break;
else i--;
if(!i && numpressed<KEYMAX)
pressedcodes[numpressed++]=code;
} else
{
i=numpressed;
ip=pressedcodes;
while(i)
if(*ip++==code)
{
*--ip=pressedcodes[--numpressed];
break;
} else i--;
}
}
void pollinput(void)
{
SDL_PollEvent(0);
}
void scaninput(void)
{
SDL_Event event;
int key,mod;
static int bs=0;
numdown=0;
buttondown=0;
while(SDL_PollEvent(&event))
{
switch(event.type)
{
case SDL_KEYDOWN:
key=event.key.keysym.sym;
mod=event.key.keysym.mod;
markkey(mapkey(key,mod),1);
break;
case SDL_KEYUP:
key=event.key.keysym.sym;
mod=event.key.keysym.mod;
markkey(mapkey(key,mod),0);
break;
case SDL_MOUSEBUTTONUP:
bs&=~(1<<(event.button.button-1));
mousex=event.button.x>>1;
mousey=event.button.y>>1;
mouseb=bs;
break;
case SDL_MOUSEBUTTONDOWN:
bs|=1<<(event.button.button-1);
mousex=event.button.x>>1;
mousey=event.button.y>>1;
mouseb=bs;
break;
case SDL_MOUSEMOTION:
mousex=event.motion.x>>1;
mousey=event.motion.y>>1;
break;
case SDL_QUIT:
exitflag = 1;
break;
}
}
}
/*
void scaninput()
{
int i,*ip,code;
numdown=0;
buttondown=0;
while(XCheckMaskEvent(dp,~0,&event))
switch(event.type)
{
case KeyPress:
code=XLookupKeysym(keyevent,0);
if(numdown<KEYMAX)
downcodes[numdown++]=code;
ip=pressedcodes;
i=numpressed;
while(i)
if(*ip++==code) break;
else i--;
if(!i && numpressed<KEYMAX)
pressedcodes[numpressed++]=code;
break;
case KeyRelease:
code=XLookupKeysym(keyevent,0);
i=numpressed;
ip=pressedcodes;
while(i)
if(*ip++==code)
{
*--ip=pressedcodes[--numpressed];
break;
} else i--;
break;
case ButtonPress:
i=1<<buttonevent->button;
buttonstate|=i;
buttondown|=i;
break;
case ButtonRelease:
buttonstate&=~(1<<buttonevent->button);
break;
case MotionNotify:
mxpos=motionevent->x;
mypos=motionevent->y;
break;
case Expose:
copyup();
needwhole=0;
break;
case FocusOut:
numpressed=0;
break;
}
}
*/
static void drawrect(int x,int y,int xs,int ys,int c)
{
uchar *p;
gfxlock();
p=videomem+y*stride+x;
while(ys--)
{
memset(p,c,xs);
p+=stride;
}
}
void greyrect(int x,int y,int xsize,int ysize)
{
static int greycolor=-1;
if(greycolor==-1)
greycolor=bestmatch(0,0,0x70);
drawrect(x,y,xsize,ysize,greycolor);
}
void clear()
{
int j;
uchar *p;
gfxlock();
p=videomem;
for(j=0;j<IYSIZE;++j)
{
memset(p,0,IXSIZE);
p+=stride;
}
}
void clearrect(int x,int y,int xsize,int ysize)
{
drawrect(x,y,xsize,ysize,0);
}
void gfxlock(void)
{
if(locked || !mustlock) return;
if ( SDL_LockSurface(thescreen) < 0 )
{
fprintf(stderr, "Couldn't lock display surface: %s\n",
SDL_GetError());
}
locked=1;
}
void gfxunlock(void)
{
if(!locked || !mustlock) return;
SDL_UnlockSurface(thescreen);
locked=0;
}

69
gfx.h
View File

@ -1,69 +0,0 @@
#ifndef GFX_H
#define GFX_H
#define KEYMAX 128
extern int usedcolors;
extern uchar mymap[];
extern int screen;
extern int fontbase,fontysize;
extern char *imageloc;
extern long map2[];
extern uchar fmap[128];
extern int buttonstate,buttondown;
extern int mxpos,mypos;
extern int pressedcodes[KEYMAX],downcodes[KEYMAX],numpressed,numdown;
void opengfx(int argc, char **argv);
void gfxlock(void);
void gfxunlock(void);
void pollinput(void);
int takedown(void);
void closegfx(void);
void greyrect(int x,int y,int xsize,int ysize);
void clearrect(int x,int y,int xsize,int ysize);
void solidfetch(gfxset *gs,solid *dest);
extern void dumpgfx();
extern void createinout(struct gfxset *);
extern void getcolors();
extern void gfxfetch(struct gfxset *,struct figure *,int);
extern void puttile(int destx,int desty,int source);
extern void store(int x,int y,int which);
extern void restore(int x,int y,int which);
extern void copyup();
extern void copyupxy(int x,int y);
extern void copyupxysize(int x,int y,int xsize,int ysize);
extern void getfigures();
extern unsigned long getcolor(char *name); /* unsigned long */
extern int checkpressed(int code);
extern int checkdown(int code);
extern int checkbutton(int button);
extern int checkbuttondown(int button);
extern int anydown();
extern int firstdown();
extern void scaninput();
extern void fontinit();
extern void writechar(int x,int y,uchar ch);
extern void clear();
extern void drawbox(int x,int y,int size,int color);
extern void drawbox2(int x,int y,int sizex,int sizey,int color);
extern void drawfillrect(int x,int y,int size,int color);
extern void bigpixel(int x,int y,int color);
extern void invert(int x,int y);
extern int getmousex();
extern int getmousey();
extern void drawsquare(int x,int y,uchar *source);
extern void colormapon();
extern void colormapoff();
extern void palette(uchar *pal);
extern void drawfigure(int x,int y,figure *fig);
extern void drawfigureany(int x,int y,figure *fig,solid *dest);
void solidcopy(solid *src,int destx,int desty,int sizex,int sizey);
void solidcopyany(solid *src,solid *dest,int destx,int desty,int sizex,int sizey);
#endif // GFXX_H

62
list.c
View File

@ -1,62 +0,0 @@
#include "bomber.h"
#include "list.h"
#include "utils.h"
static listitem *things=0, *free_things;
void allocthings(void) {
int i;
const int num = MAXTHINGS;
if (!things) {
things = calloc(sizeof(listitem), num);
} else {
memset(things, 0, sizeof(listitem)*num);
}
if(!things) nomem("Trying to allocate thing memory");
for(i=0;i<num-1;++i) {
things[i].next = &things[i+1];
}
things[i].next = 0;
free_things = things;
}
void *allocentry(void) {
listitem *entry = free_things;
if (free_things) {
free_things = free_things->next;
memset(entry, 0, sizeof(*entry));
}
return entry;
}
void freeentry(void *_entry) {
listitem *entry = _entry;
entry->next = free_things;
free_things = entry;
}
void addtail(void *_header,void *_entry) {
listitem *header = _header, *entry = _entry;
while (header->next) header = header->next;
header->next = entry;
entry->next = 0;
}
void delink(void *_header,void *_entry) {
listitem *header = _header, *entry = _entry;
while (header->next != entry) header = header->next;
header->next = entry->next;
entry->next = 0;
freeentry(entry);
}
void initheader(void *p) {
memset(p,0,sizeof(list));
}

12
list.h
View File

@ -1,12 +0,0 @@
#ifndef LIST_H
#define LIST_H
void allocthings(void);
void *allocentry(void);
void freeentry(void *entry);
void addtail(void *header,void *entry);
void delink(void *header,void *entry);
void initheader(void *p);
#endif

392
matcher.c
View File

@ -1,392 +0,0 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/uio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <sys/timeb.h>
#include <sys/time.h>
#include <time.h>
#include <signal.h>
#include <fcntl.h>
#define MAXMSG 256
#define PORT 5521
#define TIMETOLIVE 600 // seconds
typedef unsigned char uchar;
char logging=0;
unsigned char mesg[MAXMSG];
int lastsize;
int network;
char masterhostname[256];
#define PKT_REGISTER 16
#define PKT_ACK 24
#define PKT_INFO 40
#define PKT_QUERY 32
#define MAXMATCHES 16
struct registration
{
uchar id;
uchar unique[4];
uchar password[4];
uchar version[4];
uchar name[16];
uchar status;
};
struct query
{
uchar id;
uchar password[4];
uchar version[4];
};
struct gamehost
{
struct gamehost *next,*prev;
uchar machine[4];
uchar port[2];
struct registration reg;
long timeout;
};
struct gamehost *freehosts=0,*activehosts=0;
int udpsocket,myport;
struct sockaddr_in myname={0},mastername={0};
socklen_t senderlength;
struct sockaddr_in sender={0};
long longtime(void)
{
struct timeb tb;
ftime(&tb);
return tb.time;
}
char *timestr()
{
static char timestring[80];
time_t t;
int l;
time(&t);
strcpy(timestring,ctime(&t));
l=strlen(timestring);
if(l && timestring[l-1]=='\n') timestring[l-1]=0;
return timestring;
}
int putmsg(struct sockaddr_in *toname,unsigned char *msg,int len)
{
int status;
status=sendto(udpsocket,msg,len,0,
(struct sockaddr *)toname,sizeof(struct sockaddr_in));
return status;
}
void ack()
{
uchar copy[256];
*copy=PKT_ACK;
memmove(copy+1,mesg,lastsize);
putmsg(&sender,copy,lastsize+1);
}
int getmsg(int seconds)
{
int size;
lastsize=-1;
memset(&sender,0,sizeof(sender));
senderlength=sizeof(sender);
if(seconds)
{
struct timeval timeout;
fd_set readfds;
int res;
timeout.tv_sec=seconds;
timeout.tv_usec=0;
FD_ZERO(&readfds);
FD_SET(udpsocket,&readfds);
res=select(udpsocket+1,&readfds,0,0,&timeout);
if(res<=0) return -1;
}
lastsize=size=recvfrom(udpsocket,mesg,MAXMSG,0,
(struct sockaddr *)&sender,&senderlength);
return size;
}
long longind(unsigned char *p)
{
return (p[0]<<24L) | (p[1]<<16L) | (p[2]<<8) | p[3];
}
short shortind(unsigned char *p)
{
return (p[0]<<8L) | p[1];
}
void openport(int portwant)
{
int status;
myport=portwant;
udpsocket=socket(PF_INET,SOCK_DGRAM,IPPROTO_UDP);
if(udpsocket==-1)
{
perror("socket()");
exit(1);
}
memset(&myname,0,sizeof(myname));
myname.sin_family=AF_INET;
myname.sin_addr.s_addr=htonl(INADDR_ANY);
myname.sin_port=htons(myport);
status=bind(udpsocket,(struct sockaddr *) &myname,sizeof(myname));
if(status==-1)
{
perror("bind()");
exit(1);
}
}
#define PERBLOCK 512
struct gamehost *newhost()
{
struct gamehost *h,*block;
int i;
if(!freehosts)
{
block=malloc(sizeof(struct gamehost)*PERBLOCK);
if(!block) return 0;
freehosts=block;
i=PERBLOCK-1;
while(i--)
{
block->next=block+1;
++block;
}
block->next=0;
}
h=freehosts;
freehosts=freehosts->next;
memset(h,0,sizeof(struct gamehost));
return h;
}
void freehost(struct gamehost *h)
{
h->next=freehosts;
freehosts=h;
}
struct gamehost *findmatch(struct registration *key)
{
struct gamehost *h;
h=activehosts;
while(h)
{
if(!memcmp(&h->reg,key,sizeof(struct registration)-1))
return h;
h=h->next;
}
return 0;
}
void insert(struct gamehost *h)
{
if(activehosts)
{
h->next=activehosts;
h->prev=activehosts->prev;
activehosts->prev=h;
activehosts=h;
} else
{
h->next=h->prev=0;
activehosts=h;
}
}
void delete(struct gamehost *h)
{
if(h->prev)
h->prev->next=h->next;
else
activehosts=h->next;
if(h->next)
h->next->prev=h->prev;
freehost(h);
}
void doreg()
{
struct registration *new;
struct gamehost *match;
long now;
new=(struct registration *)mesg;
match=findmatch(new);
if(logging)
{
unsigned addr=ntohl(sender.sin_addr.s_addr);
unsigned short port=ntohs(sender.sin_port);
printf("reg :%s:%d.%d.%d.%d:%d %c%lx '%s'\n",timestr(),
(addr>>24)&255,(addr>>16)&255,(addr>>8)&255,addr&255,port,
new->status ? '+' : '-',(long)match,new->name);
fflush(stdout);
}
if(!match && !new->status) {ack();return;}
if(match && new->status) {ack();return;}
if(!match && new->status)
{
match=newhost();
if(!match) return; // No memory, what can we do?
memmove(match->machine,&sender.sin_addr.s_addr,4);
memmove(match->port,&sender.sin_port,2);
match->reg=*new;
now=longtime();
match->timeout=now+TIMETOLIVE;
ack();
insert(match);
return;
} else // match && !new->status
{
delete(match);
ack();
return;
}
}
void doquery()
{
uchar *password;
uchar *version;
struct gamehost *h;
uchar response[2048],*rput,*countersave;
int counter;
if(logging)
{
unsigned addr=ntohl(sender.sin_addr.s_addr);
unsigned short port=ntohs(sender.sin_port);
printf("query:%s:%d.%d.%d.%d:%d\n",timestr(),
(addr>>24)&255,(addr>>16)&255,(addr>>8)&255,addr&255,port);
fflush(stdout);
}
password=mesg+1;
version=mesg+5;
h=activehosts;
rput=response;
*rput++=PKT_INFO;
memmove(rput,password,4);
rput+=4;
memmove(rput,version,4);
rput+=4;
countersave=rput;
*rput++=0;
*rput++=0;
counter=0;
while(h)
{
if(!memcmp(password,h->reg.password,4) &&
!memcmp(version,h->reg.version,4) && counter<MAXMATCHES)
{
++counter;
memmove(rput,h->reg.unique,4);
rput+=4;
memmove(rput,h->machine,4);
rput+=4;
memmove(rput,h->port,2);
rput+=2;
memmove(rput,h->reg.name,sizeof(h->reg.name));
rput+=sizeof(h->reg.name);
}
h=h->next;
}
*countersave++=counter>>8;
*countersave++=counter;
putmsg(&sender,response,rput-response);
}
void purge(long cutoff)
{
struct gamehost *h,*h2;
h=activehosts;
while(h)
{
h2=h;
h=h->next;
if(cutoff-h2->timeout>0)
{
delete(h2);
}
}
}
int main(int argc,char **argv)
{
int i;
int want;
int size;
long purgetime;
long now;
want=PORT;
if(argc>1)
{
for(i=1;i<argc;++i)
if(!strncmp(argv[i],"-p",2))
{
if(strlen(argv[i])>2) want=atoi(argv[i]+2);
else if(i+1<argc) want=atoi(argv[i+1]);
}
}
freehosts=0;
openport(want);
purgetime=longtime()+TIMETOLIVE;
for(;;)
{
size=getmsg(10);
if(size>=1)
switch(*mesg)
{
case PKT_REGISTER:
if(size<sizeof(struct registration)) continue;
doreg();
break;
case PKT_QUERY:
if(size<sizeof(struct query)) continue;
doquery();
break;
}
now=longtime();
if(now-purgetime>0) // avoid year 203x bug...
{
purge(purgetime);
purgetime+=TIMETOLIVE;
}
}
}

404
menu.c
View File

@ -1,404 +0,0 @@
#include "bomber.h"
#include "menu.h"
#include "draw.h"
#include "gfx.h"
#include "utils.h"
#include "sound.h"
#include "network.h"
#include "announce.h"
#include "game.h"
GameOptions configopts = { 2, 1, 0, 2 };
/* Generic menu */
typedef enum {
MENU_ANY = -1,
MENU_MAIN = 0,
MENU_CONFIG = 2,
MENU_JOIN = 3
} menuname;
#define MENU_SELECT(x) ((menuname) (-x-1))
static int menuhistory[32]={0};
static char menustring[1024];
static char *menuput,*menuitems[40],*menutitle;
static int menunum, menuexit;
static int menudelta;
static void drawmenu(int selected) {
int i,j;
int tx,ty;
clear();
j=strlen(menutitle)*bigfontxsize;
drawbigstring((IXSIZE-j) >> 1,20,menutitle);
ty=((IYSIZE-(bigfontysize*menunum))>>1)-(IYSIZE>>3);
for(i=0;i<menunum;++i)
{
j=strlen(menuitems[i])*bigfontxsize;
tx=(IXSIZE-j) >> 1;
if(i==selected)
{
greyrect(0,ty-1,tx-5,bigfontysize);
greyrect(tx+j+3,ty-1,IXSIZE-(tx+j+3),bigfontysize);
}
drawbigstring(tx,ty,menuitems[i]);
ty+=bigfontyspace;
}
}
static int domenu(menuname whichmenu, int (*pause)(void)) {
char redraw;
int selected;
int mcount=0;
if(whichmenu>=0)
selected=menuhistory[whichmenu];
else
selected=-whichmenu-1;
if (!pause) pause=mypause;
redraw=1;
clearspritelist();
while(!exitflag) {
if(redraw) {
drawmenu(selected);
redraw=0;
}
if (!pause()) return -1;
scaninput();
++mcount;
clearsprites();
clearspritelist();
addsprite(IXSIZE/2-50-20,IYSIZE-80,walking[2]+(30+mcount%15));
addsprite(IXSIZE/2+50-20,IYSIZE-80,walking[3]+(30+(mcount+7)%15));
plotsprites();
copyup();
if(anydown()) playsound(3);
while(anydown()) {
switch(takedown()) {
case MYLEFT:
menudelta=-1;
return selected;
case MYRIGHT:
case ' ':
case 13:
menudelta=1;
return selected;
case 'k':
case MYUP:
if (selected) --selected;
else selected=menunum-1;
if (whichmenu>=0)
menuhistory[whichmenu]=selected;
redraw=1;
break;
case 'j':
case MYDOWN:
++selected;
if (selected==menunum) selected=0;
if (whichmenu>=0)
menuhistory[whichmenu]=selected;
redraw=1;
break;
case 0x1b:
if (MENU_MAIN == whichmenu && menuexit != selected) {
selected = menuexit;
redraw = 1;
break;
}
menudelta = 1;
return menuexit;
}
}
}
return 0;
}
static void menustart() {
menunum=-1;
menuput=menustring;
*menuput=0;
menuexit = 0;
}
static void additem_s(char *item, int len) {
if (len < 0 || (menustring+sizeof(menustring)-menuput <= len)) return;
if(menunum<0)
menutitle=menuput;
else
menuitems[menunum]=menuput;
++menunum;
memcpy(menuput,item,len+1);
menuput += len+1;
}
static void additem(char *item,...) {
char output[256];
va_list ap;
int len;
va_start(ap, item);
len = vsnprintf(output,sizeof(output),item,ap);
if (len >= 256) len = 255; /* truncated string */
additem_s(output, len);
}
static void addexit(char *item,...) {
char output[256];
va_list ap;
int len;
va_start(ap, item);
len = vsnprintf(output,sizeof(output),item,ap);
if (len >= 256) len = 255; /* truncated string */
menuexit = menunum;
additem_s(output, len);
}
/* end generic menu */
/* game menues */
static void drawjoinscreen(void) {
int i;
char name[17];
char temp[64];
#define JX (IXSIZE/3)
#define JY (IYSIZE/4)
clear();
centerbig(20,"JOIN NETWORK GAME");
drawbigstring(JX,JY,"SLOT NAME");
for(i=0;i<MAXNETNODES;++i)
{
if(!netnodes[i].used) continue;
memmove(name,netnodes[i].name,16);
name[16]=0;
sprintf(temp," %d %s",i+1,name);
drawbigstring(JX,JY+(i+1)*bigfontyspace,temp);
}
}
static int tryjoin(int which) {
int res;
if (0 == (res = send_join(&gamelistentries[which].netname, playername)))
return 0;
for ( ;; ) {
switch (res) {
case 1: return 0;
case 2:
drawjoinscreen();
copyup();
break;
case 3: return 1;
default: break;
}
scaninput();
while(anydown()) {
switch(takedown()) {
case 0x1b:
send_quit();
return 0;
}
}
res=scaninvite(200);
}
return 0;
}
static void main_menu(void) {
int sel;
while(!exitflag) {
menustart();
additem("BOMBER MAIN MENU");
additem("EXIT GAME");
additem("START SINGLE PLAYER GAME");
additem("OPTIONS");
// additem("REMAP MOVEMENT KEYS");
additem("START NETWORK GAME");
additem("JOIN NETWORK GAME");
sel=domenu(MENU_MAIN, NULL);
if(!sel) {exitflag=1;break;}
if(sel==1) {gamemode=1;break;}
if(sel==2) {gamemode=2;break;}
if(sel==3) {gamemode=3;break;}
if(sel==4) {gamemode=4;break;}
}
}
char *densities[]={"PACKED","HIGH","MEDIUM","LOW"};
char *generosities[]={"LOW","MEDIUM","HIGH","RIDICULOUS"};
static void config_menu(void) {
int sel;
for(;;) {
menustart();
additem("GAME OPTIONS");
additem("RETURN TO MAIN MENU");
additem("DENSITY: %s",densities[configopts.density]);
additem("GENEROSITY: %s",generosities[configopts.generosity]);
additem("INITIAL FLAME LENGTH: %d",configopts.flames+1);
additem("INITIAL NUMBER OF BOMBS: %d",configopts.bombs+1);
sel=domenu(MENU_CONFIG, NULL);
if(!sel) {gamemode=0;break;}
if(sel==1) {
configopts.density+=menudelta;
configopts.density&=3;
}
if(sel==2) {
configopts.generosity+=menudelta;
configopts.generosity&=3;
}
if(sel==3) {
configopts.flames+=menudelta;
configopts.flames&=7;
}
if(sel==4) {
configopts.bombs+=menudelta;
configopts.bombs&=7;
}
}
}
static void failure(char *str,...) {
gamemode=0;
}
static void draw_host_game(void) {
int i;
char *name;
char temp[64];
#define M3X (IXSIZE/3)
#define M3Y (IYSIZE/4)
clear();
centerbig(20,"HOST NETWORK GAME");
drawbigstring(M3X,M3Y,"SLOT NAME");
for(i=0;i<MAXNETNODES;++i) {
if(!netnodes[i].used) continue;
name=netnodes[i].name;
sprintf(temp," %d %s",i+1,name);
drawbigstring(M3X,M3Y+(i+2)*bigfontyspace,temp);
}
copyup();
}
static void host_game(void) {
create_seed_unique();
if (!start_network_game()) {
failure("COULD NOT REGISTER GAME");
return;
}
draw_host_game();
for(;;) {
scaninput();
while(anydown()) {
switch(takedown()) {
case 0x1b:
unregistergame();
cancel_network_game();
gamemode=0;
return;
case ' ':
case 13:
unregistergame();
if (begin_network_game()) {
gamemode=5;
return;
}
send_invites();
draw_host_game();
}
}
if (!handle_joins()) continue;
send_invites();
draw_host_game();
}
gamemode=0;
}
static int join_game_pause(void) {
if (find_more_games()) return 0;
mypause();
return 1;
}
static void join_game(void) {
int i;
int sel = -1;
if (!searchgames()) {
gamemode = 0;
return;
}
menuhistory[MENU_JOIN] = 0;
while (-1 == sel) {
menustart();
if (gamelistsize == 0) {
additem("JOIN NETWORK GAME - NO GAMES AVAILABLE");
addexit("EXIT");
} else {
additem("JOIN NETWORK GAME");
for (i = 0; i < gamelistsize; i++) {
additem(gamelistentries[i].name);
}
addexit("EXIT");
}
sel = domenu(MENU_JOIN, join_game_pause);
}
stop_search();
if(menuexit == sel || !gamelistsize) {
gamemode=0;
return;
}
if(!tryjoin(sel)) {
gamemode=0;
return;
}
network = NETWORK_SLAVE;
gamemode=5;
}
void (*modefunctions[])()= {
main_menu,
run_single_player,
config_menu,
host_game,
join_game,
run_network_game,
};
void mainloop(void) {
exitflag=0;
while(!exitflag)
modefunctions[gamemode]();
}

75
meson.build Normal file
View File

@ -0,0 +1,75 @@
project(
'sdlbomber',
'c',
version: '1.0.10',
default_options: [
'warning_level=3',
'buildtype=debugoptimized',
],
)
avahi_client_dep = dependency(
'avahi-client',
)
sdl_dep = dependency(
'sdl',
)
executable(
'sdlbomber',
'src/announce.c',
'src/bomber.c',
'src/draw.c',
'src/game.c',
'src/gfx.c',
'src/list.c',
# 'src/matcher.c',
'src/menu.c',
'src/network.c',
'src/sound.c',
'src/utils.c',
override_options: [
'c_std=c17',
],
install: true,
dependencies: [
avahi_client_dep,
sdl_dep,
]
)
install_data(
'data/bigfont.pcx',
'data/blocks1.pcx',
'data/blocks1x.pcx',
'data/blocks2.pcx',
'data/blocks2x.pcx',
'data/blocks3.pcx',
'data/blocks3x.pcx',
'data/bomb1.pcx',
'data/bomb1.raw',
'data/bomb2.pcx',
'data/bomb2.raw',
'data/death.raw',
'data/death1.pcx',
'data/death2.pcx',
'data/death3.pcx',
'data/drop.raw',
'data/field0.pcx',
'data/flames.pcx',
'data/font.pcx',
'data/pal0.pcx',
'data/pal1.pcx',
'data/pal2.pcx',
'data/pal3.pcx',
'data/pal4.pcx',
'data/pal5.pcx',
'data/pal6.pcx',
'data/pal7.pcx',
'data/pal8.pcx',
'data/pal9.pcx',
'data/power1.raw',
'data/power2.raw',
'data/tiles.pcx',
'data/walk.pcx',
)

735
network.c
View File

@ -1,735 +0,0 @@
#include "bomber.h"
#include "announce.h"
#include "game.h"
#include "menu.h"
#include "network.h"
#include "utils.h"
#define MAXMSG 4096
int udpsocket;
const unsigned char gameversion[4]={0xda,0x01,0x00,0x05};
struct netnode netnodes[64];
static int informsize;
static unsigned char regpacket[64];
static struct sockaddr_in myname={0},mastername={0};
static socklen_t senderlength;
static struct sockaddr_in sender={0};
static unsigned char mesg[MAXMSG]="";
uchar needwhole=0;
int mydatacount;
int myslot;
network_type network = NETWORK_NONE;
static unsigned char hist[ACTIONHIST][MAXNETNODES];
int actionput,actioncount;
unsigned char actionblock[ACTIONHIST*MAXNETNODES];
int myaction;
unsigned char actions[MAXNETNODES];
unsigned char latestactions[MAXNETNODES];
long latestcounts[MAXNETNODES];
enum network_packet_types {
PKT_ACK, /* perfect copy of packet received */
/* join / host game */
/* slave -> master packets */
PKT_JOIN, /* 4 bytes version #, 4 bytes joinunique #, 16 bytes name */
PKT_QUIT, /* 4 bytes unique # */
/* master -> slave packets */
PKT_INVITE, /* 4 bytes unique #, 1 byte your slot (0xff for kick, no data following it) #, any # of 1:slot,16:name sets (-1 end) */
PKT_BEGIN, /* clone of INVITE */
PKT_CONFIG, /* 4 bytes unique #, config */
PKT_ACCEPT, /* 4 bytes join unique #, 132 bytes seed + unique #, config, slot info (see invite) */
PKT_REJECT, /* 4 bytes join unique #, 4 bytes version #, 1: reason */
/* ingame actions */
/* slave -> master packets */
PKT_MYDATA, /* 4 bytes unique #,4 bytes frame #, 1 byte data */
/* master -> slave packets */
PKT_STEP, /* 4 bytes unique #, 4 bytes frame #, 8 bytes ACT_* */
PKT_INVALID = 0xff
};
enum reject_reason {
REJECT_FULL,
REJECT_VERSION
/* TODO: password? */
};
/* all bytes stored MSB first */
/*
game startup:
<master and matcher>
Master: send REGISTER to matcher with optional password, wait for ack. If
timout, resend.
matcher: Wait for REGISTER packet, when received maintain database. respond
to sender with ACK. REGISTER packet can close a game also. The REGISTER
packet sent by the master has a unique word to be used to avoid confusion.
REGISTER packet also contains a game version #
After master registers game and receives ACK, just waits for slaves to contact.
<slave and matcher>
slave: send QUERY to matcher with optional password, wait for INFO, if timeout,
resend.
matcher: respond to QUERY with INFO packet. matcher need not maintain any
database for slave requests. INFO packet contains IP addr and port for each
master machine that matches the QUERY spec (ALL or password). Only a
certain MAX # of entries are sent if there are too many to choose from.
<slave and master>
slave: send JOIN to master, wait for INVITE. If timeout, resend. JOIN packet
contains the unique word the master created. JOIN also contains username.
master: Respond to JOIN with INVITE. INVITE contains unique word from JOIN
packet. INVITE either contains NO meaning game no longer exists or is closed
or player is not invited. IF yes, INVITE contains info on other players
already in the game (username and slot # for each). Master allocates the
slots and avoids confusion based on IP addr and port #. INVITE also contains
game options structure. Whenever a new player JOINS and is admitted, master
sends updated INVITE packets to everyone already in the JOIN list. Whenever
master changes game options, master sends out another set of INVITES
Duplicate JOINS are answered with updated INVITE but nothing changes as far
as allocation.
Master player launches game after he's satisfied everyone has joined.
Master sends BEGIN packet to everyone. BEGIN is identical to INVITE except
that the data is final. Slave must respond with its first MYDATA packet with
frame # of 0. If master times out waiting, master sends duplicate BEGIN to
wayward slaves. Once master has received MYDATA from everyone, game starts.
Within game slave sends MYDATA to master and waits for STEP packet. If
timeout, slave sends duplicate MYDATA.
If master times out waiting for a slave's MYDATA, slave gets dropped. MYDATAs
received will be answered with PKT_QUIT.
*/
/* Network I/O, building/checking packets */
#if defined (TEST_LATENCY)
#define NUMQ 512
struct message {
int time;
struct sockaddr_in *to;
int tosize;
unsigned char msg[512];
int len;
} message[NUMQ]={0};
outmsgs() {
int i;
for(i=0;i<NUMQ;++i) {
if(message[i].time) {
--message[i].time;
if(message[i].time) continue;
sendto(udpsocket,message[i].msg,message[i].len,0,
message[i].to,sizeof(struct sockaddr_in));
}
}
}
#endif
static int putmsg(struct sockaddr_in *toname,unsigned char *msg,int len) {
int status;
#if defined (TEST_LATENCY)
int i;
for(i=0;i<NUMQ;++i) {
if(!message[i].time) {
message[i].time=10;
message[i].to=toname;
memcpy(message[i].msg,msg,len);
message[i].len=len;
break;
}
}
return 0;
#else
status=sendto(udpsocket,msg,len,0,
(struct sockaddr *)toname,sizeof(struct sockaddr_in));
return status;
#endif
}
static int getmsg(int msec) {
int size;
memset(&sender,0,sizeof(sender));
senderlength=sizeof(sender);
if(msec) {
struct timeval timeout;
fd_set readfds;
int res;
memset(&timeout,0,sizeof(timeout));
timeout.tv_sec=msec/1000;
timeout.tv_usec=(msec%1000)*1000;
FD_ZERO(&readfds);
FD_SET(udpsocket,&readfds);
res=select(udpsocket+1,&readfds,0,0,&timeout);
if(res<=0) return -1;
}
size=recvfrom(udpsocket,mesg,MAXMSG,0,
(struct sockaddr *)&sender,&senderlength);
return size;
}
static int isvalidunique(unsigned char *p) {
return 0 == memcmp(p, &network_unique, 4);
}
static int isvalidversion(unsigned char *p) {
return 0 == memcmp(p, gameversion, 4);
}
static unsigned char* writeuint32(unsigned char *p, Uint32 i) {
p[0]=i>>24L;
p[1]=i>>16L;
p[2]=i>>8L;
p[3]=i;
return p+4;
}
static Uint32 readuint32(unsigned char *p) {
return (p[0]<<24L) | (p[1]<<16L) | (p[2]<<8) | p[3];
}
static unsigned char* write_unique(unsigned char *p) {
memcpy(p, &network_unique, 4);
return p + 4;
}
static unsigned char* write_version(unsigned char *p) {
memcpy(p, &gameversion, 4);
return p + 4;
}
static int isvalidmsg_from_slave() {
int i;
void *host;
void *port;
if (!isvalidunique(mesg+1)) return -1;
host=&sender.sin_addr.s_addr;
port=&sender.sin_port;
for(i=1;i<MAXNETNODES;++i)
if(netnodes[i].used &&
!memcmp(&netnodes[i].netname.sin_addr.s_addr,host,4) &&
!memcmp(&netnodes[i].netname.sin_port,port,2))
return i;
return -1;
}
static int isvalidmsg_from_master() {
if (sender.sin_family != mastername.sin_family
|| sender.sin_addr.s_addr != mastername.sin_addr.s_addr
|| sender.sin_port != mastername.sin_port) return 0;
return 1;
}
/* Handling game actions */
static void addactions(void) {
memmove(hist[actionput],actions,MAXNETNODES);
++actionput;
if(actionput==ACTIONHIST)
actionput=0;
++actioncount;
}
static void buildactions(void) {
unsigned char *p;
int i,j;
p=actionblock;
i=0;
while(i<20) {
if(actioncount-i>0) {
j=actionput-i-1;
if(j<0) j+=ACTIONHIST;
memmove(p,hist[j],MAXNETNODES);
} else {
memset(p,0,MAXNETNODES);
}
p+=MAXNETNODES;
++i;
}
}
static void sendactions(int which) {
unsigned char msg[512];
msg[0] = PKT_STEP;
write_unique(msg+1);
writeuint32(msg+5, actioncount);
memcpy(msg+9,actionblock,MAXNETNODES*ACTIONHIST);
putmsg(&netnodes[which].netname,msg,MAXNETNODES*ACTIONHIST+9);
}
static void sendmine(int frame) {
unsigned char msg[64];
msg[0] = PKT_MYDATA;
write_unique(msg+1);
writeuint32(msg+5, frame);
msg[9]=myaction;
putmsg(&mastername,msg,10);
}
int networktraffic(void) {
int i;
int length;
int whosent;
unsigned char newactions[MAXNETNODES];
long now;
long count;
switch (network) {
case NETWORK_NONE:
return -1;
case NETWORK_MASTER:
memcpy(newactions,latestactions,MAXNETNODES);
newactions[0]=myaction;
now=gtime();
for(;;) {
if(gtime()-now>15) break;
length=getmsg(5);
if(length>0 && *mesg!=PKT_MYDATA) fprintf(stderr, "Strange packet %d\n", (int) *mesg);
// check for unexpected old packets...
// for example JOIN on frame 0, respond with BEGIN if player already in game
// respond with uninvite INVITE on JOIN from others
if(length<10) continue;
whosent = isvalidmsg_from_slave();
if(whosent<=0) continue;
count=readuint32(mesg+5);
if(count>latestcounts[whosent]) {
latestcounts[whosent]=count;
newactions[whosent]=mesg[9];
}
}
if(myaction==ACT_QUIT) {
for(i=1;i<MAXNETNODES;++i)
if(netnodes[i].used)
newactions[i]=ACT_QUIT;
}
memmove(actions,newactions,sizeof(actions));
addactions();
buildactions();
for(i=1;i<MAXNETNODES;++i)
if(netnodes[i].used)
sendactions(i);
for(i=1;i<MAXNETNODES;++i)
if(netnodes[i].used && actions[i]==ACT_QUIT)
netnodes[i].used=0;
return actioncount;
case NETWORK_SLAVE:
{
long latest=-1;
long lastsent;
lastsent=now=gtime();
++mydatacount;
sendmine(mydatacount);
for(;;) {
/*
if(gtime()-lastsent>=1)
{
lastsent=gtime();
sendmine(mydatacount);
}
*/
if(latest>=0 && gtime()-now>3) break;
length=getmsg(3);
if(length==MAXNETNODES*ACTIONHIST+9 &&
sender.sin_addr.s_addr==mastername.sin_addr.s_addr &&
sender.sin_port==mastername.sin_port) {
i=readuint32(mesg+5);
if(i<latest) continue;
latest=i;
memmove(actionblock,mesg+9,MAXNETNODES*ACTIONHIST);
}
}
return latest;
}
}
return -1;
}
/* Handling socket init */
int winsock=0;
void getsocket(void) {
int status;
socklen_t slen = sizeof(myname);
#if defined(__WIN32__) || defined(WIN32)
char dummydata[128];
if(WSAStartup(0x0101,(void *)dummydata)) {
printf("Windows dumped\n");
exit(1);
}
winsock=1;
#endif
udpsocket=socket(PF_INET,SOCK_DGRAM,IPPROTO_UDP);
if(udpsocket==-1) {
perror("socket()");
exit(1);
}
memset(&myname,0,sizeof(myname));
myname.sin_family=AF_INET;
myname.sin_addr.s_addr=htonl(INADDR_ANY);
myname.sin_port=htons(0);
status=bind(udpsocket,(struct sockaddr *) &myname,sizeof(myname));
if(-1 == status) {
perror("bind()");
exit(1);
}
status = getsockname(udpsocket, (struct sockaddr *) &myname, &slen);
if(-1 == status) {
perror("getsockname()");
exit(1);
}
}
void freesocket(void) {
#if defined(__WIN32__) || defined(WIN32)
if(!winsock) return;
WSACleanup();
#endif
}
/* Join / Host Games */
/* Master side */
static unsigned char* write_inform(unsigned char* put) {
int i;
*put++ = 0xff; /* slot specific for each slave */
for (i = 0; i < MAXNETNODES; ++i) {
if(!netnodes[i].used) continue;
*put++ = i;
memmove(put, netnodes[i].name, 16);
put += 16;
}
*put++ = 0xff;
return put;
}
static void send_inform_all(unsigned char type) {
int i;
unsigned char *put = mesg;
*put++ = type;
put = write_unique(put);
put = write_inform(put);
informsize = put-mesg;
for(i=1;i<MAXNETNODES;++i) {
if(netnodes[i].used) {
mesg[5] = i;
putmsg(&netnodes[i].netname, mesg, informsize);
}
}
}
static unsigned char* write_config(unsigned char* put) {
*put++ = configopts.density;
*put++ = configopts.flames;
*put++ = configopts.bombs;
*put++ = configopts.generosity;
return put;
}
static void build_config() {
unsigned char *put;
put=mesg;
*put++=PKT_CONFIG;
put = write_config(put);
informsize=put-mesg;
}
static void send_config1(int which) {
putmsg(&netnodes[which].netname,mesg,informsize);
}
void send_config() {
int i;
build_config();
for (i = 1; i < MAXNETNODES; ++i)
if (netnodes[i].used)
send_config1(i);
}
static void send_reject(struct sockaddr_in *toname, Uint32 network_join_unique, unsigned char reason) {
mesg[0] = PKT_REJECT;
memcpy(mesg+1, &network_join_unique, sizeof(network_join_unique));
write_version(mesg+5);
mesg[9] = reason;
putmsg(&sender,mesg,10);
}
static void send_accept(Uint32 network_join_unique) {
unsigned char *put = mesg;
*put++ = PKT_ACCEPT;
memcpy(put, &network_join_unique, sizeof(network_join_unique));
put += sizeof(network_join_unique);
put = write_seed_unique(put);
put = write_config(put);
put = write_inform(put);
putmsg(&sender,mesg,put-mesg);
}
int start_network_game() {
if(!registergame(playername, myname.sin_port, gameversion)) return 0;
memset(netnodes,0,sizeof(netnodes));
netnodes[0].used=1;
memmove(netnodes[0].name,playername,16);
myslot=0;
return 1;
}
int begin_network_game() {
send_inform_all(PKT_BEGIN);
network = NETWORK_MASTER;
/* TODO: wait for ack */
return 1;
}
void send_invites() {
send_inform_all(PKT_INVITE);
}
void cancel_network_game() {
int i;
mesg[0] = PKT_INVITE;
write_unique(mesg+1);
mesg[5] = 0xff;
for(i=1;i<MAXNETNODES;++i) {
if(netnodes[i].used) {
putmsg(&netnodes[i].netname,mesg,6);
}
}
}
int handle_joins() {
int size;
int i, j;
unsigned char temp[64];
Uint32 network_join_unique;
size=getmsg(40);
switch (*mesg) {
case PKT_JOIN:
if (size < 25) return 0;
memcpy(&network_join_unique, mesg+5, sizeof(network_join_unique));
if (!isvalidversion(mesg+1)) {
send_reject(&sender, network_join_unique, REJECT_VERSION);
return 0;
}
break;
case PKT_QUIT:
if (size < 5 || !isvalidunique(mesg+1)) return 0;
break;
default:
return 0;
}
/* Find host in list:
* i == MAXETNODES: host not found, otherwise the found host
* only if host not found:
* j == -1: no free slot, otherwise first free slot
*/
j = -1;
for (i = 1; i < MAXNETNODES; ++i) {
if (!netnodes[i].used) {
if (-1 == j) j = i;
continue; /* don't compare with unused host */
}
if(memcmp(&netnodes[i].netname.sin_addr.s_addr,
&sender.sin_addr.s_addr,4)) continue;
if(memcmp(&netnodes[i].netname.sin_port,
&sender.sin_port,2)) continue;
/* found host */
break;
}
switch (*mesg) {
case PKT_QUIT:
if(i < MAXNETNODES) /* if host found, reset entry */
memset(netnodes+i,0,sizeof(struct netnode));
/* send always ACK for QUITs */
*temp=PKT_ACK;
memmove(temp+1,mesg,5);
putmsg(&sender,temp,6);
break;
case PKT_JOIN:
if (i==MAXNETNODES && j==-1) { /* reject */
send_reject(&sender, network_join_unique, REJECT_FULL);
return 0;
}
if(i==MAXNETNODES) i=j;
memmove(&netnodes[i].netname.sin_addr.s_addr,
&sender.sin_addr.s_addr,4);
memmove(&netnodes[i].netname.sin_port,
&sender.sin_port,2);
netnodes[i].netname.sin_family=AF_INET;
netnodes[i].used=1;
memcpy(netnodes[i].name,mesg+9,16);
netnodes[i].name[15] = '\0';
send_accept(network_join_unique);
break;
}
return 1;
}
/* Client side */
static int read_inform(unsigned char** pbuf, int *psize) {
unsigned char *buf = *pbuf;
int size = *psize;
int i;
if (size < 1) return 0;
myslot = *buf++; size--;
if (0xff == myslot) return 1;
if (size < 1) return 0;
while (0xff != *buf) {
i = *buf++; size--;
if (size < 17 || i >= MAXNETNODES) return 0;
netnodes[i].used = 1;
memcpy(netnodes[i].name, buf, 16);
buf += 16;
size -= 16;
}
*psize = size;
*pbuf = buf;
return 1;
}
static void read_config(unsigned char* buf) {
GameOptions opts;
memset(&opts, 0, sizeof(opts));
opts.density = *buf++;
opts.flames = *buf++;
opts.bombs = *buf++;
opts.generosity = *buf++;
set_game_options(&opts);
}
/* returns 0=ignore packet,1=we're rejected,2=INVITE/CONFIG,3=BEGIN */
int scaninvite(int msec) {
int size;
unsigned char *take;
size = getmsg(msec);
if (size < 6) return 0;
if (*mesg!=PKT_INVITE && *mesg!=PKT_BEGIN && *mesg!=PKT_CONFIG) return 0;
if (!isvalidmsg_from_master()) return 0;
if (!isvalidunique(mesg+1)) return 0;
take = mesg+5;
size -= 5;
switch (*mesg) {
case PKT_INVITE:
case PKT_BEGIN:
if (!read_inform(&take, &size)) return 0;
if (0xff == myslot) return 1; /* master closed game */
break;
case PKT_CONFIG:
if (size < 4) return 0;
read_config(take);
break;
}
if (*mesg == PKT_BEGIN) {
return 3;
} else {
return 2;
}
}
int send_join(struct sockaddr_in *netname, char playername[16]) {
int size;
long now;
Uint32 join_unique = gtime();
unsigned char *buf;
mastername = *netname;
*regpacket=PKT_JOIN;
write_version(regpacket + 1);
writeuint32(regpacket+5, join_unique);
memcpy(regpacket+9, playername, 16);
now=longtime();
putmsg(&mastername,regpacket,1+4+4+16);
while(longtime()-now < 3) {
if (0 == (size = getmsg(1000))) {
/* got no message, send join again */
putmsg(&mastername,regpacket,1+4+4+16);
continue;
}
if (size < 5) continue;
if (readuint32(mesg+1) != join_unique) continue;
switch (*mesg) {
case PKT_ACCEPT:
if (size < 1+4+132+4+2) continue;
read_seed_unique(mesg + 5);
read_config(mesg+137);
buf = mesg+141;
size -= 141;
if (!read_inform(&buf,&size)) return 0;
return 2;
case PKT_REJECT:
/* TODO: print reject message */
return 0;
default:
break;
}
}
return 0;
}
void send_quit() {
long now;
int size;
*regpacket = PKT_QUIT;
write_unique(regpacket+1);
now=longtime();
while(longtime()-now<10) {
putmsg(&mastername,regpacket,5);
size=getmsg(1000);
if(size<6) continue;
if(mesg[0] != PKT_ACK || mesg[1] != PKT_QUIT) continue;
if (isvalidunique(mesg+2))
break;
}
}

186
sound.c
View File

@ -1,186 +0,0 @@
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <SDL_audio.h>
#include <SDL_error.h>
#include "sound.h"
#ifndef DATADIR
#define DATADIR "data"
#endif
static char dirlist[]=DATADIR;
static int readsound(int num);
#define NUMSOUNDS ((int)(sizeof(soundnames)/sizeof(char*)))
#define MIXMAX 16
#define SOUND_QUIET -1
static const char *soundnames[] = {
"bomb1.raw",
"power1.raw",
"death.raw",
"drop.raw",
"bomb2.raw",
"power2.raw",
};
typedef struct sample
{
char *data;
int len;
} sample;
#define SNDFRAGMENT 1024
static sample samples[NUMSOUNDS];
static int soundworking=0;
static int fragment;
// static int soundwrite,soundread;
static int *soundbuffer;
static int soundbufferlen;
static unsigned char sndclip[8192];
#define MAXSOUNDCOMMANDS 32
static char soundcommands[MAXSOUNDCOMMANDS];
static int soundtake,soundput;
static int sndplaying[MIXMAX],sndposition[MIXMAX];
static void fillaudio(void *udata,Uint8 *buffer,int len)
{
char com,*p;
int i,j,*ip;
int which;
while(soundtake!=soundput)
{
com=soundcommands[soundtake];
soundtake=(soundtake+1)&(MAXSOUNDCOMMANDS-1);
if(com==SOUND_QUIET) {memset(sndposition,0,sizeof(sndposition));continue;}
if(com<NUMSOUNDS)
{
for(i=0;i<MIXMAX;++i)
if(!sndposition[i])
{
sndposition[i]=1;
sndplaying[i]=com;
break;
}
}
}
memset(soundbuffer,0,soundbufferlen);
for(i=0;i<MIXMAX;++i)
{
if(!sndposition[i]) continue;
which=sndplaying[i];
if(sndposition[i]==samples[which].len)
{
sndposition[i]=0;
continue;
}
p=samples[which].data;
if(!p) continue;
p+=len*(sndposition[i]++ -1);
ip=soundbuffer;
j=len;
while(j--) *ip++ += *p++;
}
j=len;
ip=soundbuffer;;
while(j--) *buffer++ = sndclip[4096+*ip++];
}
int soundopen(void) {
SDL_AudioSpec wanted;
int i,j;
soundtake=soundput=0;
memset(sndposition,0,sizeof(sndposition));
memset(sndplaying,0,sizeof(sndplaying));
fragment=SNDFRAGMENT<<1;
soundbufferlen=fragment*sizeof(int);
soundbuffer=malloc(soundbufferlen);
if(!soundbuffer) return -2;
memset(&wanted,0,sizeof(wanted));
wanted.freq=22050;
wanted.channels=2;
wanted.format=AUDIO_U8;
wanted.samples=fragment>>1;
wanted.callback=fillaudio;
wanted.userdata=0;
if(SDL_OpenAudio(&wanted,0)<0)
{
fprintf(stderr,"Couldn't open audio: %s\n",SDL_GetError());
return -1;
}
soundworking=1;
for(i=0;i<8192;i++)
{
j=i-4096;
sndclip[i]=j > 127 ? 255 : (j<-128 ? 0 : j+128);
}
for(i=0;i<NUMSOUNDS;++i)
readsound(i);
SDL_PauseAudio(0);
return 0;
}
void soundclose(void) {
if(soundworking)
{
SDL_CloseAudio();
soundworking=0;
}
}
int readsound(int num) {
char name[256],*p1,*p2,ch;
int i,file,size,len;
p1=dirlist;
for(;;)
{
p2=name;
while(*p1 && (ch=*p1++)!=',')
*p2++=ch;
if(p2>name && p2[-1]!='/') *p2++='/';
strcpy(p2,soundnames[num]);
file=open(name,O_RDONLY);
if(file>=0) break;
if(!*p1)
{
samples[num].len=-1;
return 0;
}
}
size=lseek(file,0,SEEK_END);
lseek(file,0,SEEK_SET);
len=samples[num].len=(size+fragment-1)/fragment;
len*=fragment;
p1=samples[num].data=malloc(len);
if(p1)
{
i=read(file,p1,size);
if(len-size) memset(p1+size,0,len-size);
while(size--) *p1++ ^= 0x80;
} else
samples[num].data=0;
close(file);
return 0;
}
void playsound(int n) {
soundcommands[soundput]=n;
soundput=(soundput+1)&(MAXSOUNDCOMMANDS-1);
}

View File

@ -2,12 +2,12 @@
#include "announce.h"
#include "network.h"
#include <time.h>
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <unistd.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <avahi-client/client.h>
#include <avahi-client/lookup.h>
@ -20,10 +20,10 @@
#define SERVICE_TYPE "_sdlbomber._udp"
static AvahiClient *client = NULL;
static AvahiThreadedPoll *threaded_poll = NULL;
static AvahiEntryGroup *group = NULL;
static AvahiServiceBrowser *browser = NULL;
static AvahiClient* client = NULL;
static AvahiThreadedPoll* threaded_poll = NULL;
static AvahiEntryGroup* group = NULL;
static AvahiServiceBrowser* browser = NULL;
static char* name = NULL;
@ -37,32 +37,31 @@ static char buffer_glchanged[10];
gamelistentry gamelistentries[10];
int gamelistsize = 0;
static void myerror(AvahiClient *c, const char *s) {
fprintf(stderr, "Error in: %s\n (%s)", s, avahi_strerror(avahi_client_errno(client)));
static void myerror(AvahiClient* c, const char* s) {
fprintf(stderr, "Error in: %s\n (%s)", s, avahi_strerror(avahi_client_errno(c)));
}
static void create_services(AvahiClient *c);
static void create_services(AvahiClient* c);
static void entry_group_callback(AvahiEntryGroup *g, AvahiEntryGroupState state, AVAHI_GCC_UNUSED void *userdata) {
static void entry_group_callback(AvahiEntryGroup* g, AvahiEntryGroupState state, AVAHI_GCC_UNUSED void* userdata) {
group = g;
switch (state) {
case AVAHI_ENTRY_GROUP_ESTABLISHED :
case AVAHI_ENTRY_GROUP_ESTABLISHED:
break;
case AVAHI_ENTRY_GROUP_COLLISION :
{
char *n = avahi_alternative_service_name(name);
avahi_free(name);
name = n;
}
case AVAHI_ENTRY_GROUP_COLLISION: {
char* n = avahi_alternative_service_name(name);
avahi_free(name);
name = n;
}
/* And recreate the services */
avahi_entry_group_reset(group);
create_services(avahi_entry_group_get_client(g));
break;
case AVAHI_ENTRY_GROUP_FAILURE :
case AVAHI_ENTRY_GROUP_FAILURE:
fprintf(stderr, "Entry group failure: %s\n", avahi_strerror(avahi_client_errno(avahi_entry_group_get_client(g))));
avahi_threaded_poll_quit(threaded_poll);
@ -74,7 +73,7 @@ static void entry_group_callback(AvahiEntryGroup *g, AvahiEntryGroupState state,
}
}
static void create_services(AvahiClient *c) {
static void create_services(AvahiClient* c) {
int ret;
if (!group) {
@ -90,10 +89,9 @@ again:
snprintf(buf_version, sizeof(buf_version), "version=%X", (unsigned int) version);
if ((ret = avahi_entry_group_add_service(group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, name, SERVICE_TYPE, NULL, NULL, port, buf_version, NULL)) < 0) {
if (ret == AVAHI_ERR_COLLISION)
goto collision;
if (ret == AVAHI_ERR_COLLISION) goto collision;
fprintf(stderr, "Failed to add "SERVICE_TYPE": %s\n", avahi_strerror(ret));
fprintf(stderr, "Failed to add " SERVICE_TYPE ": %s\n", avahi_strerror(ret));
goto fail;
}
@ -105,12 +103,11 @@ again:
return;
collision:
{
char *n = avahi_alternative_service_name(name);
avahi_free(name);
name = n;
}
collision : {
char* n = avahi_alternative_service_name(name);
avahi_free(name);
name = n;
}
avahi_entry_group_reset(group);
goto again;
@ -118,7 +115,9 @@ fail:
avahi_threaded_poll_quit(threaded_poll);
}
static void client_callback(AvahiClient *c, AvahiClientState state, void * userdata) {
static void client_callback(AvahiClient* c, AvahiClientState state, void* userdata) {
(void) userdata;
client = c;
switch (state) {
case AVAHI_CLIENT_S_RUNNING:
@ -130,16 +129,14 @@ static void client_callback(AvahiClient *c, AvahiClientState state, void * userd
break;
case AVAHI_CLIENT_S_COLLISION:
case AVAHI_CLIENT_S_REGISTERING:
if (group) {
avahi_entry_group_reset(group);
}
if (group) { avahi_entry_group_reset(group); }
break;
case AVAHI_CLIENT_CONNECTING:
break;
}
}
int registergame(const char *playername, uint16_t p, const unsigned char v[4]) {
int registergame(const char* playername, uint16_t p, const unsigned char v[4]) {
if (name) avahi_free(name);
name = avahi_strdup(playername);
@ -163,7 +160,7 @@ void unregistergame(void) {
avahi_threaded_poll_unlock(threaded_poll);
}
static void remove_game_with_name(const char *name) {
static void remove_game_with_name(const char* name) {
int i;
for (i = 0; i < buffer_glsize; i++) {
@ -178,11 +175,26 @@ static void remove_game_with_name(const char *name) {
}
}
static void resolve_callback(AvahiServiceResolver *r, AvahiIfIndex interface, AvahiProtocol protocol, AvahiResolverEvent event,
const char *name, const char *type, const char *domain, const char *host_name, const AvahiAddress *address,
uint16_t port, AvahiStringList *txt, AvahiLookupResultFlags flags, void* userdata) {
static void resolve_callback(
AvahiServiceResolver* r,
AvahiIfIndex interface,
AvahiProtocol protocol,
AvahiResolverEvent event,
const char* name,
const char* type,
const char* domain,
const char* host_name,
const AvahiAddress* address,
uint16_t port,
AvahiStringList* txt,
AvahiLookupResultFlags flags,
void* userdata) {
int i;
uint32_t want_version;
(void) interface;
(void) host_name;
(void) flags;
(void) userdata;
assert(r);
if (protocol != AVAHI_PROTO_INET) goto done; /* ignore non IPv4 for now */
@ -194,74 +206,89 @@ static void resolve_callback(AvahiServiceResolver *r, AvahiIfIndex interface, Av
/* Called whenever a service has been resolved successfully or timed out */
switch (event) {
case AVAHI_RESOLVER_FAILURE:
fprintf(stderr, "(Resolver) Failed to resolve service '%s' of type '%s' in domain '%s': %s\n", name, type, domain, avahi_strerror(avahi_client_errno(avahi_service_resolver_get_client(r))));
break;
case AVAHI_RESOLVER_FAILURE:
fprintf(
stderr,
"(Resolver) Failed to resolve service '%s' of type '%s' in domain '%s': %s\n",
name,
type,
domain,
avahi_strerror(avahi_client_errno(avahi_service_resolver_get_client(r))));
break;
case AVAHI_RESOLVER_FOUND: {
gamelistentry *ge;
unsigned int version;
int have_version = 0;
AvahiStringList *psl;
for (psl = txt ; psl ; psl = psl->next) {
if (0 == strncmp("version=", (const char*) psl->text, 8)) {
sscanf((const char*) psl->text, "version=%X", &version);
if (version != want_version) goto done; /* version mismatch */
have_version = 1;
}
case AVAHI_RESOLVER_FOUND: {
gamelistentry* ge;
unsigned int version;
int have_version = 0;
AvahiStringList* psl;
for (psl = txt; psl; psl = psl->next) {
if (0 == strncmp("version=", (const char*) psl->text, 8)) {
sscanf((const char*) psl->text, "version=%X", &version);
if (version != want_version) goto done; /* version mismatch */
have_version = 1;
}
if (!have_version) goto done;
remove_game_with_name(name);
i = buffer_glsize++;
ge = &buffer_glentries[i];
buffer_glchanged[i] = 1;
memset(ge, 0, sizeof(*ge));
ge->netname.sin_addr.s_addr = address->data.ipv4.address;
ge->netname.sin_family = AF_INET;
ge->netname.sin_port = port;
strncpy(ge->name, name, 15);
}
if (!have_version) goto done;
remove_game_with_name(name);
i = buffer_glsize++;
ge = &buffer_glentries[i];
buffer_glchanged[i] = 1;
memset(ge, 0, sizeof(*ge));
ge->netname.sin_addr.s_addr = address->data.ipv4.address;
ge->netname.sin_family = AF_INET;
ge->netname.sin_port = port;
strncpy(ge->name, name, 15);
}
}
done:
avahi_service_resolver_free(r);
}
static void browse_callback(AvahiServiceBrowser *b, AvahiIfIndex interface, AvahiProtocol protocol, AvahiBrowserEvent event,
const char *name, const char *type, const char *domain, AvahiLookupResultFlags flags, void* userdata) {
static void browse_callback(
AvahiServiceBrowser* b,
AvahiIfIndex interface,
AvahiProtocol protocol,
AvahiBrowserEvent event,
const char* name,
const char* type,
const char* domain,
AvahiLookupResultFlags flags,
void* userdata) {
(void) flags;
assert(b);
/* Called whenever a new services becomes available on the LAN or is removed from the LAN */
switch (event) {
case AVAHI_BROWSER_FAILURE:
case AVAHI_BROWSER_FAILURE:
fprintf(stderr, "(Browser) %s\n", avahi_strerror(avahi_client_errno(client)));
avahi_threaded_poll_quit(threaded_poll);
return;
fprintf(stderr, "(Browser) %s\n", avahi_strerror(avahi_client_errno(client)));
avahi_threaded_poll_quit(threaded_poll);
return;
case AVAHI_BROWSER_NEW:
/* fprintf(stderr, "(Browser) NEW: service '%s' of type '%s' in domain '%s'\n", name, type, domain); */
case AVAHI_BROWSER_NEW:
/* fprintf(stderr, "(Browser) NEW: service '%s' of type '%s' in domain '%s'\n", name, type, domain); */
/* We ignore the returned resolver object. In the callback
function we free it. If the server is terminated before
the callback function is called the server will free
the resolver for us. */
/* We ignore the returned resolver object. In the callback
function we free it. If the server is terminated before
the callback function is called the server will free
the resolver for us. */
if (!(avahi_service_resolver_new(client, interface, protocol, name, type, domain, AVAHI_PROTO_UNSPEC, 0, resolve_callback, userdata)))
fprintf(stderr, "Failed to resolve service '%s': %s\n", name, avahi_strerror(avahi_client_errno(client)));
if (!(avahi_service_resolver_new(client, interface, protocol, name, type, domain, AVAHI_PROTO_UNSPEC, 0, resolve_callback, userdata)))
fprintf(stderr, "Failed to resolve service '%s': %s\n", name, avahi_strerror(avahi_client_errno(client)));
break;
break;
case AVAHI_BROWSER_REMOVE:
remove_game_with_name(name);
break;
case AVAHI_BROWSER_REMOVE:
remove_game_with_name(name);
break;
case AVAHI_BROWSER_ALL_FOR_NOW:
break;
case AVAHI_BROWSER_CACHE_EXHAUSTED:
break;
case AVAHI_BROWSER_ALL_FOR_NOW:
break;
case AVAHI_BROWSER_CACHE_EXHAUSTED:
break;
}
}
@ -279,7 +306,7 @@ int searchgames(void) {
avahi_threaded_poll_lock(threaded_poll);
if (NULL == (browser = avahi_service_browser_new(client, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, SERVICE_TYPE, NULL, 0, browse_callback, &gameversion))) {
if (NULL == (browser = avahi_service_browser_new(client, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, SERVICE_TYPE, NULL, 0, browse_callback, NULL))) {
fprintf(stderr, "Failed to create service browser: %s\n", avahi_strerror(avahi_client_errno(client)));
avahi_threaded_poll_unlock(threaded_poll);
return 0;

View File

@ -9,7 +9,7 @@ struct gamelistentry {
char name[16];
};
int registergame(const char *playername, uint16_t port, const unsigned char version[4]);
int registergame(const char* playername, uint16_t port, const unsigned char version[4]);
void unregistergame(void);
int searchgames(void);

View File

@ -1,35 +1,35 @@
#include <fcntl.h>
#include <netdb.h>
#include <signal.h>
#include <stdarg.h>
#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 <sys/types.h>
#include <unistd.h>
#include "bomber.h"
#include "gfx.h"
#include "announce.h"
#include "sound.h"
#include "menu.h"
#include "utils.h"
#include "network.h"
#include "game.h"
#include "bomber.h"
#include "draw.h"
#include "game.h"
#include "gfx.h"
#include "menu.h"
#include "network.h"
#include "sound.h"
#include "utils.h"
int main(int argc,char **argv) {
char *p;
int main(void) {
char* p;
strcpy(playername,"ANONYMOUS");
p=getenv("USER");
if(p) strncpy(playername,p,sizeof(playername));
strcpy(playername, "ANONYMOUS");
p = getenv("USER");
if (p) strncpy(playername, p, sizeof(playername) - 1);
create_seed_unique();
opengfx(argc, argv);
opengfx();
getsocket();
if (!initannouncer()) exit(1);

View File

@ -1,7 +1,8 @@
#ifndef BOMBER_H
#define BOMBER_H
#include "SDL.h"
#include "list.h"
#include <SDL.h>
typedef unsigned char uchar;
extern int xcolors[256];
@ -9,7 +10,6 @@ extern int xcolors[256];
#define IXSIZE 640
#define IYSIZE 480
#define MYF1 0x180
#define MYF2 0x181
#define MYF3 0x182
@ -36,33 +36,28 @@ extern int xcolors[256];
#define MYSHIFTED 0x40
#define MYALTED 0x200
typedef struct gfxset
{
typedef struct gfxset {
uchar gs_colormap[768];
uchar gs_inout[256];
uchar *gs_pic;
uchar* gs_pic;
int gs_xsize;
int gs_ysize;
} gfxset;
typedef struct figure
{
int xsize,ysize;
int xdelta,ydelta;
uchar *graphics;
typedef struct figure {
int xsize, ysize;
int xdelta, ydelta;
uchar* graphics;
} figure;
typedef struct solid
{
int xsize,ysize;
uchar *graphics;
typedef struct solid {
int xsize, ysize;
uchar* graphics;
} solid;
typedef struct player
{
struct player *next;
int xpos,ypos;
typedef struct player {
listhead list, list_all_players;
int xpos, ypos;
int flags;
int abilities;
int speed;
@ -70,14 +65,15 @@ typedef struct player
int bombsused;
int bombsavailable;
int flamelength;
int *at;
int* at;
int figcount;
int doing;
int action;
int color;
int controller;
int fixx,fixy;
figure *figure;
int fixx, fixy;
int kills, deaths;
figure* figure;
} player;
#define FLG_CONTROL 1
@ -85,29 +81,25 @@ typedef struct player
typedef struct sprite {
int flags;
int xpos,ypos;
figure *fig;
int flags;
int xpos, ypos;
figure* fig;
} sprite;
typedef struct damage {
int xpos,ypos;
int xsize,ysize;
int xpos, ypos;
int xsize, ysize;
} damage;
typedef struct list {
void *next;
} list;
typedef struct bomb {
struct bomb *next;
listhead list;
int type;
int xpos,ypos;
int px,py;
int xpos, ypos;
int px, py;
int power;
int timer;
int figcount;
player *owner;
player* owner;
} bomb;
@ -115,16 +107,17 @@ typedef struct bomb {
#define BOMB_NORMAL 1
#define BOMB_CONTROLLED 2
#define BOMBLIFE 60
#define BOMB_CONTROLLED_LIFE 150
#define FLAMELIFE 15
#define DECAYLIFE 15
typedef struct flame {
struct flame *next;
int xpos,ypos;
int px,py;
listhead list;
int xpos, ypos;
int px, py;
int timer;
int lurd;
player *owner;
player* owner;
} flame;
#define FL_UP 2
@ -133,53 +126,55 @@ typedef struct flame {
#define FL_RIGHT 4
typedef struct brickdecay {
struct brickdecay *next;
int xpos,ypos;
int px,py;
listhead list;
int xpos, ypos;
int px, py;
int timer;
} brickdecay;
typedef union listitem listitem;
union listitem {
listitem *next;
bomb b;
flame f;
brickdecay bc;
player p;
};
typedef struct generic {
struct generic *next;
int xpos,ypos;
int px,py;
int timer;
void (*process)();
void (*draw)();
void *ptr1,*ptr2;
int data1,data2;
} generic;
typedef struct bonustile {
struct bonustile *next;
int xpos,ypos;
int px,py;
listhead list;
int xpos, ypos;
int px, py;
int type;
} bonustile;
#define TILE_NONE -1
#define TILE_BOMB 5
#define TILE_FLAME 2
#define TILE_GOLDFLAME 7
#define TILE_CONTROL 9
#define TILE_SKATES 4
#define TILE_TURTLE 14
typedef struct generic {
listhead list;
int xpos, ypos;
int px, py;
int timer;
void (*process)();
void (*draw)();
void *ptr1, *ptr2;
int data1, data2;
} generic;
enum tile_types {
TILE_NONE = -1,
TILE_DISEASE = 0,
TILE_KICK = 1,
TILE_FLAME = 2,
TILE_PUNCH = 3,
TILE_SKATES = 4,
TILE_BOMB = 5,
TILE_SPOOGE = 6,
TILE_GOLDFLAME = 7,
TILE_BAD_DISEASE = 8,
TILE_TRIGGER = 9,
TILE_RANDOM = 10,
TILE_JELLY = 11,
TILE_GRAB = 12,
TILE_RANDOM2 = 13,
TILE_TURTLE = 14
};
#define ACT_INVALID 0x88
#define ACT_NONE 0
#define ACT_UP 1
#define ACT_DOWN 2
#define ACT_LEFT 3
// #define ACT_INVALID 0x88
#define ACT_NONE 0
#define ACT_UP 1
#define ACT_DOWN 2
#define ACT_LEFT 3
#define ACT_RIGHT 4
#define ACT_ENTER 5
#define ACT_QUIT 6
@ -201,14 +196,12 @@ typedef struct bonustile {
#define CODE_ALLDEAD 2
#define MAXTHINGS 500
#define MAXSETS 8
#define MAXSPRITES 128
#define MAXSPRITES 256
#define MAXDAMAGES 512
#define MAXBOMBSDETONATED 32
extern char exitflag;
extern volatile char exitflag;
extern uchar needwhole;
extern figure walking[MAXSETS][60];

482
src/draw.c Normal file
View File

@ -0,0 +1,482 @@
#include <fcntl.h>
#include <netdb.h>
#include <signal.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.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;
}
}
}

View File

@ -3,11 +3,12 @@
void loadgfx(void);
void drawbigstring(int xpos,int ypos,char *str);
void drawstring(int xpos, int ypos, const char* str);
void drawbigstring(int xpos, int ypos, const char* str);
void centerbig(int y,char *str);
void centerbig(int y, const char* str);
void addsprite(int x,int y,figure *fig);
void addsprite(int x, int y, figure* fig);
void plotsprites(void);
void erasesprites(void);
void clearsprites(void);
@ -21,7 +22,10 @@ int screentoarrayy(int y);
int arraytoscreenx(int x);
int arraytoscreeny(int y);
extern int bigfontxsize,bigfontysize,bigfontyspace;
void failure(char* str, ...);
extern int fontxsize, fontysize;
extern int bigfontxsize, bigfontysize, bigfontyspace;
/* On screen array variables */
extern int arraynumx, arraynumy, arraystartx, arraystarty, arrayspacex, arrayspacey;
@ -33,6 +37,7 @@ extern int arraynumx, arraynumy, arraystartx, arraystarty, arrayspacex, arrayspa
#define NUMDEATHFRAMES 41
extern figure blocks[3], blocksx[9], bombs1[MAXSETS][NUMBOMBFRAMES], bombs2[MAXSETS][NUMBOMBFRAMES], flamefigs[MAXSETS][NUMFLAMEFRAMES], tiles[15], death[NUMDEATHFRAMES];
extern figure blocks[3], blocksx[9], bombs1[MAXSETS][NUMBOMBFRAMES], bombs2[MAXSETS][NUMBOMBFRAMES], flamefigs[MAXSETS][NUMFLAMEFRAMES], tiles[15],
death[NUMDEATHFRAMES];
#endif

951
src/game.c Normal file
View File

@ -0,0 +1,951 @@
#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);
}

View File

@ -1,11 +1,13 @@
#ifndef GAME_H
#define GAME_H
#include "bomber.h"
#define FRACTION 9
#define SPEEDDELTA (1<<(FRACTION-1))
#define SPEEDMAX (10<<FRACTION)
#define SPEEDSTART (6<<FRACTION)
#define SPEEDTURTLE (3<<FRACTION)
#define SPEEDDELTA (1 << (FRACTION - 1))
#define SPEEDMAX (10 << FRACTION)
#define SPEEDSTART (6 << FRACTION)
#define SPEEDTURTLE (3 << FRACTION)
#define SPEEDTURTLE_TIMEOUT 250
#define TEMPNODES 2
@ -18,11 +20,10 @@ struct GameOptions {
void run_single_player(void);
void run_network_game(void);
void set_game_options(GameOptions *options);
void set_game_options(GameOptions* options);
extern char playername[16];
extern int gamemode;
extern solid background,backgroundoriginal;
extern solid background, backgroundoriginal;
#endif

923
src/gfx.c Normal file
View File

@ -0,0 +1,923 @@
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "bomber.h"
#include "gfx.h"
#define MAXCOLORS 256
int usedcolors = 0;
SDL_Surface* thescreen;
SDL_Color themap[256];
uchar* videomem;
int stride;
int mousex, mousey, mouseb;
uchar mustlock = 0, locked = 0;
uchar* block64;
int buttonstate = 0, buttondown = 0;
int mxpos, mypos;
int pressedcodes[KEYMAX], downcodes[KEYMAX], numpressed, numdown;
void dumpgfx() {
usedcolors = 0;
}
static int bestmatch(int red, int green, int blue) {
int i;
int bestcolor, bestdelta = 0;
int rdelta, gdelta, bdelta;
int delta;
i = 0;
bestcolor = -1;
while (i < usedcolors) {
rdelta = themap[i].r;
rdelta -= red;
rdelta *= rdelta;
gdelta = themap[i].g;
gdelta -= green;
gdelta *= gdelta;
bdelta = themap[i].b;
bdelta -= blue;
bdelta *= bdelta;
delta = rdelta + gdelta + bdelta;
if (bestcolor < 0 || delta < bestdelta) {
bestdelta = delta;
bestcolor = i;
}
i++;
}
return bestcolor;
}
static void updatemap(void) {
SDL_SetColors(thescreen, themap, 0, 256);
}
void createinout(gfxset* gs) {
uchar* p;
int i, j, counts[256];
uchar red, green, blue;
int cnt;
p = gs->gs_pic;
for (i = 0; i < 256; i++) counts[i] = 0;
i = gs->gs_xsize * gs->gs_ysize;
while (i--) counts[*p++]++;
cnt = 0;
gs->gs_inout[0] = 0;
for (i = 1; i < 256; i++) {
if (counts[i]) {
cnt++;
p = gs->gs_colormap + i + i + i;
red = *p++;
green = *p++;
blue = *p++;
for (j = 0; j < usedcolors; j++) {
if (red == themap[j].r && green == themap[j].g && blue == themap[j].b) {
gs->gs_inout[i] = j;
break;
}
}
if (j == usedcolors) {
if (usedcolors < MAXCOLORS) {
themap[j].r = red;
themap[j].g = green;
themap[j].b = blue;
gs->gs_inout[i] = usedcolors;
++usedcolors;
} else
gs->gs_inout[i] = bestmatch(red, green, blue);
}
}
}
updatemap();
}
static uchar* compressfig(uchar* put, gfxset* gs, int sourcex, int sourcey, int sizex, int sizey) {
int j, gswidth;
uchar *p, *p2, pixel, *map1;
int dx, dy;
gswidth = gs->gs_xsize;
map1 = gs->gs_inout;
p = gs->gs_pic + sourcex + gswidth * sourcey;
for (dy = 0; dy < sizey; dy++) {
p2 = p;
j = 0;
for (dx = 0; dx <= sizex; dx++) {
if (dx < sizex)
pixel = *p2;
else
pixel = 0;
++p2;
if (pixel)
++j;
else if (j) {
*put++ = j;
*put++ = dx - j;
*put++ = dy;
p2 -= j + 1;
while (j) *put++ = map1[*p2++], --j;
++p2;
}
}
p += gswidth;
}
*put++ = 0;
return put;
}
static void gfxfetchsingle(figure* fig, gfxset* gs, int sourcex, int sourcey, int sizex, int sizey) {
uchar *p, *p2;
int dx, dy;
/* uchar *map1; */
int gswidth;
int minx, miny, maxx, maxy;
int tx, ty;
/* map1=gs->gs_inout; */
gswidth = gs->gs_xsize;
p = gs->gs_pic + sourcex + gswidth * sourcey;
minx = miny = maxx = maxy = -1;
for (dy = 0; dy < sizey; dy++) {
p2 = p;
ty = sourcey + dy;
for (dx = 0; dx < sizex; dx++) {
if (!*p2++) continue;
if (miny == -1 || ty < miny) miny = ty;
if (maxy == -1 || ty > maxy) maxy = ty;
tx = sourcex + dx;
if (minx == -1 || tx < minx) minx = tx;
if (maxx == -1 || tx > maxx) maxx = tx;
}
p += gswidth;
}
if (minx == -1) {
minx = maxx = sourcex;
miny = maxy = sourcey;
}
fig->xdelta = minx - sourcex;
fig->ydelta = miny - sourcey;
sourcex = minx;
sourcey = miny;
fig->xsize = sizex = maxx - minx + 1;
fig->ysize = sizey = maxy - miny + 1;
p = compressfig(block64, gs, sourcex, sourcey, sizex, sizey);
fig->graphics = malloc(p - block64);
if (fig->graphics) memcpy(fig->graphics, block64, p - block64);
}
//(gfxset *gs,figure *fig,int sourcex,int sourcey,int sizex,int sizey)
void gfxfetch(gfxset* gs, figure* fig, int num) {
int x, y;
int xsize, ysize;
int fxsize, fysize;
unsigned char *p, *p2;
xsize = gs->gs_xsize;
ysize = gs->gs_ysize;
p2 = p = gs->gs_pic + xsize + 1;
fxsize = 2;
while (*p++ == 0) ++fxsize;
fysize = 2;
while (*p2 == 0) ++fysize, p2 += xsize;
x = fxsize;
y = 0;
while (num--) {
gfxfetchsingle(fig, gs, x, y, fxsize, fysize);
x += fxsize;
if (x > xsize - fxsize) {
x = 0;
y += fysize;
if (y > ysize - fysize) y = 0;
}
fig++;
}
}
void solidfetch(gfxset* gs, solid* dest) {
int xsize, ysize;
int i;
unsigned char *p, *map;
uchar* gfx;
memset(dest, 0, sizeof(solid));
xsize = gs->gs_xsize;
ysize = gs->gs_ysize;
i = xsize * ysize;
gfx = dest->graphics = malloc(i);
if (!gfx) return;
dest->xsize = xsize;
dest->ysize = ysize;
map = gs->gs_inout;
memcpy(gfx, gs->gs_pic, i);
p = gfx;
while (i--) {
if (*p) *p = map[*p];
++p;
}
}
void solidcopyany(solid* src, solid* dest, int destx, int desty, int sizex, int sizey) {
int xmax, ymax;
int j;
uchar *p1, *p2;
int w;
xmax = src->xsize;
ymax = src->ysize;
if (destx >= xmax || desty >= ymax || destx + sizex <= 0 || desty + sizey <= 0) return;
if (destx < 0) {
sizex += destx;
destx = 0;
}
if (desty < 0) {
sizey += desty;
desty = 0;
}
if (destx + sizex > xmax) sizex = xmax - destx;
if (desty + sizey > ymax) sizey = ymax - desty;
if (dest) {
w = dest->xsize;
p1 = dest->graphics + desty * w + destx;
} else {
gfxlock();
w = stride;
p1 = videomem + desty * stride + destx;
}
p2 = src->graphics + desty * xmax + destx;
for (j = 0; j < sizey; ++j) {
memcpy(p1, p2, sizex);
p1 += w;
p2 += xmax;
}
}
void solidcopy(solid* solid, int destx, int desty, int sizex, int sizey) {
solidcopyany(solid, 0, destx, desty, sizex, sizey);
}
void drawfigureany(int x, int y, figure* fig, solid* dest) {
int run;
int dx, dy;
int xsize, ysize;
int clipx, clipy, w;
unsigned char *pc, *p, *p2, *take;
take = fig->graphics;
if (dest) {
w = clipx = dest->xsize;
clipy = dest->ysize;
pc = dest->graphics;
} else {
gfxlock();
w = stride;
clipx = IXSIZE;
clipy = IYSIZE;
pc = videomem;
}
dx = fig->xdelta;
dy = fig->ydelta;
xsize = fig->xsize;
ysize = fig->ysize;
x += dx;
y += dy;
if (x >= 0 && y >= 0 && x <= clipx - xsize && y <= clipy - ysize) {
while ((run = *take++)) {
dx = *((signed char*) take);
++take;
dy = *((signed char*) take);
++take;
p = pc + w * (y + dy) + x + dx;
while (run--) *p++ = *take++;
}
} else {
while ((run = *take++)) {
dx = *((signed char*) take);
++take;
dy = *((signed char*) take);
++take;
dx += x;
dy += y;
p2 = take;
take += run;
if (dy < 0 || dy >= clipy) continue;
if (dx >= clipx) continue;
if (dx < 0) {
p2 -= dx;
run += dx;
dx = 0;
} else if (dx + run > clipx)
run = clipx - dx;
p = pc + w * dy + dx;
if (run) memcpy(p, p2, run);
}
}
}
void drawfigure(int destx, int desty, figure* fig) {
drawfigureany(destx, desty, fig, 0);
}
void copyup() {
gfxunlock();
SDL_UpdateRect(thescreen, 0, 0, 0, 0);
needwhole = 0;
}
void copyupxy(int x, int y) {
gfxunlock();
SDL_UpdateRect(thescreen, x, y, 24, 24);
}
void copyupxysize(int x, int y, int xsize, int ysize) {
gfxunlock();
SDL_UpdateRect(thescreen, x, y, xsize, ysize);
}
// static void set_color(int color, int red, int green, int blue) {
// themap[color].r=red;
// themap[color].g=green;
// themap[color].b=blue;
// }
void opengfx(void) {
unsigned long videoflags;
themap[0].r = 0;
themap[0].g = 0;
themap[0].b = 0;
usedcolors = 1;
block64 = malloc(65536);
if (!block64) {
printf("Couldn't allocate block64\n");
exit(50);
}
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_AUDIO) < 0) {
fprintf(stderr, "Couldn't initialize SDL: %s\n", SDL_GetError());
exit(1);
}
videoflags = SDL_SWSURFACE | SDL_HWPALETTE; //|SDL_FULLSCREEN;
thescreen = SDL_SetVideoMode(IXSIZE, IYSIZE, 8, videoflags);
if (thescreen == NULL) {
fprintf(stderr, "Couldn't set display mode: %s\n", SDL_GetError());
SDL_Quit();
exit(5);
}
stride = thescreen->pitch;
videomem = thescreen->pixels;
mustlock = SDL_MUSTLOCK(thescreen);
locked = 0;
SDL_ShowCursor(0);
}
void closegfx(void) {
SDL_Quit();
}
int checkpressed(int code) {
int *p, i;
i = numpressed;
p = pressedcodes;
while (i--)
if (*p++ == code) return 1;
return 0;
}
int checkdown(int code) {
int *p, i;
i = numdown;
p = downcodes;
while (i--)
if (*p++ == code) return 1;
return 0;
}
int checkbutton(int button) {
return buttonstate & (1 << button);
}
int checkbuttondown(int button) {
return buttondown & (1 << button);
}
int anydown(void) {
return numdown;
}
int takedown(void) {
int res = 0;
if (numdown) {
res = *downcodes;
--numdown;
if (numdown) memmove(downcodes, downcodes + 1, numdown * sizeof(int));
}
return res;
}
int firstdown(void) {
return *downcodes;
}
#define ENDMARK 0xaabacada
int sdlinoutnormal[] = {
SDLK_0,
'0',
SDLK_1,
'1',
SDLK_2,
'2',
SDLK_3,
'3',
SDLK_4,
'4',
SDLK_5,
'5',
SDLK_6,
'6',
SDLK_7,
'7',
SDLK_8,
'8',
SDLK_9,
'9',
SDLK_a,
'a',
SDLK_b,
'b',
SDLK_c,
'c',
SDLK_d,
'd',
SDLK_e,
'e',
SDLK_f,
'f',
SDLK_g,
'g',
SDLK_h,
'h',
SDLK_i,
'i',
SDLK_j,
'j',
SDLK_k,
'k',
SDLK_l,
'l',
SDLK_m,
'm',
SDLK_n,
'n',
SDLK_o,
'o',
SDLK_p,
'p',
SDLK_q,
'q',
SDLK_r,
'r',
SDLK_s,
's',
SDLK_t,
't',
SDLK_u,
'u',
SDLK_v,
'v',
SDLK_w,
'w',
SDLK_x,
'x',
SDLK_y,
'y',
SDLK_z,
'z',
SDLK_MINUS,
'-',
SDLK_EQUALS,
'=',
SDLK_LEFTBRACKET,
'[',
SDLK_RIGHTBRACKET,
']',
SDLK_SEMICOLON,
';',
SDLK_QUOTE,
'\'',
SDLK_SLASH,
'/',
SDLK_PERIOD,
'.',
SDLK_COMMA,
',',
SDLK_BACKQUOTE,
'`',
SDLK_BACKSPACE,
8,
SDLK_TAB,
9,
SDLK_DELETE,
MYDELETE,
SDLK_RETURN,
13,
SDLK_F1,
MYF1,
SDLK_F2,
MYF2,
SDLK_F3,
MYF3,
SDLK_F4,
MYF4,
SDLK_F5,
MYF5,
SDLK_F6,
MYF6,
SDLK_F7,
MYF7,
SDLK_F8,
MYF8,
SDLK_F9,
MYF9,
SDLK_F10,
MYF10,
SDLK_ESCAPE,
0x1b,
SDLK_LEFT,
MYLEFT,
SDLK_RIGHT,
MYRIGHT,
SDLK_UP,
MYUP,
SDLK_DOWN,
MYDOWN,
SDLK_PAGEUP,
MYPAGEUP,
SDLK_PAGEDOWN,
MYPAGEDOWN,
SDLK_SPACE,
' ',
SDLK_HOME,
MYHOME,
SDLK_END,
MYEND,
SDLK_LALT,
MYALTL,
SDLK_RALT,
MYALTR,
ENDMARK};
int sdlinoutshifted[] = {
SDLK_0,
')',
SDLK_1,
'!',
SDLK_2,
'@',
SDLK_3,
'#',
SDLK_4,
'$',
SDLK_5,
'%',
SDLK_6,
'^',
SDLK_7,
'&',
SDLK_8,
'*',
SDLK_9,
'(',
SDLK_a,
'A',
SDLK_b,
'B',
SDLK_c,
'C',
SDLK_d,
'D',
SDLK_e,
'E',
SDLK_f,
'F',
SDLK_g,
'G',
SDLK_h,
'H',
SDLK_i,
'I',
SDLK_j,
'J',
SDLK_k,
'K',
SDLK_l,
'L',
SDLK_m,
'M',
SDLK_n,
'N',
SDLK_o,
'O',
SDLK_p,
'P',
SDLK_q,
'Q',
SDLK_r,
'R',
SDLK_s,
'S',
SDLK_t,
'T',
SDLK_u,
'U',
SDLK_v,
'V',
SDLK_w,
'W',
SDLK_x,
'X',
SDLK_y,
'Y',
SDLK_z,
'Z',
SDLK_MINUS,
'_',
SDLK_EQUALS,
'+',
SDLK_LEFTBRACKET,
'{',
SDLK_RIGHTBRACKET,
'}',
SDLK_SEMICOLON,
':',
SDLK_QUOTE,
'"',
SDLK_SLASH,
'?',
SDLK_PERIOD,
'>',
SDLK_COMMA,
'<',
SDLK_BACKQUOTE,
'~',
SDLK_BACKSPACE,
8,
SDLK_TAB,
9,
SDLK_DELETE,
MYDELETE,
SDLK_RETURN,
13,
SDLK_F1,
MYF1 + MYSHIFTED,
SDLK_F2,
MYF2 + MYSHIFTED,
SDLK_F3,
MYF3 + MYSHIFTED,
SDLK_F4,
MYF4 + MYSHIFTED,
SDLK_F5,
MYF5 + MYSHIFTED,
SDLK_F6,
MYF6 + MYSHIFTED,
SDLK_F7,
MYF7 + MYSHIFTED,
SDLK_F8,
MYF8 + MYSHIFTED,
SDLK_F9,
MYF9 + MYSHIFTED,
SDLK_F10,
MYF10 + MYSHIFTED,
SDLK_ESCAPE,
0x1b,
SDLK_LEFT,
MYLEFT + MYSHIFTED,
SDLK_RIGHT,
MYRIGHT + MYSHIFTED,
SDLK_UP,
MYUP + MYSHIFTED,
SDLK_DOWN,
MYDOWN + MYSHIFTED,
SDLK_PAGEUP,
MYPAGEUP,
SDLK_PAGEDOWN,
MYPAGEDOWN,
SDLK_SPACE,
' ',
SDLK_HOME,
MYHOME,
SDLK_END,
MYEND,
SDLK_LALT,
MYALTL,
SDLK_RALT,
MYALTR,
ENDMARK};
static int looklist(int code, int* list) {
while ((unsigned int) *list != ENDMARK) {
if (*list == code) return list[1];
list += 2;
}
return -1;
}
static int mapkey(int code, int qual) {
if (qual & KMOD_SHIFT)
code = looklist(code, sdlinoutshifted);
else
code = looklist(code, sdlinoutnormal);
if (code < 0) return -1;
if (qual & KMOD_ALT) code |= MYALTED;
return code;
}
static void markkey(int code, int status) {
int i;
int* ip;
if (status) {
if (numdown < KEYMAX) downcodes[numdown++] = code;
ip = pressedcodes;
i = numpressed;
while (i)
if (*ip++ == code)
break;
else
i--;
if (!i && numpressed < KEYMAX) pressedcodes[numpressed++] = code;
} else {
i = numpressed;
ip = pressedcodes;
while (i)
if (*ip++ == code) {
*--ip = pressedcodes[--numpressed];
break;
} else
i--;
}
}
void pollinput(void) {
SDL_PollEvent(0);
}
void scaninput(void) {
SDL_Event event;
int key, mod;
static int bs = 0;
numdown = 0;
buttondown = 0;
while (SDL_PollEvent(&event)) {
switch (event.type) {
case SDL_KEYDOWN:
key = event.key.keysym.sym;
mod = event.key.keysym.mod;
markkey(mapkey(key, mod), 1);
break;
case SDL_KEYUP:
key = event.key.keysym.sym;
mod = event.key.keysym.mod;
markkey(mapkey(key, mod), 0);
break;
case SDL_MOUSEBUTTONUP:
bs &= ~(1 << (event.button.button - 1));
mousex = event.button.x >> 1;
mousey = event.button.y >> 1;
mouseb = bs;
break;
case SDL_MOUSEBUTTONDOWN:
bs |= 1 << (event.button.button - 1);
mousex = event.button.x >> 1;
mousey = event.button.y >> 1;
mouseb = bs;
break;
case SDL_MOUSEMOTION:
mousex = event.motion.x >> 1;
mousey = event.motion.y >> 1;
break;
case SDL_QUIT:
exitflag = 1;
break;
}
}
}
/*
void scaninput()
{
int i,*ip,code;
numdown=0;
buttondown=0;
while(XCheckMaskEvent(dp,~0,&event))
switch(event.type)
{
case KeyPress:
code=XLookupKeysym(keyevent,0);
if(numdown<KEYMAX)
downcodes[numdown++]=code;
ip=pressedcodes;
i=numpressed;
while(i)
if(*ip++==code) break;
else i--;
if(!i && numpressed<KEYMAX)
pressedcodes[numpressed++]=code;
break;
case KeyRelease:
code=XLookupKeysym(keyevent,0);
i=numpressed;
ip=pressedcodes;
while(i)
if(*ip++==code)
{
*--ip=pressedcodes[--numpressed];
break;
} else i--;
break;
case ButtonPress:
i=1<<buttonevent->button;
buttonstate|=i;
buttondown|=i;
break;
case ButtonRelease:
buttonstate&=~(1<<buttonevent->button);
break;
case MotionNotify:
mxpos=motionevent->x;
mypos=motionevent->y;
break;
case Expose:
copyup();
needwhole=0;
break;
case FocusOut:
numpressed=0;
break;
}
}
*/
static void drawrect(int x, int y, int xs, int ys, int c) {
uchar* p;
gfxlock();
p = videomem + y * stride + x;
while (ys--) {
memset(p, c, xs);
p += stride;
}
}
void greyrect(int x, int y, int xsize, int ysize) {
static int greycolor = -1;
if (greycolor == -1) greycolor = bestmatch(0, 0, 0x70);
drawrect(x, y, xsize, ysize, greycolor);
}
void clear() {
int j;
uchar* p;
gfxlock();
p = videomem;
for (j = 0; j < IYSIZE; ++j) {
memset(p, 0, IXSIZE);
p += stride;
}
}
void clearrect(int x, int y, int xsize, int ysize) {
drawrect(x, y, xsize, ysize, 0);
}
void gfxlock(void) {
if (locked || !mustlock) return;
if (SDL_LockSurface(thescreen) < 0) { fprintf(stderr, "Couldn't lock display surface: %s\n", SDL_GetError()); }
locked = 1;
}
void gfxunlock(void) {
if (!locked || !mustlock) return;
SDL_UnlockSurface(thescreen);
locked = 0;
}

69
src/gfx.h Normal file
View File

@ -0,0 +1,69 @@
#ifndef GFX_H
#define GFX_H
#define KEYMAX 128
extern int usedcolors;
extern uchar mymap[];
extern int screen;
extern int fontbase, fontysize;
extern char* imageloc;
extern long map2[];
extern uchar fmap[128];
extern int buttonstate, buttondown;
extern int mxpos, mypos;
extern int pressedcodes[KEYMAX], downcodes[KEYMAX], numpressed, numdown;
void opengfx(void);
void gfxlock(void);
void gfxunlock(void);
void pollinput(void);
int takedown(void);
void closegfx(void);
void greyrect(int x, int y, int xsize, int ysize);
void clearrect(int x, int y, int xsize, int ysize);
void solidfetch(gfxset* gs, solid* dest);
extern void dumpgfx(void);
extern void createinout(struct gfxset*);
extern void getcolors(void);
extern void gfxfetch(struct gfxset*, struct figure*, int);
extern void puttile(int destx, int desty, int source);
extern void store(int x, int y, int which);
extern void restore(int x, int y, int which);
extern void copyup(void);
extern void copyupxy(int x, int y);
extern void copyupxysize(int x, int y, int xsize, int ysize);
extern void getfigures(void);
extern unsigned long getcolor(char* name); /* unsigned long */
extern int checkpressed(int code);
extern int checkdown(int code);
extern int checkbutton(int button);
extern int checkbuttondown(int button);
extern int anydown(void);
extern int firstdown(void);
extern void scaninput(void);
extern void fontinit(void);
extern void writechar(int x, int y, uchar ch);
extern void clear(void);
extern void drawbox(int x, int y, int size, int color);
extern void drawbox2(int x, int y, int sizex, int sizey, int color);
extern void drawfillrect(int x, int y, int size, int color);
extern void bigpixel(int x, int y, int color);
extern void invert(int x, int y);
extern int getmousex(void);
extern int getmousey(void);
extern void drawsquare(int x, int y, uchar* source);
extern void colormapon(void);
extern void colormapoff(void);
extern void palette(uchar* pal);
extern void drawfigure(int x, int y, figure* fig);
extern void drawfigureany(int x, int y, figure* fig, solid* dest);
void solidcopy(solid* src, int destx, int desty, int sizex, int sizey);
void solidcopyany(solid* src, solid* dest, int destx, int desty, int sizex, int sizey);
#endif // GFXX_H

86
src/list.c Normal file
View File

@ -0,0 +1,86 @@
#include "list.h"
#include "bomber.h"
#include "utils.h"
typedef union genericlistitem genericlistitem;
union genericlistitem {
genericlistitem* next;
listhead list;
bomb b;
flame f;
brickdecay bc;
player p;
bonustile bt;
generic g;
};
/* doesn't use prev link */
static genericlistitem* things = 0;
static genericlistitem* free_things;
static int count_used = 0;
void alloc_things(void) {
int i;
if (!things) {
things = calloc(sizeof(genericlistitem), MAXTHINGS);
} else {
memset(things, 0, sizeof(genericlistitem) * MAXTHINGS);
}
if (!things) nomem("Trying to allocate thing memory");
for (i = 0; i < MAXTHINGS - 1; ++i) { things[i].next = &things[i + 1]; }
things[i].next = 0;
free_things = things;
count_used = 0;
}
void* _allocentry(size_t size) {
genericlistitem* entry = free_things;
if (size > sizeof(genericlistitem)) return 0;
if (free_things) {
count_used++;
free_things = free_things->next;
memset(entry, 0, sizeof(genericlistitem));
}
return entry;
}
void freeentry(void* ptr) {
genericlistitem* entry = ptr;
entry->next = free_things;
free_things = entry;
count_used--;
}
void list_add_tail(listhead* head, listhead* entry) {
entry->next = head;
entry->prev = head->prev;
head->prev->next = entry;
head->prev = entry;
}
void list_del(listhead* entry) {
entry->next->prev = entry->prev;
entry->prev->next = entry->next;
entry->next = entry->prev = entry;
}
void list_init_head(listhead* head) {
head->next = head;
head->prev = head;
}
void things_list_clear(listhead* head) {
genericlistitem* entry;
while (head->next != head) {
entry = container_of(head->next, __typeof__(*entry), list);
list_del(head->next);
freeentry(entry);
}
}

63
src/list.h Normal file
View File

@ -0,0 +1,63 @@
#ifndef LIST_H
#define LIST_H
#include <stddef.h>
/* from linux kernel (with some changes for "iso" c) */
typedef struct listhead listhead;
struct listhead {
listhead *prev, *next;
};
#define container_of(ptr, type, member) ((type*) ((char*) (__typeof__(((type*) 0)->member)*) (ptr) -offsetof(type, member)))
/**
* list_entry - get the struct for this entry
* @ptr: the &struct list_head pointer.
* @type: the type of the struct this is embedded in.
* @member: the name of the list_struct within the struct.
*/
#define list_entry(ptr, type, member) container_of(ptr, type, member)
void alloc_things(void);
void* _allocentry(size_t size);
#define allocentry(type) (type*) _allocentry(sizeof(type))
void freeentry(void* ptr);
void list_add_tail(listhead* header, listhead* entry);
#define addtail(head, entry) list_add_tail(head, &(entry->list));
/* remove entry from list */
void list_del(listhead* entry);
#define removeitem(entry) list_del(&(entry->list));
void list_init_head(listhead* head);
void things_list_clear(listhead* head); /* listhead member must be the first member */
/**
* list_for_each_entry - iterate over list of given type
* @pos: the type * to use as a loop cursor.
* @head: the head for your list.
* @member: the name of the list_struct within the struct.
*/
#define list_for_each_entry(pos, head, member) \
for (pos = list_entry((head)->next, __typeof__(*pos), member); /* prefetch(pos->member.next), */ \
&pos->member != (head); \
pos = list_entry(pos->member.next, __typeof__(*pos), member))
/**
* list_for_each_entry_safe - iterate over list of given type safe against removal of list entry
* @pos:<-->the type * to use as a loop cursor.
* @n:<><-->another type * to use as temporary storage
* @head:<->the head for your list.
* @member:>the name of the list_struct within the struct.
*/
#define list_for_each_entry_safe(pos, n, head, member) \
for (pos = list_entry((head)->next, __typeof__(*pos), member), n = list_entry(pos->member.next, __typeof__(*pos), member); &pos->member != (head); \
pos = n, n = list_entry(n->member.next, __typeof__(*n), member))
#define list_empty(head) ((head) == (head)->next)
#endif

357
src/matcher.c Normal file
View File

@ -0,0 +1,357 @@
#include <arpa/inet.h>
#include <fcntl.h>
#include <netdb.h>
#include <netinet/in.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/timeb.h>
#include <sys/types.h>
#include <sys/uio.h>
#include <time.h>
#define MAXMSG 256
#define PORT 5521
#define TIMETOLIVE 600 // seconds
typedef unsigned char uchar;
char logging = 0;
unsigned char mesg[MAXMSG];
int lastsize;
int network;
char masterhostname[256];
#define PKT_REGISTER 16
#define PKT_ACK 24
#define PKT_INFO 40
#define PKT_QUERY 32
#define MAXMATCHES 16
struct registration {
uchar id;
uchar unique[4];
uchar password[4];
uchar version[4];
uchar name[16];
uchar status;
};
struct query {
uchar id;
uchar password[4];
uchar version[4];
};
struct gamehost {
struct gamehost *next, *prev;
uchar machine[4];
uchar port[2];
struct registration reg;
long timeout;
};
struct gamehost *freehosts = 0, *activehosts = 0;
int udpsocket, myport;
struct sockaddr_in myname = {0}, mastername = {0};
socklen_t senderlength;
struct sockaddr_in sender = {0};
long longtime(void) {
struct timeb tb;
ftime(&tb);
return tb.time;
}
char* timestr() {
static char timestring[80];
time_t t;
int l;
time(&t);
strcpy(timestring, ctime(&t));
l = strlen(timestring);
if (l && timestring[l - 1] == '\n') timestring[l - 1] = 0;
return timestring;
}
int putmsg(struct sockaddr_in* toname, unsigned char* msg, int len) {
int status;
status = sendto(udpsocket, msg, len, 0, (struct sockaddr*) toname, sizeof(struct sockaddr_in));
return status;
}
void ack() {
uchar copy[256];
*copy = PKT_ACK;
memmove(copy + 1, mesg, lastsize);
putmsg(&sender, copy, lastsize + 1);
}
int getmsg(int seconds) {
int size;
lastsize = -1;
memset(&sender, 0, sizeof(sender));
senderlength = sizeof(sender);
if (seconds) {
struct timeval timeout;
fd_set readfds;
int res;
timeout.tv_sec = seconds;
timeout.tv_usec = 0;
FD_ZERO(&readfds);
FD_SET(udpsocket, &readfds);
res = select(udpsocket + 1, &readfds, 0, 0, &timeout);
if (res <= 0) return -1;
}
lastsize = size = recvfrom(udpsocket, mesg, MAXMSG, 0, (struct sockaddr*) &sender, &senderlength);
return size;
}
long longind(unsigned char* p) {
return (p[0] << 24L) | (p[1] << 16L) | (p[2] << 8) | p[3];
}
short shortind(unsigned char* p) {
return (p[0] << 8L) | p[1];
}
void openport(int portwant) {
int status;
myport = portwant;
udpsocket = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (udpsocket == -1) {
perror("socket()");
exit(1);
}
memset(&myname, 0, sizeof(myname));
myname.sin_family = AF_INET;
myname.sin_addr.s_addr = htonl(INADDR_ANY);
myname.sin_port = htons(myport);
status = bind(udpsocket, (struct sockaddr*) &myname, sizeof(myname));
if (status == -1) {
perror("bind()");
exit(1);
}
}
#define PERBLOCK 512
struct gamehost* newhost() {
struct gamehost *h, *block;
int i;
if (!freehosts) {
block = malloc(sizeof(struct gamehost) * PERBLOCK);
if (!block) return 0;
freehosts = block;
i = PERBLOCK - 1;
while (i--) {
block->next = block + 1;
++block;
}
block->next = 0;
}
h = freehosts;
freehosts = freehosts->next;
memset(h, 0, sizeof(struct gamehost));
return h;
}
void freehost(struct gamehost* h) {
h->next = freehosts;
freehosts = h;
}
struct gamehost* findmatch(struct registration* key) {
struct gamehost* h;
h = activehosts;
while (h) {
if (!memcmp(&h->reg, key, sizeof(struct registration) - 1)) return h;
h = h->next;
}
return 0;
}
void insert(struct gamehost* h) {
if (activehosts) {
h->next = activehosts;
h->prev = activehosts->prev;
activehosts->prev = h;
activehosts = h;
} else {
h->next = h->prev = 0;
activehosts = h;
}
}
void delete (struct gamehost* h) {
if (h->prev)
h->prev->next = h->next;
else
activehosts = h->next;
if (h->next) h->next->prev = h->prev;
freehost(h);
}
void doreg() {
struct registration* new;
struct gamehost* match;
long now;
new = (struct registration*) mesg;
match = findmatch(new);
if (logging) {
unsigned addr = ntohl(sender.sin_addr.s_addr);
unsigned short port = ntohs(sender.sin_port);
printf(
"reg :%s:%d.%d.%d.%d:%d %c%lx '%s'\n",
timestr(),
(addr >> 24) & 255,
(addr >> 16) & 255,
(addr >> 8) & 255,
addr & 255,
port,
new->status ? '+' : '-',
(long) match,
new->name);
fflush(stdout);
}
if (!match && !new->status) {
ack();
return;
}
if (match && new->status) {
ack();
return;
}
if (!match && new->status) {
match = newhost();
if (!match) return; // No memory, what can we do?
memmove(match->machine, &sender.sin_addr.s_addr, 4);
memmove(match->port, &sender.sin_port, 2);
match->reg = *new;
now = longtime();
match->timeout = now + TIMETOLIVE;
ack();
insert(match);
return;
} else // match && !new->status
{
delete (match);
ack();
return;
}
}
void doquery() {
uchar* password;
uchar* version;
struct gamehost* h;
uchar response[2048], *rput, *countersave;
int counter;
if (logging) {
unsigned addr = ntohl(sender.sin_addr.s_addr);
unsigned short port = ntohs(sender.sin_port);
printf("query:%s:%d.%d.%d.%d:%d\n", timestr(), (addr >> 24) & 255, (addr >> 16) & 255, (addr >> 8) & 255, addr & 255, port);
fflush(stdout);
}
password = mesg + 1;
version = mesg + 5;
h = activehosts;
rput = response;
*rput++ = PKT_INFO;
memmove(rput, password, 4);
rput += 4;
memmove(rput, version, 4);
rput += 4;
countersave = rput;
*rput++ = 0;
*rput++ = 0;
counter = 0;
while (h) {
if (!memcmp(password, h->reg.password, 4) && !memcmp(version, h->reg.version, 4) && counter < MAXMATCHES) {
++counter;
memmove(rput, h->reg.unique, 4);
rput += 4;
memmove(rput, h->machine, 4);
rput += 4;
memmove(rput, h->port, 2);
rput += 2;
memmove(rput, h->reg.name, sizeof(h->reg.name));
rput += sizeof(h->reg.name);
}
h = h->next;
}
*countersave++ = counter >> 8;
*countersave++ = counter;
putmsg(&sender, response, rput - response);
}
void purge(long cutoff) {
struct gamehost *h, *h2;
h = activehosts;
while (h) {
h2 = h;
h = h->next;
if (cutoff - h2->timeout > 0) { delete (h2); }
}
}
int main(int argc, char** argv) {
int i;
int want;
int size;
long purgetime;
long now;
want = PORT;
if (argc > 1) {
for (i = 1; i < argc; ++i)
if (!strncmp(argv[i], "-p", 2)) {
if (strlen(argv[i]) > 2)
want = atoi(argv[i] + 2);
else if (i + 1 < argc)
want = atoi(argv[i + 1]);
}
}
freehosts = 0;
openport(want);
purgetime = longtime() + TIMETOLIVE;
for (;;) {
size = getmsg(10);
if (size >= 1) switch (*mesg) {
case PKT_REGISTER:
if (size < sizeof(struct registration)) continue;
doreg();
break;
case PKT_QUERY:
if (size < sizeof(struct query)) continue;
doquery();
break;
}
now = longtime();
if (now - purgetime > 0) // avoid year 203x bug...
{
purge(purgetime);
purgetime += TIMETOLIVE;
}
}
}

387
src/menu.c Normal file
View File

@ -0,0 +1,387 @@
#include "menu.h"
#include "announce.h"
#include "bomber.h"
#include "draw.h"
#include "game.h"
#include "gfx.h"
#include "network.h"
#include "sound.h"
#include "utils.h"
GameOptions configopts = {2, 1, 0, 2};
/* Generic menu */
typedef enum { MENU_ANY = -1, MENU_MAIN = 0, MENU_CONFIG = 2, MENU_JOIN = 3 } menuname;
#define MENU_SELECT(x) ((menuname)(-x - 1))
static int menuhistory[32] = {0};
static char menustring[1024];
static char *menuput, *menuitems[40], *menutitle;
static int menunum, menuexit;
static int menudelta;
static void drawmenu(int selected) {
int i, j;
int tx, ty;
clear();
j = strlen(menutitle) * bigfontxsize;
drawbigstring((IXSIZE - j) >> 1, 20, menutitle);
ty = ((IYSIZE - (bigfontysize * menunum)) >> 1) - (IYSIZE >> 3);
for (i = 0; i < menunum; ++i) {
j = strlen(menuitems[i]) * bigfontxsize;
tx = (IXSIZE - j) >> 1;
if (i == selected) {
greyrect(0, ty - 1, tx - 5, bigfontysize);
greyrect(tx + j + 3, ty - 1, IXSIZE - (tx + j + 3), bigfontysize);
}
drawbigstring(tx, ty, menuitems[i]);
ty += bigfontyspace;
}
}
static int domenu(menuname whichmenu, int (*pause)(void)) {
char redraw;
int selected;
int mcount = 0;
if (whichmenu >= 0)
selected = menuhistory[whichmenu];
else
selected = -whichmenu - 1;
if (!pause) pause = mypause;
redraw = 1;
clearspritelist();
while (!exitflag) {
if (redraw) {
drawmenu(selected);
redraw = 0;
}
if (!pause()) return -1;
scaninput();
++mcount;
clearsprites();
clearspritelist();
addsprite(IXSIZE / 2 - 50 - 20, IYSIZE - 80, walking[2] + (30 + mcount % 15));
addsprite(IXSIZE / 2 + 50 - 20, IYSIZE - 80, walking[3] + (30 + (mcount + 7) % 15));
plotsprites();
copyup();
if (anydown()) playsound(3);
while (anydown()) {
switch (takedown()) {
case MYLEFT:
menudelta = -1;
return selected;
case MYRIGHT:
case ' ':
case 13:
menudelta = 1;
return selected;
case 'k':
case MYUP:
if (selected)
--selected;
else
selected = menunum - 1;
if (whichmenu >= 0) menuhistory[whichmenu] = selected;
redraw = 1;
break;
case 'j':
case MYDOWN:
++selected;
if (selected == menunum) selected = 0;
if (whichmenu >= 0) menuhistory[whichmenu] = selected;
redraw = 1;
break;
case 0x1b:
if (MENU_MAIN == whichmenu && menuexit != selected) {
selected = menuexit;
redraw = 1;
break;
}
menudelta = 1;
return menuexit;
}
}
}
menudelta = 0;
return menuexit;
}
static void menustart() {
menunum = -1;
menuput = menustring;
*menuput = 0;
menuexit = 0;
}
static void additem_s(char* item, int len) {
if (len < 0 || (menustring + sizeof(menustring) - menuput <= len)) return;
if (menunum < 0)
menutitle = menuput;
else
menuitems[menunum] = menuput;
++menunum;
memcpy(menuput, item, len + 1);
menuput += len + 1;
}
static void additem(char* item, ...) {
char output[256];
va_list ap;
int len;
va_start(ap, item);
len = vsnprintf(output, sizeof(output), item, ap);
if (len >= 256) len = 255; /* truncated string */
additem_s(output, len);
}
static void addexit(char* item, ...) {
char output[256];
va_list ap;
int len;
va_start(ap, item);
len = vsnprintf(output, sizeof(output), item, ap);
if (len >= 256) len = 255; /* truncated string */
menuexit = menunum;
additem_s(output, len);
}
/* end generic menu */
/* game menues */
static const char* densities[] = {"PACKED", "HIGH", "MEDIUM", "LOW"};
static const char* generosities[] = {"LOW", "MEDIUM", "HIGH", "RIDICULOUS"};
static const char* dis_en_abled[] = {"DISABLED", "ENABLED"};
static void config_menu(void) {
int sel;
while (!exitflag) {
menustart();
additem("GAME OPTIONS");
additem("RETURN TO MAIN MENU");
additem("DENSITY: %s", densities[configopts.density]);
additem("GENEROSITY: %s", generosities[configopts.generosity]);
additem("INITIAL FLAME LENGTH: %d", configopts.flames + 1);
additem("INITIAL NUMBER OF BOMBS: %d", configopts.bombs + 1);
additem("SOUND: %s", dis_en_abled[sound_enabled]);
sel = domenu(MENU_CONFIG, NULL);
switch (sel) {
case 0:
return;
case 1:
configopts.density += menudelta;
configopts.density &= 3;
break;
case 2:
configopts.generosity += menudelta;
configopts.generosity &= 3;
break;
case 3:
configopts.flames += menudelta;
configopts.flames &= 7;
break;
case 4:
configopts.bombs += menudelta;
configopts.bombs &= 7;
break;
case 5:
sound_enabled = 1 - sound_enabled;
break;
}
}
}
static void draw_host_game(void) {
int i;
char* name;
char temp[64];
#define M3X (IXSIZE / 3)
#define M3Y (IYSIZE / 4)
clear();
centerbig(20, "HOST NETWORK GAME");
drawbigstring(M3X, M3Y, "SLOT NAME");
for (i = 0; i < MAXNETNODES; ++i) {
if (!netnodes[i].used) continue;
name = netnodes[i].name;
snprintf(temp, sizeof(temp), " %d %s", i + 1, name);
drawbigstring(M3X, M3Y + (i + 2) * bigfontyspace, temp);
}
copyup();
}
static void host_game(void) {
create_seed_unique();
if (!start_network_game()) {
failure("COULD NOT REGISTER GAME");
return;
}
draw_host_game();
for (;;) {
scaninput();
while (anydown()) {
switch (takedown()) {
case 0x1b:
unregistergame();
cancel_network_game();
return;
case ' ':
case 13:
unregistergame();
if (begin_network_game()) {
run_network_game();
return;
}
send_invites();
draw_host_game();
}
}
if (!handle_joins()) continue;
send_invites();
draw_host_game();
}
}
static void drawjoinscreen(void) {
int i;
char name[17];
char temp[64];
#define JX (IXSIZE / 3)
#define JY (IYSIZE / 4)
clear();
centerbig(20, "JOIN NETWORK GAME");
drawbigstring(JX, JY, "SLOT NAME");
for (i = 0; i < MAXNETNODES; ++i) {
if (!netnodes[i].used) continue;
memmove(name, netnodes[i].name, 16);
name[16] = 0;
snprintf(temp, sizeof(temp), " %d %s", i + 1, name);
drawbigstring(JX, JY + (i + 1) * bigfontyspace, temp);
}
}
static int tryjoin(int which) {
int res;
if (0 == (res = send_join(&gamelistentries[which].netname, playername))) { return 0; }
while (!exitflag) {
switch (res) {
case 1:
failure("Game closed");
return 0;
case 2:
drawjoinscreen();
copyup();
break;
case 3:
return 1;
default:
break;
}
scaninput();
while (anydown()) {
switch (takedown()) {
case 0x1b:
send_quit();
return 0;
}
}
res = scaninvite(200);
}
return 0;
}
static int join_game_pause(void) {
if (find_more_games()) return 0;
mypause();
return 1;
}
static void join_game(void) {
int i;
int sel = -1;
if (!searchgames()) { return; }
menuhistory[MENU_JOIN] = 0;
while (-1 == sel) {
menustart();
if (gamelistsize == 0) {
additem("JOIN NETWORK GAME - NO GAMES AVAILABLE");
addexit("EXIT");
} else {
additem("JOIN NETWORK GAME");
for (i = 0; i < gamelistsize; i++) { additem(gamelistentries[i].name); }
addexit("EXIT");
}
sel = domenu(MENU_JOIN, join_game_pause);
if (menudelta < 0) sel = -1;
}
stop_search();
if (menuexit == sel || !gamelistsize) { return; }
if (!tryjoin(sel)) { return; }
run_network_game();
}
void mainloop(void) {
int sel;
exitflag = 0;
while (!exitflag) {
menustart();
additem("BOMBER MAIN MENU");
additem("EXIT GAME");
additem("START SINGLE PLAYER GAME");
additem("OPTIONS");
// additem("REMAP MOVEMENT KEYS");
additem("START NETWORK GAME");
additem("JOIN NETWORK GAME");
sel = domenu(MENU_MAIN, NULL);
if (menudelta < 0) sel = -1;
switch (sel) {
case 0:
return;
case 1:
run_single_player();
break;
case 2:
config_menu();
break;
case 3:
host_game();
break;
case 4:
join_game();
break;
}
}
}

View File

@ -3,8 +3,6 @@
void mainloop(void);
int iterate(void); /* bomber.c */
extern struct GameOptions configopts;
#endif

708
src/network.c Normal file
View File

@ -0,0 +1,708 @@
#include "network.h"
#include "announce.h"
#include "bomber.h"
#include "draw.h"
#include "game.h"
#include "menu.h"
#include "utils.h"
#define MAXMSG 4096
int udpsocket;
const unsigned char gameversion[4] = {0xda, 0x01, 0x00, 0x09};
struct netnode netnodes[MAXNETNODES];
static int informsize;
static unsigned char regpacket[64];
static struct sockaddr_in myname = {0}, mastername = {0};
static socklen_t senderlength;
static struct sockaddr_in sender = {0};
static unsigned char mesg[MAXMSG] = "";
uchar needwhole = 0;
int mydatacount;
int myslot;
network_type network = NETWORK_NONE;
int actioncount;
unsigned char actionblock[ACTIONHIST * MAXNETNODES];
int myaction;
unsigned char actions[MAXNETNODES];
unsigned char latestactions[MAXNETNODES];
long latestcounts[MAXNETNODES];
enum network_packet_types {
PKT_ACK, /* perfect copy of packet received */
/* join / host game */
/* slave -> master packets */
PKT_JOIN, /* 4 bytes version #, 4 bytes joinunique #, 16 bytes name */
PKT_QUIT, /* 4 bytes unique # */
/* master -> slave packets */
PKT_INVITE, /* 4 bytes unique #, 1 byte your slot (0xff for kick, no data following it) #, any # of 1:slot,16:name sets (-1 end) */
PKT_BEGIN, /* clone of INVITE */
PKT_CONFIG, /* 4 bytes unique #, config */
PKT_ACCEPT, /* 4 bytes join unique #, 132 bytes seed + unique #, config, slot info (see invite) */
PKT_REJECT, /* 4 bytes join unique #, 4 bytes version #, 1: reason */
/* ingame actions */
/* slave -> master packets */
PKT_MYDATA, /* 4 bytes unique #,4 bytes frame #, 1 byte data */
/* master -> slave packets */
PKT_STEP, /* 4 bytes unique #, 4 bytes frame #, history x MAXNETNODES bytes ACT_* */
PKT_INVALID = 0xff
};
enum reject_reason {
REJECT_FULL,
REJECT_VERSION
/* TODO: password? */
};
#define _REJECT_LAST REJECT_VERSION
const char* reject_reason_str[] = {"Server full", "Version mismatch"};
/* all bytes stored MSB first */
/*
game startup:
<master and matcher>
Master: send REGISTER to matcher with optional password, wait for ack. If
timout, resend.
matcher: Wait for REGISTER packet, when received maintain database. respond
to sender with ACK. REGISTER packet can close a game also. The REGISTER
packet sent by the master has a unique word to be used to avoid confusion.
REGISTER packet also contains a game version #
After master registers game and receives ACK, just waits for slaves to contact.
<slave and matcher>
slave: send QUERY to matcher with optional password, wait for INFO, if timeout,
resend.
matcher: respond to QUERY with INFO packet. matcher need not maintain any
database for slave requests. INFO packet contains IP addr and port for each
master machine that matches the QUERY spec (ALL or password). Only a
certain MAX # of entries are sent if there are too many to choose from.
<slave and master>
slave: send JOIN to master, wait for INVITE. If timeout, resend. JOIN packet
contains the unique word the master created. JOIN also contains username.
master: Respond to JOIN with INVITE. INVITE contains unique word from JOIN
packet. INVITE either contains NO meaning game no longer exists or is closed
or player is not invited. IF yes, INVITE contains info on other players
already in the game (username and slot # for each). Master allocates the
slots and avoids confusion based on IP addr and port #. INVITE also contains
game options structure. Whenever a new player JOINS and is admitted, master
sends updated INVITE packets to everyone already in the JOIN list. Whenever
master changes game options, master sends out another set of INVITES
Duplicate JOINS are answered with updated INVITE but nothing changes as far
as allocation.
Master player launches game after he's satisfied everyone has joined.
Master sends BEGIN packet to everyone. BEGIN is identical to INVITE except
that the data is final. Slave must respond with its first MYDATA packet with
frame # of 0. If master times out waiting, master sends duplicate BEGIN to
wayward slaves. Once master has received MYDATA from everyone, game starts.
Within game slave sends MYDATA to master and waits for STEP packet. If
timeout, slave sends duplicate MYDATA.
If master times out waiting for a slave's MYDATA, slave gets dropped. MYDATAs
received will be answered with PKT_QUIT.
*/
/* Network I/O, building/checking packets */
#if defined(TEST_LATENCY)
#define NUMQ 512
struct message {
int time;
struct sockaddr_in* to;
int tosize;
unsigned char msg[512];
int len;
} message[NUMQ] = {0};
outmsgs() {
int i;
for (i = 0; i < NUMQ; ++i) {
if (message[i].time) {
--message[i].time;
if (message[i].time) continue;
sendto(udpsocket, message[i].msg, message[i].len, 0, message[i].to, sizeof(struct sockaddr_in));
}
}
}
#endif
static int putmsg(struct sockaddr_in* toname, unsigned char* msg, int len) {
int status;
#if defined(TEST_LATENCY)
int i;
for (i = 0; i < NUMQ; ++i) {
if (!message[i].time) {
message[i].time = 10;
message[i].to = toname;
memcpy(message[i].msg, msg, len);
message[i].len = len;
break;
}
}
return 0;
#else
status = sendto(udpsocket, msg, len, 0, (struct sockaddr*) toname, sizeof(struct sockaddr_in));
return status;
#endif
}
static int getmsg(int msec) {
int size;
memset(&sender, 0, sizeof(sender));
senderlength = sizeof(sender);
if (msec) {
struct timeval timeout;
fd_set readfds;
int res;
memset(&timeout, 0, sizeof(timeout));
timeout.tv_sec = msec / 1000;
timeout.tv_usec = (msec % 1000) * 1000;
FD_ZERO(&readfds);
FD_SET(udpsocket, &readfds);
res = select(udpsocket + 1, &readfds, 0, 0, &timeout);
if (res <= 0) return -1;
}
size = recvfrom(udpsocket, mesg, MAXMSG, 0, (struct sockaddr*) &sender, &senderlength);
return size;
}
static int isvalidunique(unsigned char* p) {
return 0 == memcmp(p, &network_unique, 4);
}
static int isvalidversion(unsigned char* p) {
return 0 == memcmp(p, gameversion, 4);
}
static unsigned char* writeuint32(unsigned char* p, Uint32 i) {
p[0] = i >> 24L;
p[1] = i >> 16L;
p[2] = i >> 8L;
p[3] = i;
return p + 4;
}
static Uint32 readuint32(unsigned char* p) {
return (p[0] << 24L) | (p[1] << 16L) | (p[2] << 8) | p[3];
}
static unsigned char* write_unique(unsigned char* p) {
memcpy(p, &network_unique, 4);
return p + 4;
}
static unsigned char* write_version(unsigned char* p) {
memcpy(p, &gameversion, 4);
return p + 4;
}
static int isvalidmsg_from_slave() {
int i;
void* host;
void* port;
if (!isvalidunique(mesg + 1)) return -1;
host = &sender.sin_addr.s_addr;
port = &sender.sin_port;
for (i = 1; i < MAXNETNODES; ++i)
if (netnodes[i].used && !memcmp(&netnodes[i].netname.sin_addr.s_addr, host, 4) && !memcmp(&netnodes[i].netname.sin_port, port, 2)) return i;
return -1;
}
static int isvalidmsg_from_master() {
if (sender.sin_family != mastername.sin_family || sender.sin_addr.s_addr != mastername.sin_addr.s_addr || sender.sin_port != mastername.sin_port) return 0;
return 1;
}
/* Handling game actions */
static void addactions(void) {
memmove(actionblock + MAXNETNODES, actionblock, (ACTIONHIST - 1) * MAXNETNODES);
memcpy(actionblock, actions, MAXNETNODES);
++actioncount;
}
static void sendactions(int which) {
unsigned char msg[512];
msg[0] = PKT_STEP;
write_unique(msg + 1);
writeuint32(msg + 5, actioncount);
memcpy(msg + 9, actionblock, MAXNETNODES * ACTIONHIST);
putmsg(&netnodes[which].netname, msg, MAXNETNODES * ACTIONHIST + 9);
}
static void sendmine(int frame) {
unsigned char msg[64];
msg[0] = PKT_MYDATA;
write_unique(msg + 1);
writeuint32(msg + 5, frame);
msg[9] = myaction;
putmsg(&mastername, msg, 10);
}
int networktraffic(void) {
int i;
int length;
int whosent;
long now;
long count;
switch (network) {
case NETWORK_NONE:
return -1;
case NETWORK_MASTER:
memcpy(actions, latestactions, MAXNETNODES);
actions[0] = myaction;
if (myaction == ACT_QUIT) {
for (i = 1; i < MAXNETNODES; ++i) {
if (netnodes[i].used) actions[i] = ACT_QUIT;
}
} else {
for (i = 1; i < MAXNETNODES; ++i) {
if (netnodes[i].used) actions[i] &= ACT_MASK; /* only keep direction */
}
now = gtime();
for (;;) {
if (gtime() - now > 15) break;
length = getmsg(5);
if (length > 0 && *mesg != PKT_MYDATA) fprintf(stderr, "Strange packet %d\n", (int) *mesg);
/* check for unexpected old packets...
* for example JOIN on frame 0, respond with BEGIN if player already in game
* respond with uninvite INVITE on JOIN from others
*/
if (length < 10) continue;
whosent = isvalidmsg_from_slave();
if (whosent <= 0) continue;
count = readuint32(mesg + 5);
if (count > latestcounts[whosent]) {
latestcounts[whosent] = count;
actions[whosent] = (actions[whosent] & ~ACT_MASK) | mesg[9]; /* don't drop "action" keys */
}
}
}
addactions(); /* update action history block */
for (i = 1; i < MAXNETNODES; ++i) {
if (netnodes[i].used) {
sendactions(i); /* send actions to every active node */
if (actions[i] == ACT_QUIT) netnodes[i].used = 0; /* remove disconnected clients */
}
}
return actioncount;
case NETWORK_SLAVE:
count = -1; /* set to actioncount if we got at least one packet */
now = gtime();
++mydatacount;
sendmine(mydatacount);
for (;;) {
/* if we got already one packet we only wait 3msec, otherwise 30msec */
long cur = gtime();
if (count >= 0 && cur - now > 3) break;
if (exitflag || cur - now > 30) break;
length = getmsg(count >= 0 ? 3 : 20);
if (MAXNETNODES * ACTIONHIST + 9 != length) continue;
if (!isvalidmsg_from_master()) continue;
i = readuint32(mesg + 5);
if (i < actioncount) continue;
count = actioncount = i;
memcpy(actionblock, mesg + 9, MAXNETNODES * ACTIONHIST);
}
return actioncount;
}
return -1;
}
/* Handling socket init */
int winsock = 0;
void getsocket(void) {
int status;
socklen_t slen = sizeof(myname);
#if defined(__WIN32__) || defined(WIN32)
char dummydata[128];
if (WSAStartup(0x0101, (void*) dummydata)) {
printf("Windows dumped\n");
exit(1);
}
winsock = 1;
#endif
udpsocket = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (udpsocket == -1) {
perror("socket()");
exit(1);
}
memset(&myname, 0, sizeof(myname));
myname.sin_family = AF_INET;
myname.sin_addr.s_addr = htonl(INADDR_ANY);
myname.sin_port = htons(0);
status = bind(udpsocket, (struct sockaddr*) &myname, sizeof(myname));
if (-1 == status) {
perror("bind()");
exit(1);
}
status = getsockname(udpsocket, (struct sockaddr*) &myname, &slen);
if (-1 == status) {
perror("getsockname()");
exit(1);
}
}
void freesocket(void) {
#if defined(__WIN32__) || defined(WIN32)
if (!winsock) return;
WSACleanup();
#endif
}
/* Join / Host Games */
/* Master side */
static unsigned char* write_inform(unsigned char* put) {
int i;
*put++ = 0xff; /* slot specific for each slave */
for (i = 0; i < MAXNETNODES; ++i) {
if (!netnodes[i].used) continue;
*put++ = i;
memmove(put, netnodes[i].name, 16);
put += 16;
}
*put++ = 0xff;
return put;
}
static void send_inform_all(unsigned char type) {
int i;
unsigned char* put = mesg;
*put++ = type;
put = write_unique(put);
put = write_inform(put);
informsize = put - mesg;
for (i = 1; i < MAXNETNODES; ++i) {
if (netnodes[i].used) {
mesg[5] = i;
putmsg(&netnodes[i].netname, mesg, informsize);
}
}
}
static unsigned char* write_config(unsigned char* put) {
*put++ = configopts.density;
*put++ = configopts.flames;
*put++ = configopts.bombs;
*put++ = configopts.generosity;
return put;
}
static void build_config() {
unsigned char* put;
put = mesg;
*put++ = PKT_CONFIG;
put = write_config(put);
informsize = put - mesg;
}
static void send_config1(int which) {
putmsg(&netnodes[which].netname, mesg, informsize);
}
void send_config() {
int i;
build_config();
for (i = 1; i < MAXNETNODES; ++i)
if (netnodes[i].used) send_config1(i);
}
static void send_reject(struct sockaddr_in* toname, Uint32 network_join_unique, unsigned char reason) {
mesg[0] = PKT_REJECT;
memcpy(mesg + 1, &network_join_unique, sizeof(network_join_unique));
write_version(mesg + 5);
mesg[9] = reason;
putmsg(toname, mesg, 10);
}
static void send_accept(Uint32 network_join_unique) {
unsigned char* put = mesg;
*put++ = PKT_ACCEPT;
memcpy(put, &network_join_unique, sizeof(network_join_unique));
put += sizeof(network_join_unique);
put = write_seed_unique(put);
put = write_config(put);
put = write_inform(put);
putmsg(&sender, mesg, put - mesg);
}
int start_network_game() {
if (!registergame(playername, myname.sin_port, gameversion)) return 0;
memset(netnodes, 0, sizeof(netnodes));
netnodes[0].used = 1;
memmove(netnodes[0].name, playername, 16);
myslot = 0;
return 1;
}
int begin_network_game() {
send_inform_all(PKT_BEGIN);
network = NETWORK_MASTER;
/* TODO: wait for ack */
return 1;
}
void send_invites() {
send_inform_all(PKT_INVITE);
}
void cancel_network_game() {
int i;
mesg[0] = PKT_INVITE;
write_unique(mesg + 1);
mesg[5] = 0xff;
for (i = 1; i < MAXNETNODES; ++i) {
if (netnodes[i].used) { putmsg(&netnodes[i].netname, mesg, 6); }
}
}
int handle_joins() {
int size;
int i, j;
unsigned char temp[64];
Uint32 network_join_unique = 0;
size = getmsg(40);
switch (*mesg) {
case PKT_JOIN:
if (size < 25) return 0;
memcpy(&network_join_unique, mesg + 5, sizeof(network_join_unique));
if (!isvalidversion(mesg + 1)) {
send_reject(&sender, network_join_unique, REJECT_VERSION);
return 0;
}
break;
case PKT_QUIT:
if (size < 5 || !isvalidunique(mesg + 1)) return 0;
break;
default:
return 0;
}
/* Find host in list:
* i == MAXETNODES: host not found, otherwise the found host
* only if host not found:
* j == -1: no free slot, otherwise first free slot
*/
j = -1;
for (i = 1; i < MAXNETNODES; ++i) {
if (!netnodes[i].used) {
if (-1 == j) j = i;
continue; /* don't compare with unused host */
}
if (memcmp(&netnodes[i].netname.sin_addr.s_addr, &sender.sin_addr.s_addr, 4)) continue;
if (memcmp(&netnodes[i].netname.sin_port, &sender.sin_port, 2)) continue;
/* found host */
break;
}
switch (*mesg) {
case PKT_QUIT:
if (i < MAXNETNODES) /* if host found, reset entry */
memset(netnodes + i, 0, sizeof(struct netnode));
/* send always ACK for QUITs */
*temp = PKT_ACK;
memmove(temp + 1, mesg, 5);
putmsg(&sender, temp, 6);
break;
case PKT_JOIN:
if (i == MAXNETNODES && j == -1) { /* reject */
send_reject(&sender, network_join_unique, REJECT_FULL);
return 0;
}
if (i == MAXNETNODES) i = j;
memmove(&netnodes[i].netname.sin_addr.s_addr, &sender.sin_addr.s_addr, 4);
memmove(&netnodes[i].netname.sin_port, &sender.sin_port, 2);
netnodes[i].netname.sin_family = AF_INET;
netnodes[i].used = 1;
memcpy(netnodes[i].name, mesg + 9, 16);
netnodes[i].name[15] = '\0';
send_accept(network_join_unique);
break;
}
return 1;
}
/* Client side */
static int read_inform(unsigned char** pbuf, int* psize) {
unsigned char* buf = *pbuf;
int size = *psize;
int i;
if (size < 1) return 0;
myslot = *buf++;
size--;
if (0xff == myslot) return 1;
if (size < 1) return 0;
while (0xff != *buf) {
i = *buf++;
size--;
if (size < 17 || i >= MAXNETNODES) return 0;
netnodes[i].used = 1;
memcpy(netnodes[i].name, buf, 16);
buf += 16;
size -= 16;
}
*psize = size;
*pbuf = buf;
return 1;
}
static void read_config(unsigned char* buf) {
GameOptions opts;
memset(&opts, 0, sizeof(opts));
opts.density = *buf++;
opts.flames = *buf++;
opts.bombs = *buf++;
opts.generosity = *buf++;
set_game_options(&opts);
}
/* returns 0=ignore packet,1=we're rejected,2=INVITE/CONFIG,3=BEGIN */
int scaninvite(int msec) {
int size;
unsigned char* take;
size = getmsg(msec);
if (size < 6) return 0;
if (*mesg != PKT_INVITE && *mesg != PKT_BEGIN && *mesg != PKT_CONFIG) return 0;
if (!isvalidmsg_from_master()) return 0;
if (!isvalidunique(mesg + 1)) return 0;
take = mesg + 5;
size -= 5;
switch (*mesg) {
case PKT_INVITE:
case PKT_BEGIN:
if (!read_inform(&take, &size)) return 0;
if (0xff == myslot) return 1; /* master closed game */
break;
case PKT_CONFIG:
if (size < 4) return 0;
read_config(take);
break;
}
if (*mesg == PKT_BEGIN) {
network = NETWORK_SLAVE;
return 3;
} else {
return 2;
}
}
int send_join(struct sockaddr_in* netname, char playername[16]) {
int size;
long now;
Uint32 join_unique = gtime();
unsigned char* buf;
mastername = *netname;
*regpacket = PKT_JOIN;
write_version(regpacket + 1);
writeuint32(regpacket + 5, join_unique);
memcpy(regpacket + 9, playername, 16);
now = longtime();
putmsg(&mastername, regpacket, 1 + 4 + 4 + 16);
while (longtime() - now < 3) {
if (0 == (size = getmsg(1000))) {
/* got no message, send join again */
putmsg(&mastername, regpacket, 1 + 4 + 4 + 16);
continue;
}
if (size < 5) continue;
if (readuint32(mesg + 1) != join_unique) continue;
switch (*mesg) {
case PKT_ACCEPT:
if (size < 1 + 4 + 132 + 4 + 2) continue;
read_seed_unique(mesg + 5);
read_config(mesg + 137);
buf = mesg + 141;
size -= 141;
if (!read_inform(&buf, &size)) return 0;
return 2;
case PKT_REJECT:
if (size < 10 || mesg[9] > _REJECT_LAST) {
failure("Couldn't connect");
} else {
failure("Couldn't connect: %s", reject_reason_str[mesg[9]]);
}
return 0;
default:
break;
}
}
failure("Could not connect - Timeout");
return 0;
}
void send_quit() {
long now;
int size;
*regpacket = PKT_QUIT;
write_unique(regpacket + 1);
now = longtime();
while (longtime() - now < 10) {
putmsg(&mastername, regpacket, 5);
size = getmsg(1000);
if (size < 6) continue;
if (mesg[0] != PKT_ACK || mesg[1] != PKT_QUIT) continue;
if (isvalidunique(mesg + 2)) break;
}
}

View File

@ -1,22 +1,26 @@
#ifndef NETWORK_H
#define NETWORK_H
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/socket.h>
enum { MAXNETNODES = 8 };
struct netnode {
struct sockaddr_in netname;
char name[16];
char used;
int kills, deaths;
};
extern struct netnode netnodes[64];
extern struct netnode netnodes[MAXNETNODES];
void getsocket(void);
void freesocket(void);
int send_join(struct sockaddr_in *netname, char playername[16]);
int send_join(struct sockaddr_in* netname, char playername[16]);
void send_config();
void send_quit();
int scaninvite(int msec);
@ -27,11 +31,6 @@ int begin_network_game();
void send_invites();
void cancel_network_game();
#define MAXNETNODES 10
extern struct netnode netnodes[64];
extern int udpsocket;
extern const unsigned char gameversion[4];
typedef enum { NETWORK_NONE = 0, NETWORK_MASTER, NETWORK_SLAVE } network_type;
@ -44,8 +43,8 @@ int networktraffic(void);
extern int mydatacount;
extern int myslot;
extern int actionput,actioncount;
extern unsigned char actionblock[ACTIONHIST*MAXNETNODES];
extern int actionput, actioncount;
extern unsigned char actionblock[ACTIONHIST * MAXNETNODES];
extern int myaction;
extern unsigned char actions[MAXNETNODES];

179
src/sound.c Normal file
View File

@ -0,0 +1,179 @@
#include <SDL_audio.h>
#include <SDL_error.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include "sound.h"
#ifndef DATADIR
#define DATADIR "data"
#endif
int sound_enabled = 1;
static char dirlist[] = DATADIR;
static int readsound(int num);
#define NUMSOUNDS ((int) (sizeof(soundnames) / sizeof(char*)))
#define MIXMAX 16
#define SOUND_QUIET -1
static const char* soundnames[] = {
"bomb1.raw",
"power1.raw",
"death.raw",
"drop.raw",
"bomb2.raw",
"power2.raw",
};
typedef struct sample {
char* data;
int len;
} sample;
#define SNDFRAGMENT 1024
static sample samples[NUMSOUNDS];
static int soundworking = 0;
static int fragment;
// static int soundwrite,soundread;
static int* soundbuffer;
static int soundbufferlen;
static unsigned char sndclip[8192];
#define MAXSOUNDCOMMANDS 32
static char soundcommands[MAXSOUNDCOMMANDS];
static int soundtake, soundput;
static int sndplaying[MIXMAX], sndposition[MIXMAX];
static void fillaudio(void* udata, Uint8* buffer, int len) {
char com, *p;
int i, j, *ip;
int which;
(void) udata;
while (soundtake != soundput) {
com = soundcommands[soundtake];
soundtake = (soundtake + 1) & (MAXSOUNDCOMMANDS - 1);
if (com == SOUND_QUIET) {
memset(sndposition, 0, sizeof(sndposition));
continue;
}
if (com < NUMSOUNDS) {
for (i = 0; i < MIXMAX; ++i)
if (!sndposition[i]) {
sndposition[i] = 1;
sndplaying[i] = com;
break;
}
}
}
memset(soundbuffer, 0, soundbufferlen);
for (i = 0; i < MIXMAX; ++i) {
if (!sndposition[i]) continue;
which = sndplaying[i];
if (sndposition[i] == samples[which].len) {
sndposition[i] = 0;
continue;
}
p = samples[which].data;
if (!p) continue;
p += len * (sndposition[i]++ - 1);
ip = soundbuffer;
j = len;
while (j--) *ip++ += *p++;
}
j = len;
ip = soundbuffer;
;
while (j--) *buffer++ = sndclip[4096 + *ip++];
}
int soundopen(void) {
SDL_AudioSpec wanted;
int i, j;
soundtake = soundput = 0;
memset(sndposition, 0, sizeof(sndposition));
memset(sndplaying, 0, sizeof(sndplaying));
fragment = SNDFRAGMENT << 1;
soundbufferlen = fragment * sizeof(int);
soundbuffer = malloc(soundbufferlen);
if (!soundbuffer) return -2;
memset(&wanted, 0, sizeof(wanted));
wanted.freq = 22050;
wanted.channels = 2;
wanted.format = AUDIO_U8;
wanted.samples = fragment >> 1;
wanted.callback = fillaudio;
wanted.userdata = 0;
if (SDL_OpenAudio(&wanted, 0) < 0) {
fprintf(stderr, "Couldn't open audio: %s\n", SDL_GetError());
return -1;
}
soundworking = 1;
for (i = 0; i < 8192; i++) {
j = i - 4096;
sndclip[i] = j > 127 ? 255 : (j < -128 ? 0 : j + 128);
}
for (i = 0; i < NUMSOUNDS; ++i) readsound(i);
SDL_PauseAudio(0);
return 0;
}
void soundclose(void) {
if (soundworking) {
SDL_CloseAudio();
soundworking = 0;
}
}
int readsound(int num) {
char name[256], *p1, *p2, ch;
int file, size, len;
p1 = dirlist;
for (;;) {
p2 = name;
while (*p1 && (ch = *p1++) != ',') *p2++ = ch;
if (p2 > name && p2[-1] != '/') *p2++ = '/';
strcpy(p2, soundnames[num]);
file = open(name, O_RDONLY);
if (file >= 0) break;
if (!*p1) {
samples[num].len = -1;
return 0;
}
}
size = lseek(file, 0, SEEK_END);
lseek(file, 0, SEEK_SET);
len = samples[num].len = (size + fragment - 1) / fragment;
len *= fragment;
p1 = samples[num].data = malloc(len);
if (p1) {
read(file, p1, size);
if (len - size) memset(p1 + size, 0, len - size);
while (size--) *p1++ ^= 0x80;
} else
samples[num].data = 0;
close(file);
return 0;
}
void playsound(int n) {
if (sound_enabled) {
soundcommands[soundput] = n;
soundput = (soundput + 1) & (MAXSOUNDCOMMANDS - 1);
}
}

View File

@ -5,4 +5,6 @@ int soundopen(void);
void soundclose(void);
void playsound(int n);
extern int sound_enabled;
#endif

View File

@ -1,17 +1,17 @@
#include "bomber.h"
#include "utils.h"
#include "bomber.h"
#include "gfx.h"
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
int volatile hc=0;
char volatile interrupted=0;
int volatile hc = 0;
char volatile interrupted = 0;
static Uint32 cur_unique;
Uint32 network_unique;
@ -20,12 +20,12 @@ Uint32 gtime(void) {
}
Uint32 longtime(void) {
return gtime()/1000;
return gtime() / 1000;
}
/* surf random generator: x (Daniel J. Bernstein) */
#define ROT(x, b) (((x) << (b)) | ((x) >> (32-(b))))
#define MUSH(i, b) x = t[i] += (((x ^ seed[i]) + sum) ^ ROT(x,b))
#define ROT(x, b) (((x) << (b)) | ((x) >> (32 - (b))))
#define MUSH(i, b) x = t[i] += (((x ^ seed[i]) + sum) ^ ROT(x, b))
static void surf(Uint32 out[8], const Uint32 in[12], const Uint32 seed[32]) {
Uint32 t[12], x, sum = 0;
int r, i, loop;
@ -35,11 +35,20 @@ static void surf(Uint32 out[8], const Uint32 in[12], const Uint32 seed[32]) {
for (loop = 0; loop < 2; ++loop) {
for (r = 0; r < 16; ++r) {
sum += 0x9e3779b9;
MUSH(0, 5); MUSH(1, 7); MUSH(2, 9); MUSH(3, 13);
MUSH(4, 5); MUSH(5, 7); MUSH(6, 9); MUSH(7, 13);
MUSH(8, 5); MUSH(9, 7); MUSH(10, 9); MUSH(11, 13);
MUSH(0, 5);
MUSH(1, 7);
MUSH(2, 9);
MUSH(3, 13);
MUSH(4, 5);
MUSH(5, 7);
MUSH(6, 9);
MUSH(7, 13);
MUSH(8, 5);
MUSH(9, 7);
MUSH(10, 9);
MUSH(11, 13);
}
for (i = 0; i < 8; ++i) out[i] ^= t[i+4];
for (i = 0; i < 8; ++i) out[i] ^= t[i + 4];
}
}
#undef ROT
@ -62,10 +71,14 @@ static Uint32 surf_init(void) {
if (-1 == fd) {
Uint32 genseed[4][8];
surf_seed[0] = surf_seed[1] = gtime();
surf_in[0]++; surf(genseed[0], surf_in, surf_seed);
surf_in[0]++; surf(genseed[1], surf_in, surf_seed);
surf_in[0]++; surf(genseed[2], surf_in, surf_seed);
surf_in[0]++; surf(genseed[3], surf_in, surf_seed);
surf_in[0]++;
surf(genseed[0], surf_in, surf_seed);
surf_in[0]++;
surf(genseed[1], surf_in, surf_seed);
surf_in[0]++;
surf(genseed[2], surf_in, surf_seed);
surf_in[0]++;
surf(genseed[3], surf_in, surf_seed);
memcpy(surf_seed, genseed[0], 32);
memcpy(surf_seed, genseed[1], 32);
memcpy(surf_seed, genseed[2], 32);
@ -86,14 +99,15 @@ static Uint32 surf_init(void) {
static Uint32 surf_random(void) {
if (surf_left == 0) {
int i;
for (i = 0; (i < 12) && !(++surf_in[i]); i++) ;
for (i = 0; (i < 12) && !(++surf_in[i]); i++)
;
surf_left = 8;
surf(surf_out, surf_in, surf_seed);
}
return surf_out[--surf_left];
}
void read_seed_unique(unsigned char *buf) {
void read_seed_unique(unsigned char* buf) {
int i;
memset(surf_in, 0, sizeof(surf_in));
@ -101,12 +115,12 @@ void read_seed_unique(unsigned char *buf) {
surf_left = 0;
memcpy(&surf_seed, buf, sizeof(surf_seed));
memcpy(&network_unique, buf+sizeof(surf_seed), sizeof(network_unique));
memcpy(&network_unique, buf + sizeof(surf_seed), sizeof(network_unique));
cur_unique = ntohl(network_unique);
for (i = 0; i < 32; i++) surf_seed[i] = ntohl(surf_seed[i]);
}
unsigned char* write_seed_unique(unsigned char *buf) {
unsigned char* write_seed_unique(unsigned char* buf) {
int i;
for (i = 0; i < 32; i++) {
@ -200,34 +214,34 @@ Uint32 get_unique(void) {
}
#endif
void nomem(char *str) {
printf("No memory!!![%s]\n",str);
void nomem(char* str) {
printf("No memory!!![%s]\n", str);
exit(1);
}
int mypause(void) {
while(!interrupted) {
while (!interrupted) {
pollinput();
SDL_Delay(1);
}
interrupted=0;
interrupted = 0;
return 1;
}
static Uint32 sdlhandler(Uint32 time) {
#if defined (SDL_LATENCY)
#if defined(SDL_LATENCY)
outmsgs();
#endif
interrupted=1;
interrupted = 1;
hc++;
return time;
}
void pulseon(void) {
SDL_SetTimer(40,sdlhandler);
SDL_SetTimer(40, sdlhandler);
}
void hexdump(unsigned char *p, int len) {
void hexdump(unsigned char* p, int len) {
int i;
for (i = 0; i < len; i++) {
if (15 == len % 16)
@ -235,6 +249,5 @@ void hexdump(unsigned char *p, int len) {
else
fprintf(stderr, "0x%X ", p[i]);
}
if (i % 16)
fprintf(stderr, "\n");
if (i % 16) fprintf(stderr, "\n");
}

View File

@ -1,6 +1,8 @@
#ifndef UTILS_H
#define UTILS_H
#include <SDL.h>
Uint32 gtime(void);
Uint32 longtime(void);
@ -10,21 +12,21 @@ void set_unique(Uint32 unique);
Uint32 get_unique(void);
#endif
#define SEED_UNIQUE_SIZE (32*4+4)
void read_seed_unique(unsigned char *buf);
unsigned char* write_seed_unique(unsigned char *buf);
#define SEED_UNIQUE_SIZE (32 * 4 + 4)
void read_seed_unique(unsigned char* buf);
unsigned char* write_seed_unique(unsigned char* buf);
void create_seed_unique(void);
extern Uint32 network_unique;
int myrand(void);
void nomem(char *str);
void nomem(char* str);
int mypause(void);
void pulseon(void);
void hexdump(unsigned char *p, int len);
void hexdump(unsigned char* p, int len);
extern volatile int hc;
extern volatile char interrupted;