diff --git a/Makefile b/Makefile index 3759586..61d1ee1 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -#DBG = -g +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) @@ -6,18 +6,30 @@ override CFLAGS += -D_REENTRANT -O2 $(shell sdl-config --cflags) $(DBG) $(WARNFL .PHONY: all clean install all: sdlbomber -sdlbomber: bomber.o gfx.o sound.o announce.o - gcc -o sdlbomber bomber.o gfx.o sound.o announce.o $(shell sdl-config --libs) -lavahi-common -lavahi-client $(DBG) +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 -bomber.o: bomber.c bomber.h gfx.h announce.h +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 -sound.o: sound.c +list.o: list.c bomber.h list.h utils.h -announce.o: announce.c announce.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 diff --git a/Makefile.osx b/Makefile.osx index 8bd267f..e29725c 100644 --- a/Makefile.osx +++ b/Makefile.osx @@ -3,23 +3,24 @@ CC = gcc CFLAGS = -O2 -Wall -I/Library/Frameworks/SDL.framework/Headers $(DBG) -LDFLAGS += -framework SDL -framework Cocoa -o $@ +LDFLAGS += -framework SDL -framework Cocoa -o $@ -lavahi-client -all: bomber -bomber: gfx.o bomber.o sound.o SDLMain.o +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 +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.o: sound.c bomber.h sound.h +announce.o: announce.c announce.h clean: - rm -f *.o bomber + rm -f *.o sdlbomber matcher -test: all - ./bomber +test: all + ./sdlbomber diff --git a/Makefile.sdldemoswin32 b/Makefile.sdldemoswin32 index 1f74b2c..b158abf 100644 --- a/Makefile.sdldemoswin32 +++ b/Makefile.sdldemoswin32 @@ -1,9 +1,9 @@ # Makefile for bomber # -TARGET = bomber +TARGET = sdlbomber USEINET = true include ../GNUmake -$(TARGET): bomber.o gfx.o sound.o +$(TARGET): bomber.o gfx.o sound.o announce.o diff --git a/README b/README index 4f6ddfe..a23f398 100644 --- a/README +++ b/README @@ -55,40 +55,6 @@ There isn't much point in playing the game alone (single player). In that case the only thing to avoid is accidentally killing yourself. Big deal... It's really a multiplayer game. ------------------ Left to do: ------------------- -Come up with a better scheme for internet play, as it is any latency will -kill the game playability. Works fine on LAN though. - -Figure out why matching doesn't work through a firewall... -[Specific case is 2 machines on my LAN cannot be matched by a machine that -is outside the firewall, and the firewall machine is doing IP masquerading. -Net result is matcher gets remapped IP addresses and these mean nothing -inside the LAN, only make sense when used from the outside world] - -Score. - -Remap movement keys not implemented. - -Allow game hoster to set network game options and have them visible to players -who have joined the game. - -Alternate gfx sets. - -Better handling of 8 bit mode with limited palette. - -Computer controlled things that can kill you. - -Other bonus types that can be harmful, such as skull. - -Internet play where everything is asynchronous, thus playable with a fixed -latency. As it is now slaves send to master, then master sends back to -slaves, and this data xfer must complete before the game can advance a step. -For internet this would be intolerable. I've never tried though... - -Better docs, online explanations, attract mode... - -Single player with computer controlled figures that must be killed/avoided. - ---------------- Direct comments, complaints and questions to dash@xdr.com This code is GPL. diff --git a/TODO b/TODO index e69de29..9acf9a5 100644 --- a/TODO +++ b/TODO @@ -0,0 +1,33 @@ +----------------- Left to do: ------------------- +Come up with a better scheme for internet play, as it is any latency will +kill the game playability. Works fine on LAN though. + +Figure out why matching doesn't work through a firewall... +[Specific case is 2 machines on my LAN cannot be matched by a machine that +is outside the firewall, and the firewall machine is doing IP masquerading. +Net result is matcher gets remapped IP addresses and these mean nothing +inside the LAN, only make sense when used from the outside world] + +Score. + +Remap movement keys not implemented. + +Allow game hoster to set network game options and have them visible to players +who have joined the game. + +Alternate gfx sets. + +Better handling of 8 bit mode with limited palette. + +Computer controlled things that can kill you. + +Other bonus types that can be harmful, such as skull. + +Internet play where everything is asynchronous, thus playable with a fixed +latency. As it is now slaves send to master, then master sends back to +slaves, and this data xfer must complete before the game can advance a step. +For internet this would be intolerable. I've never tried though... + +Better docs, online explanations, attract mode... + +Single player with computer controlled figures that must be killed/avoided. diff --git a/announce.c b/announce.c index 66a7bba..12c353d 100644 --- a/announce.c +++ b/announce.c @@ -1,5 +1,6 @@ #include "announce.h" +#include "network.h" #include #include @@ -22,13 +23,16 @@ static AvahiClient *client = NULL; static AvahiThreadedPoll *threaded_poll = NULL; static AvahiEntryGroup *group = NULL; +static AvahiServiceBrowser *browser = NULL; static char* name = NULL; static uint16_t port = 0; -static uint32_t unique = 0, version = 0; +static uint32_t version = 0; -static volatile int all_for_now; +static gamelistentry buffer_glentries[10]; +static int buffer_glsize = 0; +static char buffer_glchanged[10]; gamelistentry gamelistentries[10]; int gamelistsize = 0; @@ -82,10 +86,9 @@ static void create_services(AvahiClient *c) { again: if (avahi_entry_group_is_empty(group)) { - char buf_unique[128], buf_version[128]; - snprintf(buf_unique, sizeof(buf_unique), "unique=%X", (unsigned int) unique); + char buf_version[128]; 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_unique, buf_version, NULL)) < 0) { + 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; @@ -136,12 +139,11 @@ static void client_callback(AvahiClient *c, AvahiClientState state, void * userd } } -int registergame(char *playername, uint16_t p, uint32_t uniq, 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); port = p; - unique = htonl(uniq); memcpy(&version, v, 4); version = htonl(version); @@ -152,7 +154,7 @@ int registergame(char *playername, uint16_t p, uint32_t uniq, unsigned char v[4] return 1; } -void unregistergame() { +void unregistergame(void) { port = 0; avahi_threaded_poll_lock(threaded_poll); @@ -161,15 +163,33 @@ void unregistergame() { avahi_threaded_poll_unlock(threaded_poll); } +static void remove_game_with_name(const char *name) { + int i; + + for (i = 0; i < buffer_glsize; i++) { + if (0 == strcmp(buffer_glentries[i].name, name)) { + /* Remove it */ + buffer_glsize--; + if (i != buffer_glsize) { + buffer_glentries[i] = buffer_glentries[buffer_glsize]; + buffer_glchanged[i] = 1; + } + } + } +} + 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 = *(uint32_t*) userdata; + uint32_t want_version; assert(r); if (protocol != AVAHI_PROTO_INET) goto done; /* ignore non IPv4 for now */ - if (gamelistsize >= GAMELIST_MAXSIZE) goto done; + if (buffer_glsize >= GAMELIST_MAXSIZE) goto done; + + memcpy(&want_version, gameversion, 4); + want_version = htonl(want_version); /* Called whenever a service has been resolved successfully or timed out */ @@ -180,28 +200,26 @@ static void resolve_callback(AvahiServiceResolver *r, AvahiIfIndex interface, Av case AVAHI_RESOLVER_FOUND: { gamelistentry *ge; - unsigned int uniq, version; - int have_unique = 0, have_version = 0; + unsigned int version; + int have_version = 0; AvahiStringList *psl; for (psl = txt ; psl ; psl = psl->next) { - if (0 == strncmp("unique=", (const char*) psl->text, 7)) { - sscanf((const char*) psl->text, "unique=%X", &uniq); - have_unique = 1; - } else if (0 == strncmp("version=", (const char*) psl->text, 8)) { + 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_unique || !have_version) goto done; - i = gamelistsize++; - ge = &gamelistentries[i]; + 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; - ge->name = avahi_strdup(name); - ge->unique = ntohl(uniq); + strncpy(ge->name, name, 15); } } @@ -237,40 +255,31 @@ static void browse_callback(AvahiServiceBrowser *b, AvahiIfIndex interface, Avah break; case AVAHI_BROWSER_REMOVE: + remove_game_with_name(name); break; case AVAHI_BROWSER_ALL_FOR_NOW: - all_for_now = 1; break; case AVAHI_BROWSER_CACHE_EXHAUSTED: - all_for_now = 1; break; } } -static void freefoundgames() { - int i; +static void freefoundgames(void) { + memset(gamelistentries, 0, sizeof(gamelistentries)); + memset(buffer_glentries, 0, sizeof(buffer_glentries)); + memset(buffer_glchanged, 0, sizeof(buffer_glchanged)); - for (i = 0; i < gamelistsize; i++) { - avahi_free(gamelistentries[i].name); - } gamelistsize = 0; + buffer_glsize = 0; } -int searchgames(unsigned char version[4]) { - int i; - AvahiServiceBrowser *sb = NULL; - uint32_t gameversion; - memcpy(&gameversion, version, 4); - gameversion = htonl(gameversion); - +int searchgames(void) { freefoundgames(); avahi_threaded_poll_lock(threaded_poll); - all_for_now = 0; - - if (NULL == (sb = 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, &gameversion))) { fprintf(stderr, "Failed to create service browser: %s\n", avahi_strerror(avahi_client_errno(client))); avahi_threaded_poll_unlock(threaded_poll); return 0; @@ -278,29 +287,40 @@ int searchgames(unsigned char version[4]) { avahi_threaded_poll_unlock(threaded_poll); - for (i = 0; i < 10; i++) { - usleep(200000); - - avahi_threaded_poll_lock(threaded_poll); - - if (all_for_now) { - avahi_service_browser_free(sb); - avahi_threaded_poll_unlock(threaded_poll); - return 1; - } - - avahi_threaded_poll_unlock(threaded_poll); - } - - avahi_threaded_poll_lock(threaded_poll); - avahi_service_browser_free(sb); - avahi_threaded_poll_unlock(threaded_poll); + usleep(200000); + find_more_games(); return 1; } +int find_more_games(void) { + int i, res = 0; -int initannouncer() { + avahi_threaded_poll_lock(threaded_poll); + + for (i = 0; i < buffer_glsize; i++) { + if (!buffer_glchanged[i]) continue; + buffer_glchanged[i] = 0; + gamelistentries[i] = buffer_glentries[i]; + res = 1; + } + if (gamelistsize != buffer_glsize) { + res = 1; + gamelistsize = buffer_glsize; + } + + avahi_threaded_poll_unlock(threaded_poll); + + return res; +} + +void stop_search(void) { + avahi_threaded_poll_lock(threaded_poll); + avahi_service_browser_free(browser); + avahi_threaded_poll_unlock(threaded_poll); +} + +int initannouncer(void) { if (!(threaded_poll = avahi_threaded_poll_new())) { fprintf(stderr, "avahi_threaded_poll_new failed\n"); return 0; @@ -325,7 +345,7 @@ int initannouncer() { return 1; } -void freeannouncer() { +void freeannouncer(void) { freefoundgames(); avahi_threaded_poll_stop(threaded_poll); diff --git a/announce.h b/announce.h index b625ae4..e264242 100644 --- a/announce.h +++ b/announce.h @@ -6,16 +6,18 @@ typedef struct gamelistentry gamelistentry; struct gamelistentry { struct sockaddr_in netname; - char *name; - uint32_t unique; + char name[16]; }; -int registergame(char *playername, uint16_t port, uint32_t unique, unsigned char version[4]); -void unregistergame(); -int searchgames(unsigned char version[4]); +int registergame(const char *playername, uint16_t port, const unsigned char version[4]); +void unregistergame(void); -int initannouncer(); -void freeannouncer(); +int searchgames(void); +int find_more_games(void); +void stop_search(void); + +int initannouncer(void); +void freeannouncer(void); #define GAMELIST_MAXSIZE 10 diff --git a/bomber.c b/bomber.c index 88d45de..9315d19 100644 --- a/bomber.c +++ b/bomber.c @@ -3,1828 +3,22 @@ #include #include #include -#include -#include #include #include #include #include +#include +#include + #include "bomber.h" #include "gfx.h" #include "announce.h" #include "sound.h" -#include -#include - -static void domode(void); -static int iterate(void); -static int scaninvite(int size); - -#define FRACTION 9 -#define MAXMSG 4096 -#define PORT 5521 -#define SPEEDDELTA (1<<(FRACTION-1)) -#define SPEEDMAX (10<>8; - } - p=myrandblock+14; - msk=0xff; - msb=0x80; - do { - *p&=msk; - *p|=msb; - p+=11; - msk>>=1; - msb>>=1; - } while(msk); - i=500; - while(i--) myrand(); - -} - -#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>FRACTION)+arraystartx; -} - -static int tovideoy(int y) { - return (y>>FRACTION)+arraystarty; -} - -#define SGN(x) ((x)==0 ? 0 : ((x)<0 ? -1 : 1)) - -static int screentoarrayx(int x) { - x+=arrayspacex << (FRACTION+2); - return ((x>>FRACTION)+(arrayspacex>>1))/arrayspacex-4; -} - -static int screentoarrayy(int y) { - y+=arrayspacey << (FRACTION+2); - return ((y>>FRACTION)+(arrayspacey>>1))/arrayspacey-4; -} - -static int arraytoscreenx(int x) { - return arrayspacex*x<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 = PKT_STEP; - memmove(msg+1,regpacket+1,4); - msg[5]=actioncount>>24L; - msg[6]=actioncount>>16L; - msg[7]=actioncount>>8L; - msg[8]=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 = PKT_MYDATA; - memmove(msg+1,regpacket+1,4); - msg[5]=frame>>24L; - msg[6]=frame>>16L; - msg[7]=frame>>8L; - msg[8]=frame; - msg[9]=myaction; - putmsg(&mastername,msg,10); -} - -static int informsize; - -static void buildinform(unsigned char type) { - unsigned char *put; - int i; - - put=mesg; - *put++=type; - memmove(put,regpacket+1,4); - put+=4; - ++put; // 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; - memcpy(put, configopts, sizeof(configopts)); - put += sizeof(configopts); - informsize=put-mesg; -} - -static void inform1(int which) { - mesg[5]=which; - putmsg(&netnodes[which].netname,mesg,informsize); -} - -static void inform(unsigned char type) { - int i; - buildinform(type); - for(i=1;i15) break; - if(!netframe && gtime()-now>=2000) { - for(i=1;i0 && *mesg!=PKT_MYDATA) printf("Strange packet %d\n",*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(); - if(whosent<=0) continue; - count=longind(mesg+5); - if(count>latestcounts[whosent]) { - latestcounts[whosent]=count; - newactions[whosent]=mesg[9]; - } - } - if(myaction==ACT_QUIT) { - for(i=1;i=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=longind(mesg+5); - if(igs_pic) free(gs->gs_pic); - gs->gs_pic=0; -} - -static void nomem(char *str) -{ - printf("No memory!!![%s]\n",str); - exit(1); -} - - -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;igs_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 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++; -} - -static 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>1,y,str); -} - -static 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 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((ch=*str++)) - { - drawfigure(xpos,ypos,bigfont+asciiremap[toupper(ch)]); - xpos+=bigfontxsize; - } -} - - -#define IBUFFLEN 1024 -int ileft=0,ihand=0,byteswide; -unsigned char ibuff[IBUFFLEN],*itake; - -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; - -} - -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 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(pxlurd|=FL_RIGHT; - fl2->lurd|=FL_LEFT; - } - if(pylurd|=FL_DOWN; - fl2->lurd|=FL_UP; - } -} - -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 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; - } -} -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; -} -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; - } - } - -} -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 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 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 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; - } -} -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 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 drawdecays() -{ -brickdecay *bd; - - bd=activedecays.next; - while(bd) - { - addsprite(tovideox(bd->xpos),tovideoy(bd->ypos), - blocksx+(bd->timer*9)/DECAYLIFE); - bd=bd->next; - } -} -int bonustotal; -int bonuschances[]= -{ -TILE_BOMB,20, -TILE_FLAME,20, -TILE_CONTROL,2, -TILE_GOLDFLAME,2, -TILE_SKATES,20, -TILE_TURTLE,5, -TILE_NONE,160 -}; -void trybonus(int px,int py) -{ -int i=0, *p,r; - - 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); -} -void deletebonus(bonustile *bonus) -{ -int px,py; - px=bonus->px; - py=bonus->py; - field[py][px]=0; - info[py][px]=0; - delink(&activebonus,bonus); -} -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 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; - } -} -void killplayer(player *pl) -{ - pl->flags|=FLG_DEAD; - playsound(2); - adddeath(pl); - detonatecontrolled(pl); -} -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); -} -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); -} -void playonce(generic *gen) -{ - if(gen->timer==gen->data1) - delink(&activegeneric,gen); -} -void drawgeneric(generic *gen) -{ - addsprite(gen->xpos,gen->ypos,((figure *)(gen->ptr1))+gen->timer); -} - -void processgenerics(void) -{ -generic *gen,*gen2; - gen=activegeneric.next; - while(gen) - { - gen2=gen; - gen=gen->next; - ++(gen2->timer); - gen2->process(gen2); - } -} -void drawgenerics(void) -{ -generic *gen; - gen=activegeneric.next; - while(gen) - { - gen->draw(gen); - gen=gen->next; - } -} - - -static void plotsprites(void) -{ -int i; -sprite *sp; -figure *fig; - - sp=sprites; - for(i=0;ifig; - drawfigure(sp->xpos,sp->ypos,fig); - ++sp; - } -} -static void erasesprites(void) -{ -int i; -sprite *sp; -figure *fig; - - sp=sprites; - for(i=0;ifig; - - solidcopy(&background, - sp->xpos+fig->xdelta,sp->ypos+fig->ydelta, - fig->xsize,fig->ysize); - ++sp; - } -} - -static void clearsprites(void) { - int i; - sprite *sp; - figure *fig; - - sp=sprites; - for(i=0;ifig; - - clearrect(sp->xpos+fig->xdelta,sp->ypos+fig->ydelta, - fig->xsize,fig->ysize); - ++sp; - } -} - -static void clearspritelist(void) { - spritesused=0; -} - - -static int centerxchange(player *pl) { - int speed; - int val; - int line; - int max; - - max=arrayspacex<speed; - val=pl->xpos+(max<<2); - val%=max; - line=max>>1; - if(val=line) { - if(val+speed>max) - return max-val; - else - return speed; - } - return 0; -} - -void centerx(player *pl) { - pl->xpos+=centerxchange(pl); -} - -static int centerychange(player *pl) { - int speed; - int val; - int line; - int max; - - max=arrayspacey<speed; - val=pl->ypos+(max<<2); - val%=max; - line=max>>1; - if(val=line) - { - if(val+speed>max) - return max-val; - else - return speed; - } - return 0; -} - -void centery(player *pl) { - pl->ypos+=centerychange(pl); -} - -static void trymove(player *pl,int dx,int dy) { - int wx,wy; - int sx,sy; - int there; - int px,py; - static int depth=0; - int tx,ty; - - ++depth; - sx=(dx*(arrayspacex+1)) << (FRACTION-1); - sy=(dy*(arrayspacey+1)) << (FRACTION-1); - - wx=screentoarrayx(pl->xpos+sx); - wy=screentoarrayy(pl->ypos+sy); - px=screentoarrayx(pl->xpos); - py=screentoarrayy(pl->ypos); - - if(wx<0 || wx>=arraynumx || wy<0 || wy>=arraynumy) { - --depth; - return; - } - there=field[wy][wx]; - if((px!=wx || py!=wy) && - (there==FIELD_BRICK||there==FIELD_BOMB||there==FIELD_BORDER)) - { - if(dx && !dy) { - ty=centerychange(pl); - if(ty && depth==1) - trymove(pl,0,-SGN(ty)); - - } else if(dy && !dx) { - tx=centerxchange(pl); - if(tx && depth==1) - trymove(pl,-SGN(tx),0); - } - - } else { - pl->xpos+=dx*pl->speed; - pl->ypos+=dy*pl->speed; - if(dx && !dy) centery(pl); - if(dy && !dx) centerx(pl); - } - --depth; -} - - -static void applybonus(player *pl,bonustile *bonus) { - int type; - int maxflame; - - maxflame=arraynumx>arraynumy ? arraynumx : arraynumy; - type=bonus->type; - deletebonus(bonus); - switch(type) - { - case TILE_BOMB: - ++(pl->bombsavailable); - break; - case TILE_FLAME: - if(pl->flamelengthflamelength); - break; - case TILE_GOLDFLAME: - pl->flamelength=maxflame; - break; - case TILE_CONTROL: - pl->flags|=FLG_CONTROL; - break; - case TILE_SKATES: - if (pl->speed < SPEEDSTART) { - pl->speed = SPEEDSTART; - } else { - pl->speed+=SPEEDDELTA; - } - if(pl->speed>SPEEDMAX) pl->speed=SPEEDMAX; - break; - case TILE_TURTLE: - pl->speed=SPEEDTURTLE; - pl->speedturtle_timeout=SPEEDTURTLE_TIMEOUT; - break; - } -} - -static void doplayer(player *pl) { - int last; - int color; - int speed; - int px,py; - int there; - int flags; - int what; - - if(pl->controller==-1) - what=myaction; - else - what=actions[pl->controller]; - - flags=pl->flags; - if(flags&FLG_DEAD) return; - color=pl->color; - last=pl->doing; - pl->doing=what; - speed=pl->speed; - px=screentoarrayx(pl->xpos); - py=screentoarrayy(pl->ypos); - there=field[py][px]; - - if(what==ACT_QUIT) { - killplayer(pl); - return; - } - - if(there==FIELD_BONUS) { - playsound((myrand()&1) ? 1 : 5); - applybonus(pl,info[py][px]); - } else if(there==FIELD_FLAME) { - killplayer(pl); - return; - } - -// if(what&ACT_TURBO) speed<<=2; - if(what&ACT_PRIMARY) { - if(there==FIELD_EMPTY && pl->bombsavailable) - dropbomb(pl,px,py, - (flags&FLG_CONTROL) ? BOMB_CONTROLLED :BOMB_NORMAL); - } - if(what&ACT_SECONDARY && (flags&FLG_CONTROL)) - detonatecontrolled(pl); - - switch(what&ACT_MASK) { - case ACT_UP: - trymove(pl,0,-1); - pl->figcount=(pl->figcount+1)%15; - pl->figure=walking[color]+pl->figcount+15; - break; - case ACT_DOWN: - trymove(pl,0,1); - pl->figcount=(pl->figcount+1)%15; - pl->figure=walking[color]+pl->figcount+30; - break; - case ACT_LEFT: - trymove(pl,-1,0); - pl->figcount=(pl->figcount+1)%15; - pl->figure=walking[color]+pl->figcount+45; - break; - case ACT_RIGHT: - trymove(pl,1,0); - pl->figcount=(pl->figcount+1)%15; - pl->figure=walking[color]+pl->figcount; - break; - case ACT_NONE: - break; - } - - if (pl->speedturtle_timeout > 0) { - pl->speedturtle_timeout--; - if (0 == pl->speedturtle_timeout) { - if (pl->speed < SPEEDSTART) pl->speed = SPEEDSTART; - } - } -} - -static void 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[GO_FLAMES]+1; - pl->bombsavailable=gameoptions[GO_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(xnext=(void *)p; - first=p; - } - ((list *)first)->next=0; -} - -void *allocentry() { -list *entry; - if(!(entry=((list *)things)->next)) return 0; - ((list *)things)->next=entry->next; - memset(entry,0,thingsize); - return entry; -} - -void freeentry(void *entry) { - ((list *)entry)->next=((list *)things)->next; - ((list *)things)->next=entry; -} - -void addtail(void *header,void *entry) { - while(((list *)header)->next) header=((list *)header)->next; - ((list *)header)->next=entry; - ((list *)entry)->next=0; -} - -void delink(void *header,void *entry) { - while(((list *)header)->next != entry) header=((list *)header)->next; - ((list *)header)->next=((list *)entry)->next; - ((list *)entry)->next=0; - freeentry(entry); -} - -static void allocthings() { - if(!things) - { - thingsize=sizeof(bomb); - if((int)sizeof(flame)>thingsize) thingsize=sizeof(flame); - if((int)sizeof(brickdecay)>thingsize) thingsize=sizeof(brickdecay); - if((int)sizeof(player)>thingsize) thingsize=sizeof(player); - thingnum=MAXTHINGS; - things=malloc(thingsize*thingnum); - if(!things) nomem("Trying to allocate thing memory"); - } - initlist(things,thingsize,thingnum); -} - -static void initheader(void *p) { - memset(p,0,sizeof(list)); -} - -static void firstzero(void) { - gountil=mycount=mydatacount=0; - memset(latestactions,0,sizeof(latestactions)); - memset(latestcounts,0,sizeof(latestcounts)); - actionput=actioncount=0; - netframe=0; -} - -static void zerocounts(void) { -} - -static void initgame() { - int i,j; - int x,y; - int bl; - int *p; - int comp; - -// gountil=mycount=mydatacount=netframe=0; - zerocounts(); - if (network != SLAVE) - memmove(gameoptions,configopts,sizeof(gameoptions)); - 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+=*p=64*(3-gameoptions[GO_GENEROSITY]); - memset(field,0,sizeof(field)); - comp=gameoptions[GO_DENSITY]; - for(j=0;j=comp ? FIELD_BRICK : FIELD_EMPTY; - } - - solidcopyany(&backgroundoriginal,&background,0,0,IXSIZE,IYSIZE); - - initplayers(); - - for(j=0;j> 1,20,menutitle); - ty=((IYSIZE-(bigfontysize*menunum))>>1)-(IYSIZE>>3); - for(i=0;i> 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 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=17) { - if((i=*take)=0) - selected=menuhistory[whichmenu]; - else - selected=0; - - redraw=1; - clearspritelist(); - pulseon(); - while(!exitflag) { - if(redraw) { - drawmenu(selected); - redraw=0; - } - mypause(); - 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(); - update(); - - 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(!whichmenu && selected) - { - selected=0; - redraw=1; - break; - } - menudelta=1; - return 0; - } - } - return 0; -} - - -static void domode0(void) { - int sel; - - pulseon(); - 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(0); - 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;} - } -} - - -static void domode1(void) { - int code; - network=0; - - initgame(); - pulseon(); - while(!(code=iterate())) ++framecount; - if(code==CODE_QUIT) gamemode=0; -} - -unsigned char gameoptions[10]; -char *densities[]={"PACKED","HIGH","MEDIUM","LOW"}; -char *generosities[]={"LOW","MEDIUM","HIGH","RIDICULOUS"}; - -static void domode2(void) { - int sel; - - for(;;) { - menustart(); - additem("GAME OPTIONS"); - additem("RETURN TO MAIN MENU"); - additem("DENSITY: %s",densities[configopts[GO_DENSITY]]); - additem("GENEROSITY: %s",generosities[configopts[GO_GENEROSITY]]); - additem("INITIAL FLAME LENGTH: %d",configopts[GO_FLAMES]+1); - additem("INITIAL NUMBER OF BOMBS: %d",configopts[GO_BOMBS]+1); - sel=domenu(2); - if(!sel) {gamemode=0;break;} - if(sel==1) { - configopts[GO_DENSITY]+=menudelta; - configopts[GO_DENSITY]&=3; - } - if(sel==2) { - configopts[GO_GENEROSITY]+=menudelta; - configopts[GO_GENEROSITY]&=3; - } - if(sel==3) { - configopts[GO_FLAMES]+=menudelta; - configopts[GO_FLAMES]&=7; - } - if(sel==4) { - configopts[GO_BOMBS]+=menudelta; - configopts[GO_BOMBS]&=7; - } - } -} - -static void failure(char *str,...) { - gamemode=0; -} - -static void drawmode3(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=21 && *mesg==PKT_JOIN) || (size>=5 && *mesg==PKT_QUIT))) - continue; - if(memcmp(mesg+1,regpacket+1,4)) continue; - j=-1; - for (i=1; i < MAXNETNODES; ++i) { - if(!netnodes[i].used) { - if(j==-1) j=i; - continue; - } - 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; - break; - } - if(*mesg==PKT_QUIT) { - if(inext; - doplayer(pl2); - } -} - -static void processquits(void) { - int i; - - if(network!=SLAVE) return; - for(i=0;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(); - update(); - 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; -} diff --git a/bomber.h b/bomber.h index f3e806b..b3cb963 100644 --- a/bomber.h +++ b/bomber.h @@ -84,27 +84,22 @@ typedef struct player #define FLG_DEAD 2 - -typedef struct sprite -{ +typedef struct sprite { int flags; int xpos,ypos; figure *fig; } sprite; -typedef struct damage -{ +typedef struct damage { int xpos,ypos; int xsize,ysize; } damage; -typedef struct list -{ +typedef struct list { void *next; } list; -typedef struct bomb -{ +typedef struct bomb { struct bomb *next; int type; int xpos,ypos; @@ -123,8 +118,7 @@ typedef struct bomb #define FLAMELIFE 15 #define DECAYLIFE 15 -typedef struct flame -{ +typedef struct flame { struct flame *next; int xpos,ypos; int px,py; @@ -138,16 +132,23 @@ typedef struct flame #define FL_LEFT 1 #define FL_RIGHT 4 -typedef struct brickdecay -{ +typedef struct brickdecay { struct brickdecay *next; int xpos,ypos; int px,py; int timer; } brickdecay; -typedef struct generic -{ +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; @@ -158,13 +159,12 @@ typedef struct generic int data1,data2; } generic; -typedef struct bonustile -{ +typedef struct bonustile { struct bonustile *next; int xpos,ypos; int px,py; int type; -}bonustile; +} bonustile; #define TILE_NONE -1 #define TILE_BOMB 5 @@ -210,122 +210,7 @@ typedef struct bonustile extern char exitflag; -extern player players[]; -extern sprite sprites[]; -extern gfxset gfxsets[NUMGFX]; extern uchar needwhole; extern figure walking[MAXSETS][60]; -extern damage damages[]; -extern bomb bombs[]; -extern void centerx(player *pl); -extern void centery(player *pl); -extern void dropbomb(player *pl,int px,int py,int type); -extern void adddetonate(bomb *bmb); -extern void flameshaft(player *owner,int px,int py,int dx,int dy,int power); -extern void detonatebomb(bomb *bmb); -extern void initlist(void *first,int size,int num); -extern void freeentry(void *entry); -extern void addtail(void *header,void *entry); -extern void delink(void *header,void *entry); -extern void *allocentry(); -extern void adddecay(int px,int py); -extern void trybonus(int px,int py); -extern void addbonus(int px,int py,int type); -extern void deletebonus(bonustile *bonus); -extern void queuesequence(int xpos,int ypos,figure *fig,int count); -extern void playonce(generic *gen); -extern void processgenerics(void); -extern void drawgenerics(void); -extern void drawgeneric(generic *gen); -extern void killplayer(player *pl); -extern void adddeath(player *pl); -extern void drawbigstring(int xpos,int ypos,char *str); -extern void bigscrprintf(char *str, ...); -extern void drawstring(int xpos,int ypos,char *str); -extern void scrprintf(char *str, ...); -extern int textx,texty,fontxsize,fontysize; -extern void texthome(void); - -extern unsigned char field[32][32]; -extern void *info[32][32]; -extern unsigned char singleoptions[10]; -extern unsigned char gameoptions[10]; -extern int getmsg(int); - -#define GO_DENSITY 0 -#define GO_FLAMES 1 -#define GO_BOMBS 2 -#define GO_GENEROSITY 3 - -// network packet types -// slave -> master packets -#define PKT_MYDATA 0 // 4 bytes unique #,4 bytes frame #, 1 byte data -#define PKT_JOIN 1 // 4 bytes unique #,16 bytes name -// master -> slave packets -#define PKT_INVITE 9 // 4 bytes unique #, any # of 1:slot,16:name sets (-1 end) -#define PKT_BEGIN 10 // clone of INVITE -#define PKT_STEP 11 // 4 bytes unique #, 4 bytes frame #, 8 bytes ACT_* -#define PKT_QUIT 12 // 4 bytes unique # -// master -> matcher packets -#define PKT_REGISTER 16 // 4:unique #,4:pword hash,4:version #,16:name, 1:status -// matcher -> master packets -#define PKT_ACK 24 // perfect copy of packet received -// slave -> matcher packets -#define PKT_QUERY 32 // 4 bytes password hash -// matcher -> slave packets -#define PKT_INFO 40 // 4: pword hash, 2: count,#(4:unique,4:IP,2:port,16:name) - -// all bytes stored MSB first - -/* -game startup: - -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: 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: 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. - -*/ - -#endif // BOMBER_H +#endif /* BOMBER_H */ diff --git a/draw.c b/draw.c new file mode 100644 index 0000000..5963e77 --- /dev/null +++ b/draw.c @@ -0,0 +1,448 @@ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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;igs_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;ixpos,sp->ypos,sp->fig); + ++sp; + } +} + +void erasesprites(void) { + int i; + sprite *sp; + figure *fig; + + sp=sprites; + for(i=0;ifig; + + 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;ifig; + + 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<xpos=arraytoscreenx(x); + pl->ypos=arraytoscreeny(y); + pl->color=color; + pl->speed=SPEEDSTART; + pl->flags=0; + pl->fixx=-4; + pl->fixy=-40; + pl->flamelength=gameoptions.flames+1; + pl->bombsavailable=gameoptions.bombs+1; + pl->controller=controller; + field[y][x]=FIELD_EMPTY; + if(x) field[y][x-1]=FIELD_EMPTY; + if(y) field[y-1][x]=FIELD_EMPTY; + if(x=comp ? FIELD_BRICK : FIELD_EMPTY; + } + + solidcopyany(&backgroundoriginal,&background,0,0,IXSIZE,IYSIZE); + + initplayers(); + + for(j=0;jpx=px; + fl->py=py; + fl->xpos=arraytoscreenx(px); + fl->ypos=arraytoscreeny(py); + fl->owner=owner; + if(px && field[py][px-1]==FIELD_FLAME) { + fl2=info[py][px-1]; + fl->lurd|=FL_LEFT; + fl2->lurd|=FL_RIGHT; + } + if(py && field[py-1][px]==FIELD_FLAME) { + fl2=info[py-1][px]; + fl->lurd|=FL_UP; + fl2->lurd|=FL_DOWN; + } + if(pxlurd|=FL_RIGHT; + fl2->lurd|=FL_LEFT; + } + if(pylurd|=FL_DOWN; + fl2->lurd|=FL_UP; + } +} + +static void dropbomb(player *pl,int px,int py,int type){ + bomb *bmb; + + if(field[py][px]!=FIELD_EMPTY) return; + bmb=allocentry(); + if(!bmb) return; + playsound(3); + addtail(&activebombs,bmb); + + --(pl->bombsavailable); + field[py][px]=FIELD_BOMB; + info[py][px]=bmb; + bmb->type=type; + bmb->px=px; + bmb->py=py; + bmb->xpos=arraytoscreenx(px); + bmb->ypos=arraytoscreeny(py); + bmb->power=pl->flamelength; + bmb->owner=pl; +} + +static void adddetonate(bomb *bmb) { + if(bmb->type==BOMB_OFF) return; + + bmb->type=BOMB_OFF; + field[bmb->py][bmb->px]=FIELD_EXPLODING; + info[bmb->py][bmb->px]=0; + detonated[detonateput++]=bmb; + detonateput%=MAXBOMBSDETONATED; +} + +static void processbombs() { + bomb *bmb; + + bmb=activebombs.next; + while(bmb) { + switch(bmb->type) { + case BOMB_NORMAL: + ++bmb->timer; + if(bmb->timer==BOMBLIFE) + adddetonate(bmb); + ++(bmb->figcount); + break; + case BOMB_CONTROLLED: + ++(bmb->figcount); + break; + } + bmb=bmb->next; + } +} + +static void adddecay(int px,int py) { + brickdecay *bd; + int xpos,ypos; + + bd=allocentry(); + if(!bd) return; + field[py][px]=FIELD_EXPLODING; + bd->xpos=arraytoscreenx(px); + bd->ypos=arraytoscreeny(py); + bd->px=px; + bd->py=py; + addtail(&activedecays,bd); + xpos=tovideox(bd->xpos); + ypos=tovideoy(bd->ypos); + solidcopyany(&backgroundoriginal,&background,xpos,ypos, + arrayspacex,arrayspacey); + solidcopy(&background,xpos,ypos,arrayspacex,arrayspacey); +} + +static void addbonus(int px,int py,int type) { + bonustile *bonus; + + bonus=allocentry(); + if(!bonus) return; + addtail(&activebonus,bonus); + bonus->px=px; + bonus->py=py; + bonus->xpos=arraytoscreenx(px); + bonus->ypos=arraytoscreeny(py); + bonus->type=type; + field[py][px]=FIELD_BONUS; + info[py][px]=bonus; +} + +static void trybonus(int px,int py) { + int i=0, r; + const int *p; + + if(field[py][px]!=FIELD_EMPTY) return; + p=bonuschances; + r=myrand()%bonustotal; + while(r>=0) { + i=*p++; + r-=*p++; + } + if(i==TILE_NONE) return; + addbonus(px,py,i); +} + +static void deletebonus(bonustile *bonus) { + int px,py; + + px=bonus->px; + py=bonus->py; + field[py][px]=0; + info[py][px]=0; + delink(&activebonus,bonus); +} + +static void flameshaft(player *owner,int px,int py,int dx,int dy,int power) { + int there; + bomb *bmb; + + while(power--) { + px+=dx; + py+=dy; + if(px<0 || py<0 || px>=arraynumx || py>=arraynumy) break; + there=field[py][px]; + switch(there) { + case FIELD_BOMB: + bmb=info[py][px]; + adddetonate(bmb); + break; + case FIELD_EMPTY: + addflame(owner,px,py); + break; + case FIELD_BRICK: + adddecay(px,py); + power=0; + break; + case FIELD_BONUS: + deletebonus(info[py][px]); + power=0; + break; + case FIELD_BORDER: + case FIELD_EXPLODING: + default: + power=0; + case FIELD_FLAME: + break; + } + } +} + +static void detonatebomb(bomb *bmb) { + int px,py; + int power; + player *owner; + + ++(bmb->owner->bombsavailable); + px=bmb->px; + py=bmb->py; + power=bmb->power; + owner=bmb->owner; + delink(&activebombs,bmb); + addflame(owner,px,py); + flameshaft(owner,px,py,-1,0,power); + flameshaft(owner,px,py,0,-1,power); + flameshaft(owner,px,py,1,0,power); + flameshaft(owner,px,py,0,1,power); +} + +static void dodetonations(void) { + int i=0; + + while(detonatetake!=detonateput) { + ++i; + detonatebomb(detonated[detonatetake]); + detonatetake=(detonatetake+1) % MAXBOMBSDETONATED; + } + if(i) playsound((myrand()&1) ? 0 : 4); +} + +static void processflames(void) { + flame *fl,*fl2; + + fl=activeflames.next; + while(fl) { + ++(fl->timer); + fl=fl->next; + } + fl=activeflames.next; + while(fl) { + if(fl->timer==FLAMELIFE) + { + field[fl->py][fl->px]=FIELD_EMPTY; + info[fl->py][fl->px]=0; + fl2=fl; + fl=fl->next; + delink(&activeflames,fl2); + } else + fl=fl->next; + } +} + +static void processdecays() { + brickdecay *bd,*bd2; + + bd=activedecays.next; + while(bd) { + ++(bd->timer); + if(bd->timer==DECAYLIFE) { + field[bd->py][bd->px]=FIELD_EMPTY; + trybonus(bd->px,bd->py); + bd2=bd; + bd=bd->next; + delink(&activedecays,bd2); + } else + bd=bd->next; + } +} + +static void drawbombs(void) { + int j; + bomb *bmb; + struct figure *figtab; + int color; + int xpos,ypos; + + bmb=activebombs.next; + while(bmb) { + color=bmb->owner->color; + figtab=(bmb->type==BOMB_NORMAL) ? bombs1[color] : bombs2[color]; + j=bmb->figcount % (NUMBOMBFRAMES<<1); + if(j>=NUMBOMBFRAMES) j=(NUMBOMBFRAMES<<1)-j-1; + xpos=tovideox(bmb->xpos); + ypos=tovideoy(bmb->ypos)-3; + + addsprite(xpos,ypos,figtab+j); + bmb=bmb->next; + } +} + +static void drawflames(void) { + flame *fl; + int xpos,ypos; + int color; + int fig; + + fl=activeflames.next; + while(fl) { + color=fl->owner->color; + xpos=tovideox(fl->xpos); + ypos=tovideoy(fl->ypos); + fig=(fl->timer*10)/FLAMELIFE; + if(fig>=5) fig=9-fig; + fig+=5*fl->lurd; + addsprite(xpos,ypos,flamefigs[0/* color */]+fig); + fl=fl->next; + } +} + +static void drawdecays() { + brickdecay *bd; + + bd=activedecays.next; + while(bd) { + addsprite(tovideox(bd->xpos),tovideoy(bd->ypos), + blocksx+(bd->timer*9)/DECAYLIFE); + bd=bd->next; + } +} + +static void drawbonus() { + bonustile *bonus; + + bonus=activebonus.next; + while(bonus) { + addsprite(tovideox(bonus->xpos),tovideoy(bonus->ypos), + tiles+bonus->type); + bonus=bonus->next; + } +} + +static void drawplayers() { + player *pl; + int xpos,ypos; + + pl=activeplayers.next; + + while(pl) { + if(!(pl->flags & FLG_DEAD)) { + if(!pl->figure) + pl->figure=walking[pl->color]+30; + xpos=tovideox(pl->xpos)+pl->fixx; + ypos=tovideoy(pl->ypos)+pl->fixy; + addsprite(xpos,ypos,pl->figure); + } + pl=pl->next; + } +} + +static void detonatecontrolled(player *pl) { + bomb *bmb; + + bmb=activebombs.next; + while(bmb) { + if(bmb->owner==pl && bmb->type==BOMB_CONTROLLED) + adddetonate(bmb); + bmb=bmb->next; + } +} + +static void playonce(generic *gen) { + if(gen->timer==gen->data1) + delink(&activegeneric,gen); +} + +static void drawgeneric(generic *gen) { + addsprite(gen->xpos,gen->ypos,((figure *)(gen->ptr1))+gen->timer); +} + +static void queuesequence(int xpos,int ypos,figure *fig,int count) { + generic *gen; + + gen=allocentry(); + if(!gen) return; + gen->xpos=xpos; + gen->ypos=ypos; + gen->data1=count; + gen->process=playonce; + gen->draw=drawgeneric; + gen->ptr1=fig; + addtail(&activegeneric,gen); +} + +static void adddeath(player *pl) { + int xpos,ypos; + + xpos=tovideox(pl->xpos)+pl->fixx-10; + ypos=tovideoy(pl->ypos)+pl->fixy; + queuesequence(xpos,ypos,death,NUMDEATHFRAMES); +} + +static void killplayer(player *pl) { + pl->flags|=FLG_DEAD; + playsound(2); + adddeath(pl); + detonatecontrolled(pl); +} + +static void processgenerics(void) { + generic *gen,*gen2; + + gen=activegeneric.next; + while(gen) { + gen2=gen; + gen=gen->next; + ++(gen2->timer); + gen2->process(gen2); + } +} + +static void drawgenerics(void) { + generic *gen; + + gen=activegeneric.next; + while(gen) { + gen->draw(gen); + gen=gen->next; + } +} + +static int centerxchange(player *pl) { + int speed; + int val; + int line; + int max; + + max=arrayspacex<speed; + val=pl->xpos+(max<<2); + val%=max; + line=max>>1; + if(val=line) { + if(val+speed>max) + return max-val; + else + return speed; + } + return 0; +} + +static void centerx(player *pl) { + pl->xpos+=centerxchange(pl); +} + +static int centerychange(player *pl) { + int speed; + int val; + int line; + int max; + + max=arrayspacey<speed; + val=pl->ypos+(max<<2); + val%=max; + line=max>>1; + if(val=line) + { + if(val+speed>max) + return max-val; + else + return speed; + } + return 0; +} + +static void centery(player *pl) { + pl->ypos+=centerychange(pl); +} + +#define SGN(x) ((x)==0 ? 0 : ((x)<0 ? -1 : 1)) + +static void trymove(player *pl,int dx,int dy) { + int wx,wy; + int sx,sy; + int there; + int px,py; + static int depth=0; + int tx,ty; + + ++depth; + sx=(dx*(arrayspacex+1)) << (FRACTION-1); + sy=(dy*(arrayspacey+1)) << (FRACTION-1); + + wx=screentoarrayx(pl->xpos+sx); + wy=screentoarrayy(pl->ypos+sy); + px=screentoarrayx(pl->xpos); + py=screentoarrayy(pl->ypos); + + if(wx<0 || wx>=arraynumx || wy<0 || wy>=arraynumy) { + --depth; + return; + } + there=field[wy][wx]; + if((px!=wx || py!=wy) && + (there==FIELD_BRICK||there==FIELD_BOMB||there==FIELD_BORDER)) + { + if(dx && !dy) { + ty=centerychange(pl); + if(ty && depth==1) + trymove(pl,0,-SGN(ty)); + + } else if(dy && !dx) { + tx=centerxchange(pl); + if(tx && depth==1) + trymove(pl,-SGN(tx),0); + } + + } else { + pl->xpos+=dx*pl->speed; + pl->ypos+=dy*pl->speed; + if(dx && !dy) centery(pl); + if(dy && !dx) centerx(pl); + } + --depth; +} + + +static void applybonus(player *pl,bonustile *bonus) { + int type; + int maxflame; + + maxflame=arraynumx>arraynumy ? arraynumx : arraynumy; + type=bonus->type; + deletebonus(bonus); + switch(type) + { + case TILE_BOMB: + ++(pl->bombsavailable); + break; + case TILE_FLAME: + if(pl->flamelengthflamelength); + break; + case TILE_GOLDFLAME: + pl->flamelength=maxflame; + break; + case TILE_CONTROL: + pl->flags|=FLG_CONTROL; + break; + case TILE_SKATES: + if (pl->speed < SPEEDSTART) { + pl->speed = SPEEDSTART; + } else { + pl->speed+=SPEEDDELTA; + } + if(pl->speed>SPEEDMAX) pl->speed=SPEEDMAX; + break; + case TILE_TURTLE: + pl->speed=SPEEDTURTLE; + pl->speedturtle_timeout=SPEEDTURTLE_TIMEOUT; + break; + } +} + +static void doplayer(player *pl) { + int last; + int color; + int speed; + int px,py; + int there; + int flags; + int what; + + if(pl->controller==-1) + what=myaction; + else + what=actions[pl->controller]; + + flags=pl->flags; + if(flags&FLG_DEAD) return; + color=pl->color; + last=pl->doing; + pl->doing=what; + speed=pl->speed; + px=screentoarrayx(pl->xpos); + py=screentoarrayy(pl->ypos); + there=field[py][px]; + + if(what==ACT_QUIT) { + killplayer(pl); + return; + } + + if(there==FIELD_BONUS) { + playsound((myrand()&1) ? 1 : 5); + applybonus(pl,info[py][px]); + } else if(there==FIELD_FLAME) { + killplayer(pl); + return; + } + +// if(what&ACT_TURBO) speed<<=2; + if(what&ACT_PRIMARY) { + if(there==FIELD_EMPTY && pl->bombsavailable) + dropbomb(pl,px,py, + (flags&FLG_CONTROL) ? BOMB_CONTROLLED :BOMB_NORMAL); + } + if(what&ACT_SECONDARY && (flags&FLG_CONTROL)) + detonatecontrolled(pl); + + switch(what&ACT_MASK) { + case ACT_UP: + trymove(pl,0,-1); + pl->figcount=(pl->figcount+1)%15; + pl->figure=walking[color]+pl->figcount+15; + break; + case ACT_DOWN: + trymove(pl,0,1); + pl->figcount=(pl->figcount+1)%15; + pl->figure=walking[color]+pl->figcount+30; + break; + case ACT_LEFT: + trymove(pl,-1,0); + pl->figcount=(pl->figcount+1)%15; + pl->figure=walking[color]+pl->figcount+45; + break; + case ACT_RIGHT: + trymove(pl,1,0); + pl->figcount=(pl->figcount+1)%15; + pl->figure=walking[color]+pl->figcount; + break; + case ACT_NONE: + break; + } + + if (pl->speedturtle_timeout > 0) { + pl->speedturtle_timeout--; + if (0 == pl->speedturtle_timeout) { + if (pl->speed < SPEEDSTART) pl->speed = SPEEDSTART; + } + } +} + +static void processplayers(void) { + player *pl,*pl2; + + pl=activeplayers.next; + while(pl) { + pl2=pl; + pl=pl->next; + doplayer(pl2); + } +} + +static void processquits(void) { + int i; + + if (network != NETWORK_SLAVE) return; + for (i = 0; i < MAXNETNODES; ++i) { + if (netnodes[i].used && actions[i]==ACT_QUIT) + netnodes[i].used=0; + } +} + +static int getaction(void) { + int what; + + what=ACT_NONE; + if(checkpressed(MYLEFT)) what=ACT_LEFT; + else if(checkpressed(MYRIGHT)) what=ACT_RIGHT; + else if(checkpressed(MYDOWN)) what=ACT_DOWN; + else if(checkpressed(MYUP)) what=ACT_UP; + else if(checkdown(13)) what=ACT_ENTER; + else if(checkdown(0x1b)) what=ACT_QUIT; + + if(checkdown(' ')) + what|=ACT_PRIMARY; + if(checkdown('b')) + what|=ACT_SECONDARY; + + return what; +} + +int iterate(void) { + int i; + static int deathcount=0; + + mypause(); + scaninput(); + + erasesprites(); + clearspritelist(); + gfxunlock(); + + myaction=getaction(); + if(!network && myaction==ACT_QUIT) return CODE_QUIT; + i=networktraffic(); + if(i<0) + gountil=mycount+1; + else + gountil=i; + + while(mycount=ACTIONHIST) // too far behind + return CODE_QUIT; + memcpy(actions,actionblock+i*MAXNETNODES,MAXNETNODES); + if(actions[myslot]==ACT_QUIT) return CODE_QUIT; + } + processbombs(); + dodetonations(); + processdecays(); + processflames(); + processgenerics(); + processquits(); + processplayers(); + } + +/* + if(!(rand()&127)) + { + i=gtime(); + while(gtime()-i<100); + } +*/ + + drawbombs(); + drawbonus(); + drawgenerics(); + drawdecays(); + drawflames(); + drawplayers(); + plotsprites(); + copyup(); + if(!activegeneric.next) { + player *pl; + int deadplayers = 0; + pl=activeplayers.next; + i=0; + while(pl) { + if(!(pl->flags & FLG_DEAD)) + ++i; + else + deadplayers++; + pl=pl->next; + } + if (deadplayers > 0 && (!i || (network && i==1))) { + ++deathcount; + if(deathcount==25) + return CODE_ALLDEAD; + } else + deathcount=0; + } + return CODE_CONT; +} + +void set_game_options(GameOptions *options) { + gameoptions = *options; +} diff --git a/game.h b/game.h new file mode 100644 index 0000000..f66409d --- /dev/null +++ b/game.h @@ -0,0 +1,28 @@ +#ifndef GAME_H +#define GAME_H + +#define FRACTION 9 +#define SPEEDDELTA (1<<(FRACTION-1)) +#define SPEEDMAX (10<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)); +} + diff --git a/list.h b/list.h new file mode 100644 index 0000000..b2a628f --- /dev/null +++ b/list.h @@ -0,0 +1,12 @@ +#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 diff --git a/menu.c b/menu.c new file mode 100644 index 0000000..2675245 --- /dev/null +++ b/menu.c @@ -0,0 +1,404 @@ + +#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> 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 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: 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: 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: 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>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;i0) { + 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=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= 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; + } +} diff --git a/network.h b/network.h new file mode 100644 index 0000000..3fa5628 --- /dev/null +++ b/network.h @@ -0,0 +1,56 @@ +#ifndef NETWORK_H +#define NETWORK_H + +#include +#include +#include + +struct netnode { + struct sockaddr_in netname; + char name[16]; + char used; +}; + +extern struct netnode netnodes[64]; + +void getsocket(void); +void freesocket(void); + +int send_join(struct sockaddr_in *netname, char playername[16]); +void send_config(); +void send_quit(); +int scaninvite(int msec); + +int start_network_game(); +int handle_joins(); +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; +extern network_type network; + +/* actions */ +int networktraffic(void); + +#define ACTIONHIST 20 + +extern int mydatacount; +extern int myslot; +extern int actionput,actioncount; +extern unsigned char actionblock[ACTIONHIST*MAXNETNODES]; + +extern int myaction; +extern unsigned char actions[MAXNETNODES]; + +extern unsigned char latestactions[MAXNETNODES]; +extern long latestcounts[MAXNETNODES]; + +#endif diff --git a/sound.c b/sound.c index 50ce9db..4ca5598 100644 --- a/sound.c +++ b/sound.c @@ -7,6 +7,10 @@ #include "sound.h" +#ifndef DATADIR +#define DATADIR "data" +#endif + static char dirlist[]=DATADIR; static int readsound(int num); diff --git a/sound.h b/sound.h index d3ae16c..ae173c0 100644 --- a/sound.h +++ b/sound.h @@ -1,10 +1,6 @@ #ifndef SOUND_H #define SOUND_H -#ifndef DATADIR -#define DATADIR "data" -#endif - int soundopen(void); void soundclose(void); void playsound(int n); diff --git a/utils.c b/utils.c new file mode 100644 index 0000000..a9e391a --- /dev/null +++ b/utils.c @@ -0,0 +1,240 @@ + +#include "bomber.h" +#include "utils.h" +#include "gfx.h" + +#include + +#include +#include +#include +#include + +int volatile hc=0; +char volatile interrupted=0; +static Uint32 cur_unique; +Uint32 network_unique; + +Uint32 gtime(void) { + return SDL_GetTicks(); +} + +Uint32 longtime(void) { + 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)) +static void surf(Uint32 out[8], const Uint32 in[12], const Uint32 seed[32]) { + Uint32 t[12], x, sum = 0; + int r, i, loop; + for (i = 0; i < 12; ++i) t[i] = in[i] ^ seed[12 + i]; + for (i = 0; i < 8; ++i) out[i] = seed[24 + i]; + x = t[11]; + 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); + } + for (i = 0; i < 8; ++i) out[i] ^= t[i+4]; + } +} +#undef ROT +#undef MUSH + +static Uint32 surf_seed[32]; +static Uint32 surf_in[12], surf_out[8]; +static int surf_left; + +static Uint32 surf_init(void) { + Uint32 unique = 0; + int fd; + + memset(surf_in, 0, sizeof(surf_in)); + memset(surf_out, 0, sizeof(surf_out)); + memset(surf_seed, 0, sizeof(surf_seed)); + surf_left = 0; + + fd = open("/dev/urandom", O_RDONLY); + 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); + memcpy(surf_seed, genseed[0], 32); + memcpy(surf_seed, genseed[1], 32); + memcpy(surf_seed, genseed[2], 32); + memcpy(surf_seed, genseed[3], 32); + surf_in[0] = gtime(); + surf(genseed[0], surf_in, surf_seed); + surf_in[0] = 0; + unique = genseed[0][0]; + } else { + read(fd, &unique, sizeof(unique)); + read(fd, &surf_seed, sizeof(surf_seed)); + close(fd); + } + + return unique; +} + +static Uint32 surf_random(void) { + if (surf_left == 0) { + int 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) { + int i; + + memset(surf_in, 0, sizeof(surf_in)); + memset(surf_out, 0, sizeof(surf_out)); + surf_left = 0; + + memcpy(&surf_seed, buf, sizeof(surf_seed)); + 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) { + int i; + + for (i = 0; i < 32; i++) { + Uint32 l = htonl(surf_seed[i]); + memcpy(buf, &l, sizeof(l)); + buf += sizeof(l); + } + memcpy(buf, &network_unique, sizeof(network_unique)); + buf += sizeof(network_unique); + return buf; +} + +void create_seed_unique(void) { + cur_unique = surf_init(); + network_unique = htonl(cur_unique); +} + +int myrand(void) { + return surf_random() & 0xffffu; +} + +#if 0 +/* random generator */ + +#define TAP1 250 +#define TAP2 103 +/* +#define TAP1 55 +#define TAP2 31 +*/ + +static unsigned char myrandblock[TAP1]; +static int myrandtake; + +static int myrand1(void) { + int i; + int val; + + i=myrandtake-TAP2; + if(i<0) i+=TAP1; + val=myrandblock[myrandtake++]^=myrandblock[i]; + if(myrandtake==TAP1) myrandtake=0; + return val; +} + +int myrand(void) { + int v; + v=myrand1(); + return (v<<8) | myrand1(); +} + +static void initmyrand(Uint32 unique) { + int i,j; + unsigned char *p; + int msb,msk; + + myrandtake=0; + p=myrandblock; + j=12345 ^ unique; + i=TAP1; + while(i--) { + j=(j*1277)&0xffff; + *p++=j>>8; + } + p=myrandblock+14; + msk=0xff; + msb=0x80; + do { + *p&=msk; + *p|=msb; + p+=11; + msk>>=1; + msb>>=1; + } while(msk); + i=500; + while(i--) myrand(); +} + +void create_unique(void) { + set_unique(gtime()); +} + +void set_unique(Uint32 unique) { + cur_unique = unique; + network_unique = htonl(cur_unique); + initmyrand(cur_unique); +} + +Uint32 get_unique(void) { + return cur_unique; +} +#endif + +void nomem(char *str) { + printf("No memory!!![%s]\n",str); + exit(1); +} + +int mypause(void) { + while(!interrupted) { + pollinput(); + SDL_Delay(1); + } + interrupted=0; + return 1; +} + +static Uint32 sdlhandler(Uint32 time) { +#if defined (SDL_LATENCY) + outmsgs(); +#endif + interrupted=1; + hc++; + return time; +} + +void pulseon(void) { + SDL_SetTimer(40,sdlhandler); +} + +void hexdump(unsigned char *p, int len) { + int i; + for (i = 0; i < len; i++) { + if (15 == len % 16) + fprintf(stderr, "0x%X\n", p[i]); + else + fprintf(stderr, "0x%X ", p[i]); + } + if (i % 16) + fprintf(stderr, "\n"); +} diff --git a/utils.h b/utils.h new file mode 100644 index 0000000..45b69b8 --- /dev/null +++ b/utils.h @@ -0,0 +1,33 @@ +#ifndef UTILS_H +#define UTILS_H + +Uint32 gtime(void); +Uint32 longtime(void); + +#if 0 +void create_unique(void); +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); +void create_seed_unique(void); + +extern Uint32 network_unique; + +int myrand(void); + +void nomem(char *str); + +int mypause(void); +void pulseon(void); + +void hexdump(unsigned char *p, int len); + +extern volatile int hc; +extern volatile char interrupted; + + +#endif \ No newline at end of file