Merge branch 'upstream'
This commit is contained in:
commit
599480e81c
24
Makefile
24
Makefile
@ -1,4 +1,4 @@
|
|||||||
#DBG = -g
|
DBG = -g
|
||||||
CC = gcc
|
CC = gcc
|
||||||
WARNFLAGS = -Wall -Wmissing-declarations -Wdeclaration-after-statement -Wcast-align -Winline -Wsign-compare -Wnested-externs -Wpointer-arith -Wformat-security
|
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)
|
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
|
.PHONY: all clean install
|
||||||
all: sdlbomber
|
all: sdlbomber
|
||||||
|
|
||||||
sdlbomber: bomber.o gfx.o sound.o announce.o
|
sdlbomber: announce.o bomber.o draw.o game.o gfx.o sound.o list.o network.o menu.o utils.o
|
||||||
gcc -o sdlbomber bomber.o gfx.o sound.o announce.o $(shell sdl-config --libs) -lavahi-common -lavahi-client $(DBG)
|
gcc -o $@ $^ $(shell sdl-config --libs) -lavahi-common -lavahi-client $(DBG)
|
||||||
|
|
||||||
matcher: matcher.c
|
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
|
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:
|
clean:
|
||||||
rm -f *.o matcher sdlbomber
|
rm -f *.o matcher sdlbomber
|
||||||
|
15
Makefile.osx
15
Makefile.osx
@ -3,23 +3,24 @@
|
|||||||
CC = gcc
|
CC = gcc
|
||||||
CFLAGS = -O2 -Wall -I/Library/Frameworks/SDL.framework/Headers $(DBG)
|
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
|
all: sdlbomber
|
||||||
bomber: gfx.o bomber.o sound.o SDLMain.o
|
sdlbomber: gfx.o bomber.o sound.o SDLMain.o announce.o
|
||||||
|
|
||||||
|
|
||||||
SDLMain.o: SDLmain.m
|
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
|
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:
|
clean:
|
||||||
rm -f *.o bomber
|
rm -f *.o sdlbomber matcher
|
||||||
|
|
||||||
test: all
|
test: all
|
||||||
./bomber
|
./sdlbomber
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
# Makefile for bomber
|
# Makefile for bomber
|
||||||
#
|
#
|
||||||
|
|
||||||
TARGET = bomber
|
TARGET = sdlbomber
|
||||||
USEINET = true
|
USEINET = true
|
||||||
|
|
||||||
include ../GNUmake
|
include ../GNUmake
|
||||||
|
|
||||||
$(TARGET): bomber.o gfx.o sound.o
|
$(TARGET): bomber.o gfx.o sound.o announce.o
|
||||||
|
34
README
34
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...
|
case the only thing to avoid is accidentally killing yourself. Big deal...
|
||||||
It's really a multiplayer game.
|
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
|
Direct comments, complaints and questions to dash@xdr.com
|
||||||
This code is GPL.
|
This code is GPL.
|
||||||
|
33
TODO
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.
|
132
announce.c
132
announce.c
@ -1,5 +1,6 @@
|
|||||||
|
|
||||||
#include "announce.h"
|
#include "announce.h"
|
||||||
|
#include "network.h"
|
||||||
|
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
@ -22,13 +23,16 @@
|
|||||||
static AvahiClient *client = NULL;
|
static AvahiClient *client = NULL;
|
||||||
static AvahiThreadedPoll *threaded_poll = NULL;
|
static AvahiThreadedPoll *threaded_poll = NULL;
|
||||||
static AvahiEntryGroup *group = NULL;
|
static AvahiEntryGroup *group = NULL;
|
||||||
|
static AvahiServiceBrowser *browser = NULL;
|
||||||
|
|
||||||
static char* name = NULL;
|
static char* name = NULL;
|
||||||
|
|
||||||
static uint16_t port = 0;
|
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];
|
gamelistentry gamelistentries[10];
|
||||||
int gamelistsize = 0;
|
int gamelistsize = 0;
|
||||||
@ -82,10 +86,9 @@ static void create_services(AvahiClient *c) {
|
|||||||
|
|
||||||
again:
|
again:
|
||||||
if (avahi_entry_group_is_empty(group)) {
|
if (avahi_entry_group_is_empty(group)) {
|
||||||
char buf_unique[128], buf_version[128];
|
char buf_version[128];
|
||||||
snprintf(buf_unique, sizeof(buf_unique), "unique=%X", (unsigned int) unique);
|
|
||||||
snprintf(buf_version, sizeof(buf_version), "version=%X", (unsigned int) version);
|
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)
|
if (ret == AVAHI_ERR_COLLISION)
|
||||||
goto 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);
|
if (name) avahi_free(name);
|
||||||
name = avahi_strdup(playername);
|
name = avahi_strdup(playername);
|
||||||
|
|
||||||
port = p;
|
port = p;
|
||||||
unique = htonl(uniq);
|
|
||||||
memcpy(&version, v, 4);
|
memcpy(&version, v, 4);
|
||||||
version = htonl(version);
|
version = htonl(version);
|
||||||
|
|
||||||
@ -152,7 +154,7 @@ int registergame(char *playername, uint16_t p, uint32_t uniq, unsigned char v[4]
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void unregistergame() {
|
void unregistergame(void) {
|
||||||
port = 0;
|
port = 0;
|
||||||
|
|
||||||
avahi_threaded_poll_lock(threaded_poll);
|
avahi_threaded_poll_lock(threaded_poll);
|
||||||
@ -161,15 +163,33 @@ void unregistergame() {
|
|||||||
avahi_threaded_poll_unlock(threaded_poll);
|
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,
|
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,
|
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) {
|
uint16_t port, AvahiStringList *txt, AvahiLookupResultFlags flags, void* userdata) {
|
||||||
int i;
|
int i;
|
||||||
uint32_t want_version = *(uint32_t*) userdata;
|
uint32_t want_version;
|
||||||
assert(r);
|
assert(r);
|
||||||
|
|
||||||
if (protocol != AVAHI_PROTO_INET) goto done; /* ignore non IPv4 for now */
|
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 */
|
/* 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: {
|
case AVAHI_RESOLVER_FOUND: {
|
||||||
gamelistentry *ge;
|
gamelistentry *ge;
|
||||||
unsigned int uniq, version;
|
unsigned int version;
|
||||||
int have_unique = 0, have_version = 0;
|
int have_version = 0;
|
||||||
AvahiStringList *psl;
|
AvahiStringList *psl;
|
||||||
for (psl = txt ; psl ; psl = psl->next) {
|
for (psl = txt ; psl ; psl = psl->next) {
|
||||||
if (0 == strncmp("unique=", (const char*) psl->text, 7)) {
|
if (0 == strncmp("version=", (const char*) psl->text, 8)) {
|
||||||
sscanf((const char*) psl->text, "unique=%X", &uniq);
|
|
||||||
have_unique = 1;
|
|
||||||
} else if (0 == strncmp("version=", (const char*) psl->text, 8)) {
|
|
||||||
sscanf((const char*) psl->text, "version=%X", &version);
|
sscanf((const char*) psl->text, "version=%X", &version);
|
||||||
if (version != want_version) goto done; /* version mismatch */
|
if (version != want_version) goto done; /* version mismatch */
|
||||||
have_version = 1;
|
have_version = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!have_unique || !have_version) goto done;
|
if (!have_version) goto done;
|
||||||
i = gamelistsize++;
|
remove_game_with_name(name);
|
||||||
ge = &gamelistentries[i];
|
i = buffer_glsize++;
|
||||||
|
ge = &buffer_glentries[i];
|
||||||
|
buffer_glchanged[i] = 1;
|
||||||
memset(ge, 0, sizeof(*ge));
|
memset(ge, 0, sizeof(*ge));
|
||||||
ge->netname.sin_addr.s_addr = address->data.ipv4.address;
|
ge->netname.sin_addr.s_addr = address->data.ipv4.address;
|
||||||
ge->netname.sin_family = AF_INET;
|
ge->netname.sin_family = AF_INET;
|
||||||
ge->netname.sin_port = port;
|
ge->netname.sin_port = port;
|
||||||
ge->name = avahi_strdup(name);
|
strncpy(ge->name, name, 15);
|
||||||
ge->unique = ntohl(uniq);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -237,40 +255,31 @@ static void browse_callback(AvahiServiceBrowser *b, AvahiIfIndex interface, Avah
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case AVAHI_BROWSER_REMOVE:
|
case AVAHI_BROWSER_REMOVE:
|
||||||
|
remove_game_with_name(name);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case AVAHI_BROWSER_ALL_FOR_NOW:
|
case AVAHI_BROWSER_ALL_FOR_NOW:
|
||||||
all_for_now = 1;
|
|
||||||
break;
|
break;
|
||||||
case AVAHI_BROWSER_CACHE_EXHAUSTED:
|
case AVAHI_BROWSER_CACHE_EXHAUSTED:
|
||||||
all_for_now = 1;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void freefoundgames() {
|
static void freefoundgames(void) {
|
||||||
int i;
|
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;
|
gamelistsize = 0;
|
||||||
|
buffer_glsize = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int searchgames(unsigned char version[4]) {
|
int searchgames(void) {
|
||||||
int i;
|
|
||||||
AvahiServiceBrowser *sb = NULL;
|
|
||||||
uint32_t gameversion;
|
|
||||||
memcpy(&gameversion, version, 4);
|
|
||||||
gameversion = htonl(gameversion);
|
|
||||||
|
|
||||||
freefoundgames();
|
freefoundgames();
|
||||||
|
|
||||||
avahi_threaded_poll_lock(threaded_poll);
|
avahi_threaded_poll_lock(threaded_poll);
|
||||||
|
|
||||||
all_for_now = 0;
|
if (NULL == (browser = avahi_service_browser_new(client, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, SERVICE_TYPE, NULL, 0, browse_callback, &gameversion))) {
|
||||||
|
|
||||||
if (NULL == (sb = 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)));
|
fprintf(stderr, "Failed to create service browser: %s\n", avahi_strerror(avahi_client_errno(client)));
|
||||||
avahi_threaded_poll_unlock(threaded_poll);
|
avahi_threaded_poll_unlock(threaded_poll);
|
||||||
return 0;
|
return 0;
|
||||||
@ -278,29 +287,40 @@ int searchgames(unsigned char version[4]) {
|
|||||||
|
|
||||||
avahi_threaded_poll_unlock(threaded_poll);
|
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);
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
return 1;
|
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())) {
|
if (!(threaded_poll = avahi_threaded_poll_new())) {
|
||||||
fprintf(stderr, "avahi_threaded_poll_new failed\n");
|
fprintf(stderr, "avahi_threaded_poll_new failed\n");
|
||||||
return 0;
|
return 0;
|
||||||
@ -325,7 +345,7 @@ int initannouncer() {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void freeannouncer() {
|
void freeannouncer(void) {
|
||||||
freefoundgames();
|
freefoundgames();
|
||||||
|
|
||||||
avahi_threaded_poll_stop(threaded_poll);
|
avahi_threaded_poll_stop(threaded_poll);
|
||||||
|
16
announce.h
16
announce.h
@ -6,16 +6,18 @@
|
|||||||
typedef struct gamelistentry gamelistentry;
|
typedef struct gamelistentry gamelistentry;
|
||||||
struct gamelistentry {
|
struct gamelistentry {
|
||||||
struct sockaddr_in netname;
|
struct sockaddr_in netname;
|
||||||
char *name;
|
char name[16];
|
||||||
uint32_t unique;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
int registergame(char *playername, uint16_t port, uint32_t unique, unsigned char version[4]);
|
int registergame(const char *playername, uint16_t port, const unsigned char version[4]);
|
||||||
void unregistergame();
|
void unregistergame(void);
|
||||||
int searchgames(unsigned char version[4]);
|
|
||||||
|
|
||||||
int initannouncer();
|
int searchgames(void);
|
||||||
void freeannouncer();
|
int find_more_games(void);
|
||||||
|
void stop_search(void);
|
||||||
|
|
||||||
|
int initannouncer(void);
|
||||||
|
void freeannouncer(void);
|
||||||
|
|
||||||
#define GAMELIST_MAXSIZE 10
|
#define GAMELIST_MAXSIZE 10
|
||||||
|
|
||||||
|
153
bomber.h
153
bomber.h
@ -84,27 +84,22 @@ typedef struct player
|
|||||||
#define FLG_DEAD 2
|
#define FLG_DEAD 2
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct sprite {
|
||||||
typedef struct sprite
|
|
||||||
{
|
|
||||||
int flags;
|
int flags;
|
||||||
int xpos,ypos;
|
int xpos,ypos;
|
||||||
figure *fig;
|
figure *fig;
|
||||||
} sprite;
|
} sprite;
|
||||||
|
|
||||||
typedef struct damage
|
typedef struct damage {
|
||||||
{
|
|
||||||
int xpos,ypos;
|
int xpos,ypos;
|
||||||
int xsize,ysize;
|
int xsize,ysize;
|
||||||
} damage;
|
} damage;
|
||||||
|
|
||||||
typedef struct list
|
typedef struct list {
|
||||||
{
|
|
||||||
void *next;
|
void *next;
|
||||||
} list;
|
} list;
|
||||||
|
|
||||||
typedef struct bomb
|
typedef struct bomb {
|
||||||
{
|
|
||||||
struct bomb *next;
|
struct bomb *next;
|
||||||
int type;
|
int type;
|
||||||
int xpos,ypos;
|
int xpos,ypos;
|
||||||
@ -123,8 +118,7 @@ typedef struct bomb
|
|||||||
#define FLAMELIFE 15
|
#define FLAMELIFE 15
|
||||||
#define DECAYLIFE 15
|
#define DECAYLIFE 15
|
||||||
|
|
||||||
typedef struct flame
|
typedef struct flame {
|
||||||
{
|
|
||||||
struct flame *next;
|
struct flame *next;
|
||||||
int xpos,ypos;
|
int xpos,ypos;
|
||||||
int px,py;
|
int px,py;
|
||||||
@ -138,16 +132,23 @@ typedef struct flame
|
|||||||
#define FL_LEFT 1
|
#define FL_LEFT 1
|
||||||
#define FL_RIGHT 4
|
#define FL_RIGHT 4
|
||||||
|
|
||||||
typedef struct brickdecay
|
typedef struct brickdecay {
|
||||||
{
|
|
||||||
struct brickdecay *next;
|
struct brickdecay *next;
|
||||||
int xpos,ypos;
|
int xpos,ypos;
|
||||||
int px,py;
|
int px,py;
|
||||||
int timer;
|
int timer;
|
||||||
} brickdecay;
|
} 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;
|
struct generic *next;
|
||||||
int xpos,ypos;
|
int xpos,ypos;
|
||||||
int px,py;
|
int px,py;
|
||||||
@ -158,13 +159,12 @@ typedef struct generic
|
|||||||
int data1,data2;
|
int data1,data2;
|
||||||
} generic;
|
} generic;
|
||||||
|
|
||||||
typedef struct bonustile
|
typedef struct bonustile {
|
||||||
{
|
|
||||||
struct bonustile *next;
|
struct bonustile *next;
|
||||||
int xpos,ypos;
|
int xpos,ypos;
|
||||||
int px,py;
|
int px,py;
|
||||||
int type;
|
int type;
|
||||||
}bonustile;
|
} bonustile;
|
||||||
|
|
||||||
#define TILE_NONE -1
|
#define TILE_NONE -1
|
||||||
#define TILE_BOMB 5
|
#define TILE_BOMB 5
|
||||||
@ -210,122 +210,7 @@ typedef struct bonustile
|
|||||||
|
|
||||||
extern char exitflag;
|
extern char exitflag;
|
||||||
|
|
||||||
extern player players[];
|
|
||||||
extern sprite sprites[];
|
|
||||||
extern gfxset gfxsets[NUMGFX];
|
|
||||||
extern uchar needwhole;
|
extern uchar needwhole;
|
||||||
extern figure walking[MAXSETS][60];
|
extern figure walking[MAXSETS][60];
|
||||||
extern damage damages[];
|
|
||||||
extern bomb bombs[];
|
|
||||||
|
|
||||||
extern void centerx(player *pl);
|
#endif /* BOMBER_H */
|
||||||
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
|
|
||||||
|
448
draw.c
Normal file
448
draw.c
Normal file
@ -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");
|
||||||
|
}
|
38
draw.h
Normal file
38
draw.h
Normal file
@ -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
Normal file
943
game.c
Normal file
@ -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;
|
||||||
|
}
|
||||||
|
if(py && field[py-1][px]==FIELD_FLAME) {
|
||||||
|
fl2=info[py-1][px];
|
||||||
|
fl->lurd|=FL_UP;
|
||||||
|
fl2->lurd|=FL_DOWN;
|
||||||
|
}
|
||||||
|
if(px<arraynumx-1 && field[py][px+1]==FIELD_FLAME) {
|
||||||
|
fl2=info[py][px+1];
|
||||||
|
fl->lurd|=FL_RIGHT;
|
||||||
|
fl2->lurd|=FL_LEFT;
|
||||||
|
}
|
||||||
|
if(py<arraynumy-1 && field[py+1][px]==FIELD_FLAME) {
|
||||||
|
fl2=info[py+1][px];
|
||||||
|
fl->lurd|=FL_DOWN;
|
||||||
|
fl2->lurd|=FL_UP;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dropbomb(player *pl,int px,int py,int type){
|
||||||
|
bomb *bmb;
|
||||||
|
|
||||||
|
if(field[py][px]!=FIELD_EMPTY) return;
|
||||||
|
bmb=allocentry();
|
||||||
|
if(!bmb) return;
|
||||||
|
playsound(3);
|
||||||
|
addtail(&activebombs,bmb);
|
||||||
|
|
||||||
|
--(pl->bombsavailable);
|
||||||
|
field[py][px]=FIELD_BOMB;
|
||||||
|
info[py][px]=bmb;
|
||||||
|
bmb->type=type;
|
||||||
|
bmb->px=px;
|
||||||
|
bmb->py=py;
|
||||||
|
bmb->xpos=arraytoscreenx(px);
|
||||||
|
bmb->ypos=arraytoscreeny(py);
|
||||||
|
bmb->power=pl->flamelength;
|
||||||
|
bmb->owner=pl;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void adddetonate(bomb *bmb) {
|
||||||
|
if(bmb->type==BOMB_OFF) return;
|
||||||
|
|
||||||
|
bmb->type=BOMB_OFF;
|
||||||
|
field[bmb->py][bmb->px]=FIELD_EXPLODING;
|
||||||
|
info[bmb->py][bmb->px]=0;
|
||||||
|
detonated[detonateput++]=bmb;
|
||||||
|
detonateput%=MAXBOMBSDETONATED;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void processbombs() {
|
||||||
|
bomb *bmb;
|
||||||
|
|
||||||
|
bmb=activebombs.next;
|
||||||
|
while(bmb) {
|
||||||
|
switch(bmb->type) {
|
||||||
|
case BOMB_NORMAL:
|
||||||
|
++bmb->timer;
|
||||||
|
if(bmb->timer==BOMBLIFE)
|
||||||
|
adddetonate(bmb);
|
||||||
|
++(bmb->figcount);
|
||||||
|
break;
|
||||||
|
case BOMB_CONTROLLED:
|
||||||
|
++(bmb->figcount);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
bmb=bmb->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void adddecay(int px,int py) {
|
||||||
|
brickdecay *bd;
|
||||||
|
int xpos,ypos;
|
||||||
|
|
||||||
|
bd=allocentry();
|
||||||
|
if(!bd) return;
|
||||||
|
field[py][px]=FIELD_EXPLODING;
|
||||||
|
bd->xpos=arraytoscreenx(px);
|
||||||
|
bd->ypos=arraytoscreeny(py);
|
||||||
|
bd->px=px;
|
||||||
|
bd->py=py;
|
||||||
|
addtail(&activedecays,bd);
|
||||||
|
xpos=tovideox(bd->xpos);
|
||||||
|
ypos=tovideoy(bd->ypos);
|
||||||
|
solidcopyany(&backgroundoriginal,&background,xpos,ypos,
|
||||||
|
arrayspacex,arrayspacey);
|
||||||
|
solidcopy(&background,xpos,ypos,arrayspacex,arrayspacey);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void addbonus(int px,int py,int type) {
|
||||||
|
bonustile *bonus;
|
||||||
|
|
||||||
|
bonus=allocentry();
|
||||||
|
if(!bonus) return;
|
||||||
|
addtail(&activebonus,bonus);
|
||||||
|
bonus->px=px;
|
||||||
|
bonus->py=py;
|
||||||
|
bonus->xpos=arraytoscreenx(px);
|
||||||
|
bonus->ypos=arraytoscreeny(py);
|
||||||
|
bonus->type=type;
|
||||||
|
field[py][px]=FIELD_BONUS;
|
||||||
|
info[py][px]=bonus;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void trybonus(int px,int py) {
|
||||||
|
int i=0, r;
|
||||||
|
const int *p;
|
||||||
|
|
||||||
|
if(field[py][px]!=FIELD_EMPTY) return;
|
||||||
|
p=bonuschances;
|
||||||
|
r=myrand()%bonustotal;
|
||||||
|
while(r>=0) {
|
||||||
|
i=*p++;
|
||||||
|
r-=*p++;
|
||||||
|
}
|
||||||
|
if(i==TILE_NONE) return;
|
||||||
|
addbonus(px,py,i);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void deletebonus(bonustile *bonus) {
|
||||||
|
int px,py;
|
||||||
|
|
||||||
|
px=bonus->px;
|
||||||
|
py=bonus->py;
|
||||||
|
field[py][px]=0;
|
||||||
|
info[py][px]=0;
|
||||||
|
delink(&activebonus,bonus);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void flameshaft(player *owner,int px,int py,int dx,int dy,int power) {
|
||||||
|
int there;
|
||||||
|
bomb *bmb;
|
||||||
|
|
||||||
|
while(power--) {
|
||||||
|
px+=dx;
|
||||||
|
py+=dy;
|
||||||
|
if(px<0 || py<0 || px>=arraynumx || py>=arraynumy) break;
|
||||||
|
there=field[py][px];
|
||||||
|
switch(there) {
|
||||||
|
case FIELD_BOMB:
|
||||||
|
bmb=info[py][px];
|
||||||
|
adddetonate(bmb);
|
||||||
|
break;
|
||||||
|
case FIELD_EMPTY:
|
||||||
|
addflame(owner,px,py);
|
||||||
|
break;
|
||||||
|
case FIELD_BRICK:
|
||||||
|
adddecay(px,py);
|
||||||
|
power=0;
|
||||||
|
break;
|
||||||
|
case FIELD_BONUS:
|
||||||
|
deletebonus(info[py][px]);
|
||||||
|
power=0;
|
||||||
|
break;
|
||||||
|
case FIELD_BORDER:
|
||||||
|
case FIELD_EXPLODING:
|
||||||
|
default:
|
||||||
|
power=0;
|
||||||
|
case FIELD_FLAME:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void detonatebomb(bomb *bmb) {
|
||||||
|
int px,py;
|
||||||
|
int power;
|
||||||
|
player *owner;
|
||||||
|
|
||||||
|
++(bmb->owner->bombsavailable);
|
||||||
|
px=bmb->px;
|
||||||
|
py=bmb->py;
|
||||||
|
power=bmb->power;
|
||||||
|
owner=bmb->owner;
|
||||||
|
delink(&activebombs,bmb);
|
||||||
|
addflame(owner,px,py);
|
||||||
|
flameshaft(owner,px,py,-1,0,power);
|
||||||
|
flameshaft(owner,px,py,0,-1,power);
|
||||||
|
flameshaft(owner,px,py,1,0,power);
|
||||||
|
flameshaft(owner,px,py,0,1,power);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dodetonations(void) {
|
||||||
|
int i=0;
|
||||||
|
|
||||||
|
while(detonatetake!=detonateput) {
|
||||||
|
++i;
|
||||||
|
detonatebomb(detonated[detonatetake]);
|
||||||
|
detonatetake=(detonatetake+1) % MAXBOMBSDETONATED;
|
||||||
|
}
|
||||||
|
if(i) playsound((myrand()&1) ? 0 : 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void processflames(void) {
|
||||||
|
flame *fl,*fl2;
|
||||||
|
|
||||||
|
fl=activeflames.next;
|
||||||
|
while(fl) {
|
||||||
|
++(fl->timer);
|
||||||
|
fl=fl->next;
|
||||||
|
}
|
||||||
|
fl=activeflames.next;
|
||||||
|
while(fl) {
|
||||||
|
if(fl->timer==FLAMELIFE)
|
||||||
|
{
|
||||||
|
field[fl->py][fl->px]=FIELD_EMPTY;
|
||||||
|
info[fl->py][fl->px]=0;
|
||||||
|
fl2=fl;
|
||||||
|
fl=fl->next;
|
||||||
|
delink(&activeflames,fl2);
|
||||||
|
} else
|
||||||
|
fl=fl->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void processdecays() {
|
||||||
|
brickdecay *bd,*bd2;
|
||||||
|
|
||||||
|
bd=activedecays.next;
|
||||||
|
while(bd) {
|
||||||
|
++(bd->timer);
|
||||||
|
if(bd->timer==DECAYLIFE) {
|
||||||
|
field[bd->py][bd->px]=FIELD_EMPTY;
|
||||||
|
trybonus(bd->px,bd->py);
|
||||||
|
bd2=bd;
|
||||||
|
bd=bd->next;
|
||||||
|
delink(&activedecays,bd2);
|
||||||
|
} else
|
||||||
|
bd=bd->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void drawbombs(void) {
|
||||||
|
int j;
|
||||||
|
bomb *bmb;
|
||||||
|
struct figure *figtab;
|
||||||
|
int color;
|
||||||
|
int xpos,ypos;
|
||||||
|
|
||||||
|
bmb=activebombs.next;
|
||||||
|
while(bmb) {
|
||||||
|
color=bmb->owner->color;
|
||||||
|
figtab=(bmb->type==BOMB_NORMAL) ? bombs1[color] : bombs2[color];
|
||||||
|
j=bmb->figcount % (NUMBOMBFRAMES<<1);
|
||||||
|
if(j>=NUMBOMBFRAMES) j=(NUMBOMBFRAMES<<1)-j-1;
|
||||||
|
xpos=tovideox(bmb->xpos);
|
||||||
|
ypos=tovideoy(bmb->ypos)-3;
|
||||||
|
|
||||||
|
addsprite(xpos,ypos,figtab+j);
|
||||||
|
bmb=bmb->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void drawflames(void) {
|
||||||
|
flame *fl;
|
||||||
|
int xpos,ypos;
|
||||||
|
int color;
|
||||||
|
int fig;
|
||||||
|
|
||||||
|
fl=activeflames.next;
|
||||||
|
while(fl) {
|
||||||
|
color=fl->owner->color;
|
||||||
|
xpos=tovideox(fl->xpos);
|
||||||
|
ypos=tovideoy(fl->ypos);
|
||||||
|
fig=(fl->timer*10)/FLAMELIFE;
|
||||||
|
if(fig>=5) fig=9-fig;
|
||||||
|
fig+=5*fl->lurd;
|
||||||
|
addsprite(xpos,ypos,flamefigs[0/* color */]+fig);
|
||||||
|
fl=fl->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void drawdecays() {
|
||||||
|
brickdecay *bd;
|
||||||
|
|
||||||
|
bd=activedecays.next;
|
||||||
|
while(bd) {
|
||||||
|
addsprite(tovideox(bd->xpos),tovideoy(bd->ypos),
|
||||||
|
blocksx+(bd->timer*9)/DECAYLIFE);
|
||||||
|
bd=bd->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void drawbonus() {
|
||||||
|
bonustile *bonus;
|
||||||
|
|
||||||
|
bonus=activebonus.next;
|
||||||
|
while(bonus) {
|
||||||
|
addsprite(tovideox(bonus->xpos),tovideoy(bonus->ypos),
|
||||||
|
tiles+bonus->type);
|
||||||
|
bonus=bonus->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void drawplayers() {
|
||||||
|
player *pl;
|
||||||
|
int xpos,ypos;
|
||||||
|
|
||||||
|
pl=activeplayers.next;
|
||||||
|
|
||||||
|
while(pl) {
|
||||||
|
if(!(pl->flags & FLG_DEAD)) {
|
||||||
|
if(!pl->figure)
|
||||||
|
pl->figure=walking[pl->color]+30;
|
||||||
|
xpos=tovideox(pl->xpos)+pl->fixx;
|
||||||
|
ypos=tovideoy(pl->ypos)+pl->fixy;
|
||||||
|
addsprite(xpos,ypos,pl->figure);
|
||||||
|
}
|
||||||
|
pl=pl->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void detonatecontrolled(player *pl) {
|
||||||
|
bomb *bmb;
|
||||||
|
|
||||||
|
bmb=activebombs.next;
|
||||||
|
while(bmb) {
|
||||||
|
if(bmb->owner==pl && bmb->type==BOMB_CONTROLLED)
|
||||||
|
adddetonate(bmb);
|
||||||
|
bmb=bmb->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void playonce(generic *gen) {
|
||||||
|
if(gen->timer==gen->data1)
|
||||||
|
delink(&activegeneric,gen);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void drawgeneric(generic *gen) {
|
||||||
|
addsprite(gen->xpos,gen->ypos,((figure *)(gen->ptr1))+gen->timer);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void queuesequence(int xpos,int ypos,figure *fig,int count) {
|
||||||
|
generic *gen;
|
||||||
|
|
||||||
|
gen=allocentry();
|
||||||
|
if(!gen) return;
|
||||||
|
gen->xpos=xpos;
|
||||||
|
gen->ypos=ypos;
|
||||||
|
gen->data1=count;
|
||||||
|
gen->process=playonce;
|
||||||
|
gen->draw=drawgeneric;
|
||||||
|
gen->ptr1=fig;
|
||||||
|
addtail(&activegeneric,gen);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void adddeath(player *pl) {
|
||||||
|
int xpos,ypos;
|
||||||
|
|
||||||
|
xpos=tovideox(pl->xpos)+pl->fixx-10;
|
||||||
|
ypos=tovideoy(pl->ypos)+pl->fixy;
|
||||||
|
queuesequence(xpos,ypos,death,NUMDEATHFRAMES);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void killplayer(player *pl) {
|
||||||
|
pl->flags|=FLG_DEAD;
|
||||||
|
playsound(2);
|
||||||
|
adddeath(pl);
|
||||||
|
detonatecontrolled(pl);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void processgenerics(void) {
|
||||||
|
generic *gen,*gen2;
|
||||||
|
|
||||||
|
gen=activegeneric.next;
|
||||||
|
while(gen) {
|
||||||
|
gen2=gen;
|
||||||
|
gen=gen->next;
|
||||||
|
++(gen2->timer);
|
||||||
|
gen2->process(gen2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void drawgenerics(void) {
|
||||||
|
generic *gen;
|
||||||
|
|
||||||
|
gen=activegeneric.next;
|
||||||
|
while(gen) {
|
||||||
|
gen->draw(gen);
|
||||||
|
gen=gen->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int centerxchange(player *pl) {
|
||||||
|
int speed;
|
||||||
|
int val;
|
||||||
|
int line;
|
||||||
|
int max;
|
||||||
|
|
||||||
|
max=arrayspacex<<FRACTION;
|
||||||
|
speed=pl->speed;
|
||||||
|
val=pl->xpos+(max<<2);
|
||||||
|
val%=max;
|
||||||
|
line=max>>1;
|
||||||
|
if(val<line) {
|
||||||
|
if(val-speed<0)
|
||||||
|
return -val;
|
||||||
|
else
|
||||||
|
return -speed;
|
||||||
|
} else if(val>=line) {
|
||||||
|
if(val+speed>max)
|
||||||
|
return max-val;
|
||||||
|
else
|
||||||
|
return speed;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void centerx(player *pl) {
|
||||||
|
pl->xpos+=centerxchange(pl);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int centerychange(player *pl) {
|
||||||
|
int speed;
|
||||||
|
int val;
|
||||||
|
int line;
|
||||||
|
int max;
|
||||||
|
|
||||||
|
max=arrayspacey<<FRACTION;
|
||||||
|
speed=pl->speed;
|
||||||
|
val=pl->ypos+(max<<2);
|
||||||
|
val%=max;
|
||||||
|
line=max>>1;
|
||||||
|
if(val<line)
|
||||||
|
{
|
||||||
|
if(val-speed<0)
|
||||||
|
return -val;
|
||||||
|
else
|
||||||
|
return -speed;
|
||||||
|
} else if(val>=line)
|
||||||
|
{
|
||||||
|
if(val+speed>max)
|
||||||
|
return max-val;
|
||||||
|
else
|
||||||
|
return speed;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void centery(player *pl) {
|
||||||
|
pl->ypos+=centerychange(pl);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define SGN(x) ((x)==0 ? 0 : ((x)<0 ? -1 : 1))
|
||||||
|
|
||||||
|
static void trymove(player *pl,int dx,int dy) {
|
||||||
|
int wx,wy;
|
||||||
|
int sx,sy;
|
||||||
|
int there;
|
||||||
|
int px,py;
|
||||||
|
static int depth=0;
|
||||||
|
int tx,ty;
|
||||||
|
|
||||||
|
++depth;
|
||||||
|
sx=(dx*(arrayspacex+1)) << (FRACTION-1);
|
||||||
|
sy=(dy*(arrayspacey+1)) << (FRACTION-1);
|
||||||
|
|
||||||
|
wx=screentoarrayx(pl->xpos+sx);
|
||||||
|
wy=screentoarrayy(pl->ypos+sy);
|
||||||
|
px=screentoarrayx(pl->xpos);
|
||||||
|
py=screentoarrayy(pl->ypos);
|
||||||
|
|
||||||
|
if(wx<0 || wx>=arraynumx || wy<0 || wy>=arraynumy) {
|
||||||
|
--depth;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
there=field[wy][wx];
|
||||||
|
if((px!=wx || py!=wy) &&
|
||||||
|
(there==FIELD_BRICK||there==FIELD_BOMB||there==FIELD_BORDER))
|
||||||
|
{
|
||||||
|
if(dx && !dy) {
|
||||||
|
ty=centerychange(pl);
|
||||||
|
if(ty && depth==1)
|
||||||
|
trymove(pl,0,-SGN(ty));
|
||||||
|
|
||||||
|
} else if(dy && !dx) {
|
||||||
|
tx=centerxchange(pl);
|
||||||
|
if(tx && depth==1)
|
||||||
|
trymove(pl,-SGN(tx),0);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
pl->xpos+=dx*pl->speed;
|
||||||
|
pl->ypos+=dy*pl->speed;
|
||||||
|
if(dx && !dy) centery(pl);
|
||||||
|
if(dy && !dx) centerx(pl);
|
||||||
|
}
|
||||||
|
--depth;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void applybonus(player *pl,bonustile *bonus) {
|
||||||
|
int type;
|
||||||
|
int maxflame;
|
||||||
|
|
||||||
|
maxflame=arraynumx>arraynumy ? arraynumx : arraynumy;
|
||||||
|
type=bonus->type;
|
||||||
|
deletebonus(bonus);
|
||||||
|
switch(type)
|
||||||
|
{
|
||||||
|
case TILE_BOMB:
|
||||||
|
++(pl->bombsavailable);
|
||||||
|
break;
|
||||||
|
case TILE_FLAME:
|
||||||
|
if(pl->flamelength<maxflame)
|
||||||
|
++(pl->flamelength);
|
||||||
|
break;
|
||||||
|
case TILE_GOLDFLAME:
|
||||||
|
pl->flamelength=maxflame;
|
||||||
|
break;
|
||||||
|
case TILE_CONTROL:
|
||||||
|
pl->flags|=FLG_CONTROL;
|
||||||
|
break;
|
||||||
|
case TILE_SKATES:
|
||||||
|
if (pl->speed < SPEEDSTART) {
|
||||||
|
pl->speed = SPEEDSTART;
|
||||||
|
} else {
|
||||||
|
pl->speed+=SPEEDDELTA;
|
||||||
|
}
|
||||||
|
if(pl->speed>SPEEDMAX) pl->speed=SPEEDMAX;
|
||||||
|
break;
|
||||||
|
case TILE_TURTLE:
|
||||||
|
pl->speed=SPEEDTURTLE;
|
||||||
|
pl->speedturtle_timeout=SPEEDTURTLE_TIMEOUT;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void doplayer(player *pl) {
|
||||||
|
int last;
|
||||||
|
int color;
|
||||||
|
int speed;
|
||||||
|
int px,py;
|
||||||
|
int there;
|
||||||
|
int flags;
|
||||||
|
int what;
|
||||||
|
|
||||||
|
if(pl->controller==-1)
|
||||||
|
what=myaction;
|
||||||
|
else
|
||||||
|
what=actions[pl->controller];
|
||||||
|
|
||||||
|
flags=pl->flags;
|
||||||
|
if(flags&FLG_DEAD) return;
|
||||||
|
color=pl->color;
|
||||||
|
last=pl->doing;
|
||||||
|
pl->doing=what;
|
||||||
|
speed=pl->speed;
|
||||||
|
px=screentoarrayx(pl->xpos);
|
||||||
|
py=screentoarrayy(pl->ypos);
|
||||||
|
there=field[py][px];
|
||||||
|
|
||||||
|
if(what==ACT_QUIT) {
|
||||||
|
killplayer(pl);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(there==FIELD_BONUS) {
|
||||||
|
playsound((myrand()&1) ? 1 : 5);
|
||||||
|
applybonus(pl,info[py][px]);
|
||||||
|
} else if(there==FIELD_FLAME) {
|
||||||
|
killplayer(pl);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if(what&ACT_TURBO) speed<<=2;
|
||||||
|
if(what&ACT_PRIMARY) {
|
||||||
|
if(there==FIELD_EMPTY && pl->bombsavailable)
|
||||||
|
dropbomb(pl,px,py,
|
||||||
|
(flags&FLG_CONTROL) ? BOMB_CONTROLLED :BOMB_NORMAL);
|
||||||
|
}
|
||||||
|
if(what&ACT_SECONDARY && (flags&FLG_CONTROL))
|
||||||
|
detonatecontrolled(pl);
|
||||||
|
|
||||||
|
switch(what&ACT_MASK) {
|
||||||
|
case ACT_UP:
|
||||||
|
trymove(pl,0,-1);
|
||||||
|
pl->figcount=(pl->figcount+1)%15;
|
||||||
|
pl->figure=walking[color]+pl->figcount+15;
|
||||||
|
break;
|
||||||
|
case ACT_DOWN:
|
||||||
|
trymove(pl,0,1);
|
||||||
|
pl->figcount=(pl->figcount+1)%15;
|
||||||
|
pl->figure=walking[color]+pl->figcount+30;
|
||||||
|
break;
|
||||||
|
case ACT_LEFT:
|
||||||
|
trymove(pl,-1,0);
|
||||||
|
pl->figcount=(pl->figcount+1)%15;
|
||||||
|
pl->figure=walking[color]+pl->figcount+45;
|
||||||
|
break;
|
||||||
|
case ACT_RIGHT:
|
||||||
|
trymove(pl,1,0);
|
||||||
|
pl->figcount=(pl->figcount+1)%15;
|
||||||
|
pl->figure=walking[color]+pl->figcount;
|
||||||
|
break;
|
||||||
|
case ACT_NONE:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pl->speedturtle_timeout > 0) {
|
||||||
|
pl->speedturtle_timeout--;
|
||||||
|
if (0 == pl->speedturtle_timeout) {
|
||||||
|
if (pl->speed < SPEEDSTART) pl->speed = SPEEDSTART;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void processplayers(void) {
|
||||||
|
player *pl,*pl2;
|
||||||
|
|
||||||
|
pl=activeplayers.next;
|
||||||
|
while(pl) {
|
||||||
|
pl2=pl;
|
||||||
|
pl=pl->next;
|
||||||
|
doplayer(pl2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void processquits(void) {
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (network != NETWORK_SLAVE) return;
|
||||||
|
for (i = 0; i < MAXNETNODES; ++i) {
|
||||||
|
if (netnodes[i].used && actions[i]==ACT_QUIT)
|
||||||
|
netnodes[i].used=0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int getaction(void) {
|
||||||
|
int what;
|
||||||
|
|
||||||
|
what=ACT_NONE;
|
||||||
|
if(checkpressed(MYLEFT)) what=ACT_LEFT;
|
||||||
|
else if(checkpressed(MYRIGHT)) what=ACT_RIGHT;
|
||||||
|
else if(checkpressed(MYDOWN)) what=ACT_DOWN;
|
||||||
|
else if(checkpressed(MYUP)) what=ACT_UP;
|
||||||
|
else if(checkdown(13)) what=ACT_ENTER;
|
||||||
|
else if(checkdown(0x1b)) what=ACT_QUIT;
|
||||||
|
|
||||||
|
if(checkdown(' '))
|
||||||
|
what|=ACT_PRIMARY;
|
||||||
|
if(checkdown('b'))
|
||||||
|
what|=ACT_SECONDARY;
|
||||||
|
|
||||||
|
return what;
|
||||||
|
}
|
||||||
|
|
||||||
|
int iterate(void) {
|
||||||
|
int i;
|
||||||
|
static int deathcount=0;
|
||||||
|
|
||||||
|
mypause();
|
||||||
|
scaninput();
|
||||||
|
|
||||||
|
erasesprites();
|
||||||
|
clearspritelist();
|
||||||
|
gfxunlock();
|
||||||
|
|
||||||
|
myaction=getaction();
|
||||||
|
if(!network && myaction==ACT_QUIT) return CODE_QUIT;
|
||||||
|
i=networktraffic();
|
||||||
|
if(i<0)
|
||||||
|
gountil=mycount+1;
|
||||||
|
else
|
||||||
|
gountil=i;
|
||||||
|
|
||||||
|
while(mycount<gountil) {
|
||||||
|
++mycount;
|
||||||
|
if(network) {
|
||||||
|
i=gountil-mycount;
|
||||||
|
if(i>=ACTIONHIST) // too far behind
|
||||||
|
return CODE_QUIT;
|
||||||
|
memcpy(actions,actionblock+i*MAXNETNODES,MAXNETNODES);
|
||||||
|
if(actions[myslot]==ACT_QUIT) return CODE_QUIT;
|
||||||
|
}
|
||||||
|
processbombs();
|
||||||
|
dodetonations();
|
||||||
|
processdecays();
|
||||||
|
processflames();
|
||||||
|
processgenerics();
|
||||||
|
processquits();
|
||||||
|
processplayers();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
if(!(rand()&127))
|
||||||
|
{
|
||||||
|
i=gtime();
|
||||||
|
while(gtime()-i<100);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
drawbombs();
|
||||||
|
drawbonus();
|
||||||
|
drawgenerics();
|
||||||
|
drawdecays();
|
||||||
|
drawflames();
|
||||||
|
drawplayers();
|
||||||
|
plotsprites();
|
||||||
|
copyup();
|
||||||
|
if(!activegeneric.next) {
|
||||||
|
player *pl;
|
||||||
|
int deadplayers = 0;
|
||||||
|
pl=activeplayers.next;
|
||||||
|
i=0;
|
||||||
|
while(pl) {
|
||||||
|
if(!(pl->flags & FLG_DEAD))
|
||||||
|
++i;
|
||||||
|
else
|
||||||
|
deadplayers++;
|
||||||
|
pl=pl->next;
|
||||||
|
}
|
||||||
|
if (deadplayers > 0 && (!i || (network && i==1))) {
|
||||||
|
++deathcount;
|
||||||
|
if(deathcount==25)
|
||||||
|
return CODE_ALLDEAD;
|
||||||
|
} else
|
||||||
|
deathcount=0;
|
||||||
|
}
|
||||||
|
return CODE_CONT;
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_game_options(GameOptions *options) {
|
||||||
|
gameoptions = *options;
|
||||||
|
}
|
28
game.h
Normal file
28
game.h
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
#ifndef GAME_H
|
||||||
|
#define GAME_H
|
||||||
|
|
||||||
|
#define FRACTION 9
|
||||||
|
#define SPEEDDELTA (1<<(FRACTION-1))
|
||||||
|
#define SPEEDMAX (10<<FRACTION)
|
||||||
|
#define SPEEDSTART (6<<FRACTION)
|
||||||
|
#define SPEEDTURTLE (3<<FRACTION)
|
||||||
|
#define SPEEDTURTLE_TIMEOUT 250
|
||||||
|
|
||||||
|
#define TEMPNODES 2
|
||||||
|
|
||||||
|
typedef struct GameOptions GameOptions;
|
||||||
|
struct GameOptions {
|
||||||
|
unsigned char density, flames, bombs, generosity;
|
||||||
|
};
|
||||||
|
|
||||||
|
void run_single_player(void);
|
||||||
|
void run_network_game(void);
|
||||||
|
|
||||||
|
void set_game_options(GameOptions *options);
|
||||||
|
|
||||||
|
extern char playername[16];
|
||||||
|
extern int gamemode;
|
||||||
|
|
||||||
|
extern solid background,backgroundoriginal;
|
||||||
|
|
||||||
|
#endif
|
62
list.c
Normal file
62
list.c
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
|
||||||
|
#include "bomber.h"
|
||||||
|
#include "list.h"
|
||||||
|
#include "utils.h"
|
||||||
|
|
||||||
|
static listitem *things=0, *free_things;
|
||||||
|
|
||||||
|
void allocthings(void) {
|
||||||
|
int i;
|
||||||
|
const int num = MAXTHINGS;
|
||||||
|
|
||||||
|
if (!things) {
|
||||||
|
things = calloc(sizeof(listitem), num);
|
||||||
|
} else {
|
||||||
|
memset(things, 0, sizeof(listitem)*num);
|
||||||
|
}
|
||||||
|
if(!things) nomem("Trying to allocate thing memory");
|
||||||
|
for(i=0;i<num-1;++i) {
|
||||||
|
things[i].next = &things[i+1];
|
||||||
|
}
|
||||||
|
things[i].next = 0;
|
||||||
|
free_things = things;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *allocentry(void) {
|
||||||
|
listitem *entry = free_things;
|
||||||
|
|
||||||
|
if (free_things) {
|
||||||
|
free_things = free_things->next;
|
||||||
|
memset(entry, 0, sizeof(*entry));
|
||||||
|
}
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
void freeentry(void *_entry) {
|
||||||
|
listitem *entry = _entry;
|
||||||
|
|
||||||
|
entry->next = free_things;
|
||||||
|
free_things = entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
void addtail(void *_header,void *_entry) {
|
||||||
|
listitem *header = _header, *entry = _entry;
|
||||||
|
|
||||||
|
while (header->next) header = header->next;
|
||||||
|
header->next = entry;
|
||||||
|
entry->next = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void delink(void *_header,void *_entry) {
|
||||||
|
listitem *header = _header, *entry = _entry;
|
||||||
|
|
||||||
|
while (header->next != entry) header = header->next;
|
||||||
|
header->next = entry->next;
|
||||||
|
entry->next = 0;
|
||||||
|
freeentry(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
void initheader(void *p) {
|
||||||
|
memset(p,0,sizeof(list));
|
||||||
|
}
|
||||||
|
|
12
list.h
Normal file
12
list.h
Normal file
@ -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
|
404
menu.c
Normal file
404
menu.c
Normal file
@ -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<menunum;++i)
|
||||||
|
{
|
||||||
|
j=strlen(menuitems[i])*bigfontxsize;
|
||||||
|
tx=(IXSIZE-j) >> 1;
|
||||||
|
if(i==selected)
|
||||||
|
{
|
||||||
|
greyrect(0,ty-1,tx-5,bigfontysize);
|
||||||
|
greyrect(tx+j+3,ty-1,IXSIZE-(tx+j+3),bigfontysize);
|
||||||
|
}
|
||||||
|
drawbigstring(tx,ty,menuitems[i]);
|
||||||
|
ty+=bigfontyspace;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int domenu(menuname whichmenu, int (*pause)(void)) {
|
||||||
|
char redraw;
|
||||||
|
int selected;
|
||||||
|
int mcount=0;
|
||||||
|
|
||||||
|
if(whichmenu>=0)
|
||||||
|
selected=menuhistory[whichmenu];
|
||||||
|
else
|
||||||
|
selected=-whichmenu-1;
|
||||||
|
|
||||||
|
if (!pause) pause=mypause;
|
||||||
|
|
||||||
|
redraw=1;
|
||||||
|
clearspritelist();
|
||||||
|
while(!exitflag) {
|
||||||
|
if(redraw) {
|
||||||
|
drawmenu(selected);
|
||||||
|
redraw=0;
|
||||||
|
}
|
||||||
|
if (!pause()) return -1;
|
||||||
|
scaninput();
|
||||||
|
++mcount;
|
||||||
|
|
||||||
|
clearsprites();
|
||||||
|
clearspritelist();
|
||||||
|
addsprite(IXSIZE/2-50-20,IYSIZE-80,walking[2]+(30+mcount%15));
|
||||||
|
addsprite(IXSIZE/2+50-20,IYSIZE-80,walking[3]+(30+(mcount+7)%15));
|
||||||
|
plotsprites();
|
||||||
|
copyup();
|
||||||
|
|
||||||
|
if(anydown()) playsound(3);
|
||||||
|
while(anydown()) {
|
||||||
|
switch(takedown()) {
|
||||||
|
case MYLEFT:
|
||||||
|
menudelta=-1;
|
||||||
|
return selected;
|
||||||
|
case MYRIGHT:
|
||||||
|
case ' ':
|
||||||
|
case 13:
|
||||||
|
menudelta=1;
|
||||||
|
return selected;
|
||||||
|
case 'k':
|
||||||
|
case MYUP:
|
||||||
|
if (selected) --selected;
|
||||||
|
else selected=menunum-1;
|
||||||
|
if (whichmenu>=0)
|
||||||
|
menuhistory[whichmenu]=selected;
|
||||||
|
redraw=1;
|
||||||
|
break;
|
||||||
|
case 'j':
|
||||||
|
case MYDOWN:
|
||||||
|
++selected;
|
||||||
|
if (selected==menunum) selected=0;
|
||||||
|
if (whichmenu>=0)
|
||||||
|
menuhistory[whichmenu]=selected;
|
||||||
|
redraw=1;
|
||||||
|
break;
|
||||||
|
case 0x1b:
|
||||||
|
if (MENU_MAIN == whichmenu && menuexit != selected) {
|
||||||
|
selected = menuexit;
|
||||||
|
redraw = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
menudelta = 1;
|
||||||
|
return menuexit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void menustart() {
|
||||||
|
menunum=-1;
|
||||||
|
menuput=menustring;
|
||||||
|
*menuput=0;
|
||||||
|
menuexit = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void additem_s(char *item, int len) {
|
||||||
|
if (len < 0 || (menustring+sizeof(menustring)-menuput <= len)) return;
|
||||||
|
|
||||||
|
if(menunum<0)
|
||||||
|
menutitle=menuput;
|
||||||
|
else
|
||||||
|
menuitems[menunum]=menuput;
|
||||||
|
++menunum;
|
||||||
|
memcpy(menuput,item,len+1);
|
||||||
|
menuput += len+1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void additem(char *item,...) {
|
||||||
|
char output[256];
|
||||||
|
va_list ap;
|
||||||
|
int len;
|
||||||
|
|
||||||
|
va_start(ap, item);
|
||||||
|
|
||||||
|
len = vsnprintf(output,sizeof(output),item,ap);
|
||||||
|
if (len >= 256) len = 255; /* truncated string */
|
||||||
|
|
||||||
|
additem_s(output, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void addexit(char *item,...) {
|
||||||
|
char output[256];
|
||||||
|
va_list ap;
|
||||||
|
int len;
|
||||||
|
|
||||||
|
va_start(ap, item);
|
||||||
|
|
||||||
|
len = vsnprintf(output,sizeof(output),item,ap);
|
||||||
|
if (len >= 256) len = 255; /* truncated string */
|
||||||
|
|
||||||
|
menuexit = menunum;
|
||||||
|
additem_s(output, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* end generic menu */
|
||||||
|
|
||||||
|
/* game menues */
|
||||||
|
|
||||||
|
static void drawjoinscreen(void) {
|
||||||
|
int i;
|
||||||
|
char name[17];
|
||||||
|
char temp[64];
|
||||||
|
|
||||||
|
#define JX (IXSIZE/3)
|
||||||
|
#define JY (IYSIZE/4)
|
||||||
|
|
||||||
|
clear();
|
||||||
|
centerbig(20,"JOIN NETWORK GAME");
|
||||||
|
drawbigstring(JX,JY,"SLOT NAME");
|
||||||
|
for(i=0;i<MAXNETNODES;++i)
|
||||||
|
{
|
||||||
|
if(!netnodes[i].used) continue;
|
||||||
|
memmove(name,netnodes[i].name,16);
|
||||||
|
name[16]=0;
|
||||||
|
sprintf(temp," %d %s",i+1,name);
|
||||||
|
drawbigstring(JX,JY+(i+1)*bigfontyspace,temp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int tryjoin(int which) {
|
||||||
|
int res;
|
||||||
|
|
||||||
|
if (0 == (res = send_join(&gamelistentries[which].netname, playername)))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
for ( ;; ) {
|
||||||
|
switch (res) {
|
||||||
|
case 1: return 0;
|
||||||
|
case 2:
|
||||||
|
drawjoinscreen();
|
||||||
|
copyup();
|
||||||
|
break;
|
||||||
|
case 3: return 1;
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
scaninput();
|
||||||
|
while(anydown()) {
|
||||||
|
switch(takedown()) {
|
||||||
|
case 0x1b:
|
||||||
|
send_quit();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
res=scaninvite(200);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void main_menu(void) {
|
||||||
|
int sel;
|
||||||
|
|
||||||
|
while(!exitflag) {
|
||||||
|
menustart();
|
||||||
|
additem("BOMBER MAIN MENU");
|
||||||
|
additem("EXIT GAME");
|
||||||
|
additem("START SINGLE PLAYER GAME");
|
||||||
|
additem("OPTIONS");
|
||||||
|
// additem("REMAP MOVEMENT KEYS");
|
||||||
|
additem("START NETWORK GAME");
|
||||||
|
additem("JOIN NETWORK GAME");
|
||||||
|
sel=domenu(MENU_MAIN, NULL);
|
||||||
|
if(!sel) {exitflag=1;break;}
|
||||||
|
if(sel==1) {gamemode=1;break;}
|
||||||
|
if(sel==2) {gamemode=2;break;}
|
||||||
|
if(sel==3) {gamemode=3;break;}
|
||||||
|
if(sel==4) {gamemode=4;break;}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
char *densities[]={"PACKED","HIGH","MEDIUM","LOW"};
|
||||||
|
char *generosities[]={"LOW","MEDIUM","HIGH","RIDICULOUS"};
|
||||||
|
|
||||||
|
static void config_menu(void) {
|
||||||
|
int sel;
|
||||||
|
|
||||||
|
for(;;) {
|
||||||
|
menustart();
|
||||||
|
additem("GAME OPTIONS");
|
||||||
|
additem("RETURN TO MAIN MENU");
|
||||||
|
additem("DENSITY: %s",densities[configopts.density]);
|
||||||
|
additem("GENEROSITY: %s",generosities[configopts.generosity]);
|
||||||
|
additem("INITIAL FLAME LENGTH: %d",configopts.flames+1);
|
||||||
|
additem("INITIAL NUMBER OF BOMBS: %d",configopts.bombs+1);
|
||||||
|
sel=domenu(MENU_CONFIG, NULL);
|
||||||
|
if(!sel) {gamemode=0;break;}
|
||||||
|
if(sel==1) {
|
||||||
|
configopts.density+=menudelta;
|
||||||
|
configopts.density&=3;
|
||||||
|
}
|
||||||
|
if(sel==2) {
|
||||||
|
configopts.generosity+=menudelta;
|
||||||
|
configopts.generosity&=3;
|
||||||
|
}
|
||||||
|
if(sel==3) {
|
||||||
|
configopts.flames+=menudelta;
|
||||||
|
configopts.flames&=7;
|
||||||
|
}
|
||||||
|
if(sel==4) {
|
||||||
|
configopts.bombs+=menudelta;
|
||||||
|
configopts.bombs&=7;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void failure(char *str,...) {
|
||||||
|
gamemode=0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void draw_host_game(void) {
|
||||||
|
int i;
|
||||||
|
char *name;
|
||||||
|
char temp[64];
|
||||||
|
|
||||||
|
#define M3X (IXSIZE/3)
|
||||||
|
#define M3Y (IYSIZE/4)
|
||||||
|
|
||||||
|
clear();
|
||||||
|
centerbig(20,"HOST NETWORK GAME");
|
||||||
|
drawbigstring(M3X,M3Y,"SLOT NAME");
|
||||||
|
for(i=0;i<MAXNETNODES;++i) {
|
||||||
|
if(!netnodes[i].used) continue;
|
||||||
|
name=netnodes[i].name;
|
||||||
|
sprintf(temp," %d %s",i+1,name);
|
||||||
|
drawbigstring(M3X,M3Y+(i+2)*bigfontyspace,temp);
|
||||||
|
}
|
||||||
|
|
||||||
|
copyup();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void host_game(void) {
|
||||||
|
create_seed_unique();
|
||||||
|
if (!start_network_game()) {
|
||||||
|
failure("COULD NOT REGISTER GAME");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
draw_host_game();
|
||||||
|
for(;;) {
|
||||||
|
scaninput();
|
||||||
|
while(anydown()) {
|
||||||
|
switch(takedown()) {
|
||||||
|
case 0x1b:
|
||||||
|
unregistergame();
|
||||||
|
cancel_network_game();
|
||||||
|
gamemode=0;
|
||||||
|
return;
|
||||||
|
case ' ':
|
||||||
|
case 13:
|
||||||
|
unregistergame();
|
||||||
|
if (begin_network_game()) {
|
||||||
|
gamemode=5;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
send_invites();
|
||||||
|
draw_host_game();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!handle_joins()) continue;
|
||||||
|
|
||||||
|
send_invites();
|
||||||
|
draw_host_game();
|
||||||
|
}
|
||||||
|
|
||||||
|
gamemode=0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int join_game_pause(void) {
|
||||||
|
if (find_more_games()) return 0;
|
||||||
|
mypause();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void join_game(void) {
|
||||||
|
int i;
|
||||||
|
int sel = -1;
|
||||||
|
|
||||||
|
if (!searchgames()) {
|
||||||
|
gamemode = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
menuhistory[MENU_JOIN] = 0;
|
||||||
|
|
||||||
|
while (-1 == sel) {
|
||||||
|
menustart();
|
||||||
|
if (gamelistsize == 0) {
|
||||||
|
additem("JOIN NETWORK GAME - NO GAMES AVAILABLE");
|
||||||
|
addexit("EXIT");
|
||||||
|
} else {
|
||||||
|
additem("JOIN NETWORK GAME");
|
||||||
|
for (i = 0; i < gamelistsize; i++) {
|
||||||
|
additem(gamelistentries[i].name);
|
||||||
|
}
|
||||||
|
addexit("EXIT");
|
||||||
|
}
|
||||||
|
sel = domenu(MENU_JOIN, join_game_pause);
|
||||||
|
}
|
||||||
|
stop_search();
|
||||||
|
|
||||||
|
if(menuexit == sel || !gamelistsize) {
|
||||||
|
gamemode=0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(!tryjoin(sel)) {
|
||||||
|
gamemode=0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
network = NETWORK_SLAVE;
|
||||||
|
gamemode=5;
|
||||||
|
}
|
||||||
|
|
||||||
|
void (*modefunctions[])()= {
|
||||||
|
main_menu,
|
||||||
|
run_single_player,
|
||||||
|
config_menu,
|
||||||
|
host_game,
|
||||||
|
join_game,
|
||||||
|
run_network_game,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
void mainloop(void) {
|
||||||
|
exitflag=0;
|
||||||
|
while(!exitflag)
|
||||||
|
modefunctions[gamemode]();
|
||||||
|
}
|
10
menu.h
Normal file
10
menu.h
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
#ifndef MENU_H
|
||||||
|
#define MENU_H
|
||||||
|
|
||||||
|
void mainloop(void);
|
||||||
|
|
||||||
|
int iterate(void); /* bomber.c */
|
||||||
|
|
||||||
|
extern struct GameOptions configopts;
|
||||||
|
|
||||||
|
#endif
|
735
network.c
Normal file
735
network.c
Normal file
@ -0,0 +1,735 @@
|
|||||||
|
|
||||||
|
#include "bomber.h"
|
||||||
|
#include "announce.h"
|
||||||
|
#include "game.h"
|
||||||
|
#include "menu.h"
|
||||||
|
#include "network.h"
|
||||||
|
#include "utils.h"
|
||||||
|
|
||||||
|
#define MAXMSG 4096
|
||||||
|
|
||||||
|
int udpsocket;
|
||||||
|
const unsigned char gameversion[4]={0xda,0x01,0x00,0x05};
|
||||||
|
|
||||||
|
struct netnode netnodes[64];
|
||||||
|
|
||||||
|
static int informsize;
|
||||||
|
static unsigned char regpacket[64];
|
||||||
|
|
||||||
|
static struct sockaddr_in myname={0},mastername={0};
|
||||||
|
static socklen_t senderlength;
|
||||||
|
static struct sockaddr_in sender={0};
|
||||||
|
|
||||||
|
static unsigned char mesg[MAXMSG]="";
|
||||||
|
uchar needwhole=0;
|
||||||
|
int mydatacount;
|
||||||
|
int myslot;
|
||||||
|
network_type network = NETWORK_NONE;
|
||||||
|
|
||||||
|
static unsigned char hist[ACTIONHIST][MAXNETNODES];
|
||||||
|
int actionput,actioncount;
|
||||||
|
unsigned char actionblock[ACTIONHIST*MAXNETNODES];
|
||||||
|
|
||||||
|
int myaction;
|
||||||
|
unsigned char actions[MAXNETNODES];
|
||||||
|
|
||||||
|
unsigned char latestactions[MAXNETNODES];
|
||||||
|
long latestcounts[MAXNETNODES];
|
||||||
|
|
||||||
|
enum network_packet_types {
|
||||||
|
PKT_ACK, /* perfect copy of packet received */
|
||||||
|
/* join / host game */
|
||||||
|
/* slave -> master packets */
|
||||||
|
PKT_JOIN, /* 4 bytes version #, 4 bytes joinunique #, 16 bytes name */
|
||||||
|
PKT_QUIT, /* 4 bytes unique # */
|
||||||
|
/* master -> slave packets */
|
||||||
|
PKT_INVITE, /* 4 bytes unique #, 1 byte your slot (0xff for kick, no data following it) #, any # of 1:slot,16:name sets (-1 end) */
|
||||||
|
PKT_BEGIN, /* clone of INVITE */
|
||||||
|
PKT_CONFIG, /* 4 bytes unique #, config */
|
||||||
|
PKT_ACCEPT, /* 4 bytes join unique #, 132 bytes seed + unique #, config, slot info (see invite) */
|
||||||
|
PKT_REJECT, /* 4 bytes join unique #, 4 bytes version #, 1: reason */
|
||||||
|
/* ingame actions */
|
||||||
|
/* slave -> master packets */
|
||||||
|
PKT_MYDATA, /* 4 bytes unique #,4 bytes frame #, 1 byte data */
|
||||||
|
/* master -> slave packets */
|
||||||
|
PKT_STEP, /* 4 bytes unique #, 4 bytes frame #, 8 bytes ACT_* */
|
||||||
|
|
||||||
|
PKT_INVALID = 0xff
|
||||||
|
};
|
||||||
|
|
||||||
|
enum reject_reason {
|
||||||
|
REJECT_FULL,
|
||||||
|
REJECT_VERSION
|
||||||
|
/* TODO: password? */
|
||||||
|
};
|
||||||
|
|
||||||
|
/* all bytes stored MSB first */
|
||||||
|
|
||||||
|
/*
|
||||||
|
game startup:
|
||||||
|
<master and matcher>
|
||||||
|
Master: send REGISTER to matcher with optional password, wait for ack. If
|
||||||
|
timout, resend.
|
||||||
|
matcher: Wait for REGISTER packet, when received maintain database. respond
|
||||||
|
to sender with ACK. REGISTER packet can close a game also. The REGISTER
|
||||||
|
packet sent by the master has a unique word to be used to avoid confusion.
|
||||||
|
REGISTER packet also contains a game version #
|
||||||
|
|
||||||
|
After master registers game and receives ACK, just waits for slaves to contact.
|
||||||
|
|
||||||
|
<slave and matcher>
|
||||||
|
slave: send QUERY to matcher with optional password, wait for INFO, if timeout,
|
||||||
|
resend.
|
||||||
|
matcher: respond to QUERY with INFO packet. matcher need not maintain any
|
||||||
|
database for slave requests. INFO packet contains IP addr and port for each
|
||||||
|
master machine that matches the QUERY spec (ALL or password). Only a
|
||||||
|
certain MAX # of entries are sent if there are too many to choose from.
|
||||||
|
|
||||||
|
<slave and master>
|
||||||
|
slave: send JOIN to master, wait for INVITE. If timeout, resend. JOIN packet
|
||||||
|
contains the unique word the master created. JOIN also contains username.
|
||||||
|
master: Respond to JOIN with INVITE. INVITE contains unique word from JOIN
|
||||||
|
packet. INVITE either contains NO meaning game no longer exists or is closed
|
||||||
|
or player is not invited. IF yes, INVITE contains info on other players
|
||||||
|
already in the game (username and slot # for each). Master allocates the
|
||||||
|
slots and avoids confusion based on IP addr and port #. INVITE also contains
|
||||||
|
game options structure. Whenever a new player JOINS and is admitted, master
|
||||||
|
sends updated INVITE packets to everyone already in the JOIN list. Whenever
|
||||||
|
master changes game options, master sends out another set of INVITES
|
||||||
|
|
||||||
|
Duplicate JOINS are answered with updated INVITE but nothing changes as far
|
||||||
|
as allocation.
|
||||||
|
|
||||||
|
Master player launches game after he's satisfied everyone has joined.
|
||||||
|
|
||||||
|
Master sends BEGIN packet to everyone. BEGIN is identical to INVITE except
|
||||||
|
that the data is final. Slave must respond with its first MYDATA packet with
|
||||||
|
frame # of 0. If master times out waiting, master sends duplicate BEGIN to
|
||||||
|
wayward slaves. Once master has received MYDATA from everyone, game starts.
|
||||||
|
|
||||||
|
Within game slave sends MYDATA to master and waits for STEP packet. If
|
||||||
|
timeout, slave sends duplicate MYDATA.
|
||||||
|
|
||||||
|
If master times out waiting for a slave's MYDATA, slave gets dropped. MYDATAs
|
||||||
|
received will be answered with PKT_QUIT.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/* Network I/O, building/checking packets */
|
||||||
|
|
||||||
|
#if defined (TEST_LATENCY)
|
||||||
|
#define NUMQ 512
|
||||||
|
struct message {
|
||||||
|
int time;
|
||||||
|
struct sockaddr_in *to;
|
||||||
|
int tosize;
|
||||||
|
unsigned char msg[512];
|
||||||
|
int len;
|
||||||
|
} message[NUMQ]={0};
|
||||||
|
|
||||||
|
|
||||||
|
outmsgs() {
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for(i=0;i<NUMQ;++i) {
|
||||||
|
if(message[i].time) {
|
||||||
|
--message[i].time;
|
||||||
|
if(message[i].time) continue;
|
||||||
|
sendto(udpsocket,message[i].msg,message[i].len,0,
|
||||||
|
message[i].to,sizeof(struct sockaddr_in));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static int putmsg(struct sockaddr_in *toname,unsigned char *msg,int len) {
|
||||||
|
int status;
|
||||||
|
|
||||||
|
#if defined (TEST_LATENCY)
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for(i=0;i<NUMQ;++i) {
|
||||||
|
if(!message[i].time) {
|
||||||
|
message[i].time=10;
|
||||||
|
message[i].to=toname;
|
||||||
|
memcpy(message[i].msg,msg,len);
|
||||||
|
message[i].len=len;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
#else
|
||||||
|
status=sendto(udpsocket,msg,len,0,
|
||||||
|
(struct sockaddr *)toname,sizeof(struct sockaddr_in));
|
||||||
|
return status;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static int getmsg(int msec) {
|
||||||
|
int size;
|
||||||
|
|
||||||
|
memset(&sender,0,sizeof(sender));
|
||||||
|
senderlength=sizeof(sender);
|
||||||
|
if(msec) {
|
||||||
|
struct timeval timeout;
|
||||||
|
fd_set readfds;
|
||||||
|
int res;
|
||||||
|
|
||||||
|
memset(&timeout,0,sizeof(timeout));
|
||||||
|
timeout.tv_sec=msec/1000;
|
||||||
|
timeout.tv_usec=(msec%1000)*1000;
|
||||||
|
FD_ZERO(&readfds);
|
||||||
|
FD_SET(udpsocket,&readfds);
|
||||||
|
res=select(udpsocket+1,&readfds,0,0,&timeout);
|
||||||
|
if(res<=0) return -1;
|
||||||
|
}
|
||||||
|
size=recvfrom(udpsocket,mesg,MAXMSG,0,
|
||||||
|
(struct sockaddr *)&sender,&senderlength);
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int isvalidunique(unsigned char *p) {
|
||||||
|
return 0 == memcmp(p, &network_unique, 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int isvalidversion(unsigned char *p) {
|
||||||
|
return 0 == memcmp(p, gameversion, 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned char* writeuint32(unsigned char *p, Uint32 i) {
|
||||||
|
p[0]=i>>24L;
|
||||||
|
p[1]=i>>16L;
|
||||||
|
p[2]=i>>8L;
|
||||||
|
p[3]=i;
|
||||||
|
return p+4;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Uint32 readuint32(unsigned char *p) {
|
||||||
|
return (p[0]<<24L) | (p[1]<<16L) | (p[2]<<8) | p[3];
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned char* write_unique(unsigned char *p) {
|
||||||
|
memcpy(p, &network_unique, 4);
|
||||||
|
return p + 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned char* write_version(unsigned char *p) {
|
||||||
|
memcpy(p, &gameversion, 4);
|
||||||
|
return p + 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int isvalidmsg_from_slave() {
|
||||||
|
int i;
|
||||||
|
void *host;
|
||||||
|
void *port;
|
||||||
|
|
||||||
|
if (!isvalidunique(mesg+1)) return -1;
|
||||||
|
host=&sender.sin_addr.s_addr;
|
||||||
|
port=&sender.sin_port;
|
||||||
|
for(i=1;i<MAXNETNODES;++i)
|
||||||
|
if(netnodes[i].used &&
|
||||||
|
!memcmp(&netnodes[i].netname.sin_addr.s_addr,host,4) &&
|
||||||
|
!memcmp(&netnodes[i].netname.sin_port,port,2))
|
||||||
|
return i;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int isvalidmsg_from_master() {
|
||||||
|
if (sender.sin_family != mastername.sin_family
|
||||||
|
|| sender.sin_addr.s_addr != mastername.sin_addr.s_addr
|
||||||
|
|| sender.sin_port != mastername.sin_port) return 0;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Handling game actions */
|
||||||
|
|
||||||
|
static void addactions(void) {
|
||||||
|
memmove(hist[actionput],actions,MAXNETNODES);
|
||||||
|
++actionput;
|
||||||
|
if(actionput==ACTIONHIST)
|
||||||
|
actionput=0;
|
||||||
|
++actioncount;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void buildactions(void) {
|
||||||
|
unsigned char *p;
|
||||||
|
int i,j;
|
||||||
|
p=actionblock;
|
||||||
|
i=0;
|
||||||
|
while(i<20) {
|
||||||
|
if(actioncount-i>0) {
|
||||||
|
j=actionput-i-1;
|
||||||
|
if(j<0) j+=ACTIONHIST;
|
||||||
|
memmove(p,hist[j],MAXNETNODES);
|
||||||
|
} else {
|
||||||
|
memset(p,0,MAXNETNODES);
|
||||||
|
}
|
||||||
|
p+=MAXNETNODES;
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sendactions(int which) {
|
||||||
|
unsigned char msg[512];
|
||||||
|
|
||||||
|
msg[0] = PKT_STEP;
|
||||||
|
write_unique(msg+1);
|
||||||
|
writeuint32(msg+5, actioncount);
|
||||||
|
memcpy(msg+9,actionblock,MAXNETNODES*ACTIONHIST);
|
||||||
|
putmsg(&netnodes[which].netname,msg,MAXNETNODES*ACTIONHIST+9);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sendmine(int frame) {
|
||||||
|
unsigned char msg[64];
|
||||||
|
|
||||||
|
msg[0] = PKT_MYDATA;
|
||||||
|
write_unique(msg+1);
|
||||||
|
writeuint32(msg+5, frame);
|
||||||
|
msg[9]=myaction;
|
||||||
|
putmsg(&mastername,msg,10);
|
||||||
|
}
|
||||||
|
|
||||||
|
int networktraffic(void) {
|
||||||
|
int i;
|
||||||
|
int length;
|
||||||
|
int whosent;
|
||||||
|
unsigned char newactions[MAXNETNODES];
|
||||||
|
long now;
|
||||||
|
long count;
|
||||||
|
|
||||||
|
switch (network) {
|
||||||
|
case NETWORK_NONE:
|
||||||
|
return -1;
|
||||||
|
case NETWORK_MASTER:
|
||||||
|
memcpy(newactions,latestactions,MAXNETNODES);
|
||||||
|
newactions[0]=myaction;
|
||||||
|
now=gtime();
|
||||||
|
for(;;) {
|
||||||
|
if(gtime()-now>15) break;
|
||||||
|
length=getmsg(5);
|
||||||
|
if(length>0 && *mesg!=PKT_MYDATA) fprintf(stderr, "Strange packet %d\n", (int) *mesg);
|
||||||
|
// check for unexpected old packets...
|
||||||
|
// for example JOIN on frame 0, respond with BEGIN if player already in game
|
||||||
|
// respond with uninvite INVITE on JOIN from others
|
||||||
|
if(length<10) continue;
|
||||||
|
whosent = isvalidmsg_from_slave();
|
||||||
|
if(whosent<=0) continue;
|
||||||
|
count=readuint32(mesg+5);
|
||||||
|
if(count>latestcounts[whosent]) {
|
||||||
|
latestcounts[whosent]=count;
|
||||||
|
newactions[whosent]=mesg[9];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(myaction==ACT_QUIT) {
|
||||||
|
for(i=1;i<MAXNETNODES;++i)
|
||||||
|
if(netnodes[i].used)
|
||||||
|
newactions[i]=ACT_QUIT;
|
||||||
|
}
|
||||||
|
|
||||||
|
memmove(actions,newactions,sizeof(actions));
|
||||||
|
addactions();
|
||||||
|
buildactions();
|
||||||
|
for(i=1;i<MAXNETNODES;++i)
|
||||||
|
if(netnodes[i].used)
|
||||||
|
sendactions(i);
|
||||||
|
for(i=1;i<MAXNETNODES;++i)
|
||||||
|
if(netnodes[i].used && actions[i]==ACT_QUIT)
|
||||||
|
netnodes[i].used=0;
|
||||||
|
return actioncount;
|
||||||
|
case NETWORK_SLAVE:
|
||||||
|
{
|
||||||
|
long latest=-1;
|
||||||
|
long lastsent;
|
||||||
|
|
||||||
|
lastsent=now=gtime();
|
||||||
|
++mydatacount;
|
||||||
|
sendmine(mydatacount);
|
||||||
|
for(;;) {
|
||||||
|
/*
|
||||||
|
if(gtime()-lastsent>=1)
|
||||||
|
{
|
||||||
|
lastsent=gtime();
|
||||||
|
sendmine(mydatacount);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
if(latest>=0 && gtime()-now>3) break;
|
||||||
|
length=getmsg(3);
|
||||||
|
if(length==MAXNETNODES*ACTIONHIST+9 &&
|
||||||
|
sender.sin_addr.s_addr==mastername.sin_addr.s_addr &&
|
||||||
|
sender.sin_port==mastername.sin_port) {
|
||||||
|
i=readuint32(mesg+5);
|
||||||
|
if(i<latest) continue;
|
||||||
|
latest=i;
|
||||||
|
memmove(actionblock,mesg+9,MAXNETNODES*ACTIONHIST);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return latest;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Handling socket init */
|
||||||
|
|
||||||
|
int winsock=0;
|
||||||
|
|
||||||
|
void getsocket(void) {
|
||||||
|
int status;
|
||||||
|
socklen_t slen = sizeof(myname);
|
||||||
|
#if defined(__WIN32__) || defined(WIN32)
|
||||||
|
char dummydata[128];
|
||||||
|
|
||||||
|
if(WSAStartup(0x0101,(void *)dummydata)) {
|
||||||
|
printf("Windows dumped\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
winsock=1;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
udpsocket=socket(PF_INET,SOCK_DGRAM,IPPROTO_UDP);
|
||||||
|
if(udpsocket==-1) {
|
||||||
|
perror("socket()");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
memset(&myname,0,sizeof(myname));
|
||||||
|
myname.sin_family=AF_INET;
|
||||||
|
myname.sin_addr.s_addr=htonl(INADDR_ANY);
|
||||||
|
myname.sin_port=htons(0);
|
||||||
|
status=bind(udpsocket,(struct sockaddr *) &myname,sizeof(myname));
|
||||||
|
if(-1 == status) {
|
||||||
|
perror("bind()");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
status = getsockname(udpsocket, (struct sockaddr *) &myname, &slen);
|
||||||
|
if(-1 == status) {
|
||||||
|
perror("getsockname()");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void freesocket(void) {
|
||||||
|
#if defined(__WIN32__) || defined(WIN32)
|
||||||
|
if(!winsock) return;
|
||||||
|
WSACleanup();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Join / Host Games */
|
||||||
|
|
||||||
|
/* Master side */
|
||||||
|
|
||||||
|
static unsigned char* write_inform(unsigned char* put) {
|
||||||
|
int i;
|
||||||
|
|
||||||
|
*put++ = 0xff; /* slot specific for each slave */
|
||||||
|
for (i = 0; i < MAXNETNODES; ++i) {
|
||||||
|
if(!netnodes[i].used) continue;
|
||||||
|
*put++ = i;
|
||||||
|
memmove(put, netnodes[i].name, 16);
|
||||||
|
put += 16;
|
||||||
|
}
|
||||||
|
*put++ = 0xff;
|
||||||
|
return put;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void send_inform_all(unsigned char type) {
|
||||||
|
int i;
|
||||||
|
unsigned char *put = mesg;
|
||||||
|
|
||||||
|
*put++ = type;
|
||||||
|
put = write_unique(put);
|
||||||
|
put = write_inform(put);
|
||||||
|
informsize = put-mesg;
|
||||||
|
|
||||||
|
for(i=1;i<MAXNETNODES;++i) {
|
||||||
|
if(netnodes[i].used) {
|
||||||
|
mesg[5] = i;
|
||||||
|
putmsg(&netnodes[i].netname, mesg, informsize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned char* write_config(unsigned char* put) {
|
||||||
|
*put++ = configopts.density;
|
||||||
|
*put++ = configopts.flames;
|
||||||
|
*put++ = configopts.bombs;
|
||||||
|
*put++ = configopts.generosity;
|
||||||
|
return put;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void build_config() {
|
||||||
|
unsigned char *put;
|
||||||
|
|
||||||
|
put=mesg;
|
||||||
|
*put++=PKT_CONFIG;
|
||||||
|
put = write_config(put);
|
||||||
|
informsize=put-mesg;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void send_config1(int which) {
|
||||||
|
putmsg(&netnodes[which].netname,mesg,informsize);
|
||||||
|
}
|
||||||
|
|
||||||
|
void send_config() {
|
||||||
|
int i;
|
||||||
|
build_config();
|
||||||
|
for (i = 1; i < MAXNETNODES; ++i)
|
||||||
|
if (netnodes[i].used)
|
||||||
|
send_config1(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void send_reject(struct sockaddr_in *toname, Uint32 network_join_unique, unsigned char reason) {
|
||||||
|
mesg[0] = PKT_REJECT;
|
||||||
|
memcpy(mesg+1, &network_join_unique, sizeof(network_join_unique));
|
||||||
|
write_version(mesg+5);
|
||||||
|
mesg[9] = reason;
|
||||||
|
putmsg(&sender,mesg,10);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void send_accept(Uint32 network_join_unique) {
|
||||||
|
unsigned char *put = mesg;
|
||||||
|
|
||||||
|
*put++ = PKT_ACCEPT;
|
||||||
|
memcpy(put, &network_join_unique, sizeof(network_join_unique));
|
||||||
|
put += sizeof(network_join_unique);
|
||||||
|
put = write_seed_unique(put);
|
||||||
|
put = write_config(put);
|
||||||
|
put = write_inform(put);
|
||||||
|
putmsg(&sender,mesg,put-mesg);
|
||||||
|
}
|
||||||
|
|
||||||
|
int start_network_game() {
|
||||||
|
if(!registergame(playername, myname.sin_port, gameversion)) return 0;
|
||||||
|
memset(netnodes,0,sizeof(netnodes));
|
||||||
|
netnodes[0].used=1;
|
||||||
|
memmove(netnodes[0].name,playername,16);
|
||||||
|
myslot=0;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int begin_network_game() {
|
||||||
|
send_inform_all(PKT_BEGIN);
|
||||||
|
network = NETWORK_MASTER;
|
||||||
|
/* TODO: wait for ack */
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void send_invites() {
|
||||||
|
send_inform_all(PKT_INVITE);
|
||||||
|
}
|
||||||
|
|
||||||
|
void cancel_network_game() {
|
||||||
|
int i;
|
||||||
|
|
||||||
|
mesg[0] = PKT_INVITE;
|
||||||
|
write_unique(mesg+1);
|
||||||
|
mesg[5] = 0xff;
|
||||||
|
|
||||||
|
for(i=1;i<MAXNETNODES;++i) {
|
||||||
|
if(netnodes[i].used) {
|
||||||
|
putmsg(&netnodes[i].netname,mesg,6);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int handle_joins() {
|
||||||
|
int size;
|
||||||
|
int i, j;
|
||||||
|
unsigned char temp[64];
|
||||||
|
Uint32 network_join_unique;
|
||||||
|
|
||||||
|
size=getmsg(40);
|
||||||
|
switch (*mesg) {
|
||||||
|
case PKT_JOIN:
|
||||||
|
if (size < 25) return 0;
|
||||||
|
memcpy(&network_join_unique, mesg+5, sizeof(network_join_unique));
|
||||||
|
if (!isvalidversion(mesg+1)) {
|
||||||
|
send_reject(&sender, network_join_unique, REJECT_VERSION);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case PKT_QUIT:
|
||||||
|
if (size < 5 || !isvalidunique(mesg+1)) return 0;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Find host in list:
|
||||||
|
* i == MAXETNODES: host not found, otherwise the found host
|
||||||
|
* only if host not found:
|
||||||
|
* j == -1: no free slot, otherwise first free slot
|
||||||
|
*/
|
||||||
|
j = -1;
|
||||||
|
for (i = 1; i < MAXNETNODES; ++i) {
|
||||||
|
if (!netnodes[i].used) {
|
||||||
|
if (-1 == j) j = i;
|
||||||
|
continue; /* don't compare with unused host */
|
||||||
|
}
|
||||||
|
if(memcmp(&netnodes[i].netname.sin_addr.s_addr,
|
||||||
|
&sender.sin_addr.s_addr,4)) continue;
|
||||||
|
if(memcmp(&netnodes[i].netname.sin_port,
|
||||||
|
&sender.sin_port,2)) continue;
|
||||||
|
/* found host */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (*mesg) {
|
||||||
|
case PKT_QUIT:
|
||||||
|
if(i < MAXNETNODES) /* if host found, reset entry */
|
||||||
|
memset(netnodes+i,0,sizeof(struct netnode));
|
||||||
|
/* send always ACK for QUITs */
|
||||||
|
*temp=PKT_ACK;
|
||||||
|
memmove(temp+1,mesg,5);
|
||||||
|
putmsg(&sender,temp,6);
|
||||||
|
break;
|
||||||
|
case PKT_JOIN:
|
||||||
|
if (i==MAXNETNODES && j==-1) { /* reject */
|
||||||
|
send_reject(&sender, network_join_unique, REJECT_FULL);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(i==MAXNETNODES) i=j;
|
||||||
|
memmove(&netnodes[i].netname.sin_addr.s_addr,
|
||||||
|
&sender.sin_addr.s_addr,4);
|
||||||
|
memmove(&netnodes[i].netname.sin_port,
|
||||||
|
&sender.sin_port,2);
|
||||||
|
netnodes[i].netname.sin_family=AF_INET;
|
||||||
|
netnodes[i].used=1;
|
||||||
|
memcpy(netnodes[i].name,mesg+9,16);
|
||||||
|
netnodes[i].name[15] = '\0';
|
||||||
|
|
||||||
|
send_accept(network_join_unique);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Client side */
|
||||||
|
|
||||||
|
static int read_inform(unsigned char** pbuf, int *psize) {
|
||||||
|
unsigned char *buf = *pbuf;
|
||||||
|
int size = *psize;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (size < 1) return 0;
|
||||||
|
myslot = *buf++; size--;
|
||||||
|
if (0xff == myslot) return 1;
|
||||||
|
|
||||||
|
if (size < 1) return 0;
|
||||||
|
while (0xff != *buf) {
|
||||||
|
i = *buf++; size--;
|
||||||
|
if (size < 17 || i >= MAXNETNODES) return 0;
|
||||||
|
netnodes[i].used = 1;
|
||||||
|
memcpy(netnodes[i].name, buf, 16);
|
||||||
|
buf += 16;
|
||||||
|
size -= 16;
|
||||||
|
}
|
||||||
|
|
||||||
|
*psize = size;
|
||||||
|
*pbuf = buf;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void read_config(unsigned char* buf) {
|
||||||
|
GameOptions opts;
|
||||||
|
memset(&opts, 0, sizeof(opts));
|
||||||
|
opts.density = *buf++;
|
||||||
|
opts.flames = *buf++;
|
||||||
|
opts.bombs = *buf++;
|
||||||
|
opts.generosity = *buf++;
|
||||||
|
set_game_options(&opts);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* returns 0=ignore packet,1=we're rejected,2=INVITE/CONFIG,3=BEGIN */
|
||||||
|
int scaninvite(int msec) {
|
||||||
|
int size;
|
||||||
|
unsigned char *take;
|
||||||
|
|
||||||
|
size = getmsg(msec);
|
||||||
|
|
||||||
|
if (size < 6) return 0;
|
||||||
|
if (*mesg!=PKT_INVITE && *mesg!=PKT_BEGIN && *mesg!=PKT_CONFIG) return 0;
|
||||||
|
if (!isvalidmsg_from_master()) return 0;
|
||||||
|
if (!isvalidunique(mesg+1)) return 0;
|
||||||
|
|
||||||
|
take = mesg+5;
|
||||||
|
size -= 5;
|
||||||
|
|
||||||
|
switch (*mesg) {
|
||||||
|
case PKT_INVITE:
|
||||||
|
case PKT_BEGIN:
|
||||||
|
if (!read_inform(&take, &size)) return 0;
|
||||||
|
if (0xff == myslot) return 1; /* master closed game */
|
||||||
|
break;
|
||||||
|
case PKT_CONFIG:
|
||||||
|
if (size < 4) return 0;
|
||||||
|
read_config(take);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*mesg == PKT_BEGIN) {
|
||||||
|
return 3;
|
||||||
|
} else {
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int send_join(struct sockaddr_in *netname, char playername[16]) {
|
||||||
|
int size;
|
||||||
|
long now;
|
||||||
|
Uint32 join_unique = gtime();
|
||||||
|
unsigned char *buf;
|
||||||
|
|
||||||
|
mastername = *netname;
|
||||||
|
*regpacket=PKT_JOIN;
|
||||||
|
write_version(regpacket + 1);
|
||||||
|
writeuint32(regpacket+5, join_unique);
|
||||||
|
memcpy(regpacket+9, playername, 16);
|
||||||
|
now=longtime();
|
||||||
|
putmsg(&mastername,regpacket,1+4+4+16);
|
||||||
|
while(longtime()-now < 3) {
|
||||||
|
if (0 == (size = getmsg(1000))) {
|
||||||
|
/* got no message, send join again */
|
||||||
|
putmsg(&mastername,regpacket,1+4+4+16);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (size < 5) continue;
|
||||||
|
if (readuint32(mesg+1) != join_unique) continue;
|
||||||
|
switch (*mesg) {
|
||||||
|
case PKT_ACCEPT:
|
||||||
|
if (size < 1+4+132+4+2) continue;
|
||||||
|
read_seed_unique(mesg + 5);
|
||||||
|
read_config(mesg+137);
|
||||||
|
buf = mesg+141;
|
||||||
|
size -= 141;
|
||||||
|
if (!read_inform(&buf,&size)) return 0;
|
||||||
|
return 2;
|
||||||
|
case PKT_REJECT:
|
||||||
|
/* TODO: print reject message */
|
||||||
|
return 0;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void send_quit() {
|
||||||
|
long now;
|
||||||
|
int size;
|
||||||
|
|
||||||
|
*regpacket = PKT_QUIT;
|
||||||
|
write_unique(regpacket+1);
|
||||||
|
now=longtime();
|
||||||
|
while(longtime()-now<10) {
|
||||||
|
putmsg(&mastername,regpacket,5);
|
||||||
|
size=getmsg(1000);
|
||||||
|
if(size<6) continue;
|
||||||
|
if(mesg[0] != PKT_ACK || mesg[1] != PKT_QUIT) continue;
|
||||||
|
if (isvalidunique(mesg+2))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
56
network.h
Normal file
56
network.h
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
#ifndef NETWORK_H
|
||||||
|
#define NETWORK_H
|
||||||
|
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
|
||||||
|
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
|
4
sound.c
4
sound.c
@ -7,6 +7,10 @@
|
|||||||
|
|
||||||
#include "sound.h"
|
#include "sound.h"
|
||||||
|
|
||||||
|
#ifndef DATADIR
|
||||||
|
#define DATADIR "data"
|
||||||
|
#endif
|
||||||
|
|
||||||
static char dirlist[]=DATADIR;
|
static char dirlist[]=DATADIR;
|
||||||
|
|
||||||
static int readsound(int num);
|
static int readsound(int num);
|
||||||
|
4
sound.h
4
sound.h
@ -1,10 +1,6 @@
|
|||||||
#ifndef SOUND_H
|
#ifndef SOUND_H
|
||||||
#define SOUND_H
|
#define SOUND_H
|
||||||
|
|
||||||
#ifndef DATADIR
|
|
||||||
#define DATADIR "data"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
int soundopen(void);
|
int soundopen(void);
|
||||||
void soundclose(void);
|
void soundclose(void);
|
||||||
void playsound(int n);
|
void playsound(int n);
|
||||||
|
240
utils.c
Normal file
240
utils.c
Normal file
@ -0,0 +1,240 @@
|
|||||||
|
|
||||||
|
#include "bomber.h"
|
||||||
|
#include "utils.h"
|
||||||
|
#include "gfx.h"
|
||||||
|
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
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");
|
||||||
|
}
|
33
utils.h
Normal file
33
utils.h
Normal file
@ -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
|
Loading…
Reference in New Issue
Block a user