Merge branch 'upstream'

debian
Stefan 13 years ago
commit 599480e81c
  1. 24
      Makefile
  2. 17
      Makefile.osx
  3. 4
      Makefile.sdldemoswin32
  4. 34
      README
  5. 33
      TODO
  6. 124
      announce.c
  7. 16
      announce.h
  8. 2433
      bomber.c
  9. 155
      bomber.h
  10. 448
      draw.c
  11. 38
      draw.h
  12. 943
      game.c
  13. 28
      game.h
  14. 62
      list.c
  15. 12
      list.h
  16. 404
      menu.c
  17. 10
      menu.h
  18. 735
      network.c
  19. 56
      network.h
  20. 4
      sound.c
  21. 4
      sound.h
  22. 240
      utils.c
  23. 33
      utils.h

@ -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
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
announce.o: announce.c announce.h
utils.o: utils.c bomber.h utils.h gfx.h
clean:
rm -f *.o matcher sdlbomber

17
Makefile.osx vendored

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

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

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

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

@ -1,5 +1,6 @@
#include "announce.h"
#include "network.h"
#include <time.h>
#include <stdio.h>
@ -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);
usleep(200000);
find_more_games();
avahi_threaded_poll_lock(threaded_poll);
return 1;
}
if (all_for_now) {
avahi_service_browser_free(sb);
avahi_threaded_poll_unlock(threaded_poll);
return 1;
}
int find_more_games(void) {
int i, res = 0;
avahi_threaded_poll_unlock(threaded_poll);
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_lock(threaded_poll);
avahi_service_browser_free(sb);
avahi_threaded_poll_unlock(threaded_poll);
return 1;
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() {
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);

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

2433
bomber.c

File diff suppressed because it is too large Load Diff

@ -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 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.
*/
#endif // BOMBER_H
#endif /* BOMBER_H */

448
draw.c

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

@ -0,0 +1,38 @@
#ifndef DRAW_H
#define DRAW_H
void loadgfx(void);
void drawbigstring(int xpos,int ypos,char *str);
void centerbig(int y,char *str);
void addsprite(int x,int y,figure *fig);
void plotsprites(void);
void erasesprites(void);
void clearsprites(void);
void clearspritelist(void);
int tovideox(int x);
int tovideoy(int y);
int screentoarrayx(int x);
int screentoarrayy(int y);
int arraytoscreenx(int x);
int arraytoscreeny(int y);
extern int bigfontxsize,bigfontysize,bigfontyspace;
/* On screen array variables */
extern int arraynumx, arraynumy, arraystartx, arraystarty, arrayspacex, arrayspacey;
/* Animation specific #defines */
#define NUMBOMBFRAMES 10
#define NUMWALKFRAMES 60
#define NUMFLAMEFRAMES 80
#define NUMDEATHFRAMES 41
extern figure blocks[3], blocksx[9], bombs1[MAXSETS][NUMBOMBFRAMES], bombs2[MAXSETS][NUMBOMBFRAMES], flamefigs[MAXSETS][NUMFLAMEFRAMES], tiles[15], death[NUMDEATHFRAMES];
#endif

943
game.c

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