From 82023866ab4c7483652e9d4605290e39ced3bec3 Mon Sep 17 00:00:00 2001 From: Magnus Auvinen Date: Thu, 24 May 2007 20:54:08 +0000 Subject: large change. moved around all source. splitted server and client into separate files --- src/client.cpp | 713 ------------ src/datafile.cpp | 444 -------- src/datafile.h | 20 - src/editor.cpp | 1229 --------------------- src/editor/editor.cpp | 1229 +++++++++++++++++++++ src/engine/client/client.cpp | 712 ++++++++++++ src/engine/client/gfx.cpp | 583 ++++++++++ src/engine/client/snd.cpp | 520 +++++++++ src/engine/client/ui.cpp | 115 ++ src/engine/client/ui.h | 33 + src/engine/datafile.cpp | 444 ++++++++ src/engine/datafile.h | 20 + src/engine/interface.h | 711 ++++++++++++ src/engine/lzw.cpp | 223 ++++ src/engine/lzw.h | 2 + src/engine/map.cpp | 48 + src/engine/packet.h | 288 +++++ src/engine/server/server.cpp | 5 + src/engine/snapshot.h | 19 + src/engine/versions.h | 2 + src/game/client/game_client.cpp | 1993 +++++++++++++++++++++++++++++++++ src/game/client/mapres_image.cpp | 41 + src/game/client/mapres_image.h | 18 + src/game/client/mapres_tilemap.cpp | 54 + src/game/client/mapres_tilemap.h | 19 + src/game/client/menu.cpp | 600 ++++++++++ src/game/game.h | 2 +- src/game/game_client.cpp | 1993 --------------------------------- src/game/game_server.cpp | 2122 ----------------------------------- src/game/mapres_col.cpp | 2 +- src/game/mapres_image.cpp | 41 - src/game/mapres_image.h | 18 - src/game/mapres_tilemap.cpp | 54 - src/game/mapres_tilemap.h | 19 - src/game/server/game_server.cpp | 2131 ++++++++++++++++++++++++++++++++++++ src/gfx.cpp | 581 ---------- src/interface.h | 711 ------------ src/lzw.cpp | 223 ---- src/lzw.h | 2 - src/map.cpp | 48 - src/menu.cpp | 600 ---------- src/packet.h | 287 ----- src/server.cpp | 668 ----------- src/snapshot.h | 19 - src/snd.cpp | 519 --------- src/ui.cpp | 115 -- src/ui.h | 33 - src/versions.h | 2 - 48 files changed, 9812 insertions(+), 10463 deletions(-) delete mode 100644 src/client.cpp delete mode 100644 src/datafile.cpp delete mode 100644 src/datafile.h delete mode 100644 src/editor.cpp create mode 100644 src/editor/editor.cpp create mode 100644 src/engine/client/client.cpp create mode 100644 src/engine/client/gfx.cpp create mode 100644 src/engine/client/snd.cpp create mode 100644 src/engine/client/ui.cpp create mode 100644 src/engine/client/ui.h create mode 100644 src/engine/datafile.cpp create mode 100644 src/engine/datafile.h create mode 100644 src/engine/interface.h create mode 100644 src/engine/lzw.cpp create mode 100644 src/engine/lzw.h create mode 100644 src/engine/map.cpp create mode 100644 src/engine/packet.h create mode 100644 src/engine/server/server.cpp create mode 100644 src/engine/snapshot.h create mode 100644 src/engine/versions.h create mode 100644 src/game/client/game_client.cpp create mode 100644 src/game/client/mapres_image.cpp create mode 100644 src/game/client/mapres_image.h create mode 100644 src/game/client/mapres_tilemap.cpp create mode 100644 src/game/client/mapres_tilemap.h create mode 100644 src/game/client/menu.cpp delete mode 100644 src/game/game_client.cpp delete mode 100644 src/game/game_server.cpp delete mode 100644 src/game/mapres_image.cpp delete mode 100644 src/game/mapres_image.h delete mode 100644 src/game/mapres_tilemap.cpp delete mode 100644 src/game/mapres_tilemap.h create mode 100644 src/game/server/game_server.cpp delete mode 100644 src/gfx.cpp delete mode 100644 src/interface.h delete mode 100644 src/lzw.cpp delete mode 100644 src/lzw.h delete mode 100644 src/map.cpp delete mode 100644 src/menu.cpp delete mode 100644 src/packet.h delete mode 100644 src/server.cpp delete mode 100644 src/snapshot.h delete mode 100644 src/snd.cpp delete mode 100644 src/ui.cpp delete mode 100644 src/ui.h delete mode 100644 src/versions.h (limited to 'src') diff --git a/src/client.cpp b/src/client.cpp deleted file mode 100644 index 2f231117..00000000 --- a/src/client.cpp +++ /dev/null @@ -1,713 +0,0 @@ -#include -#include -#include -#include -#include - -#include -#include -#include -#include "interface.h" - - -#include "packet.h" -#include "snapshot.h" -#include "ui.h" - -#include "lzw.h" - -#include "versions.h" - -using namespace baselib; - -// --- string handling (MOVE THESE!!) --- -void snap_encode_string(const char *src, int *dst, int length, int max_length) -{ - const unsigned char *p = (const unsigned char *)src; - - // handle whole int - for(int i = 0; i < length/4; i++) - { - *dst = (p[0]<<24|p[1]<<16|p[2]<<8|p[3]); - p += 4; - dst++; - } - - // take care of the left overs - int left = length%4; - if(left) - { - unsigned last = 0; - switch(left) - { - case 3: last |= p[2]<<8; - case 2: last |= p[1]<<16; - case 1: last |= p[0]<<24; - } - *dst = last; - } -} - -void snap_decode_string(const int *src, char *dst, int max_length) -{ - dbg_assert((max_length%4) == 0, "length must be power of 4"); - for(int i = 0; i < max_length; i++) - dst[0] = 0; - - for(int i = 0; i < max_length/4; i++) - { - dst[0] = (*src>>24)&0xff; - dst[1] = (*src>>16)&0xff; - dst[2] = (*src>>8)&0xff; - dst[3] = (*src)&0xff; - src++; - dst+=4; - } - dst[-1] = 0; // make sure to zero terminate -} - -// --- input wrappers --- -static int keyboard_state[2][keys::last]; -static int keyboard_current = 0; -static int keyboard_first = 1; - -void inp_mouse_relative(int *x, int *y) { mouse::position(x, y); } -int inp_key_pressed(int key) { return keyboard_state[keyboard_current][key]; } -int inp_key_was_pressed(int key) { return keyboard_state[keyboard_current^1][key]; } -int inp_key_down(int key) { return inp_key_pressed(key)&&!inp_key_was_pressed(key); } -int inp_mouse_button_pressed(int button) { return mouse::pressed(button); } - -void inp_update() -{ - if(keyboard_first) - { - // make sure to reset - keyboard_first = 0; - inp_update(); - } - - keyboard_current = keyboard_current^1; - for(int i = 0; i < keys::last; i++) - keyboard_state[keyboard_current][i] = keys::pressed(i); -} - -// --- input snapping --- -static int input_data[MAX_INPUT_SIZE]; -static int input_data_size; -static int input_is_changed = 1; -void snap_input(void *data, int size) -{ - if(input_data_size != size || memcmp(input_data, data, size)) - input_is_changed = 1; - mem_copy(input_data, data, size); - input_data_size = size; -} - -// -- snapshot handling --- -enum -{ - SNAP_INCOMMING=2, - NUM_SNAPSHOT_TYPES=3, -}; - -static snapshot *snapshots[NUM_SNAPSHOT_TYPES]; -static char snapshot_data[NUM_SNAPSHOT_TYPES][MAX_SNAPSHOT_SIZE]; -static int recived_snapshots; -static int64 snapshot_start_time; -static int64 local_start_time; - -float client_localtime() -{ - return (time_get()-local_start_time)/(float)(time_freq()); -} - -void *snap_get_item(int snapid, int index, snap_item *item) -{ - dbg_assert(snapid >= 0 && snapid < NUM_SNAPSHOT_TYPES, "invalid snapid"); - snapshot::item *i = snapshots[snapid]->get_item(index); - item->type = i->type(); - item->id = i->id(); - return (void *)i->data; -} - -int snap_num_items(int snapid) -{ - dbg_assert(snapid >= 0 && snapid < NUM_SNAPSHOT_TYPES, "invalid snapid"); - return snapshots[snapid]->num_items; -} - -static void snap_init() -{ - snapshots[SNAP_INCOMMING] = (snapshot*)snapshot_data[0]; - snapshots[SNAP_CURRENT] = (snapshot*)snapshot_data[1]; - snapshots[SNAP_PREV] = (snapshot*)snapshot_data[2]; - mem_zero(snapshot_data, NUM_SNAPSHOT_TYPES*MAX_SNAPSHOT_SIZE); - recived_snapshots = 0; -} - -float snap_intratick() -{ - return (time_get() - snapshot_start_time)/(float)(time_freq()/SERVER_TICK_SPEED); -} - -void *snap_find_item(int snapid, int type, int id) -{ - // TODO: linear search. should be fixed. - for(int i = 0; i < snapshots[snapid]->num_items; i++) - { - snapshot::item *itm = snapshots[snapid]->get_item(i); - if(itm->type() == type && itm->id() == id) - return (void *)itm->data; - } - return 0x0; -} - - -int menu_loop(); -float frametime = 0.0001f; - -float client_frametime() -{ - return frametime; -} - -void unpack(const char *src, const char *fmt, ...) -{ -} - -/*int modc_onmsg(int msg) -{ - msg_get("iis") -}*/ - -/* - i = int (int i) - s = string (const char *str) - r = raw data (int size, void *data) -*/ - -/* -class packet2 -{ -private: - // packet data - struct header - { - unsigned msg; - unsigned ack; - unsigned seq; - }; - - unsigned char packet_data[MAX_PACKET_SIZE]; - unsigned char *current; - - enum - { - MAX_PACKET_SIZE = 1024, - }; - -public: - packet2() - { - current = packet_data; - current += sizeof(header); - } - - int pack(char *dst, const char *fmt, ...) - { - va_list arg_list; - va_start(arg_list, fmt); - while(*fmt) - { - if(*fmt == 's') - { - // pack string - const char *s = va_arg(arg_list, const char*); - *dst++ = 2; - while(*s) - { - *dst = *s; - dst++; - s++; - } - *dst = 0; // null terminate - dst++; - fmt++; - } - else if(*fmt == 'i') - { - // pack int - int i = va_arg(arg_list, int); - *dst++ = 1; - *dst++ = (i>>24)&0xff; - *dst++ = (i>>16)&0xff; - *dst++ = (i>>8)&0xff; - *dst++ = i&0xff; - fmt++; - } - else - { - dbg_break(); // error - break; - } - } - va_end(arg_list); - } -}; -*/ -/* -int msg_get(const char *fmt) -{ - -} - -int client_msg_send(int msg, const char *fmt, ...) - -int server_msg_send(int msg, const char *fmt, ...) -{ - -}*/ - -// --- client --- -class client -{ -public: - socket_udp4 socket; - connection conn; - int64 reconnect_timer; - - int snapshot_part; - - int debug_font; // TODO: rfemove this line - - // data to hold three snapshots - // previous, - char name[MAX_NAME_LENGTH]; - - bool fullscreen; - - enum - { - STATE_OFFLINE, - STATE_CONNECTING, - STATE_LOADING, - STATE_ONLINE, - STATE_BROKEN, - STATE_QUIT, - }; - - int state; - int get_state() { return state; } - void set_state(int s) - { - dbg_msg("game", "state change. last=%d current=%d", state, s); - state = s; - } - - void set_name(const char *new_name) - { - mem_zero(name, MAX_NAME_LENGTH); - strncpy(name, new_name, MAX_NAME_LENGTH); - name[MAX_NAME_LENGTH-1] = 0; - } - - void set_fullscreen(bool flag) { fullscreen = flag; } - - void send_packet(packet *p) - { - conn.send(p); - } - - void send_connect() - { - recived_snapshots = 0; - - /* - pack(NETMSG_CLIENT_CONNECT, "sssss", - TEEWARS_NETVERSION, - name, - "no clan", - "password", - "myskin"); - */ - - packet p(NETMSG_CLIENT_CONNECT); - p.write_str(TEEWARS_NETVERSION); // payload - p.write_str(name); - p.write_str("no clan"); - p.write_str("password"); - p.write_str("myskin"); - send_packet(&p); - } - - void send_done() - { - packet p(NETMSG_CLIENT_DONE); - send_packet(&p); - } - - void send_error(const char *error) - { - /* - pack(NETMSG_CLIENT_ERROR, "s", error); - */ - packet p(NETMSG_CLIENT_ERROR); - p.write_str(error); - send_packet(&p); - //send_packet(&p); - //send_packet(&p); - } - - void send_input() - { - /* - pack(NETMSG_CLIENT_ERROR, "s", error); - */ - packet p(NETMSG_CLIENT_INPUT); - p.write_int(input_data_size); - for(int i = 0; i < input_data_size/4; i++) - p.write_int(input_data[i]); - send_packet(&p); - } - - void disconnect() - { - send_error("disconnected"); - set_state(STATE_OFFLINE); - map_unload(); - } - - void connect(netaddr4 *server_address) - { - conn.init(&socket, server_address); - - // start by sending connect - send_connect(); - set_state(STATE_CONNECTING); - reconnect_timer = time_get()+time_freq(); - } - - bool load_data() - { - debug_font = gfx_load_texture_tga("data/debug_font.tga"); - return true; - } - - void render() - { - gfx_clear(0.0f,0.0f,0.0f); - - // this should be moved around abit - if(get_state() == STATE_ONLINE) - { - modc_render(); - } - else if (get_state() != STATE_CONNECTING && get_state() != STATE_LOADING) - { - netaddr4 server_address; - int status = modmenu_render(&server_address, name, MAX_NAME_LENGTH); - - if (status == -1) - set_state(STATE_QUIT); - else if (status) - connect(&server_address); - } - else if (get_state() == STATE_CONNECTING) - { - static int64 start = time_get(); - static int tee_texture; - static int connecting_texture; - static bool inited = false; - - if (!inited) - { - tee_texture = gfx_load_texture_tga("data/gui_tee.tga"); - connecting_texture = gfx_load_texture_tga("data/gui/connecting.tga"); - - inited = true; - } - - gfx_mapscreen(0,0,400.0f,300.0f); - - float t = (time_get() - start) / (double)time_freq(); - - float speed = 2*sin(t); - - speed = 1.0f; - - float x = 208 + sin(t*speed) * 32; - float w = sin(t*speed + 3.149) * 64; - - ui_do_image(tee_texture, x, 95, w, 64); - ui_do_image(connecting_texture, 88, 150, 256, 64); - } - } - - void run(netaddr4 *server_address) - { - snapshot_part = 0; - - // init graphics and sound - if(!gfx_init(fullscreen)) - return; - - snd_init(); // sound is allowed to fail - - // load data - if(!load_data()) - return; - - // init snapshotting - snap_init(); - - // init the mod - modc_init(); - - // init menu - modmenu_init(); - - // open socket - if(!socket.open(0)) - { - dbg_msg("network/client", "failed to open socket"); - return; - } - - // connect to the server if wanted - if (server_address) - connect(server_address); - - int64 inputs_per_second = 50; - int64 time_per_input = time_freq()/inputs_per_second; - int64 game_starttime = time_get(); - int64 last_input = game_starttime; - - int64 reporttime = time_get(); - int64 reportinterval = time_freq()*1; - int frames = 0; - - mouse::set_mode(mouse::mode_relative); - - while (1) - { - frames++; - int64 frame_start_time = time_get(); - - // send input - if(get_state() == STATE_ONLINE) - { - if(input_is_changed || time_get() > last_input+time_freq()) - { - send_input(); - input_is_changed = 0; - last_input = time_get(); - } - } - - // update input - inp_update(); - - // - if(keys::pressed(keys::f1)) - mouse::set_mode(mouse::mode_absolute); - if(keys::pressed(keys::f2)) - mouse::set_mode(mouse::mode_relative); - - // pump the network - pump_network(); - - // render - render(); - - // swap the buffers - gfx_swap(); - - // check conditions - if(get_state() == STATE_BROKEN || get_state() == STATE_QUIT) - break; - - // be nice - //thread_sleep(1); - - if(reporttime < time_get()) - { - unsigned sent, recved; - conn.counter_get(&sent, &recved); - dbg_msg("client/report", "fps=%.02f", - frames/(float)(reportinterval/time_freq())); - frames = 0; - reporttime += reportinterval; - conn.counter_reset(); - } - - if (keys::pressed(keys::esc)) - if (get_state() == STATE_CONNECTING || get_state() == STATE_ONLINE) - disconnect(); - - // update frametime - frametime = (time_get()-frame_start_time)/(float)time_freq(); - } - - modc_shutdown(); - disconnect(); - - modmenu_shutdown(); - - gfx_shutdown(); - snd_shutdown(); - } - - void error(const char *msg) - { - dbg_msg("game", "error: %s", msg); - send_error(msg); - set_state(STATE_BROKEN); - } - - void process_packet(packet *p) - { - if(p->msg() == NETMSG_SERVER_ACCEPT) - { - const char *map; - map = p->read_str(); - - if(p->is_good()) - { - dbg_msg("client/network", "connection accepted, map=%s", map); - set_state(STATE_LOADING); - - if(map_load(map)) - { - modc_entergame(); - send_done(); - dbg_msg("client/network", "loading done"); - // now we will wait for two snapshots - // to finish the connection - } - else - { - error("failure to load map"); - } - } - } - else if(p->msg() == NETMSG_SERVER_SNAP) - { - //dbg_msg("client/network", "got snapshot"); - int num_parts = p->read_int(); - int part = p->read_int(); - int part_size = p->read_int(); - - if(p->is_good()) - { - if(snapshot_part == part) - { - const char *d = p->read_raw(part_size); - mem_copy((char*)snapshots[SNAP_INCOMMING] + part*MAX_SNAPSHOT_PACKSIZE, d, part_size); - snapshot_part++; - - if(snapshot_part == num_parts) - { - snapshot *tmp = snapshots[SNAP_PREV]; - snapshots[SNAP_PREV] = snapshots[SNAP_CURRENT]; - snapshots[SNAP_CURRENT] = tmp; - - // decompress snapshot - lzw_decompress(snapshots[SNAP_INCOMMING], snapshots[SNAP_CURRENT]); - - // apply snapshot, cycle pointers - recived_snapshots++; - snapshot_start_time = time_get(); - - // we got two snapshots until we see us self as connected - if(recived_snapshots == 2) - { - local_start_time = time_get(); - set_state(STATE_ONLINE); - } - - if(recived_snapshots > 2) - modc_newsnapshot(); - - snapshot_part = 0; - } - - } - else - { - dbg_msg("client", "snapshot reset!"); - snapshot_part = 0; - } - } - } - else - { - dbg_msg("server/client", "unknown packet %x", p->msg()); - } - } - - void pump_network() - { - while(1) - { - packet p; - netaddr4 from; - int bytes = socket.recv(&from, p.data(), p.max_size()); - - if(bytes <= 0) - break; - - process_packet(&p); - } - - // - if(get_state() == STATE_CONNECTING && time_get() > reconnect_timer) - { - send_connect(); - reconnect_timer = time_get() + time_freq(); - } - } -}; - -int client_main(int argc, char **argv) -{ - dbg_msg("client", "starting..."); - netaddr4 server_address(127, 0, 0, 1, 8303); - const char *name = "nameless jerk"; - bool connect_at_once = false; - bool fullscreen = true; - - // init network, need to be done first so we can do lookups - net_init(); - - // parse arguments - for(int i = 1; i < argc; i++) - { - if(argv[i][0] == '-' && argv[i][1] == 'c' && argv[i][2] == 0 && argc - i > 1) - { - // -c SERVER - i++; - if(net_host_lookup(argv[i], 8303, &server_address) != 0) - dbg_msg("main", "could not find the address of %s, connecting to localhost", argv[i]); - else - connect_at_once = true; - } - else if(argv[i][0] == '-' && argv[i][1] == 'n' && argv[i][2] == 0 && argc - i > 1) - { - // -n NAME - i++; - name = argv[i]; - } - else if(argv[i][0] == '-' && argv[i][1] == 'w' && argv[i][2] == 0) - { - // -w - fullscreen = false; - } - } - - // start the server - client c; - c.set_name(name); - c.set_fullscreen(fullscreen); - c.run(connect_at_once ? &server_address : 0x0); - return 0; -} diff --git a/src/datafile.cpp b/src/datafile.cpp deleted file mode 100644 index 789aa722..00000000 --- a/src/datafile.cpp +++ /dev/null @@ -1,444 +0,0 @@ -#include -#include - -#include "datafile.h" - -static const int DEBUG=0; - -struct item_type -{ - int type; - int start; - int num; -}; - -struct item -{ - int type_and_id; - int size; -}; - -struct datafile_header -{ - int id; - int version; - int size; - int swaplen; - int num_item_types; - int num_items; - int num_raw_data; - int item_size; - int data_size; -}; - -struct datafile_data -{ - int num_item_types; - int num_items; - int num_raw_data; - int item_size; - int data_size; - char start[4]; -}; - -struct datafile_info -{ - item_type *item_types; - int *item_offsets; - int *data_offsets; - - char *item_start; - char *data_start; -}; - -struct datafile -{ - datafile_info info; - datafile_data data; -}; - -datafile *datafile_load(const char *filename) -{ - dbg_msg("datafile", "datafile loading. filename='%s'", filename); - - baselib::file_stream file; - if(!file.open_r(filename)) - return 0; - - // TODO: change this header - int header[4]; - file.read(header, sizeof(header)); - if(((header[0]>>24)&0xff) != 'D' || ((header[0]>>16)&0xff) != 'A' || (header[0]>>8)&0xff != 'T' || (header[0]&0xff)!= 'A') - { - dbg_msg("datafile", "wrong signature. %x %x %x %x", header[0], header[1], header[2], header[3]); - return 0; - } - - int version = header[1]; - if(version != 3) - { - dbg_msg("datafile", "wrong version. version=%x", version); - return 0; - } - - unsigned size = header[2]; - unsigned swapsize = header[3]; - - if(DEBUG) - dbg_msg("datafile", "loading. size=%d", size); - - // TODO: use this variable for good and awesome - (void)swapsize; - - //map_unload(); - datafile *df = (datafile*)mem_alloc(size+sizeof(datafile_info), 1); - unsigned readsize = file.read(&df->data, size); - if(readsize != size) - { - dbg_msg("datafile", "couldn't load the whole thing, wanted=%d got=%d", size, readsize); - return 0; - } - - // TODO: byteswap - //map->byteswap(); - - if(DEBUG) - dbg_msg("datafile", "item_size=%d", df->data.item_size); - - - df->info.item_types = (item_type *)df->data.start; - df->info.item_offsets = (int *)&df->info.item_types[df->data.num_item_types]; - df->info.data_offsets = (int *)&df->info.item_offsets[df->data.num_items]; - - df->info.item_start = (char *)&df->info.data_offsets[df->data.num_raw_data]; - df->info.data_start = df->info.item_start + df->data.item_size; - - if(DEBUG) - dbg_msg("datafile", "datafile loading done. datafile='%s'", filename); - - if(DEBUG) - { - for(int i = 0; i < df->data.num_raw_data; i++) - { - void *p = datafile_get_data(df, i); - dbg_msg("datafile", "%d %d", (int)((char*)p - (char*)(&df->data)), size); - } - - for(int i = 0; i < datafile_num_items(df); i++) - { - int type, id; - void *data = datafile_get_item(df, i, &type, &id); - dbg_msg("map", "\t%d: type=%x id=%x p=%p offset=%d", i, type, id, data, df->info.item_offsets[i]); - int *idata = (int*)data; - for(int k = 0; k < 3; k++) - dbg_msg("datafile", "\t\t%d=%d (%x)", k, idata[k], idata[k]); - } - - for(int i = 0; i < df->data.num_item_types; i++) - { - dbg_msg("map", "\t%d: type=%x start=%d num=%d", i, - df->info.item_types[i].type, - df->info.item_types[i].start, - df->info.item_types[i].num); - for(int k = 0; k < df->info.item_types[i].num; k++) - { - int type, id; - datafile_get_item(df, df->info.item_types[i].start+k, &type, &id); - if(type != df->info.item_types[i].type) - dbg_msg("map", "\tERROR"); - } - } - } - - return df; -} - -void *datafile_get_data(datafile *df, int index) -{ - return df->info.data_start+df->info.data_offsets[index]; -} - -void *datafile_get_item(datafile *df, int index, int *type, int *id) -{ - item *i = (item *)(df->info.item_start+df->info.item_offsets[index]); - if(type) - *type = (i->type_and_id>>16)&0xffff; // remove sign extention - if(id) - *id = i->type_and_id&0xffff; - return (void *)(i+1); -} - -void datafile_get_type(datafile *df, int type, int *start, int *num) -{ - for(int i = 0; i < df->data.num_item_types; i++) - { - if(df->info.item_types[i].type == type) - { - *start = df->info.item_types[i].start; - *num = df->info.item_types[i].num; - return; - } - } - - *start = 0; - *num = 0; -} - -void *datafile_find_item(datafile *df, int type, int id) -{ - int start, num; - datafile_get_type(df, type, &start, &num); - for(int i = 0; i < num; i++) - { - int item_id; - void *item = datafile_get_item(df, start+i,0, &item_id); - if(id == item_id) - return item; - } - return 0; -} - -int datafile_num_items(datafile *df) -{ - return df->data.num_items; -} - -void datafile_unload(datafile *df) -{ - if(df) - mem_free(df); -} - -// DATAFILE output -struct data_info -{ - int size; - void *data; -}; - -struct item_info -{ - int type; - int id; - int size; - int next; - int prev; - void *data; -}; - -struct itemtype_info -{ - int num; - int first; - int last; -}; - -// -struct datafile_out -{ - baselib::file_stream file; - int num_items; - int num_datas; - int num_item_types; - itemtype_info item_types[0xffff]; - item_info items[1024]; - data_info datas[1024]; -}; - -datafile_out *datafile_create(const char *filename) -{ - datafile_out *df = new datafile_out; - if(!df->file.open_w(filename)) - { - delete df; - return 0; - } - - df->num_items = 0; - df->num_datas = 0; - df->num_item_types = 0; - mem_zero(&df->item_types, sizeof(df->item_types)); - - for(int i = 0; i < 0xffff; i++) - { - df->item_types[i].first = -1; - df->item_types[i].last = -1; - } - - return df; -} - -int datafile_add_item(datafile_out *df, int type, int id, int size, void *data) -{ - df->items[df->num_items].type = type; - df->items[df->num_items].id = id; - df->items[df->num_items].size = size; - - // copy data - df->items[df->num_items].data = mem_alloc(size, 1); - mem_copy(df->items[df->num_items].data, data, size); - - if(!df->item_types[type].num) // count item types - df->num_item_types++; - - // link - df->items[df->num_items].prev = df->item_types[type].last; - df->items[df->num_items].next = -1; - - if(df->item_types[type].last != -1) - df->items[df->item_types[type].last].next = df->num_items; - df->item_types[type].last = df->num_items; - - if(df->item_types[type].first == -1) - df->item_types[type].first = df->num_items; - - df->item_types[type].num++; - - df->num_items++; - return df->num_items-1; -} - -int datafile_add_data(datafile_out *df, int size, void *data) -{ - df->datas[df->num_items].size = size; - df->datas[df->num_datas].data = data; - df->num_datas++; - return df->num_datas-1; -} - -int datafile_finish(datafile_out *df) -{ - // we should now write this file! - if(DEBUG) - dbg_msg("datafile", "writing"); - - // calculate sizes - int itemsize = 0; - for(int i = 0; i < df->num_items; i++) - { - if(DEBUG) - dbg_msg("datafile", "item=%d size=%d (%d)", i, df->items[i].size, df->items[i].size+sizeof(item)); - itemsize += df->items[i].size + sizeof(item); - } - - int datasize = 0; - for(int i = 0; i < df->num_datas; i++) - datasize += df->datas[i].size; - - // calculate the complete size - int typessize = df->num_item_types*sizeof(item_type); - int headersize = sizeof(datafile_header); - int offsetsize = df->num_items*sizeof(int) + df->num_datas*sizeof(int); - int filesize = headersize + typessize + offsetsize + itemsize + datasize; - int swapsize = filesize - datasize; - - if(DEBUG) - dbg_msg("datafile", "num_item_types=%d typessize=%d itemsize=%d datasize=%d", df->num_item_types, typessize, itemsize, datasize); - - // construct header - datafile_header header; - header.id = ('D'<<24) | ('A'<<16) | ('T'<<8) | ('A'); - header.version = 3; - header.size = filesize - 16; - header.swaplen = swapsize - 16; - header.num_item_types = df->num_item_types; - header.num_items = df->num_items; - header.num_raw_data = df->num_datas; - header.item_size = itemsize; - header.data_size = datasize; - - // TODO: apply swapping - // write header - if(DEBUG) - dbg_msg("datafile", "headersize=%d", sizeof(header)); - df->file.write(&header, sizeof(header)); - - // write types - for(int i = 0, count = 0; i < 0xffff; i++) - { - if(df->item_types[i].num) - { - // write info - item_type info; - info.type = i; - info.start = count; - info.num = df->item_types[i].num; - if(DEBUG) - dbg_msg("datafile", "writing type=%x start=%d num=%d", info.type, info.start, info.num); - df->file.write(&info, sizeof(info)); - - count += df->item_types[i].num; - } - } - - // write item offsets - for(int i = 0, offset = 0; i < 0xffff; i++) - { - if(df->item_types[i].num) - { - // write all items in of this type - int k = df->item_types[i].first; - while(k != -1) - { - if(DEBUG) - dbg_msg("datafile", "writing item offset num=%d offset=%d", k, offset); - df->file.write(&offset, sizeof(offset)); - offset += df->items[k].size + sizeof(item); - - // next - k = df->items[k].next; - } - } - } - - // write data offsets - for(int i = 0, offset = 0; i < df->num_datas; i++) - { - if(DEBUG) - dbg_msg("datafile", "writing data offset num=%d offset=%d", i, offset); - df->file.write(&offset, sizeof(offset)); - offset += df->datas[i].size; - } - - // write items - for(int i = 0; i < 0xffff; i++) - { - if(df->item_types[i].num) - { - // write all items in of this type - int k = df->item_types[i].first; - while(k != -1) - { - item itm; - itm.type_and_id = (i<<16)|df->items[k].id; - itm.size = df->items[k].size; - if(DEBUG) - dbg_msg("datafile", "writing item type=%x idx=%d id=%d size=%d", i, k, df->items[k].id, df->items[k].size); - df->file.write(&itm, sizeof(itm)); - df->file.write(df->items[k].data, df->items[k].size); - - // next - k = df->items[k].next; - } - } - } - - // write data - for(int i = 0; i < df->num_datas; i++) - { - if(DEBUG) - dbg_msg("datafile", "writing data id=%d size=%d", i, df->datas[i].size); - df->file.write(df->datas[i].data, df->datas[i].size); - } - - // free data - for(int i = 0; i < df->num_items; i++) - mem_free(df->items[i].data); - - - delete df; - - if(DEBUG) - dbg_msg("datafile", "done"); - return 0; -} diff --git a/src/datafile.h b/src/datafile.h deleted file mode 100644 index 4e49fa03..00000000 --- a/src/datafile.h +++ /dev/null @@ -1,20 +0,0 @@ - -// raw datafile access -struct datafile; - -// read access -datafile *datafile_load(const char *filename); -datafile *datafile_load_old(const char *filename); -void *datafile_get_data(datafile *df, int index); -void *datafile_get_item(datafile *df, int index, int *type, int *id); -void datafile_get_type(datafile *df, int type, int *start, int *num); -void *datafile_find_item(datafile *df, int type, int id); -int datafile_num_items(datafile *df); -void datafile_unload(datafile *df); - -// write access -struct datafile_out; -datafile_out *datafile_create(const char *filename); -int datafile_add_data(datafile_out *df, int size, void *data); -int datafile_add_item(datafile_out *df, int type, int id, int size, void *data); -int datafile_finish(datafile_out *df); diff --git a/src/editor.cpp b/src/editor.cpp deleted file mode 100644 index 9b633730..00000000 --- a/src/editor.cpp +++ /dev/null @@ -1,1229 +0,0 @@ -#include -#include -#include -#include - -#include "interface.h" -#include "datafile.h" -#include "ui.h" - -#include "game/mapres_image.h" -#include "game/mapres_tilemap.h" -//#include "game/mapres_col.h" -#include "game/mapres.h" -#include "game/game.h" - -using namespace baselib; - -static int font_texture = 0; - -struct ent_type -{ - const char *name; - int id; - int item_id; -}; - -static ent_type ent_types[] = { - {"spawn", MAPRES_SPAWNPOINT, 0}, - {"gun", MAPRES_ITEM, ITEM_WEAPON_GUN}, - {"shotgun", MAPRES_ITEM, ITEM_WEAPON_SHOTGUN}, - {"rocket", MAPRES_ITEM, ITEM_WEAPON_ROCKET}, - {"sniper", MAPRES_ITEM, ITEM_WEAPON_SNIPER}, - {"hammer", MAPRES_ITEM, ITEM_WEAPON_HAMMER}, - {"health_1", MAPRES_ITEM, ITEM_HEALTH_1}, - {"health_5", MAPRES_ITEM, ITEM_HEALTH_5}, - {"health_10", MAPRES_ITEM, ITEM_HEALTH_10}, - {"armor_1", MAPRES_ITEM, ITEM_ARMOR_1}, - {"armor_5", MAPRES_ITEM, ITEM_ARMOR_5}, - {"armor_10", MAPRES_ITEM, ITEM_ARMOR_10}, - {"ninja!", MAPRES_ITEM, ITEM_NINJA}, - {0, 0} -}; - - -/******************************************************** - ENTITIES -*********************************************************/ -struct entity -{ - int type; - int x, y; -}; - -static const int MAX_ENTITIES = 1024; -static entity entites[MAX_ENTITIES]; -static int num_entities = 0; - -static int ents_count() -{ - return num_entities; -} - -static entity *ents_get(int index) -{ - return &entites[index]; -} - -static int ents_delete(int index) -{ - if(index < 0 || index >= ents_count()) - return -1; - num_entities--; - entites[index] = entites[num_entities]; - return 0; -} - -static int ents_new(int type, int x, int y) -{ - entites[num_entities].type = type; - entites[num_entities].x = x; - entites[num_entities].y = y; - num_entities++; - return num_entities-1; -} - -/******************************************************** - TILEMAP -*********************************************************/ -struct tile -{ - unsigned char index; - unsigned char flags; -}; - -struct tilemap -{ - int width; - int height; - tile *tiles; -}; - -// allocates a new tilemap and inits the structure -static void tilemap_new(tilemap *tm, int width, int height) -{ - unsigned size = sizeof(tile)*width*height; - mem_zero(tm, sizeof(tilemap)); - - tm->width = width; - tm->height = height; - tm->tiles = (tile *)mem_alloc(size, 1); - mem_zero(tm->tiles, size); -} - -// resizes an tilemap, copies the old data -static void tilemap_resize(tilemap *tm, int new_width, int new_height, int snap) -{ - if(new_width <= 0) new_width = 1; - if(new_height <= 0) new_height = 1; - - new_width = (new_width+snap-1)/snap*snap; - new_height = (new_height+snap-1)/snap*snap; - - unsigned size = sizeof(tile)*new_width*new_height; - tile *newtiles = (tile *)mem_alloc(size, 1); - mem_zero(newtiles, size); - - // copy old tiles - int w = new_width < tm->width ? new_width : tm->width; - int h = new_height < tm->height ? new_height : tm->height; - for(int y = 0; y < h; y++) - for(int x = 0; x < w; x++) - newtiles[y*new_width+x] = tm->tiles[y*tm->width+x]; - - // free old tiles and set new values - mem_free(tm->tiles); - tm->tiles = newtiles; - tm->width = new_width; - tm->height = new_height; -} - -static void tilemap_destroy(tilemap *tm) -{ - mem_free(tm->tiles); - tm->tiles = 0; - tm->width = 0; - tm->height = 0; -} - -static int tilemap_blit(tilemap *dst, tilemap *src, int x, int y) -{ - int count = 0; - // TODO: performance of this could be better - for(int iy = 0; iy < src->height; iy++) - for(int ix = 0; ix < src->width; ix++) - { - if(x+ix >= dst->width || y+iy >= dst->height) - continue; - if(x+ix < 0 || y+iy < 0) - continue; - - count++; - dst->tiles[(y+iy)*dst->width + x+ix] = src->tiles[iy*src->width + ix]; - } - return count; -} - -/******************************************************** - LAYERS -*********************************************************/ -struct layer -{ - tilemap tm; - int tileset_id; - int visible; - int main_layer; -}; - -static const int MAX_LAYERS = 64; -static layer layers[MAX_LAYERS]; -static int num_layers = 0; -static int current_layer = 0; - -static int layers_remove(int index) -{ - if(index < 0 || index >= num_layers) - return 0; - - // free the memory - mem_free(layers[index].tm.tiles); - - // move the layers - for(int i = index; i < num_layers-1; i++) - layers[i] = layers[i+1]; - num_layers--; - return 1; -} - -static int layers_count() -{ - return num_layers; -} - -static layer *layers_get_current() -{ - return &layers[current_layer]; -} - -static layer *layers_get(int index) -{ - return &layers[index]; -} - -static int layers_new(int w, int h) -{ - if(num_layers+1 >= MAX_LAYERS) - return -1; - - tilemap_new(&layers[num_layers].tm, w, h); - layers[num_layers].tileset_id = -1; - layers[num_layers].visible = 1; - layers[num_layers].main_layer = 0; - num_layers++; - return num_layers-1; -} - -static int layers_swap(int index1, int index2) -{ - // swap the two layers - layer temp = layers[index1]; - layers[index1] = layers[index2]; - layers[index2] = temp; - return -1; -} - -static int layers_moveup(int index) -{ - if(index < 0 || index >= num_layers) - return index; - - if(index == 0) - return 0; - - layers_swap(index, index-1); - return index-1; -} - -static int layers_movedown(int index) -{ - if(index < 0 || index >= num_layers) - return index; - - if(index+1 == num_layers) - return index; - - layers_swap(index, index+1); - return index+1; -} - -/******************************************************** - TILESET -*********************************************************/ -struct tileset -{ - int tex_id; - image_info img; -}; - -static const int MAX_TILESETS = 64; -static tileset tilesets[MAX_TILESETS]; -static int num_tilesets = 0; - -static int tilesets_new() -{ - tilesets[num_tilesets].img.width = 0; - tilesets[num_tilesets].img.height = 0; - tilesets[num_tilesets].img.data = 0; - tilesets[num_tilesets].tex_id = -1;// gfx_load_texture_raw(img.width, img.height, img.data); - num_tilesets++; - return num_tilesets-1; -} - - -static void tilesets_clear() -{ - // TODO: remove texture aswell - for(int i = 0; i < num_tilesets; i++) - mem_free(tilesets[num_tilesets].img.data); - num_tilesets = 0; - -} - -static int tilesets_set_img(int index, int w, int h, void *data) -{ - tilesets[index].img.width = w; - tilesets[index].img.height = h; - - if(tilesets[index].img.data) - mem_free(tilesets[index].img.data); - tilesets[index].img.data = data; - tilesets[index].tex_id = gfx_load_texture_raw(w, h, data); - return index; -} - -static int tilesets_count() -{ - return num_tilesets; -} - -static tileset *tilesets_get(int index) -{ - return &tilesets[index]; -} - -/******************************************************** - UI -*********************************************************/ -static void render_tilemap(tilemap *tm, float sx, float sy, float scale) -{ - float frac = (1.0f/512.0f); //2.0f; - gfx_quads_begin(); - for(int y = 0; y < tm->height; y++) - for(int x = 0; x < tm->width; x++) - { - unsigned char d = tm->tiles[y*tm->width+x].index; - if(d) - { - gfx_quads_setsubset( - (d%16)/16.0f+frac, - (d/16)/16.0f+frac, - (d%16)/16.0f+1.0f/16.0f-frac, - (d/16)/16.0f+1.0f/16.0f-frac); - gfx_quads_drawTL(sx+x*scale, sy+y*scale, scale, scale); - } - - //gfx_quads_setsubset(x/16.0f,y/16.0f,(x+1)/16.0f,(y+1)/16.0f); - //gfx_quads_drawTL(sx+x*w,sy+y*h,w,h); - } - gfx_quads_end(); -} - -/******************************************************** - EDITOR -*********************************************************/ - -static tilemap brush = {0}; -static tilemap chooser = {0}; -static float world_offset_x = 0, world_offset_y = 0; -static int world_zoom = 3; -static const char *editor_filename = 0; -static int editor_mode = 0; // 0 == tiles, 1 == ents -static int editor_selected_ent = -1; - -static const int TILEMAPFLAG_READONLY = 1; -static const int TILEMAPFLAG_UISPACE = 2; - -static int ui_do_tilemap(void *id, tilemap *tm, int flags, float x, float y, float scale) -{ - /* - int do_input = 1; - if(inp_key_pressed(keys::lalt) || inp_key_pressed(keys::ralt)) - do_input = 0;*/ - - - float mx = ui_mouse_world_x(); - float my = ui_mouse_world_y(); - if(flags&TILEMAPFLAG_UISPACE) - { - mx = ui_mouse_x(); - my = ui_mouse_y(); - } - - int tmx = (int)((mx-x)/scale); // tilemap x - int tmy = (int)((my-y)/scale); // tilemap y - - static int start_tmx, start_tmy; - static int grabbing = 0; - - //float start_tmx_wx = start_tmx*scale+x; - //float start_tmx_wy = start_tmy*scale+y; - - int select_x = 0; - int select_y = 0; - int select_w = 1; - int select_h = 1; - float select_wx = 0; - float select_wy = 0; - float select_ww = 0; - float select_wh = 0; - - if(ui_hot_item() == id) - { - int x0 = start_tmx; - int y0 = start_tmy; - int x1 = tmx; - int y1 = tmy; - - if(x1 < x0) - { - int tmp = x1; - x1 = x0; - x0 = tmp; - } - - if(y1 < y0) - { - int tmp = y1; - y1 = y0; - y0 = tmp; - } - - select_w = x1-x0; - select_h = y1-y0; - select_w++; - select_h++; - select_x = x0; - select_y = y0; - - select_wx = select_x*scale+x; - select_wy = select_y*scale+y; - select_ww = select_w*scale; - select_wh = select_h*scale; - } - // ui_do_tilemap always tries to steal the focus - ui_set_hot_item(id); - - // render the tilemap - render_tilemap(tm, x, y, scale); - - if(ui_hot_item() == id) - { - if(brush.tiles != 0) - { - // draw brush - render_tilemap(&brush, (tmx-brush.width/2)*scale, (tmy-brush.height/2)*scale, scale); - - gfx_texture_set(-1); - gfx_blend_additive(); - gfx_quads_begin(); - gfx_quads_setcolor(1.0f, 0.0f, 0.0f, 0.25f); - gfx_quads_drawTL((tmx-brush.width/2)*scale, (tmy-brush.height/2)*scale, brush.width*scale, brush.height*scale); - gfx_quads_end(); - gfx_blend_normal(); - } - - if(grabbing == 0) - { - if(ui_mouse_button(0)) - { - //grabbing = 1; - start_tmx = (int)((mx-x)/scale); - start_tmy = (int)((my-y)/scale); - if(brush.tiles == 0) - { - dbg_msg("editor", "grabbing..."); - grabbing = 1; // grab tiles - } - else - { - // paint - if(!(flags&TILEMAPFLAG_READONLY)) - { - layer *l = layers_get_current(); - int px = tmx-brush.width/2; - int py = tmy-brush.height/2; - tilemap_blit(&l->tm, &brush, px, py); - //dbg_msg("editor", "painted %d tiles at (%d,%d)", c, px, py); - } - } - } - } - else - { - gfx_texture_set(-1); - gfx_blend_additive(); - gfx_quads_begin(); - gfx_quads_setcolor(1.0f, 1.0f, 1.0f, 0.25f); - gfx_quads_drawTL(select_wx, select_wy, select_ww, select_wh); - gfx_quads_end(); - gfx_blend_normal(); - - if(!ui_mouse_button(0)) - { - grabbing = 0; - - if(brush.tiles == 0) - { - // create brush - dbg_msg("editor", "creating brush w=%d h=%d p0=(%d,%d)", select_w, select_h, select_x, select_y); - tilemap_new(&brush, select_w, select_h); - - // copy data - for(int y = 0; y < select_h; y++) - for(int x = 0; x < select_w; x++) - brush.tiles[y*select_w+x] = tm->tiles[(select_y+y)*tm->width + select_x+x]; - } - } - } - } - - // raw rect around tilemap - gfx_texture_set(-1); - gfx_quads_begin(); - float w = tm->width*scale; - float h = tm->height*scale; - gfx_quads_drawTL(x-2, y-2, 2, h+4); - gfx_quads_drawTL(x-2, y-2, w+4, 2); - gfx_quads_drawTL(x+w, y-2, 2, h+4); - gfx_quads_drawTL(x-2, y+h, w+4, 2); - gfx_quads_end(); - - return 0; -} - -static int ui_do_entity(void *id, entity *ent, int selected) -{ - float x = (float)ent->x; - float y = (float)ent->y; - float w = 16; - float h = 16; - - float mx = ui_mouse_world_x(); - float my = ui_mouse_world_y(); - - int inside = 0; - int r = 0; - if(mx > x-w/2 && mx < x+w/2 && my > y-h/2 && my < y+h/2) - inside = 1; - - if(inside) - ui_set_hot_item(id); - - if(ui_hot_item() == id && ui_mouse_button(0)) - { - ui_set_active_item(id); - r = 1; - } - - if(ui_active_item() == id) - { - if(!ui_mouse_button(0)) - ui_set_active_item(0); - ent->x = (int)ui_mouse_world_x(); - ent->y = (int)ui_mouse_world_y(); - ent->x = (ent->x/32)*32+16; - ent->y = (ent->y/32)*32+16; - } - - // raw rect around tilemap - gfx_texture_set(-1); - gfx_quads_begin(); - if(selected) - gfx_quads_setcolor(1.0f, 0.5f, 0.5f, 0.95f); - else if(ui_hot_item() == id) - gfx_quads_setcolor(1.0f, 1.0f, 1.0f, 0.95f); - else - gfx_quads_setcolor(0.75f, 0.75f, 0.75f, 0.95f); - - gfx_quads_drawTL(x-w/2, y-w/2, w, h); - gfx_quads_end(); - - gfx_texture_set(font_texture); - if(ent->type >= 0) - gfx_quads_text(x-4, y-h/2-4, 24.0f, ent_types[ent->type].name); - - return r; -} - - -static int editor_reset() -{ - // delete all layers - while(layers_count()) - layers_remove(layers_count()-1); - - //layers_new(50, 50); - tilemap_destroy(&brush); - current_layer = 0; - - tilesets_clear(); - - // init chooser - static tile chooser_tiles[256]; - for(int i = 0; i < 256; i++) - { - chooser_tiles[i].index = i; - chooser_tiles[i].flags = 0; - } - - chooser.width = 16; - chooser.height = 16; - chooser.tiles = chooser_tiles; - - return 0; -} - -void draw_editor_button(void *id, const char *text, int checked, float x, float y, float w, float h, void *extra) -{ - gfx_blend_normal(); - gfx_texture_set(-1); - gfx_quads_begin(); - if(ui_hot_item() == id) - gfx_quads_setcolor(1,1,1,1); - else if(checked) - gfx_quads_setcolor(0.75f,0.5f,0.5f,1); - else - gfx_quads_setcolor(0.5f,0.5f,0.5f,1); - - gfx_quads_drawTL(x,y,w,h); - gfx_quads_end(); - gfx_texture_set(font_texture); - gfx_quads_text(x, y, 6.2f, text); -} - -static int editor_loadimage = -1; - -static void editor_listdir_callback(const char *name, int is_dir, void *user) -{ - if(name[0] == '.') // skip this shit! - return; - - int *y = (int*)user; - if(ui_do_button((void*)(*y + 1), name, 0, 10, 10 + *y * 8, 100, 6, draw_editor_button)) - { - char buf[512]; - sprintf(buf, "tilesets/%s", name); - - image_info img; - if(!gfx_load_tga(&img, buf)) - return; - - tilesets_set_img(editor_loadimage, img.width, img.height, img.data); - editor_loadimage = -1; - } - *y += 1; -} - -static void editor_render_loadfile_dialog() -{ - gfx_clear(0.2f,0.2f,0.8f); - // GUI coordsys - gfx_mapscreen(0,0,400.0f,300.0f); - - int index = 0; - fs_listdir("tilesets", editor_listdir_callback, &index); -} - -static void editor_render() -{ - if(editor_loadimage != -1) - { - editor_render_loadfile_dialog(); - return; - } - - // background color - gfx_clear(0.2f,0.2f,0.8f); - - // world coordsys - float zoom = world_zoom; - gfx_mapscreen(world_offset_x,world_offset_y,world_offset_x+400.0f*zoom,world_offset_y+300.0f*zoom); - - for(int i = 0; i < layers_count(); i++) - { - layer *l = layers_get(i); - - gfx_texture_set(-1); - if(l->tileset_id >= 0 && l->tileset_id < tilesets_count()) - gfx_texture_set(tilesets_get(l->tileset_id)->tex_id); - - if(editor_mode == 0) - { - if(l == layers_get_current()) - { - // do current layer - ui_do_tilemap(&l->tm, &l->tm, 0, 0, 0, 32.0f); - } - else if(l->visible) - { - // render layer - render_tilemap(&l->tm, 0, 0, 32.0f); - } - } - else - render_tilemap(&l->tm, 0, 0, 32.0f); - } - - if(editor_mode == 1) - { - // ents mode - for(int i = 0; i < ents_count(); i++) - { - if(ui_do_entity(ents_get(i), ents_get(i), i == editor_selected_ent)) - editor_selected_ent = i; - - } - } - - // GUI coordsys - gfx_mapscreen(0,0,400.0f,300.0f); - - // toolbox - float toolbox_width = 50.0f; - - if(editor_mode == 0) - { - float layerbox_x = 0; - float layerbox_y = 0; - int count = 1; - int main_layer = -1; - for(int i = 0; i < layers_count(); i++) - { - layer *l = layers_get(i); - char buf[128]; - if(l->main_layer) - { - main_layer = i; - sprintf(buf, "Main Layer (%dx%d)", l->tm.width, l->tm.height); - count = 1; - } - else - { - if(main_layer == -1) - sprintf(buf, "Background %d (%dx%d)", count, l->tm.width, l->tm.height); - else - sprintf(buf, "Foreground %d (%dx%d)", count, l->tm.width, l->tm.height); - count++; - } - - // show / hide layer - const char *text = " "; - if(layers_get(i)->visible) - text = "V"; - - if(ui_do_button(&layers_get(i)->visible, text, 0, layerbox_x, layerbox_y+i*8, 6, 6, draw_editor_button)) - layers_get(i)->visible = layers_get(i)->visible^1; - - if(ui_do_button(&layers_get(i)->tileset_id, buf, current_layer == i, layerbox_x+8, layerbox_y+i*8, toolbox_width-8, 6, draw_editor_button)) - { - // select layer - current_layer = i; - } - } - - // draw buttons - { - static int push_button, pull_button; - float y = 150; - float x = 0; - if(ui_do_button(&push_button, "push", 0, x, y, toolbox_width, 6, draw_editor_button)) - current_layer = layers_moveup(current_layer); - y += 7; - - if(ui_do_button(&pull_button, "pull", 0, x, y, toolbox_width, 6, draw_editor_button)) - current_layer = layers_movedown(current_layer); - y += 10; - - static int w_inc, w_dec; - int resize_amount = 10; - if(ui_do_button(&w_dec, "width-", 0, x, y, toolbox_width, 6, draw_editor_button)) - tilemap_resize(&layers_get_current()->tm, layers_get_current()->tm.width-resize_amount, layers_get_current()->tm.height, resize_amount); - y += 7; - if(ui_do_button(&w_inc, "width+", 0, x, y, toolbox_width, 6, draw_editor_button)) - tilemap_resize(&layers_get_current()->tm, layers_get_current()->tm.width+resize_amount, layers_get_current()->tm.height, resize_amount); - y += 10; - - static int h_inc, h_dec; - if(ui_do_button(&h_dec, "height-", 0, x, y, toolbox_width, 6, draw_editor_button)) - tilemap_resize(&layers_get_current()->tm, layers_get_current()->tm.width, layers_get_current()->tm.height-resize_amount, resize_amount); - y += 7; - if(ui_do_button(&h_inc, "height+", 0, x, y, toolbox_width, 6, draw_editor_button)) - tilemap_resize(&layers_get_current()->tm, layers_get_current()->tm.width, layers_get_current()->tm.height+resize_amount, resize_amount); - y += 10; - } - - - float tilesetsbox_x = 0; - float tilesetsbox_y = 230; - for(int i = 0; i < tilesets_count(); i++) - { - char buf[128]; - sprintf(buf, "#%d %dx%d", i, tilesets_get(i)->img.width, tilesets_get(i)->img.height); - if(ui_do_button(&tilesets_get(i)->img, "L", layers_get(current_layer)->tileset_id == i, tilesetsbox_x, tilesetsbox_y+i*7, 6, 6, draw_editor_button)) - { - // load image - editor_loadimage = i; - } - - if(ui_do_button(tilesets_get(i), buf, layers_get(current_layer)->tileset_id == i, tilesetsbox_x+8, tilesetsbox_y+i*7, toolbox_width-8, 6, draw_editor_button)) - { - // select tileset for layer - dbg_msg("editor", "settings tileset %d=i", current_layer, i); - layers_get(current_layer)->tileset_id = i; - } - } - - // (add) button for tilesets - static int load_tileset; - if(ui_do_button(&load_tileset, "(Add)", 0, tilesetsbox_x, tilesetsbox_y+tilesets_count()*7+3, toolbox_width, 6, draw_editor_button)) - tilesets_new(); - - if(brush.tiles != 0) - { - // right mouse button or C clears the brush - if(ui_mouse_button(1) || inp_key_pressed('C')) - tilemap_destroy(&brush); - } - - if(inp_key_pressed(keys::space)) - { - // render chooser - float chooser_x = 0; - float chooser_y = 0; - - gfx_texture_set(-1); - layer *l = layers_get_current(); - if(l && l->tileset_id >= 0 && l->tileset_id < tilesets_count()) - gfx_texture_set(tilesets_get(l->tileset_id)->tex_id); - ui_do_tilemap(&chooser, &chooser, TILEMAPFLAG_READONLY|TILEMAPFLAG_UISPACE, chooser_x, chooser_y, 16.0f); - } - } - else - { - int current_type = -1; - if(editor_selected_ent >= 0 && editor_selected_ent < ents_count()) - current_type = ents_get(editor_selected_ent)->type; - - float y = 0; - for(int i = 0; ent_types[i].name; i++) - { - if(ui_do_button(&ent_types[i], ent_types[i].name, current_type==i, 0, y, toolbox_width, 6, draw_editor_button)) - { - if(editor_selected_ent >= 0 && editor_selected_ent < ents_count()) - ents_get(editor_selected_ent)->type = i; - } - y += 8; - } - - y += 8; - static int add, del; - if(ui_do_button(&add, "Add", 0, 0, y, toolbox_width, 6, draw_editor_button)) - { - int x = (int)(world_offset_x+400*zoom/2)/32*32+16; - int y = (int)(world_offset_y+300*zoom/2)/32*32+16; - ents_new(0, x, y); - } - - y += 8; - if(ui_do_button(&del, "Del", 0, 0, y, toolbox_width, 6, draw_editor_button)) - ents_delete(editor_selected_ent); - } -} - -int editor_load(const char *filename) -{ - datafile *df = datafile_load(filename); - if(!df) - return 0; - - // load tilesets - { - int start, count; - datafile_get_type(df, MAPRES_IMAGE, &start, &count); - for(int i = 0; i < count; i++) - { - mapres_image *img = (mapres_image *)datafile_get_item(df, start+i, 0, 0); - void *data = datafile_get_data(df, img->image_data); - int id = tilesets_new(); - void *data_cpy = mem_alloc(img->width*img->height*4, 1); - mem_copy(data_cpy, data, img->width*img->height*4); - tilesets_set_img(id, img->width, img->height, data_cpy); - } - } - - // load tilemaps - { - int start, num; - datafile_get_type(df, MAPRES_TILEMAP, &start, &num); - for(int t = 0; t < num; t++) - { - mapres_tilemap *tmap = (mapres_tilemap *)datafile_get_item(df, start+t,0,0); - //unsigned char *data = (unsigned char *)datafile_get_data(df, tmap->data); - - layer *l = layers_get(layers_new(tmap->width, tmap->height)); - mem_copy(l->tm.tiles, datafile_get_data(df, tmap->data), tmap->width*tmap->height*2); - l->tileset_id = tmap->image; - l->main_layer = tmap->main; - - // force a main layer - if(num == 1) - l->main_layer = 1; - } - } - - // load entities - { - int type = -1; - for(int i = 0; ent_types[i].name; i++) - { - if(ent_types[i].id == MAPRES_SPAWNPOINT) - { - type = i; - break; - } - } - - int start, num; - datafile_get_type(df, MAPRES_SPAWNPOINT, &start, &num); - for(int t = 0; t < num; t++) - { - mapres_spawnpoint *sp = (mapres_spawnpoint *)datafile_get_item(df, start+t,0,0); - ents_new(type, sp->x, sp->y); - } - } - - { - int start, num; - datafile_get_type(df, MAPRES_ITEM, &start, &num); - for(int t = 0; t < num; t++) - { - mapres_item *it = (mapres_item *)datafile_get_item(df, start+t,0,0); - - int type = -1; - for(int i = 0; ent_types[i].name; i++) - { - if(ent_types[i].id == MAPRES_ITEM && ent_types[i].item_id == it->type) - { - dbg_msg("editor", "i type=%x mapped=%d", it->type, i); - type = i; - break; - } - } - - ents_new(type, it->x, it->y); - } - } - return 1; -} - -int editor_save(const char *filename) -{ - datafile_out *df = datafile_create(filename); - - // add tilesets - for(int i = 0; i < tilesets_count(); i++) - { - mapres_image img; - tileset *ts = tilesets_get(i); - img.width = ts->img.width; - img.height = ts->img.height; - img.image_data = datafile_add_data(df, ts->img.width*ts->img.height*4, ts->img.data); - datafile_add_item(df, MAPRES_IMAGE, i, sizeof(img), &img); - } - - // add tilemaps - for(int i = 0; i < layers_count(); i++) - { - layer *l = layers_get(i); - mapres_tilemap tm; - tm.image = l->tileset_id; - tm.width = l->tm.width; - tm.height = l->tm.height; - tm.x = 0; - tm.y = 0; - tm.main = l->main_layer; - tm.scale = 1<<16; - tm.data = datafile_add_data(df, l->tm.width*l->tm.height*2, l->tm.tiles); - datafile_add_item(df, MAPRES_TILEMAP, i, sizeof(tm), &tm); - } - - // add collision - char *collisiondata = 0x0; - for(int i = 0; i < layers_count(); i++) - { - layer *l = layers_get(i); - if(l->main_layer) - { - mapres_collision col; - col.width = l->tm.width; - col.height = l->tm.height; - - collisiondata = (char *)mem_alloc(col.width*col.height, 1); - for(int y = 0, c = 0; y < col.height; y++) - for(int x = 0; x < col.width; x++, c++) - { - if(l->tm.tiles[c].index) - collisiondata[c] = 1; - else - collisiondata[c] = 0; - } - - col.data_index = datafile_add_data(df, col.width*col.height, collisiondata); - datafile_add_item(df, MAPRES_COLLISIONMAP, 0, sizeof(col), &col); - break; - } - } - - // add spawnpoints - for(int i = 0, id = 0; i < ents_count(); i++) - { - entity *ent = ents_get(i); - if(ent->type >= 0 && ent_types[ent->type].id == MAPRES_SPAWNPOINT) - { - mapres_spawnpoint sp; - sp.x = ent->x; - sp.y = ent->y; - sp.type = 0; - datafile_add_item(df, MAPRES_SPAWNPOINT, id, sizeof(sp), &sp); - id++; - } - } - - // add items - for(int i = 0, id = 0; i < ents_count(); i++) - { - entity *ent = ents_get(i); - if(ent->type >= 0 && ent_types[ent->type].id == MAPRES_ITEM) - { - mapres_item it; - it.x = ent->x; - it.y = ent->y; - it.type = ent_types[ent->type].item_id; - dbg_msg("editor", "i mapped=%d type=%x", ent->type, it.type); - datafile_add_item(df, MAPRES_ITEM, id, sizeof(it), &it); - id++; - } - } - - // finish adn clean up - datafile_finish(df); - mem_free(collisiondata); - - return 0; -} - -static int editor_loop() -{ - int mouse_x = 0; - int mouse_y = 0; - - mouse::set_mode(mouse::mode_relative); - - while(!inp_key_pressed(keys::esc)) - { - // update input - inp_update(); - - // handle mouse movement - float mx, my, mwx, mwy; - int rx, ry; - { - inp_mouse_relative(&rx, &ry); - mouse_x += rx; - mouse_y += ry; - if(mouse_x < 0) mouse_x = 0; - if(mouse_y < 0) mouse_y = 0; - if(mouse_x > gfx_screenwidth()) mouse_x = gfx_screenwidth(); - if(mouse_y > gfx_screenheight()) mouse_y = gfx_screenheight(); - - // update the ui - mx = (mouse_x/(float)gfx_screenwidth())*400.0f; - my = (mouse_y/(float)gfx_screenheight())*300.0f; - mwx = world_offset_x+mx*world_zoom; // adjust to zoom and offset - mwy = world_offset_y+my*world_zoom; // adjust to zoom and offset - - int buttons = 0; - if(inp_mouse_button_pressed(0)) buttons |= 1; - if(inp_mouse_button_pressed(1)) buttons |= 2; - if(inp_mouse_button_pressed(2)) buttons |= 4; - - ui_update(mx,my,mwx,mwy,buttons); - } - - // - editor_render(); - - if(inp_key_pressed(keys::lalt) || inp_key_pressed(keys::ralt)) - { - static int moveid; - ui_set_hot_item(&moveid); - if(inp_mouse_button_pressed(0)) - { - world_offset_x -= rx*2; - world_offset_y -= ry*2; - } - } - - - // render butt ugly mouse cursor - gfx_texture_set(-1); - gfx_quads_begin(); - gfx_quads_setcolor(0,0,0,1); - gfx_quads_draw_freeform(mx,my,mx,my, - mx+7,my, - mx,my+7); - gfx_quads_setcolor(1,1,1,1); - gfx_quads_draw_freeform(mx+1,my+1,mx+1,my+1, - mx+5,my+1, - mx+1,my+5); - gfx_quads_end(); - - // swap the buffers - gfx_swap(); - - // - if(keys::pressed(keys::f1)) - mouse::set_mode(mouse::mode_absolute); - if(keys::pressed(keys::f2)) - mouse::set_mode(mouse::mode_relative); - - - // mode switch - if(inp_key_down(keys::tab)) - editor_mode ^= 1; - - // zoom in - if(inp_key_down(keys::kp_add)) - { - world_zoom--; - if(world_zoom < 3) - world_zoom = 3; - } - - // zoom out - if(inp_key_down(keys::kp_subtract)) - { - world_zoom++; - if(world_zoom > 8) - world_zoom = 8; - } - - if(inp_key_pressed(keys::lctrl) || inp_key_pressed(keys::rctrl)) - { - if(inp_key_down('L')) - { - int w = 50, h = 50; - for(int i = 0; i < layers_count(); i++) - { - layer *l = layers_get(i); - if(l->main_layer) - { - w = l->tm.width; - h = l->tm.height; - break; - } - } - // copy main layer size - layers_new(w, h); - } - - if(inp_key_down('S')) - { - dbg_msg("editor", "save"); - editor_save(editor_filename); - } - - } - - if(inp_key_down(keys::f5)) - { - dbg_msg("editor", "quick save"); - editor_save("quicksave.map"); - } - - if(inp_key_down(keys::f8)) - { - dbg_msg("editor", "quick load"); - int s = current_layer; - editor_reset(); - editor_load("quicksave.map"); - current_layer = s; - if(current_layer >= layers_count()) - current_layer = layers_count(); - - } - - // be nice - thread_sleep(1); - } - - return 0; -} - - -int editor_main(int argc, char **argv) -{ - dbg_msg("editor", "starting..."); - - // parse arguments - for(int i = 1; i < argc; i++) - { - if(argv[i][0] == '-' && argv[i][1] == 'e' && argv[i][2] == 0 && argc - i > 1) - { - // -e NAME - i++; - editor_filename = argv[i]; - } - } - - if(!editor_filename) - { - dbg_msg("editor", "no filename given"); - return -1; - } - - if(!gfx_init(false)) - return -1; - - // reset and start - font_texture = gfx_load_texture_tga("data/debug_font.tga"); - editor_reset(); - - // load or new - if(!editor_load(editor_filename)) - { - layer *l = layers_get(layers_new(50, 50)); - l->main_layer = 1; - } - - /* - ents_new(0, 10, 10); - ents_new(0, 10, 10); - ents_new(0, 10, 10); - ents_new(0, 10, 10); - */ - - editor_loop(); - - return 0; -} diff --git a/src/editor/editor.cpp b/src/editor/editor.cpp new file mode 100644 index 00000000..9b633730 --- /dev/null +++ b/src/editor/editor.cpp @@ -0,0 +1,1229 @@ +#include +#include +#include +#include + +#include "interface.h" +#include "datafile.h" +#include "ui.h" + +#include "game/mapres_image.h" +#include "game/mapres_tilemap.h" +//#include "game/mapres_col.h" +#include "game/mapres.h" +#include "game/game.h" + +using namespace baselib; + +static int font_texture = 0; + +struct ent_type +{ + const char *name; + int id; + int item_id; +}; + +static ent_type ent_types[] = { + {"spawn", MAPRES_SPAWNPOINT, 0}, + {"gun", MAPRES_ITEM, ITEM_WEAPON_GUN}, + {"shotgun", MAPRES_ITEM, ITEM_WEAPON_SHOTGUN}, + {"rocket", MAPRES_ITEM, ITEM_WEAPON_ROCKET}, + {"sniper", MAPRES_ITEM, ITEM_WEAPON_SNIPER}, + {"hammer", MAPRES_ITEM, ITEM_WEAPON_HAMMER}, + {"health_1", MAPRES_ITEM, ITEM_HEALTH_1}, + {"health_5", MAPRES_ITEM, ITEM_HEALTH_5}, + {"health_10", MAPRES_ITEM, ITEM_HEALTH_10}, + {"armor_1", MAPRES_ITEM, ITEM_ARMOR_1}, + {"armor_5", MAPRES_ITEM, ITEM_ARMOR_5}, + {"armor_10", MAPRES_ITEM, ITEM_ARMOR_10}, + {"ninja!", MAPRES_ITEM, ITEM_NINJA}, + {0, 0} +}; + + +/******************************************************** + ENTITIES +*********************************************************/ +struct entity +{ + int type; + int x, y; +}; + +static const int MAX_ENTITIES = 1024; +static entity entites[MAX_ENTITIES]; +static int num_entities = 0; + +static int ents_count() +{ + return num_entities; +} + +static entity *ents_get(int index) +{ + return &entites[index]; +} + +static int ents_delete(int index) +{ + if(index < 0 || index >= ents_count()) + return -1; + num_entities--; + entites[index] = entites[num_entities]; + return 0; +} + +static int ents_new(int type, int x, int y) +{ + entites[num_entities].type = type; + entites[num_entities].x = x; + entites[num_entities].y = y; + num_entities++; + return num_entities-1; +} + +/******************************************************** + TILEMAP +*********************************************************/ +struct tile +{ + unsigned char index; + unsigned char flags; +}; + +struct tilemap +{ + int width; + int height; + tile *tiles; +}; + +// allocates a new tilemap and inits the structure +static void tilemap_new(tilemap *tm, int width, int height) +{ + unsigned size = sizeof(tile)*width*height; + mem_zero(tm, sizeof(tilemap)); + + tm->width = width; + tm->height = height; + tm->tiles = (tile *)mem_alloc(size, 1); + mem_zero(tm->tiles, size); +} + +// resizes an tilemap, copies the old data +static void tilemap_resize(tilemap *tm, int new_width, int new_height, int snap) +{ + if(new_width <= 0) new_width = 1; + if(new_height <= 0) new_height = 1; + + new_width = (new_width+snap-1)/snap*snap; + new_height = (new_height+snap-1)/snap*snap; + + unsigned size = sizeof(tile)*new_width*new_height; + tile *newtiles = (tile *)mem_alloc(size, 1); + mem_zero(newtiles, size); + + // copy old tiles + int w = new_width < tm->width ? new_width : tm->width; + int h = new_height < tm->height ? new_height : tm->height; + for(int y = 0; y < h; y++) + for(int x = 0; x < w; x++) + newtiles[y*new_width+x] = tm->tiles[y*tm->width+x]; + + // free old tiles and set new values + mem_free(tm->tiles); + tm->tiles = newtiles; + tm->width = new_width; + tm->height = new_height; +} + +static void tilemap_destroy(tilemap *tm) +{ + mem_free(tm->tiles); + tm->tiles = 0; + tm->width = 0; + tm->height = 0; +} + +static int tilemap_blit(tilemap *dst, tilemap *src, int x, int y) +{ + int count = 0; + // TODO: performance of this could be better + for(int iy = 0; iy < src->height; iy++) + for(int ix = 0; ix < src->width; ix++) + { + if(x+ix >= dst->width || y+iy >= dst->height) + continue; + if(x+ix < 0 || y+iy < 0) + continue; + + count++; + dst->tiles[(y+iy)*dst->width + x+ix] = src->tiles[iy*src->width + ix]; + } + return count; +} + +/******************************************************** + LAYERS +*********************************************************/ +struct layer +{ + tilemap tm; + int tileset_id; + int visible; + int main_layer; +}; + +static const int MAX_LAYERS = 64; +static layer layers[MAX_LAYERS]; +static int num_layers = 0; +static int current_layer = 0; + +static int layers_remove(int index) +{ + if(index < 0 || index >= num_layers) + return 0; + + // free the memory + mem_free(layers[index].tm.tiles); + + // move the layers + for(int i = index; i < num_layers-1; i++) + layers[i] = layers[i+1]; + num_layers--; + return 1; +} + +static int layers_count() +{ + return num_layers; +} + +static layer *layers_get_current() +{ + return &layers[current_layer]; +} + +static layer *layers_get(int index) +{ + return &layers[index]; +} + +static int layers_new(int w, int h) +{ + if(num_layers+1 >= MAX_LAYERS) + return -1; + + tilemap_new(&layers[num_layers].tm, w, h); + layers[num_layers].tileset_id = -1; + layers[num_layers].visible = 1; + layers[num_layers].main_layer = 0; + num_layers++; + return num_layers-1; +} + +static int layers_swap(int index1, int index2) +{ + // swap the two layers + layer temp = layers[index1]; + layers[index1] = layers[index2]; + layers[index2] = temp; + return -1; +} + +static int layers_moveup(int index) +{ + if(index < 0 || index >= num_layers) + return index; + + if(index == 0) + return 0; + + layers_swap(index, index-1); + return index-1; +} + +static int layers_movedown(int index) +{ + if(index < 0 || index >= num_layers) + return index; + + if(index+1 == num_layers) + return index; + + layers_swap(index, index+1); + return index+1; +} + +/******************************************************** + TILESET +*********************************************************/ +struct tileset +{ + int tex_id; + image_info img; +}; + +static const int MAX_TILESETS = 64; +static tileset tilesets[MAX_TILESETS]; +static int num_tilesets = 0; + +static int tilesets_new() +{ + tilesets[num_tilesets].img.width = 0; + tilesets[num_tilesets].img.height = 0; + tilesets[num_tilesets].img.data = 0; + tilesets[num_tilesets].tex_id = -1;// gfx_load_texture_raw(img.width, img.height, img.data); + num_tilesets++; + return num_tilesets-1; +} + + +static void tilesets_clear() +{ + // TODO: remove texture aswell + for(int i = 0; i < num_tilesets; i++) + mem_free(tilesets[num_tilesets].img.data); + num_tilesets = 0; + +} + +static int tilesets_set_img(int index, int w, int h, void *data) +{ + tilesets[index].img.width = w; + tilesets[index].img.height = h; + + if(tilesets[index].img.data) + mem_free(tilesets[index].img.data); + tilesets[index].img.data = data; + tilesets[index].tex_id = gfx_load_texture_raw(w, h, data); + return index; +} + +static int tilesets_count() +{ + return num_tilesets; +} + +static tileset *tilesets_get(int index) +{ + return &tilesets[index]; +} + +/******************************************************** + UI +*********************************************************/ +static void render_tilemap(tilemap *tm, float sx, float sy, float scale) +{ + float frac = (1.0f/512.0f); //2.0f; + gfx_quads_begin(); + for(int y = 0; y < tm->height; y++) + for(int x = 0; x < tm->width; x++) + { + unsigned char d = tm->tiles[y*tm->width+x].index; + if(d) + { + gfx_quads_setsubset( + (d%16)/16.0f+frac, + (d/16)/16.0f+frac, + (d%16)/16.0f+1.0f/16.0f-frac, + (d/16)/16.0f+1.0f/16.0f-frac); + gfx_quads_drawTL(sx+x*scale, sy+y*scale, scale, scale); + } + + //gfx_quads_setsubset(x/16.0f,y/16.0f,(x+1)/16.0f,(y+1)/16.0f); + //gfx_quads_drawTL(sx+x*w,sy+y*h,w,h); + } + gfx_quads_end(); +} + +/******************************************************** + EDITOR +*********************************************************/ + +static tilemap brush = {0}; +static tilemap chooser = {0}; +static float world_offset_x = 0, world_offset_y = 0; +static int world_zoom = 3; +static const char *editor_filename = 0; +static int editor_mode = 0; // 0 == tiles, 1 == ents +static int editor_selected_ent = -1; + +static const int TILEMAPFLAG_READONLY = 1; +static const int TILEMAPFLAG_UISPACE = 2; + +static int ui_do_tilemap(void *id, tilemap *tm, int flags, float x, float y, float scale) +{ + /* + int do_input = 1; + if(inp_key_pressed(keys::lalt) || inp_key_pressed(keys::ralt)) + do_input = 0;*/ + + + float mx = ui_mouse_world_x(); + float my = ui_mouse_world_y(); + if(flags&TILEMAPFLAG_UISPACE) + { + mx = ui_mouse_x(); + my = ui_mouse_y(); + } + + int tmx = (int)((mx-x)/scale); // tilemap x + int tmy = (int)((my-y)/scale); // tilemap y + + static int start_tmx, start_tmy; + static int grabbing = 0; + + //float start_tmx_wx = start_tmx*scale+x; + //float start_tmx_wy = start_tmy*scale+y; + + int select_x = 0; + int select_y = 0; + int select_w = 1; + int select_h = 1; + float select_wx = 0; + float select_wy = 0; + float select_ww = 0; + float select_wh = 0; + + if(ui_hot_item() == id) + { + int x0 = start_tmx; + int y0 = start_tmy; + int x1 = tmx; + int y1 = tmy; + + if(x1 < x0) + { + int tmp = x1; + x1 = x0; + x0 = tmp; + } + + if(y1 < y0) + { + int tmp = y1; + y1 = y0; + y0 = tmp; + } + + select_w = x1-x0; + select_h = y1-y0; + select_w++; + select_h++; + select_x = x0; + select_y = y0; + + select_wx = select_x*scale+x; + select_wy = select_y*scale+y; + select_ww = select_w*scale; + select_wh = select_h*scale; + } + // ui_do_tilemap always tries to steal the focus + ui_set_hot_item(id); + + // render the tilemap + render_tilemap(tm, x, y, scale); + + if(ui_hot_item() == id) + { + if(brush.tiles != 0) + { + // draw brush + render_tilemap(&brush, (tmx-brush.width/2)*scale, (tmy-brush.height/2)*scale, scale); + + gfx_texture_set(-1); + gfx_blend_additive(); + gfx_quads_begin(); + gfx_quads_setcolor(1.0f, 0.0f, 0.0f, 0.25f); + gfx_quads_drawTL((tmx-brush.width/2)*scale, (tmy-brush.height/2)*scale, brush.width*scale, brush.height*scale); + gfx_quads_end(); + gfx_blend_normal(); + } + + if(grabbing == 0) + { + if(ui_mouse_button(0)) + { + //grabbing = 1; + start_tmx = (int)((mx-x)/scale); + start_tmy = (int)((my-y)/scale); + if(brush.tiles == 0) + { + dbg_msg("editor", "grabbing..."); + grabbing = 1; // grab tiles + } + else + { + // paint + if(!(flags&TILEMAPFLAG_READONLY)) + { + layer *l = layers_get_current(); + int px = tmx-brush.width/2; + int py = tmy-brush.height/2; + tilemap_blit(&l->tm, &brush, px, py); + //dbg_msg("editor", "painted %d tiles at (%d,%d)", c, px, py); + } + } + } + } + else + { + gfx_texture_set(-1); + gfx_blend_additive(); + gfx_quads_begin(); + gfx_quads_setcolor(1.0f, 1.0f, 1.0f, 0.25f); + gfx_quads_drawTL(select_wx, select_wy, select_ww, select_wh); + gfx_quads_end(); + gfx_blend_normal(); + + if(!ui_mouse_button(0)) + { + grabbing = 0; + + if(brush.tiles == 0) + { + // create brush + dbg_msg("editor", "creating brush w=%d h=%d p0=(%d,%d)", select_w, select_h, select_x, select_y); + tilemap_new(&brush, select_w, select_h); + + // copy data + for(int y = 0; y < select_h; y++) + for(int x = 0; x < select_w; x++) + brush.tiles[y*select_w+x] = tm->tiles[(select_y+y)*tm->width + select_x+x]; + } + } + } + } + + // raw rect around tilemap + gfx_texture_set(-1); + gfx_quads_begin(); + float w = tm->width*scale; + float h = tm->height*scale; + gfx_quads_drawTL(x-2, y-2, 2, h+4); + gfx_quads_drawTL(x-2, y-2, w+4, 2); + gfx_quads_drawTL(x+w, y-2, 2, h+4); + gfx_quads_drawTL(x-2, y+h, w+4, 2); + gfx_quads_end(); + + return 0; +} + +static int ui_do_entity(void *id, entity *ent, int selected) +{ + float x = (float)ent->x; + float y = (float)ent->y; + float w = 16; + float h = 16; + + float mx = ui_mouse_world_x(); + float my = ui_mouse_world_y(); + + int inside = 0; + int r = 0; + if(mx > x-w/2 && mx < x+w/2 && my > y-h/2 && my < y+h/2) + inside = 1; + + if(inside) + ui_set_hot_item(id); + + if(ui_hot_item() == id && ui_mouse_button(0)) + { + ui_set_active_item(id); + r = 1; + } + + if(ui_active_item() == id) + { + if(!ui_mouse_button(0)) + ui_set_active_item(0); + ent->x = (int)ui_mouse_world_x(); + ent->y = (int)ui_mouse_world_y(); + ent->x = (ent->x/32)*32+16; + ent->y = (ent->y/32)*32+16; + } + + // raw rect around tilemap + gfx_texture_set(-1); + gfx_quads_begin(); + if(selected) + gfx_quads_setcolor(1.0f, 0.5f, 0.5f, 0.95f); + else if(ui_hot_item() == id) + gfx_quads_setcolor(1.0f, 1.0f, 1.0f, 0.95f); + else + gfx_quads_setcolor(0.75f, 0.75f, 0.75f, 0.95f); + + gfx_quads_drawTL(x-w/2, y-w/2, w, h); + gfx_quads_end(); + + gfx_texture_set(font_texture); + if(ent->type >= 0) + gfx_quads_text(x-4, y-h/2-4, 24.0f, ent_types[ent->type].name); + + return r; +} + + +static int editor_reset() +{ + // delete all layers + while(layers_count()) + layers_remove(layers_count()-1); + + //layers_new(50, 50); + tilemap_destroy(&brush); + current_layer = 0; + + tilesets_clear(); + + // init chooser + static tile chooser_tiles[256]; + for(int i = 0; i < 256; i++) + { + chooser_tiles[i].index = i; + chooser_tiles[i].flags = 0; + } + + chooser.width = 16; + chooser.height = 16; + chooser.tiles = chooser_tiles; + + return 0; +} + +void draw_editor_button(void *id, const char *text, int checked, float x, float y, float w, float h, void *extra) +{ + gfx_blend_normal(); + gfx_texture_set(-1); + gfx_quads_begin(); + if(ui_hot_item() == id) + gfx_quads_setcolor(1,1,1,1); + else if(checked) + gfx_quads_setcolor(0.75f,0.5f,0.5f,1); + else + gfx_quads_setcolor(0.5f,0.5f,0.5f,1); + + gfx_quads_drawTL(x,y,w,h); + gfx_quads_end(); + gfx_texture_set(font_texture); + gfx_quads_text(x, y, 6.2f, text); +} + +static int editor_loadimage = -1; + +static void editor_listdir_callback(const char *name, int is_dir, void *user) +{ + if(name[0] == '.') // skip this shit! + return; + + int *y = (int*)user; + if(ui_do_button((void*)(*y + 1), name, 0, 10, 10 + *y * 8, 100, 6, draw_editor_button)) + { + char buf[512]; + sprintf(buf, "tilesets/%s", name); + + image_info img; + if(!gfx_load_tga(&img, buf)) + return; + + tilesets_set_img(editor_loadimage, img.width, img.height, img.data); + editor_loadimage = -1; + } + *y += 1; +} + +static void editor_render_loadfile_dialog() +{ + gfx_clear(0.2f,0.2f,0.8f); + // GUI coordsys + gfx_mapscreen(0,0,400.0f,300.0f); + + int index = 0; + fs_listdir("tilesets", editor_listdir_callback, &index); +} + +static void editor_render() +{ + if(editor_loadimage != -1) + { + editor_render_loadfile_dialog(); + return; + } + + // background color + gfx_clear(0.2f,0.2f,0.8f); + + // world coordsys + float zoom = world_zoom; + gfx_mapscreen(world_offset_x,world_offset_y,world_offset_x+400.0f*zoom,world_offset_y+300.0f*zoom); + + for(int i = 0; i < layers_count(); i++) + { + layer *l = layers_get(i); + + gfx_texture_set(-1); + if(l->tileset_id >= 0 && l->tileset_id < tilesets_count()) + gfx_texture_set(tilesets_get(l->tileset_id)->tex_id); + + if(editor_mode == 0) + { + if(l == layers_get_current()) + { + // do current layer + ui_do_tilemap(&l->tm, &l->tm, 0, 0, 0, 32.0f); + } + else if(l->visible) + { + // render layer + render_tilemap(&l->tm, 0, 0, 32.0f); + } + } + else + render_tilemap(&l->tm, 0, 0, 32.0f); + } + + if(editor_mode == 1) + { + // ents mode + for(int i = 0; i < ents_count(); i++) + { + if(ui_do_entity(ents_get(i), ents_get(i), i == editor_selected_ent)) + editor_selected_ent = i; + + } + } + + // GUI coordsys + gfx_mapscreen(0,0,400.0f,300.0f); + + // toolbox + float toolbox_width = 50.0f; + + if(editor_mode == 0) + { + float layerbox_x = 0; + float layerbox_y = 0; + int count = 1; + int main_layer = -1; + for(int i = 0; i < layers_count(); i++) + { + layer *l = layers_get(i); + char buf[128]; + if(l->main_layer) + { + main_layer = i; + sprintf(buf, "Main Layer (%dx%d)", l->tm.width, l->tm.height); + count = 1; + } + else + { + if(main_layer == -1) + sprintf(buf, "Background %d (%dx%d)", count, l->tm.width, l->tm.height); + else + sprintf(buf, "Foreground %d (%dx%d)", count, l->tm.width, l->tm.height); + count++; + } + + // show / hide layer + const char *text = " "; + if(layers_get(i)->visible) + text = "V"; + + if(ui_do_button(&layers_get(i)->visible, text, 0, layerbox_x, layerbox_y+i*8, 6, 6, draw_editor_button)) + layers_get(i)->visible = layers_get(i)->visible^1; + + if(ui_do_button(&layers_get(i)->tileset_id, buf, current_layer == i, layerbox_x+8, layerbox_y+i*8, toolbox_width-8, 6, draw_editor_button)) + { + // select layer + current_layer = i; + } + } + + // draw buttons + { + static int push_button, pull_button; + float y = 150; + float x = 0; + if(ui_do_button(&push_button, "push", 0, x, y, toolbox_width, 6, draw_editor_button)) + current_layer = layers_moveup(current_layer); + y += 7; + + if(ui_do_button(&pull_button, "pull", 0, x, y, toolbox_width, 6, draw_editor_button)) + current_layer = layers_movedown(current_layer); + y += 10; + + static int w_inc, w_dec; + int resize_amount = 10; + if(ui_do_button(&w_dec, "width-", 0, x, y, toolbox_width, 6, draw_editor_button)) + tilemap_resize(&layers_get_current()->tm, layers_get_current()->tm.width-resize_amount, layers_get_current()->tm.height, resize_amount); + y += 7; + if(ui_do_button(&w_inc, "width+", 0, x, y, toolbox_width, 6, draw_editor_button)) + tilemap_resize(&layers_get_current()->tm, layers_get_current()->tm.width+resize_amount, layers_get_current()->tm.height, resize_amount); + y += 10; + + static int h_inc, h_dec; + if(ui_do_button(&h_dec, "height-", 0, x, y, toolbox_width, 6, draw_editor_button)) + tilemap_resize(&layers_get_current()->tm, layers_get_current()->tm.width, layers_get_current()->tm.height-resize_amount, resize_amount); + y += 7; + if(ui_do_button(&h_inc, "height+", 0, x, y, toolbox_width, 6, draw_editor_button)) + tilemap_resize(&layers_get_current()->tm, layers_get_current()->tm.width, layers_get_current()->tm.height+resize_amount, resize_amount); + y += 10; + } + + + float tilesetsbox_x = 0; + float tilesetsbox_y = 230; + for(int i = 0; i < tilesets_count(); i++) + { + char buf[128]; + sprintf(buf, "#%d %dx%d", i, tilesets_get(i)->img.width, tilesets_get(i)->img.height); + if(ui_do_button(&tilesets_get(i)->img, "L", layers_get(current_layer)->tileset_id == i, tilesetsbox_x, tilesetsbox_y+i*7, 6, 6, draw_editor_button)) + { + // load image + editor_loadimage = i; + } + + if(ui_do_button(tilesets_get(i), buf, layers_get(current_layer)->tileset_id == i, tilesetsbox_x+8, tilesetsbox_y+i*7, toolbox_width-8, 6, draw_editor_button)) + { + // select tileset for layer + dbg_msg("editor", "settings tileset %d=i", current_layer, i); + layers_get(current_layer)->tileset_id = i; + } + } + + // (add) button for tilesets + static int load_tileset; + if(ui_do_button(&load_tileset, "(Add)", 0, tilesetsbox_x, tilesetsbox_y+tilesets_count()*7+3, toolbox_width, 6, draw_editor_button)) + tilesets_new(); + + if(brush.tiles != 0) + { + // right mouse button or C clears the brush + if(ui_mouse_button(1) || inp_key_pressed('C')) + tilemap_destroy(&brush); + } + + if(inp_key_pressed(keys::space)) + { + // render chooser + float chooser_x = 0; + float chooser_y = 0; + + gfx_texture_set(-1); + layer *l = layers_get_current(); + if(l && l->tileset_id >= 0 && l->tileset_id < tilesets_count()) + gfx_texture_set(tilesets_get(l->tileset_id)->tex_id); + ui_do_tilemap(&chooser, &chooser, TILEMAPFLAG_READONLY|TILEMAPFLAG_UISPACE, chooser_x, chooser_y, 16.0f); + } + } + else + { + int current_type = -1; + if(editor_selected_ent >= 0 && editor_selected_ent < ents_count()) + current_type = ents_get(editor_selected_ent)->type; + + float y = 0; + for(int i = 0; ent_types[i].name; i++) + { + if(ui_do_button(&ent_types[i], ent_types[i].name, current_type==i, 0, y, toolbox_width, 6, draw_editor_button)) + { + if(editor_selected_ent >= 0 && editor_selected_ent < ents_count()) + ents_get(editor_selected_ent)->type = i; + } + y += 8; + } + + y += 8; + static int add, del; + if(ui_do_button(&add, "Add", 0, 0, y, toolbox_width, 6, draw_editor_button)) + { + int x = (int)(world_offset_x+400*zoom/2)/32*32+16; + int y = (int)(world_offset_y+300*zoom/2)/32*32+16; + ents_new(0, x, y); + } + + y += 8; + if(ui_do_button(&del, "Del", 0, 0, y, toolbox_width, 6, draw_editor_button)) + ents_delete(editor_selected_ent); + } +} + +int editor_load(const char *filename) +{ + datafile *df = datafile_load(filename); + if(!df) + return 0; + + // load tilesets + { + int start, count; + datafile_get_type(df, MAPRES_IMAGE, &start, &count); + for(int i = 0; i < count; i++) + { + mapres_image *img = (mapres_image *)datafile_get_item(df, start+i, 0, 0); + void *data = datafile_get_data(df, img->image_data); + int id = tilesets_new(); + void *data_cpy = mem_alloc(img->width*img->height*4, 1); + mem_copy(data_cpy, data, img->width*img->height*4); + tilesets_set_img(id, img->width, img->height, data_cpy); + } + } + + // load tilemaps + { + int start, num; + datafile_get_type(df, MAPRES_TILEMAP, &start, &num); + for(int t = 0; t < num; t++) + { + mapres_tilemap *tmap = (mapres_tilemap *)datafile_get_item(df, start+t,0,0); + //unsigned char *data = (unsigned char *)datafile_get_data(df, tmap->data); + + layer *l = layers_get(layers_new(tmap->width, tmap->height)); + mem_copy(l->tm.tiles, datafile_get_data(df, tmap->data), tmap->width*tmap->height*2); + l->tileset_id = tmap->image; + l->main_layer = tmap->main; + + // force a main layer + if(num == 1) + l->main_layer = 1; + } + } + + // load entities + { + int type = -1; + for(int i = 0; ent_types[i].name; i++) + { + if(ent_types[i].id == MAPRES_SPAWNPOINT) + { + type = i; + break; + } + } + + int start, num; + datafile_get_type(df, MAPRES_SPAWNPOINT, &start, &num); + for(int t = 0; t < num; t++) + { + mapres_spawnpoint *sp = (mapres_spawnpoint *)datafile_get_item(df, start+t,0,0); + ents_new(type, sp->x, sp->y); + } + } + + { + int start, num; + datafile_get_type(df, MAPRES_ITEM, &start, &num); + for(int t = 0; t < num; t++) + { + mapres_item *it = (mapres_item *)datafile_get_item(df, start+t,0,0); + + int type = -1; + for(int i = 0; ent_types[i].name; i++) + { + if(ent_types[i].id == MAPRES_ITEM && ent_types[i].item_id == it->type) + { + dbg_msg("editor", "i type=%x mapped=%d", it->type, i); + type = i; + break; + } + } + + ents_new(type, it->x, it->y); + } + } + return 1; +} + +int editor_save(const char *filename) +{ + datafile_out *df = datafile_create(filename); + + // add tilesets + for(int i = 0; i < tilesets_count(); i++) + { + mapres_image img; + tileset *ts = tilesets_get(i); + img.width = ts->img.width; + img.height = ts->img.height; + img.image_data = datafile_add_data(df, ts->img.width*ts->img.height*4, ts->img.data); + datafile_add_item(df, MAPRES_IMAGE, i, sizeof(img), &img); + } + + // add tilemaps + for(int i = 0; i < layers_count(); i++) + { + layer *l = layers_get(i); + mapres_tilemap tm; + tm.image = l->tileset_id; + tm.width = l->tm.width; + tm.height = l->tm.height; + tm.x = 0; + tm.y = 0; + tm.main = l->main_layer; + tm.scale = 1<<16; + tm.data = datafile_add_data(df, l->tm.width*l->tm.height*2, l->tm.tiles); + datafile_add_item(df, MAPRES_TILEMAP, i, sizeof(tm), &tm); + } + + // add collision + char *collisiondata = 0x0; + for(int i = 0; i < layers_count(); i++) + { + layer *l = layers_get(i); + if(l->main_layer) + { + mapres_collision col; + col.width = l->tm.width; + col.height = l->tm.height; + + collisiondata = (char *)mem_alloc(col.width*col.height, 1); + for(int y = 0, c = 0; y < col.height; y++) + for(int x = 0; x < col.width; x++, c++) + { + if(l->tm.tiles[c].index) + collisiondata[c] = 1; + else + collisiondata[c] = 0; + } + + col.data_index = datafile_add_data(df, col.width*col.height, collisiondata); + datafile_add_item(df, MAPRES_COLLISIONMAP, 0, sizeof(col), &col); + break; + } + } + + // add spawnpoints + for(int i = 0, id = 0; i < ents_count(); i++) + { + entity *ent = ents_get(i); + if(ent->type >= 0 && ent_types[ent->type].id == MAPRES_SPAWNPOINT) + { + mapres_spawnpoint sp; + sp.x = ent->x; + sp.y = ent->y; + sp.type = 0; + datafile_add_item(df, MAPRES_SPAWNPOINT, id, sizeof(sp), &sp); + id++; + } + } + + // add items + for(int i = 0, id = 0; i < ents_count(); i++) + { + entity *ent = ents_get(i); + if(ent->type >= 0 && ent_types[ent->type].id == MAPRES_ITEM) + { + mapres_item it; + it.x = ent->x; + it.y = ent->y; + it.type = ent_types[ent->type].item_id; + dbg_msg("editor", "i mapped=%d type=%x", ent->type, it.type); + datafile_add_item(df, MAPRES_ITEM, id, sizeof(it), &it); + id++; + } + } + + // finish adn clean up + datafile_finish(df); + mem_free(collisiondata); + + return 0; +} + +static int editor_loop() +{ + int mouse_x = 0; + int mouse_y = 0; + + mouse::set_mode(mouse::mode_relative); + + while(!inp_key_pressed(keys::esc)) + { + // update input + inp_update(); + + // handle mouse movement + float mx, my, mwx, mwy; + int rx, ry; + { + inp_mouse_relative(&rx, &ry); + mouse_x += rx; + mouse_y += ry; + if(mouse_x < 0) mouse_x = 0; + if(mouse_y < 0) mouse_y = 0; + if(mouse_x > gfx_screenwidth()) mouse_x = gfx_screenwidth(); + if(mouse_y > gfx_screenheight()) mouse_y = gfx_screenheight(); + + // update the ui + mx = (mouse_x/(float)gfx_screenwidth())*400.0f; + my = (mouse_y/(float)gfx_screenheight())*300.0f; + mwx = world_offset_x+mx*world_zoom; // adjust to zoom and offset + mwy = world_offset_y+my*world_zoom; // adjust to zoom and offset + + int buttons = 0; + if(inp_mouse_button_pressed(0)) buttons |= 1; + if(inp_mouse_button_pressed(1)) buttons |= 2; + if(inp_mouse_button_pressed(2)) buttons |= 4; + + ui_update(mx,my,mwx,mwy,buttons); + } + + // + editor_render(); + + if(inp_key_pressed(keys::lalt) || inp_key_pressed(keys::ralt)) + { + static int moveid; + ui_set_hot_item(&moveid); + if(inp_mouse_button_pressed(0)) + { + world_offset_x -= rx*2; + world_offset_y -= ry*2; + } + } + + + // render butt ugly mouse cursor + gfx_texture_set(-1); + gfx_quads_begin(); + gfx_quads_setcolor(0,0,0,1); + gfx_quads_draw_freeform(mx,my,mx,my, + mx+7,my, + mx,my+7); + gfx_quads_setcolor(1,1,1,1); + gfx_quads_draw_freeform(mx+1,my+1,mx+1,my+1, + mx+5,my+1, + mx+1,my+5); + gfx_quads_end(); + + // swap the buffers + gfx_swap(); + + // + if(keys::pressed(keys::f1)) + mouse::set_mode(mouse::mode_absolute); + if(keys::pressed(keys::f2)) + mouse::set_mode(mouse::mode_relative); + + + // mode switch + if(inp_key_down(keys::tab)) + editor_mode ^= 1; + + // zoom in + if(inp_key_down(keys::kp_add)) + { + world_zoom--; + if(world_zoom < 3) + world_zoom = 3; + } + + // zoom out + if(inp_key_down(keys::kp_subtract)) + { + world_zoom++; + if(world_zoom > 8) + world_zoom = 8; + } + + if(inp_key_pressed(keys::lctrl) || inp_key_pressed(keys::rctrl)) + { + if(inp_key_down('L')) + { + int w = 50, h = 50; + for(int i = 0; i < layers_count(); i++) + { + layer *l = layers_get(i); + if(l->main_layer) + { + w = l->tm.width; + h = l->tm.height; + break; + } + } + // copy main layer size + layers_new(w, h); + } + + if(inp_key_down('S')) + { + dbg_msg("editor", "save"); + editor_save(editor_filename); + } + + } + + if(inp_key_down(keys::f5)) + { + dbg_msg("editor", "quick save"); + editor_save("quicksave.map"); + } + + if(inp_key_down(keys::f8)) + { + dbg_msg("editor", "quick load"); + int s = current_layer; + editor_reset(); + editor_load("quicksave.map"); + current_layer = s; + if(current_layer >= layers_count()) + current_layer = layers_count(); + + } + + // be nice + thread_sleep(1); + } + + return 0; +} + + +int editor_main(int argc, char **argv) +{ + dbg_msg("editor", "starting..."); + + // parse arguments + for(int i = 1; i < argc; i++) + { + if(argv[i][0] == '-' && argv[i][1] == 'e' && argv[i][2] == 0 && argc - i > 1) + { + // -e NAME + i++; + editor_filename = argv[i]; + } + } + + if(!editor_filename) + { + dbg_msg("editor", "no filename given"); + return -1; + } + + if(!gfx_init(false)) + return -1; + + // reset and start + font_texture = gfx_load_texture_tga("data/debug_font.tga"); + editor_reset(); + + // load or new + if(!editor_load(editor_filename)) + { + layer *l = layers_get(layers_new(50, 50)); + l->main_layer = 1; + } + + /* + ents_new(0, 10, 10); + ents_new(0, 10, 10); + ents_new(0, 10, 10); + ents_new(0, 10, 10); + */ + + editor_loop(); + + return 0; +} diff --git a/src/engine/client/client.cpp b/src/engine/client/client.cpp new file mode 100644 index 00000000..4aceaf0f --- /dev/null +++ b/src/engine/client/client.cpp @@ -0,0 +1,712 @@ +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include "ui.h" + +#include + +#include + +using namespace baselib; + +// --- string handling (MOVE THESE!!) --- +void snap_encode_string(const char *src, int *dst, int length, int max_length) +{ + const unsigned char *p = (const unsigned char *)src; + + // handle whole int + for(int i = 0; i < length/4; i++) + { + *dst = (p[0]<<24|p[1]<<16|p[2]<<8|p[3]); + p += 4; + dst++; + } + + // take care of the left overs + int left = length%4; + if(left) + { + unsigned last = 0; + switch(left) + { + case 3: last |= p[2]<<8; + case 2: last |= p[1]<<16; + case 1: last |= p[0]<<24; + } + *dst = last; + } +} + +void snap_decode_string(const int *src, char *dst, int max_length) +{ + dbg_assert((max_length%4) == 0, "length must be power of 4"); + for(int i = 0; i < max_length; i++) + dst[0] = 0; + + for(int i = 0; i < max_length/4; i++) + { + dst[0] = (*src>>24)&0xff; + dst[1] = (*src>>16)&0xff; + dst[2] = (*src>>8)&0xff; + dst[3] = (*src)&0xff; + src++; + dst+=4; + } + dst[-1] = 0; // make sure to zero terminate +} + +// --- input wrappers --- +static int keyboard_state[2][keys::last]; +static int keyboard_current = 0; +static int keyboard_first = 1; + +void inp_mouse_relative(int *x, int *y) { mouse::position(x, y); } +int inp_key_pressed(int key) { return keyboard_state[keyboard_current][key]; } +int inp_key_was_pressed(int key) { return keyboard_state[keyboard_current^1][key]; } +int inp_key_down(int key) { return inp_key_pressed(key)&&!inp_key_was_pressed(key); } +int inp_mouse_button_pressed(int button) { return mouse::pressed(button); } + +void inp_update() +{ + if(keyboard_first) + { + // make sure to reset + keyboard_first = 0; + inp_update(); + } + + keyboard_current = keyboard_current^1; + for(int i = 0; i < keys::last; i++) + keyboard_state[keyboard_current][i] = keys::pressed(i); +} + +// --- input snapping --- +static int input_data[MAX_INPUT_SIZE]; +static int input_data_size; +static int input_is_changed = 1; +void snap_input(void *data, int size) +{ + if(input_data_size != size || memcmp(input_data, data, size)) + input_is_changed = 1; + mem_copy(input_data, data, size); + input_data_size = size; +} + +// -- snapshot handling --- +enum +{ + SNAP_INCOMMING=2, + NUM_SNAPSHOT_TYPES=3, +}; + +static snapshot *snapshots[NUM_SNAPSHOT_TYPES]; +static char snapshot_data[NUM_SNAPSHOT_TYPES][MAX_SNAPSHOT_SIZE]; +static int recived_snapshots; +static int64 snapshot_start_time; +static int64 local_start_time; + +float client_localtime() +{ + return (time_get()-local_start_time)/(float)(time_freq()); +} + +void *snap_get_item(int snapid, int index, snap_item *item) +{ + dbg_assert(snapid >= 0 && snapid < NUM_SNAPSHOT_TYPES, "invalid snapid"); + snapshot::item *i = snapshots[snapid]->get_item(index); + item->type = i->type(); + item->id = i->id(); + return (void *)i->data; +} + +int snap_num_items(int snapid) +{ + dbg_assert(snapid >= 0 && snapid < NUM_SNAPSHOT_TYPES, "invalid snapid"); + return snapshots[snapid]->num_items; +} + +static void snap_init() +{ + snapshots[SNAP_INCOMMING] = (snapshot*)snapshot_data[0]; + snapshots[SNAP_CURRENT] = (snapshot*)snapshot_data[1]; + snapshots[SNAP_PREV] = (snapshot*)snapshot_data[2]; + mem_zero(snapshot_data, NUM_SNAPSHOT_TYPES*MAX_SNAPSHOT_SIZE); + recived_snapshots = 0; +} + +float snap_intratick() +{ + return (time_get() - snapshot_start_time)/(float)(time_freq()/SERVER_TICK_SPEED); +} + +void *snap_find_item(int snapid, int type, int id) +{ + // TODO: linear search. should be fixed. + for(int i = 0; i < snapshots[snapid]->num_items; i++) + { + snapshot::item *itm = snapshots[snapid]->get_item(i); + if(itm->type() == type && itm->id() == id) + return (void *)itm->data; + } + return 0x0; +} + + +int menu_loop(); +float frametime = 0.0001f; + +float client_frametime() +{ + return frametime; +} + +void unpack(const char *src, const char *fmt, ...) +{ +} + +/*int modc_onmsg(int msg) +{ + msg_get("iis") +}*/ + +/* + i = int (int i) + s = string (const char *str) + r = raw data (int size, void *data) +*/ + +/* +class packet2 +{ +private: + // packet data + struct header + { + unsigned msg; + unsigned ack; + unsigned seq; + }; + + unsigned char packet_data[MAX_PACKET_SIZE]; + unsigned char *current; + + enum + { + MAX_PACKET_SIZE = 1024, + }; + +public: + packet2() + { + current = packet_data; + current += sizeof(header); + } + + int pack(char *dst, const char *fmt, ...) + { + va_list arg_list; + va_start(arg_list, fmt); + while(*fmt) + { + if(*fmt == 's') + { + // pack string + const char *s = va_arg(arg_list, const char*); + *dst++ = 2; + while(*s) + { + *dst = *s; + dst++; + s++; + } + *dst = 0; // null terminate + dst++; + fmt++; + } + else if(*fmt == 'i') + { + // pack int + int i = va_arg(arg_list, int); + *dst++ = 1; + *dst++ = (i>>24)&0xff; + *dst++ = (i>>16)&0xff; + *dst++ = (i>>8)&0xff; + *dst++ = i&0xff; + fmt++; + } + else + { + dbg_break(); // error + break; + } + } + va_end(arg_list); + } +}; +*/ +/* +int msg_get(const char *fmt) +{ + +} + +int client_msg_send(int msg, const char *fmt, ...) + +int server_msg_send(int msg, const char *fmt, ...) +{ + +}*/ + +// --- client --- +class client +{ +public: + socket_udp4 socket; + connection conn; + int64 reconnect_timer; + + int snapshot_part; + + int debug_font; // TODO: rfemove this line + + // data to hold three snapshots + // previous, + char name[MAX_NAME_LENGTH]; + + bool fullscreen; + + enum + { + STATE_OFFLINE, + STATE_CONNECTING, + STATE_LOADING, + STATE_ONLINE, + STATE_BROKEN, + STATE_QUIT, + }; + + int state; + int get_state() { return state; } + void set_state(int s) + { + dbg_msg("game", "state change. last=%d current=%d", state, s); + state = s; + } + + void set_name(const char *new_name) + { + mem_zero(name, MAX_NAME_LENGTH); + strncpy(name, new_name, MAX_NAME_LENGTH); + name[MAX_NAME_LENGTH-1] = 0; + } + + void set_fullscreen(bool flag) { fullscreen = flag; } + + void send_packet(packet *p) + { + conn.send(p); + } + + void send_connect() + { + recived_snapshots = 0; + + /* + pack(NETMSG_CLIENT_CONNECT, "sssss", + TEEWARS_NETVERSION, + name, + "no clan", + "password", + "myskin"); + */ + + packet p(NETMSG_CLIENT_CONNECT); + p.write_str(TEEWARS_NETVERSION); // payload + p.write_str(name); + p.write_str("no clan"); + p.write_str("password"); + p.write_str("myskin"); + send_packet(&p); + } + + void send_done() + { + packet p(NETMSG_CLIENT_DONE); + send_packet(&p); + } + + void send_error(const char *error) + { + /* + pack(NETMSG_CLIENT_ERROR, "s", error); + */ + packet p(NETMSG_CLIENT_ERROR); + p.write_str(error); + send_packet(&p); + //send_packet(&p); + //send_packet(&p); + } + + void send_input() + { + /* + pack(NETMSG_CLIENT_ERROR, "s", error); + */ + packet p(NETMSG_CLIENT_INPUT); + p.write_int(input_data_size); + for(int i = 0; i < input_data_size/4; i++) + p.write_int(input_data[i]); + send_packet(&p); + } + + void disconnect() + { + send_error("disconnected"); + set_state(STATE_OFFLINE); + map_unload(); + } + + void connect(netaddr4 *server_address) + { + conn.init(&socket, server_address); + + // start by sending connect + send_connect(); + set_state(STATE_CONNECTING); + reconnect_timer = time_get()+time_freq(); + } + + bool load_data() + { + debug_font = gfx_load_texture_tga("data/debug_font.tga"); + return true; + } + + void render() + { + gfx_clear(0.0f,0.0f,0.0f); + + // this should be moved around abit + if(get_state() == STATE_ONLINE) + { + modc_render(); + } + else if (get_state() != STATE_CONNECTING && get_state() != STATE_LOADING) + { + netaddr4 server_address; + int status = modmenu_render(&server_address, name, MAX_NAME_LENGTH); + + if (status == -1) + set_state(STATE_QUIT); + else if (status) + connect(&server_address); + } + else if (get_state() == STATE_CONNECTING) + { + static int64 start = time_get(); + static int tee_texture; + static int connecting_texture; + static bool inited = false; + + if (!inited) + { + tee_texture = gfx_load_texture_tga("data/gui_tee.tga"); + connecting_texture = gfx_load_texture_tga("data/gui/connecting.tga"); + + inited = true; + } + + gfx_mapscreen(0,0,400.0f,300.0f); + + float t = (time_get() - start) / (double)time_freq(); + + float speed = 2*sin(t); + + speed = 1.0f; + + float x = 208 + sin(t*speed) * 32; + float w = sin(t*speed + 3.149) * 64; + + ui_do_image(tee_texture, x, 95, w, 64); + ui_do_image(connecting_texture, 88, 150, 256, 64); + } + } + + void run(netaddr4 *server_address) + { + snapshot_part = 0; + + // init graphics and sound + if(!gfx_init(fullscreen)) + return; + + snd_init(); // sound is allowed to fail + + // load data + if(!load_data()) + return; + + // init snapshotting + snap_init(); + + // init the mod + modc_init(); + + // init menu + modmenu_init(); + + // open socket + if(!socket.open(0)) + { + dbg_msg("network/client", "failed to open socket"); + return; + } + + // connect to the server if wanted + if (server_address) + connect(server_address); + + //int64 inputs_per_second = 50; + //int64 time_per_input = time_freq()/inputs_per_second; + int64 game_starttime = time_get(); + int64 last_input = game_starttime; + + int64 reporttime = time_get(); + int64 reportinterval = time_freq()*1; + int frames = 0; + + mouse::set_mode(mouse::mode_relative); + + while (1) + { + frames++; + int64 frame_start_time = time_get(); + + // send input + if(get_state() == STATE_ONLINE) + { + if(input_is_changed || time_get() > last_input+time_freq()) + { + send_input(); + input_is_changed = 0; + last_input = time_get(); + } + } + + // update input + inp_update(); + + // + if(keys::pressed(keys::f1)) + mouse::set_mode(mouse::mode_absolute); + if(keys::pressed(keys::f2)) + mouse::set_mode(mouse::mode_relative); + + // pump the network + pump_network(); + + // render + render(); + + // swap the buffers + gfx_swap(); + + // check conditions + if(get_state() == STATE_BROKEN || get_state() == STATE_QUIT) + break; + + // be nice + //thread_sleep(1); + + if(reporttime < time_get()) + { + unsigned sent, recved; + conn.counter_get(&sent, &recved); + dbg_msg("client/report", "fps=%.02f", + frames/(float)(reportinterval/time_freq())); + frames = 0; + reporttime += reportinterval; + conn.counter_reset(); + } + + if (keys::pressed(keys::esc)) + if (get_state() == STATE_CONNECTING || get_state() == STATE_ONLINE) + disconnect(); + + // update frametime + frametime = (time_get()-frame_start_time)/(float)time_freq(); + } + + modc_shutdown(); + disconnect(); + + modmenu_shutdown(); + + gfx_shutdown(); + snd_shutdown(); + } + + void error(const char *msg) + { + dbg_msg("game", "error: %s", msg); + send_error(msg); + set_state(STATE_BROKEN); + } + + void process_packet(packet *p) + { + if(p->msg() == NETMSG_SERVER_ACCEPT) + { + const char *map; + map = p->read_str(); + + if(p->is_good()) + { + dbg_msg("client/network", "connection accepted, map=%s", map); + set_state(STATE_LOADING); + + if(map_load(map)) + { + modc_entergame(); + send_done(); + dbg_msg("client/network", "loading done"); + // now we will wait for two snapshots + // to finish the connection + } + else + { + error("failure to load map"); + } + } + } + else if(p->msg() == NETMSG_SERVER_SNAP) + { + //dbg_msg("client/network", "got snapshot"); + int num_parts = p->read_int(); + int part = p->read_int(); + int part_size = p->read_int(); + + if(p->is_good()) + { + if(snapshot_part == part) + { + const char *d = p->read_raw(part_size); + mem_copy((char*)snapshots[SNAP_INCOMMING] + part*MAX_SNAPSHOT_PACKSIZE, d, part_size); + snapshot_part++; + + if(snapshot_part == num_parts) + { + snapshot *tmp = snapshots[SNAP_PREV]; + snapshots[SNAP_PREV] = snapshots[SNAP_CURRENT]; + snapshots[SNAP_CURRENT] = tmp; + + // decompress snapshot + lzw_decompress(snapshots[SNAP_INCOMMING], snapshots[SNAP_CURRENT]); + + // apply snapshot, cycle pointers + recived_snapshots++; + snapshot_start_time = time_get(); + + // we got two snapshots until we see us self as connected + if(recived_snapshots == 2) + { + local_start_time = time_get(); + set_state(STATE_ONLINE); + } + + if(recived_snapshots > 2) + modc_newsnapshot(); + + snapshot_part = 0; + } + + } + else + { + dbg_msg("client", "snapshot reset!"); + snapshot_part = 0; + } + } + } + else + { + dbg_msg("server/client", "unknown packet %x", p->msg()); + } + } + + void pump_network() + { + while(1) + { + packet p; + netaddr4 from; + int bytes = socket.recv(&from, p.data(), p.max_size()); + + if(bytes <= 0) + break; + + process_packet(&p); + } + + // + if(get_state() == STATE_CONNECTING && time_get() > reconnect_timer) + { + send_connect(); + reconnect_timer = time_get() + time_freq(); + } + } +}; + +int main(int argc, char **argv) +{ + dbg_msg("client", "starting..."); + netaddr4 server_address(127, 0, 0, 1, 8303); + const char *name = "nameless jerk"; + bool connect_at_once = false; + bool fullscreen = true; + + // init network, need to be done first so we can do lookups + net_init(); + + // parse arguments + for(int i = 1; i < argc; i++) + { + if(argv[i][0] == '-' && argv[i][1] == 'c' && argv[i][2] == 0 && argc - i > 1) + { + // -c SERVER + i++; + if(net_host_lookup(argv[i], 8303, &server_address) != 0) + dbg_msg("main", "could not find the address of %s, connecting to localhost", argv[i]); + else + connect_at_once = true; + } + else if(argv[i][0] == '-' && argv[i][1] == 'n' && argv[i][2] == 0 && argc - i > 1) + { + // -n NAME + i++; + name = argv[i]; + } + else if(argv[i][0] == '-' && argv[i][1] == 'w' && argv[i][2] == 0) + { + // -w + fullscreen = false; + } + } + + // start the server + client c; + c.set_name(name); + c.set_fullscreen(fullscreen); + c.run(connect_at_once ? &server_address : 0x0); + return 0; +} diff --git a/src/engine/client/gfx.cpp b/src/engine/client/gfx.cpp new file mode 100644 index 00000000..a824feac --- /dev/null +++ b/src/engine/client/gfx.cpp @@ -0,0 +1,583 @@ +#include +#include +#include + +#include + +using namespace baselib; + +static opengl::context context; + +struct custom_vertex +{ + vec3 pos; + vec2 tex; + vec4 color; +}; + +const int vertexBufferSize = 2048; +//static custom_vertex vertices[4]; +static custom_vertex* g_pVertices = 0; +static int g_iVertexStart = 0; +static int g_iVertexEnd = 0; +static vec4 g_QuadColor[4]; +static vec2 g_QuadTexture[4]; + +static opengl::vertex_buffer vertex_buffer; +//static int screen_width = 800; +//static int screen_height = 600; +static int screen_width = 1024; +static int screen_height = 768; +static float rotation = 0; +static int quads_drawing = 0; + + +struct texture_holder +{ + opengl::texture tex; + int flags; + int next; +}; + +static const int MAX_TEXTURES = 128; + +static texture_holder textures[MAX_TEXTURES]; +static int first_free_texture; + +static const char null_texture_data[] = { + 0xff,0x00,0x00,0xff, 0xff,0x00,0x00,0xff, 0x00,0xff,0x00,0xff, 0x00,0xff,0x00,0xff, + 0xff,0x00,0x00,0xff, 0xff,0x00,0x00,0xff, 0x00,0xff,0x00,0xff, 0x00,0xff,0x00,0xff, + 0x00,0x00,0xff,0xff, 0x00,0x00,0xff,0xff, 0xff,0xff,0x00,0xff, 0xff,0xff,0x00,0xff, + 0x00,0x00,0xff,0xff, 0x00,0x00,0xff,0xff, 0xff,0xff,0x00,0xff, 0xff,0xff,0x00,0xff, +}; + +static void draw_quad(bool _bflush = false) +{ + if (!_bflush && ((g_iVertexEnd + 4) < vertexBufferSize)) + { + // Just add + g_iVertexEnd += 4; + } + else if (g_iVertexEnd) + { + if (!_bflush) + g_iVertexEnd += 4; + if(GLEW_VERSION_2_0) + { + // set the data + vertex_buffer.data(g_pVertices, vertexBufferSize * sizeof(custom_vertex), GL_DYNAMIC_DRAW); + opengl::stream_vertex(&vertex_buffer, 3, GL_FLOAT, sizeof(custom_vertex), 0); + opengl::stream_texcoord(&vertex_buffer, 0, 2, GL_FLOAT, + sizeof(custom_vertex), + sizeof(vec3)); + opengl::stream_color(&vertex_buffer, 4, GL_FLOAT, + sizeof(custom_vertex), + sizeof(vec3)+sizeof(vec2)); + opengl::draw_arrays(GL_QUADS, 0, g_iVertexEnd); + } + else + { + glVertexPointer(3, GL_FLOAT, + sizeof(custom_vertex), + (char*)g_pVertices); + glTexCoordPointer(2, GL_FLOAT, + sizeof(custom_vertex), + (char*)g_pVertices + sizeof(vec3)); + glColorPointer(4, GL_FLOAT, + sizeof(custom_vertex), + (char*)g_pVertices + sizeof(vec3) + sizeof(vec2)); + glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glEnableClientState(GL_COLOR_ARRAY); + glDrawArrays(GL_QUADS, 0, g_iVertexEnd); + } + // Reset pointer + g_iVertexEnd = 0; + } +} + +bool gfx_init(bool fullscreen) +{ + if(!context.create(screen_width, screen_height, 24, 8, 16, 0, fullscreen?opengl::context::FLAG_FULLSCREEN:0)) + { + dbg_msg("game", "failed to create gl context"); + return false; + } + // Init vertices + if (g_pVertices) + mem_free(g_pVertices); + g_pVertices = (custom_vertex*)mem_alloc(sizeof(custom_vertex) * vertexBufferSize, 1); + g_iVertexStart = 0; + g_iVertexEnd = 0; + + context.set_title("---"); + + /* + dbg_msg("gfx", "OpenGL version %d.%d.%d", context.version_major(), + context.version_minor(), + context.version_rev());*/ + + gfx_mapscreen(0,0,screen_width, screen_height); + + // TODO: make wrappers for this + glEnable(GL_BLEND); + + // model + mat4 mat = mat4::identity; + opengl::matrix_modelview(&mat); + + // Set all z to -5.0f + for (int i = 0; i < vertexBufferSize; i++) + g_pVertices[i].pos.z = -5.0f; + + if(GLEW_VERSION_2_0) + { + // set the streams + vertex_buffer.data(g_pVertices, sizeof(vertexBufferSize), GL_DYNAMIC_DRAW); + opengl::stream_vertex(&vertex_buffer, 3, GL_FLOAT, sizeof(custom_vertex), 0); + opengl::stream_texcoord(&vertex_buffer, 0, 2, GL_FLOAT, + sizeof(custom_vertex), + sizeof(vec3)); + opengl::stream_color(&vertex_buffer, 4, GL_FLOAT, + sizeof(custom_vertex), + sizeof(vec3)+sizeof(vec2)); + } + + // init textures + first_free_texture = 0; + for(int i = 0; i < MAX_TEXTURES; i++) + textures[i].next = i+1; + textures[MAX_TEXTURES-1].next = -1; + + // create null texture, will get id=0 + gfx_load_texture_raw(4,4,null_texture_data); + + return true; +} + +int gfx_unload_texture(int index) +{ + textures[index].tex.clear(); + textures[index].next = first_free_texture; + first_free_texture = index; + return 0; +} + +void gfx_blend_normal() +{ + // TODO: wrapper for this + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); +} + +void gfx_blend_additive() +{ + // TODO: wrapper for this + glBlendFunc(GL_SRC_ALPHA, GL_ONE); +} + +int gfx_load_texture_raw(int w, int h, const void *data) +{ + // grab texture + int tex = first_free_texture; + first_free_texture = textures[tex].next; + textures[tex].next = -1; + + // set data and return + // TODO: should be RGBA, not BGRA + dbg_msg("gfx", "%d = %dx%d", tex, w, h); + textures[tex].tex.data2d(w, h, GL_RGBA, GL_BGRA, GL_UNSIGNED_BYTE, data); + return tex; +} + +// simple uncompressed RGBA loaders +int gfx_load_texture_tga(const char *filename) +{ + image_info img; + + if(gfx_load_tga(&img, filename)) + { + int id = gfx_load_texture_raw(img.width, img.height, img.data); + mem_free(img.data); + return id; + } + + return 0; +} + +int gfx_load_tga(image_info *img, const char *filename) +{ + // open file for reading + file_stream file; + if(!file.open_r(filename)) + { + dbg_msg("game/tga", "failed to open file. filename='%s'", filename); + return 0; + } + + // read header + unsigned char headers[18]; + file.read(headers, sizeof(headers)); + img->width = headers[12]+(headers[13]<<8); + img->height = headers[14]+(headers[15]<<8); + + bool flipx = (headers[17] >> 4) & 1; + bool flipy = !((headers[17] >> 5) & 1); + + (void)flipx; // TODO: make use of this flag + + if(headers[2] != 2) // needs to be uncompressed RGB + { + dbg_msg("game/tga", "tga not uncompressed rgb. filename='%s'", filename); + return 0; + } + + if(headers[16] != 32) // needs to be RGBA + { + dbg_msg("game/tga", "tga is 32bit. filename='%s'", filename); + return 0; + } + + // read data + int data_size = img->width*img->height*4; + img->data = mem_alloc(data_size, 1); + + if (flipy) + { + for (int i = 0; i < img->height; i++) + { + file.read((char *)img->data + (img->height-i-1)*img->width*4, img->width*4); + } + } + else + file.read(img->data, data_size); + file.close(); + + return 1; +} + +void gfx_shutdown() +{ + if (g_pVertices) + mem_free(g_pVertices); + context.destroy(); +} + +void gfx_swap() +{ + context.swap(); +} + +int gfx_screenwidth() +{ + return screen_width; +} + +int gfx_screenheight() +{ + return screen_height; +} + +void gfx_texture_set(int slot) +{ + dbg_assert(quads_drawing == 0, "called gfx_texture_set within quads_begin"); + if(slot == -1) + opengl::texture_disable(0); + else + opengl::texture_2d(0, &textures[slot].tex); +} + +void gfx_clear(float r, float g, float b) +{ + glClearColor(r,g,b,1.0f); + opengl::clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); +} + +void gfx_mapscreen(float tl_x, float tl_y, float br_x, float br_y) +{ + mat4 mat; + mat.ortho(tl_x, br_x, br_y, tl_y, 1.0f, 10.f); + opengl::matrix_projection(&mat); +} + +void gfx_setoffset(float x, float y) +{ + //const float scale = 0.75f; + const float scale = 1.0f; + mat4 mat = mat4::identity; + mat.m[0] = scale; + mat.m[5] = scale; + mat.m[10] = scale; + mat.m[12] = x*scale; + mat.m[13] = y*scale; + opengl::matrix_modelview(&mat); +} + +void gfx_quads_begin() +{ + dbg_assert(quads_drawing == 0, "called quads_begin twice"); + quads_drawing++; + + gfx_quads_setsubset(0,0,1,1); + gfx_quads_setrotation(0); + gfx_quads_setcolor(1,1,1,1); +} + +void gfx_quads_end() +{ + dbg_assert(quads_drawing == 1, "called quads_end without quads_begin"); + draw_quad(true); + quads_drawing--; +} + + +void gfx_quads_setrotation(float angle) +{ + dbg_assert(quads_drawing == 1, "called gfx_quads_setrotation without quads_begin"); + rotation = angle; +} + +void gfx_quads_setcolorvertex(int i, float r, float g, float b, float a) +{ + dbg_assert(quads_drawing == 1, "called gfx_quads_setcolorvertex without quads_begin"); + g_QuadColor[i].r = r; + g_QuadColor[i].g = g; + g_QuadColor[i].b = b; + g_QuadColor[i].a = a; +} + +void gfx_quads_setcolor(float r, float g, float b, float a) +{ + dbg_assert(quads_drawing == 1, "called gfx_quads_setcolor without quads_begin"); + g_QuadColor[0] = vec4(r,g,b,a); + g_QuadColor[1] = vec4(r,g,b,a); + g_QuadColor[2] = vec4(r,g,b,a); + g_QuadColor[3] = vec4(r,g,b,a); + /*gfx_quads_setcolorvertex(0,r,g,b,a); + gfx_quads_setcolorvertex(1,r,g,b,a); + gfx_quads_setcolorvertex(2,r,g,b,a); + gfx_quads_setcolorvertex(3,r,g,b,a);*/ +} + +void gfx_quads_setsubset(float tl_u, float tl_v, float br_u, float br_v) +{ + dbg_assert(quads_drawing == 1, "called gfx_quads_setsubset without quads_begin"); + + g_QuadTexture[0].x = tl_u; + g_QuadTexture[0].y = tl_v; + //g_pVertices[g_iVertexEnd].tex.u = tl_u; + //g_pVertices[g_iVertexEnd].tex.v = tl_v; + + g_QuadTexture[1].x = br_u; + g_QuadTexture[1].y = tl_v; + //g_pVertices[g_iVertexEnd + 2].tex.u = br_u; + //g_pVertices[g_iVertexEnd + 2].tex.v = tl_v; + + g_QuadTexture[2].x = br_u; + g_QuadTexture[2].y = br_v; + //g_pVertices[g_iVertexEnd + 1].tex.u = tl_u; + //g_pVertices[g_iVertexEnd + 1].tex.v = br_v; + + g_QuadTexture[3].x = tl_u; + g_QuadTexture[3].y = br_v; + //g_pVertices[g_iVertexEnd + 3].tex.u = br_u; + //g_pVertices[g_iVertexEnd + 3].tex.v = br_v; +} + +static void rotate(vec3 ¢er, vec3 &point) +{ + vec3 p = point-center; + point.x = p.x * cosf(rotation) - p.y * sinf(rotation) + center.x; + point.y = p.x * sinf(rotation) + p.y * cosf(rotation) + center.y; +} + +void gfx_quads_draw(float x, float y, float w, float h) +{ + gfx_quads_drawTL(x-w/2, y-h/2,w,h); +} + +void gfx_quads_drawTL(float x, float y, float width, float height) +{ + dbg_assert(quads_drawing == 1, "called quads_draw without quads_begin"); + + vec3 center; + center.x = x + width/2; + center.y = y + height/2; + center.z = 0; + + g_pVertices[g_iVertexEnd].pos.x = x; + g_pVertices[g_iVertexEnd].pos.y = y; + g_pVertices[g_iVertexEnd].tex.u = g_QuadTexture[0].x; + g_pVertices[g_iVertexEnd].tex.v = g_QuadTexture[0].y; + g_pVertices[g_iVertexEnd].color = g_QuadColor[0]; + rotate(center, g_pVertices[g_iVertexEnd].pos); + + g_pVertices[g_iVertexEnd + 1].pos.x = x+width; + g_pVertices[g_iVertexEnd + 1].pos.y = y; + g_pVertices[g_iVertexEnd + 1].tex.u = g_QuadTexture[1].x; + g_pVertices[g_iVertexEnd + 1].tex.v = g_QuadTexture[1].y; + g_pVertices[g_iVertexEnd + 1].color = g_QuadColor[1]; + rotate(center, g_pVertices[g_iVertexEnd + 1].pos); + + g_pVertices[g_iVertexEnd + 2].pos.x = x + width; + g_pVertices[g_iVertexEnd + 2].pos.y = y+height; + g_pVertices[g_iVertexEnd + 2].tex.u = g_QuadTexture[2].x; + g_pVertices[g_iVertexEnd + 2].tex.v = g_QuadTexture[2].y; + g_pVertices[g_iVertexEnd + 2].color = g_QuadColor[2]; + rotate(center, g_pVertices[g_iVertexEnd + 2].pos); + + g_pVertices[g_iVertexEnd + 3].pos.x = x; + g_pVertices[g_iVertexEnd + 3].pos.y = y+height; + g_pVertices[g_iVertexEnd + 3].tex.u = g_QuadTexture[3].x; + g_pVertices[g_iVertexEnd + 3].tex.v = g_QuadTexture[3].y; + g_pVertices[g_iVertexEnd + 3].color = g_QuadColor[3]; + rotate(center, g_pVertices[g_iVertexEnd + 3].pos); + + draw_quad(); +} + +void gfx_quads_draw_freeform( + float x0, float y0, + float x1, float y1, + float x2, float y2, + float x3, float y3) +{ + dbg_assert(quads_drawing == 1, "called quads_draw_freeform without quads_begin"); + + g_pVertices[g_iVertexEnd].pos.x = x0; + g_pVertices[g_iVertexEnd].pos.y = y0; + g_pVertices[g_iVertexEnd].tex.u = g_QuadTexture[0].x; + g_pVertices[g_iVertexEnd].tex.v = g_QuadTexture[0].y; + g_pVertices[g_iVertexEnd].color = g_QuadColor[0]; + + g_pVertices[g_iVertexEnd + 1].pos.x = x1; + g_pVertices[g_iVertexEnd + 1].pos.y = y1; + g_pVertices[g_iVertexEnd + 1].tex.u = g_QuadTexture[1].x; + g_pVertices[g_iVertexEnd + 1].tex.v = g_QuadTexture[1].y; + g_pVertices[g_iVertexEnd + 1].color = g_QuadColor[1]; + + g_pVertices[g_iVertexEnd + 2].pos.x = x3; + g_pVertices[g_iVertexEnd + 2].pos.y = y3; + g_pVertices[g_iVertexEnd + 2].tex.u = g_QuadTexture[2].x; + g_pVertices[g_iVertexEnd + 2].tex.v = g_QuadTexture[2].y; + g_pVertices[g_iVertexEnd + 2].color = g_QuadColor[2]; + + g_pVertices[g_iVertexEnd + 3].pos.x = x2; + g_pVertices[g_iVertexEnd + 3].pos.y = y2; + g_pVertices[g_iVertexEnd + 3].tex.u = g_QuadTexture[3].x; + g_pVertices[g_iVertexEnd + 3].tex.v = g_QuadTexture[3].y; + g_pVertices[g_iVertexEnd + 3].color = g_QuadColor[3]; + + draw_quad(); +} + +void gfx_quads_text(float x, float y, float size, const char *text) +{ + gfx_quads_begin(); + while(*text) + { + char c = *text; + text++; + + gfx_quads_setsubset( + (c%16)/16.0f, + (c/16)/16.0f, + (c%16)/16.0f+1.0f/16.0f, + (c/16)/16.0f+1.0f/16.0f); + + gfx_quads_drawTL(x,y,size,size); + x += size/2; + } + + gfx_quads_end(); +} + +struct pretty_font +{ + float m_CharStartTable[256]; + float m_CharEndTable[256]; + int font_texture; +}; + +pretty_font default_font = +{ +{ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0.421875, 0.359375, 0.265625, 0.25, 0.1875, 0.25, 0.4375, 0.390625, 0.390625, 0.34375, 0.28125, 0.421875, 0.390625, 0.4375, 0.203125, + 0.265625, 0.28125, 0.28125, 0.265625, 0.25, 0.28125, 0.28125, 0.265625, 0.28125, 0.265625, 0.4375, 0.421875, 0.3125, 0.28125, 0.3125, 0.3125, + 0.25, 0.234375, 0.28125, 0.265625, 0.265625, 0.296875, 0.3125, 0.25, 0.25, 0.421875, 0.28125, 0.265625, 0.328125, 0.171875, 0.234375, 0.25, + 0.28125, 0.234375, 0.265625, 0.265625, 0.28125, 0.265625, 0.234375, 0.09375, 0.234375, 0.234375, 0.265625, 0.390625, 0.203125, 0.390625, 0.296875, 0.28125, + 0.375, 0.3125, 0.3125, 0.3125, 0.296875, 0.3125, 0.359375, 0.296875, 0.3125, 0.4375, 0.390625, 0.328125, 0.4375, 0.203125, 0.3125, 0.296875, + 0.3125, 0.296875, 0.359375, 0.3125, 0.328125, 0.3125, 0.296875, 0.203125, 0.296875, 0.296875, 0.328125, 0.375, 0.421875, 0.375, 0.28125, 0.3125, + 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, + 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, + 0, 0.421875, 0.3125, 0.265625, 0.25, 0.25, 0.421875, 0.265625, 0.375, 0.21875, 0.375, 0.328125, 0.3125, 0, 0.21875, 0.28125, + 0.359375, 0.28125, 0.34375, 0.34375, 0.421875, 0.3125, 0.265625, 0.421875, 0.421875, 0.34375, 0.375, 0.328125, 0.125, 0.125, 0.125, 0.296875, + 0.234375, 0.234375, 0.234375, 0.234375, 0.234375, 0.234375, 0.109375, 0.265625, 0.296875, 0.296875, 0.296875, 0.296875, 0.375, 0.421875, 0.359375, 0.390625, + 0.21875, 0.234375, 0.25, 0.25, 0.25, 0.25, 0.25, 0.296875, 0.21875, 0.265625, 0.265625, 0.265625, 0.265625, 0.234375, 0.28125, 0.3125, + 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.1875, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.375, 0.421875, 0.359375, 0.390625, + 0.3125, 0.3125, 0.296875, 0.296875, 0.296875, 0.296875, 0.296875, 0.28125, 0.28125, 0.3125, 0.3125, 0.3125, 0.3125, 0.296875, 0.3125, 0.296875, +}, +{ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0.2, 0.5625, 0.625, 0.71875, 0.734375, 0.796875, 0.765625, 0.546875, 0.59375, 0.59375, 0.65625, 0.703125, 0.546875, 0.59375, 0.5625, 0.6875, + 0.71875, 0.609375, 0.703125, 0.703125, 0.71875, 0.703125, 0.703125, 0.6875, 0.703125, 0.703125, 0.5625, 0.546875, 0.671875, 0.703125, 0.671875, 0.671875, + 0.734375, 0.75, 0.734375, 0.734375, 0.734375, 0.6875, 0.6875, 0.734375, 0.71875, 0.5625, 0.65625, 0.765625, 0.703125, 0.8125, 0.75, 0.734375, + 0.734375, 0.765625, 0.71875, 0.71875, 0.703125, 0.71875, 0.75, 0.890625, 0.75, 0.75, 0.71875, 0.59375, 0.6875, 0.59375, 0.6875, 0.703125, + 0.5625, 0.671875, 0.6875, 0.671875, 0.671875, 0.671875, 0.625, 0.671875, 0.671875, 0.5625, 0.546875, 0.703125, 0.5625, 0.78125, 0.671875, 0.671875, + 0.6875, 0.671875, 0.65625, 0.671875, 0.65625, 0.671875, 0.6875, 0.78125, 0.6875, 0.671875, 0.65625, 0.609375, 0.546875, 0.609375, 0.703125, 0.671875, + 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, + 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, + 0, 0.5625, 0.671875, 0.734375, 0.734375, 0.734375, 0.546875, 0.71875, 0.609375, 0.765625, 0.609375, 0.65625, 0.671875, 0, 0.765625, 0.703125, + 0.625, 0.703125, 0.640625, 0.640625, 0.609375, 0.671875, 0.703125, 0.546875, 0.5625, 0.578125, 0.609375, 0.65625, 0.859375, 0.859375, 0.859375, 0.671875, + 0.75, 0.75, 0.75, 0.75, 0.75, 0.75, 0.84375, 0.734375, 0.6875, 0.6875, 0.6875, 0.6875, 0.5625, 0.609375, 0.640625, 0.59375, + 0.734375, 0.75, 0.734375, 0.734375, 0.734375, 0.734375, 0.734375, 0.6875, 0.765625, 0.71875, 0.71875, 0.71875, 0.71875, 0.75, 0.734375, 0.6875, + 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.796875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.5625, 0.609375, 0.625, 0.59375, + 0.6875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.703125, 0.703125, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.6875, 0.671875, +}, +0 +}; + +pretty_font *current_font = &default_font; + +void gfx_pretty_text(float x, float y, float size, const char *text) +{ + const float spacing = 0.05f; + + gfx_quads_begin(); + + while (*text) + { + const int c = *text; + const float width = current_font->m_CharEndTable[c] - current_font->m_CharStartTable[c]; + + text++; + + gfx_quads_setsubset( + (c%16)/16.0f + current_font->m_CharStartTable[c]/16.0f, // startx + (c/16)/16.0f, // starty + (c%16)/16.0f + current_font->m_CharEndTable[c]/16.0f + 0.001, // endx + (c/16)/16.0f+1.0f/16.0f); // endy + + gfx_quads_drawTL(x, y, width * size, size); + + x += (width + spacing) * size; + } + + gfx_quads_end(); +} + +float gfx_pretty_text_width(float size, const char *text) +{ + const float spacing = 0.05f; + float width = 0.0f; + + while (*text) + { + const int c = *text++; + width += size * (current_font->m_CharEndTable[c] - current_font->m_CharStartTable[c] + spacing); + } + + return width; +} diff --git a/src/engine/client/snd.cpp b/src/engine/client/snd.cpp new file mode 100644 index 00000000..dd6baa9a --- /dev/null +++ b/src/engine/client/snd.cpp @@ -0,0 +1,520 @@ +#include +#include +#include + +#include + +using namespace baselib; + +static const int NUM_FRAMES_STOP = 512; +static const float NUM_FRAMES_STOP_INV = 1.0f/(float)NUM_FRAMES_STOP; +static const int NUM_FRAMES_LERP = 512; +static const float NUM_FRAMES_LERP_INV = 1.0f/(float)NUM_FRAMES_LERP; + +static const float GLOBAL_VOLUME_SCALE = 0.75f; + +static const int64 GLOBAL_SOUND_DELAY = 1000; + +// --- sound --- +class sound_data +{ +public: + short *data; + int num_samples; + int rate; + int channels; + int sustain_start; + int sustain_end; + int64 last_played; +}; + +inline short clamp(int i) +{ + if(i > 0x7fff) + return 0x7fff; + if(i < -0x7fff) + return -0x7fff; + return i; +} + +class mixer : public audio_stream +{ +public: + class channel + { + public: + channel() + { data = 0; lerp = -1; stop = -1; } + + sound_data *data; + int tick; + int loop; + float pan; + float vol; + float old_vol; + float new_vol; + int lerp; + int stop; + }; + + enum + { + MAX_CHANNELS=8, + }; + + channel channels[MAX_CHANNELS]; + + virtual void fill(void *output, unsigned long frames) + { + //dbg_msg("snd", "mixing!"); + + short *out = (short*)output; + bool clamp_flag = false; + + int active_channels = 0; + for(unsigned long i = 0; i < frames; i++) + { + int left = 0; + int right = 0; + + for(int c = 0; c < MAX_CHANNELS; c++) + { + if(channels[c].data) + { + if(channels[c].data->channels == 1) + { + left += (1.0f-(channels[c].pan+1.0f)*0.5f) * channels[c].vol * channels[c].data->data[channels[c].tick]; + right += (channels[c].pan+1.0f)*0.5f * channels[c].vol * channels[c].data->data[channels[c].tick]; + channels[c].tick++; + } + else + { + float pl = channels[c].pan<0.0f?-channels[c].pan:1.0f; + float pr = channels[c].pan>0.0f?1.0f-channels[c].pan:1.0f; + left += pl*channels[c].vol * channels[c].data->data[channels[c].tick]; + right += pr*channels[c].vol * channels[c].data->data[channels[c].tick + 1]; + channels[c].tick += 2; + } + + if(channels[c].loop) + { + if(channels[c].data->sustain_start >= 0 && channels[c].tick >= channels[c].data->sustain_end) + channels[c].tick = channels[c].data->sustain_start; + else if(channels[c].tick > channels[c].data->num_samples) + channels[c].tick = 0; + } + else if(channels[c].tick > channels[c].data->num_samples) + channels[c].data = 0; + + if(channels[c].stop == 0) + { + channels[c].stop = -1; + channels[c].data = 0; + } + else if(channels[c].stop > 0) + { + channels[c].vol = channels[c].old_vol * (float)channels[c].stop * NUM_FRAMES_STOP_INV; + channels[c].stop--; + } + if(channels[c].lerp > 0) + { + channels[c].vol = (1.0f - (float)channels[c].lerp * NUM_FRAMES_LERP_INV) * channels[c].new_vol + + (float)channels[c].lerp * NUM_FRAMES_LERP_INV * channels[c].old_vol; + channels[c].lerp--; + } + active_channels++; + } + } + + // TODO: remove these + + *out = clamp(left); // left + if(*out != left) clamp_flag = true; + out++; + *out = clamp(right); // right + if(*out != right) clamp_flag = true; + out++; + } + + if(clamp_flag) + dbg_msg("snd", "CLAMPED!"); + } + + int play(sound_data *sound, unsigned loop, float vol, float pan) + { + if(time_get() - sound->last_played < GLOBAL_SOUND_DELAY) + return -1; + + for(int c = 0; c < MAX_CHANNELS; c++) + { + if(channels[c].data == 0) + { + channels[c].data = sound; + channels[c].tick = 0; + channels[c].loop = loop; + channels[c].vol = vol * GLOBAL_VOLUME_SCALE; + channels[c].pan = pan; + sound->last_played = time_get(); + return c; + } + } + + return -1; + } + + void stop(int id) + { + dbg_assert(id >= 0 && id < MAX_CHANNELS, "id out of bounds"); + channels[id].old_vol = channels[id].vol; + channels[id].stop = NUM_FRAMES_STOP; + } + + void set_vol(int id, float vol) + { + dbg_assert(id >= 0 && id < MAX_CHANNELS, "id out of bounds"); + channels[id].new_vol = vol * GLOBAL_VOLUME_SCALE; + channels[id].old_vol = channels[id].vol; + channels[id].lerp = NUM_FRAMES_LERP; + } +}; + +static mixer mixer; +//static sound_data test_sound; + +/* +extern "C" +{ +#include "wavpack/wavpack.h" +}*/ + +/* +static file_stream *read_func_filestream; +static int32_t read_func(void *buff, int32_t bcount) +{ + return read_func_filestream->read(buff, bcount); +} +static uchar *format_samples(int bps, uchar *dst, int32_t *src, uint32_t samcnt) +{ + int32_t temp; + + switch (bps) { + + case 1: + while (samcnt--) + *dst++ = *src++ + 128; + + break; + + case 2: + while (samcnt--) { + *dst++ = (uchar)(temp = *src++); + *dst++ = (uchar)(temp >> 8); + } + + break; + + case 3: + while (samcnt--) { + *dst++ = (uchar)(temp = *src++); + *dst++ = (uchar)(temp >> 8); + *dst++ = (uchar)(temp >> 16); + } + + break; + + case 4: + while (samcnt--) { + *dst++ = (uchar)(temp = *src++); + *dst++ = (uchar)(temp >> 8); + *dst++ = (uchar)(temp >> 16); + *dst++ = (uchar)(temp >> 24); + } + + break; + } + + return dst; +}*/ + +/* +struct sound_holder +{ + sound_data sound; + int next; +}; + +static const int MAX_SOUNDS = 256; +static sound_holder sounds[MAX_SOUNDS]; +static int first_free_sound; + +bool snd_load_wv(const char *filename, sound_data *snd) +{ + // open file + file_stream file; + if(!file.open_r(filename)) + { + dbg_msg("sound/wv", "failed to open file. filename='%s'", filename); + return false; + } + read_func_filestream = &file; + + // get info + WavpackContext *wpc; + char error[128]; + wpc = WavpackOpenFileInput(read_func, error); + if(!wpc) + { + dbg_msg("sound/wv", "failed to open file. err=%s filename='%s'", error, filename); + return false; + } + + + snd->num_samples = WavpackGetNumSamples(wpc); + int bps = WavpackGetBytesPerSample(wpc); + int channels = WavpackGetReducedChannels(wpc); + snd->rate = WavpackGetSampleRate(wpc); + int bits = WavpackGetBitsPerSample(wpc); + + (void)bps; + (void)channels; + (void)bits; + + // decompress + int datasize = snd->num_samples*2; + snd->data = (short*)mem_alloc(datasize, 1); + int totalsamples = 0; + while(1) + { + int buffer[1024*4]; + int samples_unpacked = WavpackUnpackSamples(wpc, buffer, 1024*4); + totalsamples += samples_unpacked; + + if(samples_unpacked) + { + // convert + } + } + + if(snd->num_samples != totalsamples) + { + dbg_msg("sound/wv", "wrong amount of samples. filename='%s'", filename); + mem_free(snd->data); + return false;; + } + + return false; +}*/ + +struct sound_holder +{ + sound_data sound; + int next; +}; + +static const int MAX_SOUNDS = 1024; +static sound_holder sounds[MAX_SOUNDS]; +static int first_free_sound; + +bool snd_init() +{ + first_free_sound = 0; + for(int i = 0; i < MAX_SOUNDS; i++) + sounds[i].next = i+1; + sounds[MAX_SOUNDS-1].next = -1; + return mixer.create(); +} + +bool snd_shutdown() +{ + mixer.destroy(); + return true; +} + +static int snd_alloc_sound() +{ + if(first_free_sound < 0) + return -1; + int id = first_free_sound; + first_free_sound = sounds[id].next; + sounds[id].next = -1; + return id; +} + +int snd_load_wav(const char *filename) +{ + sound_data snd; + + // open file for reading + file_stream file; + if(!file.open_r(filename)) + { + dbg_msg("sound/wav", "failed to open file. filename='%s'", filename); + return -1; + } + + int id = -1; + int state = 0; + while(1) + { + // read chunk header + unsigned char head[8]; + if(file.read(head, sizeof(head)) != 8) + { + break; + } + + int chunk_size = head[4] | (head[5]<<8) | (head[6]<<16) | (head[7]<<24); + head[4] = 0; + + if(state == 0) + { + // read the riff and wave headers + if(head[0] != 'R' || head[1] != 'I' || head[2] != 'F' || head[3] != 'F') + { + dbg_msg("sound/wav", "not a RIFF file. filename='%s'", filename); + return -1; + } + + unsigned char type[4]; + file.read(type, 4); + + if(type[0] != 'W' || type[1] != 'A' || type[2] != 'V' || type[3] != 'E') + { + dbg_msg("sound/wav", "RIFF file is not a WAVE. filename='%s'", filename); + return -1; + } + + state++; + } + else if(state == 1) + { + // read the format chunk + if(head[0] == 'f' && head[1] == 'm' && head[2] == 't' && head[3] == ' ') + { + unsigned char fmt[16]; + if(file.read(fmt, sizeof(fmt)) != sizeof(fmt)) + { + dbg_msg("sound/wav", "failed to read format. filename='%s'", filename); + return -1; + } + + // decode format + int compression_code = fmt[0] | (fmt[1]<<8); + snd.channels = fmt[2] | (fmt[3]<<8); + snd.rate = fmt[4] | (fmt[5]<<8) | (fmt[6]<<16) | (fmt[7]<<24); + + if(compression_code != 1) + { + dbg_msg("sound/wav", "file is not uncompressed. filename='%s'", filename); + return -1; + } + + if(snd.channels > 2) + { + dbg_msg("sound/wav", "file is not mono or stereo. filename='%s'", filename); + return -1; + } + + if(snd.rate != 44100) + { + dbg_msg("sound/wav", "file is %d Hz, not 44100 Hz. filename='%s'", snd.rate, filename); + return -1; + } + + int bps = fmt[14] | (fmt[15]<<8); + if(bps != 16) + { + dbg_msg("sound/wav", "bps is %d, not 16, filname='%s'", bps, filename); + return -1; + } + + // skip extra bytes (not used for uncompressed) + //int extra_bytes = fmt[14] | (fmt[15]<<8); + //dbg_msg("sound/wav", "%d", extra_bytes); + //file.skip(extra_bytes); + + // next state + state++; + } + else + file.skip(chunk_size); + } + else if(state == 2) + { + // read the data + if(head[0] == 'd' && head[1] == 'a' && head[2] == 't' && head[3] == 'a') + { + snd.data = (short*)mem_alloc(chunk_size, 1); + file.read(snd.data, chunk_size); + snd.num_samples = chunk_size/(2); + snd.sustain_start = -1; + snd.sustain_end = -1; + snd.last_played = 0; + id = snd_alloc_sound(); + sounds[id].sound = snd; + state++; + } + else + file.skip(chunk_size); + } + else if(state == 3) + { + if(head[0] == 's' && head[1] == 'm' && head[2] == 'p' && head[3] == 'l') + { + int smpl[9]; + int loop[6]; + + file.read(smpl, sizeof(smpl)); + + if(smpl[7] > 0) + { + file.read(loop, sizeof(loop)); + sounds[id].sound.sustain_start = loop[2] * sounds[id].sound.channels; + sounds[id].sound.sustain_end = loop[3] * sounds[id].sound.channels; + } + + if(smpl[7] > 1) + file.skip((smpl[7]-1) * sizeof(loop)); + + file.skip(smpl[8]); + state++; + } + else + file.skip(chunk_size); + } + else + file.skip(chunk_size); + } + + if(id >= 0) + dbg_msg("sound/wav", "loaded %s", filename); + else + dbg_msg("sound/wav", "failed to load %s", filename); + + return id; +} + +int snd_play(int id, int loop, float vol, float pan) +{ + if(id < 0) + { + dbg_msg("snd", "bad sound id"); + return -1; + } + + dbg_assert(sounds[id].sound.data != 0, "null sound"); + dbg_assert(sounds[id].next == -1, "sound isn't allocated"); + return mixer.play(&sounds[id].sound, loop, vol, pan); +} + +void snd_stop(int id) +{ + if(id >= 0) + mixer.stop(id); +} + +void snd_set_vol(int id, float vol) +{ + if(id >= 0) + mixer.set_vol(id, vol); +} diff --git a/src/engine/client/ui.cpp b/src/engine/client/ui.cpp new file mode 100644 index 00000000..3353feca --- /dev/null +++ b/src/engine/client/ui.cpp @@ -0,0 +1,115 @@ +#include +#include "ui.h" + +/******************************************************** + UI +*********************************************************/ +//static unsigned mouse_buttons_last = 0; + +struct pretty_font +{ + char m_CharStartTable[256]; + char m_CharEndTable[256]; + int font_texture; +}; + +extern pretty_font *current_font; +void gfx_pretty_text(float x, float y, float size, const char *text); + + +static void *hot_item = 0; +static void *active_item = 0; +static void *becomming_hot_item = 0; +static float mouse_x, mouse_y; // in gui space +static float mouse_wx, mouse_wy; // in world space +static unsigned mouse_buttons = 0; + +float ui_mouse_x() { return mouse_x; } +float ui_mouse_y() { return mouse_y; } +float ui_mouse_world_x() { return mouse_wx; } +float ui_mouse_world_y() { return mouse_wy; } +int ui_mouse_button(int index) { return (mouse_buttons>>index)&1; } + +void ui_set_hot_item(void *id) { becomming_hot_item = id; } +void ui_set_active_item(void *id) { active_item = id; } +void *ui_hot_item() { return hot_item; } +void *ui_active_item() { return active_item; } + +int ui_update(float mx, float my, float mwx, float mwy, int buttons) +{ + //mouse_buttons_last = mouse_buttons; + mouse_x = mx; + mouse_y = my; + mouse_wx = mwx; + mouse_wy = mwy; + mouse_buttons = buttons; + hot_item = becomming_hot_item; + becomming_hot_item = 0; + return 0; +} + +/* +static int ui_mouse_button_released(int index) +{ + return ((mouse_buttons_last>>index)&1) && !(); +}*/ + +int ui_mouse_inside(float x, float y, float w, float h) +{ + if(mouse_x >= x && mouse_x <= x+w && mouse_y >= y && mouse_y <= y+h) + return 1; + return 0; +} + +void ui_do_image(int texture, float x, float y, float w, float h) +{ + gfx_blend_normal(); + gfx_texture_set(texture); + gfx_quads_begin(); + gfx_quads_setcolor(1,1,1,1); + gfx_quads_setsubset( + 0.0f, // startx + 0.0f, // starty + 1.0f, // endx + 1.0f); // endy + gfx_quads_drawTL(x,y,w,h); + gfx_quads_end(); +} + +void ui_do_label(float x, float y, char *text) +{ + gfx_blend_normal(); + gfx_texture_set(current_font->font_texture); + gfx_pretty_text(x, y, 18.f, text); +} + +int ui_do_button(void *id, const char *text, int checked, float x, float y, float w, float h, draw_button_callback draw_func, void *extra) +{ + // logic + int r = 0; + int inside = ui_mouse_inside(x,y,w,h); + + if(inside) + { + ui_set_hot_item(id); + + if(ui_mouse_button(0)) + ui_set_active_item(id); + } + + if(ui_active_item() == id && ui_hot_item() == id && !ui_mouse_button(0)) + { + ui_set_active_item(0); + r = 1; + } + + draw_func(id, text, checked, x, y, w, h, extra); + + return r; +} + +int ui_do_button(void *id, const char *text, int checked, float x, float y, float w, float h, draw_button_callback draw_func) +{ + return ui_do_button(id, text, checked, x, y, w, h, draw_func, 0x0); +} + diff --git a/src/engine/client/ui.h b/src/engine/client/ui.h new file mode 100644 index 00000000..1a420906 --- /dev/null +++ b/src/engine/client/ui.h @@ -0,0 +1,33 @@ +#ifndef _UI_H +#define _UI_H +/* +extern void *hot_item; +extern void *active_item; +extern void *becomming_hot_item; +extern float mouse_x, mouse_y; // in gui space +extern float mouse_wx, mouse_wy; // in world space +extern unsigned mouse_buttons;*/ + +int ui_update(float mx, float my, float mwx, float mwy, int buttons); + +float ui_mouse_x(); +float ui_mouse_y(); +float ui_mouse_world_x(); +float ui_mouse_world_y(); +int ui_mouse_button(int index); + +void ui_set_hot_item(void *id); +void ui_set_active_item(void *id); +void *ui_hot_item(); +void *ui_active_item(); + +int ui_mouse_inside(float x, float y, float w, float h); + +typedef void (*draw_button_callback)(void *id, const char *text, int checked, float x, float y, float w, float h, void *extra); + +void ui_do_image(int texture, float x, float y, float w, float h); +void ui_do_label(float x, float y, char *text); +int ui_do_button(void *id, const char *text, int checked, float x, float y, float w, float h, draw_button_callback draw_func, void *extra); +int ui_do_button(void *id, const char *text, int checked, float x, float y, float w, float h, draw_button_callback draw_func); + +#endif diff --git a/src/engine/datafile.cpp b/src/engine/datafile.cpp new file mode 100644 index 00000000..789aa722 --- /dev/null +++ b/src/engine/datafile.cpp @@ -0,0 +1,444 @@ +#include +#include + +#include "datafile.h" + +static const int DEBUG=0; + +struct item_type +{ + int type; + int start; + int num; +}; + +struct item +{ + int type_and_id; + int size; +}; + +struct datafile_header +{ + int id; + int version; + int size; + int swaplen; + int num_item_types; + int num_items; + int num_raw_data; + int item_size; + int data_size; +}; + +struct datafile_data +{ + int num_item_types; + int num_items; + int num_raw_data; + int item_size; + int data_size; + char start[4]; +}; + +struct datafile_info +{ + item_type *item_types; + int *item_offsets; + int *data_offsets; + + char *item_start; + char *data_start; +}; + +struct datafile +{ + datafile_info info; + datafile_data data; +}; + +datafile *datafile_load(const char *filename) +{ + dbg_msg("datafile", "datafile loading. filename='%s'", filename); + + baselib::file_stream file; + if(!file.open_r(filename)) + return 0; + + // TODO: change this header + int header[4]; + file.read(header, sizeof(header)); + if(((header[0]>>24)&0xff) != 'D' || ((header[0]>>16)&0xff) != 'A' || (header[0]>>8)&0xff != 'T' || (header[0]&0xff)!= 'A') + { + dbg_msg("datafile", "wrong signature. %x %x %x %x", header[0], header[1], header[2], header[3]); + return 0; + } + + int version = header[1]; + if(version != 3) + { + dbg_msg("datafile", "wrong version. version=%x", version); + return 0; + } + + unsigned size = header[2]; + unsigned swapsize = header[3]; + + if(DEBUG) + dbg_msg("datafile", "loading. size=%d", size); + + // TODO: use this variable for good and awesome + (void)swapsize; + + //map_unload(); + datafile *df = (datafile*)mem_alloc(size+sizeof(datafile_info), 1); + unsigned readsize = file.read(&df->data, size); + if(readsize != size) + { + dbg_msg("datafile", "couldn't load the whole thing, wanted=%d got=%d", size, readsize); + return 0; + } + + // TODO: byteswap + //map->byteswap(); + + if(DEBUG) + dbg_msg("datafile", "item_size=%d", df->data.item_size); + + + df->info.item_types = (item_type *)df->data.start; + df->info.item_offsets = (int *)&df->info.item_types[df->data.num_item_types]; + df->info.data_offsets = (int *)&df->info.item_offsets[df->data.num_items]; + + df->info.item_start = (char *)&df->info.data_offsets[df->data.num_raw_data]; + df->info.data_start = df->info.item_start + df->data.item_size; + + if(DEBUG) + dbg_msg("datafile", "datafile loading done. datafile='%s'", filename); + + if(DEBUG) + { + for(int i = 0; i < df->data.num_raw_data; i++) + { + void *p = datafile_get_data(df, i); + dbg_msg("datafile", "%d %d", (int)((char*)p - (char*)(&df->data)), size); + } + + for(int i = 0; i < datafile_num_items(df); i++) + { + int type, id; + void *data = datafile_get_item(df, i, &type, &id); + dbg_msg("map", "\t%d: type=%x id=%x p=%p offset=%d", i, type, id, data, df->info.item_offsets[i]); + int *idata = (int*)data; + for(int k = 0; k < 3; k++) + dbg_msg("datafile", "\t\t%d=%d (%x)", k, idata[k], idata[k]); + } + + for(int i = 0; i < df->data.num_item_types; i++) + { + dbg_msg("map", "\t%d: type=%x start=%d num=%d", i, + df->info.item_types[i].type, + df->info.item_types[i].start, + df->info.item_types[i].num); + for(int k = 0; k < df->info.item_types[i].num; k++) + { + int type, id; + datafile_get_item(df, df->info.item_types[i].start+k, &type, &id); + if(type != df->info.item_types[i].type) + dbg_msg("map", "\tERROR"); + } + } + } + + return df; +} + +void *datafile_get_data(datafile *df, int index) +{ + return df->info.data_start+df->info.data_offsets[index]; +} + +void *datafile_get_item(datafile *df, int index, int *type, int *id) +{ + item *i = (item *)(df->info.item_start+df->info.item_offsets[index]); + if(type) + *type = (i->type_and_id>>16)&0xffff; // remove sign extention + if(id) + *id = i->type_and_id&0xffff; + return (void *)(i+1); +} + +void datafile_get_type(datafile *df, int type, int *start, int *num) +{ + for(int i = 0; i < df->data.num_item_types; i++) + { + if(df->info.item_types[i].type == type) + { + *start = df->info.item_types[i].start; + *num = df->info.item_types[i].num; + return; + } + } + + *start = 0; + *num = 0; +} + +void *datafile_find_item(datafile *df, int type, int id) +{ + int start, num; + datafile_get_type(df, type, &start, &num); + for(int i = 0; i < num; i++) + { + int item_id; + void *item = datafile_get_item(df, start+i,0, &item_id); + if(id == item_id) + return item; + } + return 0; +} + +int datafile_num_items(datafile *df) +{ + return df->data.num_items; +} + +void datafile_unload(datafile *df) +{ + if(df) + mem_free(df); +} + +// DATAFILE output +struct data_info +{ + int size; + void *data; +}; + +struct item_info +{ + int type; + int id; + int size; + int next; + int prev; + void *data; +}; + +struct itemtype_info +{ + int num; + int first; + int last; +}; + +// +struct datafile_out +{ + baselib::file_stream file; + int num_items; + int num_datas; + int num_item_types; + itemtype_info item_types[0xffff]; + item_info items[1024]; + data_info datas[1024]; +}; + +datafile_out *datafile_create(const char *filename) +{ + datafile_out *df = new datafile_out; + if(!df->file.open_w(filename)) + { + delete df; + return 0; + } + + df->num_items = 0; + df->num_datas = 0; + df->num_item_types = 0; + mem_zero(&df->item_types, sizeof(df->item_types)); + + for(int i = 0; i < 0xffff; i++) + { + df->item_types[i].first = -1; + df->item_types[i].last = -1; + } + + return df; +} + +int datafile_add_item(datafile_out *df, int type, int id, int size, void *data) +{ + df->items[df->num_items].type = type; + df->items[df->num_items].id = id; + df->items[df->num_items].size = size; + + // copy data + df->items[df->num_items].data = mem_alloc(size, 1); + mem_copy(df->items[df->num_items].data, data, size); + + if(!df->item_types[type].num) // count item types + df->num_item_types++; + + // link + df->items[df->num_items].prev = df->item_types[type].last; + df->items[df->num_items].next = -1; + + if(df->item_types[type].last != -1) + df->items[df->item_types[type].last].next = df->num_items; + df->item_types[type].last = df->num_items; + + if(df->item_types[type].first == -1) + df->item_types[type].first = df->num_items; + + df->item_types[type].num++; + + df->num_items++; + return df->num_items-1; +} + +int datafile_add_data(datafile_out *df, int size, void *data) +{ + df->datas[df->num_items].size = size; + df->datas[df->num_datas].data = data; + df->num_datas++; + return df->num_datas-1; +} + +int datafile_finish(datafile_out *df) +{ + // we should now write this file! + if(DEBUG) + dbg_msg("datafile", "writing"); + + // calculate sizes + int itemsize = 0; + for(int i = 0; i < df->num_items; i++) + { + if(DEBUG) + dbg_msg("datafile", "item=%d size=%d (%d)", i, df->items[i].size, df->items[i].size+sizeof(item)); + itemsize += df->items[i].size + sizeof(item); + } + + int datasize = 0; + for(int i = 0; i < df->num_datas; i++) + datasize += df->datas[i].size; + + // calculate the complete size + int typessize = df->num_item_types*sizeof(item_type); + int headersize = sizeof(datafile_header); + int offsetsize = df->num_items*sizeof(int) + df->num_datas*sizeof(int); + int filesize = headersize + typessize + offsetsize + itemsize + datasize; + int swapsize = filesize - datasize; + + if(DEBUG) + dbg_msg("datafile", "num_item_types=%d typessize=%d itemsize=%d datasize=%d", df->num_item_types, typessize, itemsize, datasize); + + // construct header + datafile_header header; + header.id = ('D'<<24) | ('A'<<16) | ('T'<<8) | ('A'); + header.version = 3; + header.size = filesize - 16; + header.swaplen = swapsize - 16; + header.num_item_types = df->num_item_types; + header.num_items = df->num_items; + header.num_raw_data = df->num_datas; + header.item_size = itemsize; + header.data_size = datasize; + + // TODO: apply swapping + // write header + if(DEBUG) + dbg_msg("datafile", "headersize=%d", sizeof(header)); + df->file.write(&header, sizeof(header)); + + // write types + for(int i = 0, count = 0; i < 0xffff; i++) + { + if(df->item_types[i].num) + { + // write info + item_type info; + info.type = i; + info.start = count; + info.num = df->item_types[i].num; + if(DEBUG) + dbg_msg("datafile", "writing type=%x start=%d num=%d", info.type, info.start, info.num); + df->file.write(&info, sizeof(info)); + + count += df->item_types[i].num; + } + } + + // write item offsets + for(int i = 0, offset = 0; i < 0xffff; i++) + { + if(df->item_types[i].num) + { + // write all items in of this type + int k = df->item_types[i].first; + while(k != -1) + { + if(DEBUG) + dbg_msg("datafile", "writing item offset num=%d offset=%d", k, offset); + df->file.write(&offset, sizeof(offset)); + offset += df->items[k].size + sizeof(item); + + // next + k = df->items[k].next; + } + } + } + + // write data offsets + for(int i = 0, offset = 0; i < df->num_datas; i++) + { + if(DEBUG) + dbg_msg("datafile", "writing data offset num=%d offset=%d", i, offset); + df->file.write(&offset, sizeof(offset)); + offset += df->datas[i].size; + } + + // write items + for(int i = 0; i < 0xffff; i++) + { + if(df->item_types[i].num) + { + // write all items in of this type + int k = df->item_types[i].first; + while(k != -1) + { + item itm; + itm.type_and_id = (i<<16)|df->items[k].id; + itm.size = df->items[k].size; + if(DEBUG) + dbg_msg("datafile", "writing item type=%x idx=%d id=%d size=%d", i, k, df->items[k].id, df->items[k].size); + df->file.write(&itm, sizeof(itm)); + df->file.write(df->items[k].data, df->items[k].size); + + // next + k = df->items[k].next; + } + } + } + + // write data + for(int i = 0; i < df->num_datas; i++) + { + if(DEBUG) + dbg_msg("datafile", "writing data id=%d size=%d", i, df->datas[i].size); + df->file.write(df->datas[i].data, df->datas[i].size); + } + + // free data + for(int i = 0; i < df->num_items; i++) + mem_free(df->items[i].data); + + + delete df; + + if(DEBUG) + dbg_msg("datafile", "done"); + return 0; +} diff --git a/src/engine/datafile.h b/src/engine/datafile.h new file mode 100644 index 00000000..4e49fa03 --- /dev/null +++ b/src/engine/datafile.h @@ -0,0 +1,20 @@ + +// raw datafile access +struct datafile; + +// read access +datafile *datafile_load(const char *filename); +datafile *datafile_load_old(const char *filename); +void *datafile_get_data(datafile *df, int index); +void *datafile_get_item(datafile *df, int index, int *type, int *id); +void datafile_get_type(datafile *df, int type, int *start, int *num); +void *datafile_find_item(datafile *df, int type, int id); +int datafile_num_items(datafile *df); +void datafile_unload(datafile *df); + +// write access +struct datafile_out; +datafile_out *datafile_create(const char *filename); +int datafile_add_data(datafile_out *df, int size, void *data); +int datafile_add_item(datafile_out *df, int type, int id, int size, void *data); +int datafile_finish(datafile_out *df); diff --git a/src/engine/interface.h b/src/engine/interface.h new file mode 100644 index 00000000..95ea0252 --- /dev/null +++ b/src/engine/interface.h @@ -0,0 +1,711 @@ +#ifndef FILE_INTERFACE_H +#define FILE_INTERFACE_H + +/* + Title: Engine Interface +*/ + +// TODO: Move the definitions of these keys here +#include + +enum +{ + MAX_CLIENTS=8, + SERVER_TICK_SPEED=50, + SERVER_CLIENT_TIMEOUT=5, + SNAP_CURRENT=0, + SNAP_PREV=1, +}; + +struct snap_item +{ + int type; + int id; +}; + +struct client_info +{ +public: + const char *name; + int latency; +}; + +struct image_info +{ + int width, height; + void *data; +}; + +int gfx_load_tga(image_info *img, const char *filename); + + +/* + Group: Graphics +*/ + +// graphics +bool gfx_init(bool fullscreen); // NOT EXPOSED +void gfx_shutdown(); // NOT EXPOSED +void gfx_swap(); // NOT EXPOSED + +// textures +/* + Function: gfx_load_texture_tga + Loads a TGA from file. + + Arguments: + filename - Null terminated string to the file to load. + + Returns: + An ID to the texture. -1 on failure. + + See Also: + +*/ +int gfx_load_texture_tga(const char *filename); + +/* + Function: gfx_load_texture_raw + Loads a texture from memory. + + Arguments: + w - Width of the texture. + h - Height of the texture. + data - Pointer to the pixel data. + + Returns: + An ID to the texture. -1 on failure. + + Remarks: + The pixel data should be in RGBA format with 8 bit per component. + So the total size of the data should be w*h*4. + + See Also: + +*/ +int gfx_load_texture_raw(int w, int h, const void *data); + +/* + Function: gfx_texture_set + Sets the active texture. + + Arguments: + id - ID to the texture to set. +*/ +void gfx_texture_set(int id); + +/* + Function: gfx_unload_texture + Unloads a texture. + + Arguments: + id - ID to the texture to unload. + + See Also: + , + + Remarks: + NOT IMPLEMENTED +*/ +int gfx_unload_texture(int id); // NOT IMPLEMENTED + +void gfx_clear(float r, float g, float b); + +/* + Function: gfx_screenwidth + Returns the screen width. + + See Also: + +*/ +int gfx_screenwidth(); + +/* + Function: gfx_screenheight + Returns the screen height. + + See Also: + +*/ +int gfx_screenheight(); + +/* + Function: gfx_mapscreen + Specifies the coordinate system for the screen. + + Arguments: + tl_x - Top-left X + tl_y - Top-left Y + br_x - Bottom-right X + br_y - Bottom-right y +*/ +void gfx_mapscreen(float tl_x, float tl_y, float br_x, float br_y); + +/* + Function: gfx_blend_normal + Set the active blending mode to normal (src, 1-src). + + Remarks: + This must be used before calling . + This is equal to glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA). + + See Also: + +*/ +void gfx_blend_normal(); + +/* + Function: gfx_blend_additive + Set the active blending mode to additive (src, one). + + Remarks: + This must be used before calling . + This is equal to glBlendFunc(GL_SRC_ALPHA, GL_ONE). + + See Also: + +*/ +void gfx_blend_additive(); + +/* + Function: gfx_quads_begin + Begins a quad drawing session. + + Remarks: + This functions resets the rotation, color and subset. + End the session by using . + You can't change texture or blending mode during a session. + + See Also: + +*/ +void gfx_quads_begin(); + +/* + Function: gfx_quads_end + Ends a quad session. + + See Also: + +*/ +void gfx_quads_end(); + +/* + Function: gfx_quads_setrotation + Sets the rotation to use when drawing a quad. + + Arguments: + angle - Angle in radians. + + Remarks: + The angle is reset when is called. +*/ +void gfx_quads_setrotation(float angle); + +/* + Function: gfx_quads_setcolorvertex + Sets the color of a vertex. + + Arguments: + i - Index to the vertex. + r - Red value. + g - Green value. + b - Blue value. + a - Alpha value. + + Remarks: + The color values are from 0.0 to 1.0. + The color is reset when is called. +*/ +void gfx_quads_setcolorvertex(int i, float r, float g, float b, float a); + +/* + Function: gfx_quads_setcolor + Sets the color of all the vertices. + + Arguments: + r - Red value. + g - Green value. + b - Blue value. + a - Alpha value. + + Remarks: + The color values are from 0.0 to 1.0. + The color is reset when is called. +*/ +void gfx_quads_setcolor(float r, float g, float b, float a); + +/* + Function: gfx_quads_setsubset + Sets the uv coordinates to use. + + Arguments: + tl_u - Top-left U value. + tl_v - Top-left V value. + br_u - Bottom-right U value. + br_v - Bottom-right V value. + + Remarks: + O,0 is top-left of the texture and 1,1 is bottom-right. + The color is reset when is called. +*/ +void gfx_quads_setsubset(float tl_u, float tl_v, float br_u, float br_v); + +/* + Function: gfx_quads_drawTL + Draws a quad by specifying the top-left point. + + Arguments: + x - X coordinate of the top-left corner. + y - Y coordinate of the top-left corner. + width - Width of the quad. + height - Height of the quad. + + Remarks: + Rotation still occurs from the center of the quad. + You must call before calling this function. + + See Also: + +*/ +void gfx_quads_drawTL(float x, float y, float width, float height); + +/* + Function: gfx_quads_draw + Draws a quad by specifying the center point. + + Arguments: + x - X coordinate of the center. + y - Y coordinate of the center. + width - Width of the quad. + height - Height of the quad. + + Remarks: + You must call before calling this function. + + See Also: + +*/ +void gfx_quads_draw(float x, float y, float w, float h); + +void gfx_quads_draw_freeform( + float x0, float y0, + float x1, float y1, + float x2, float y2, + float x3, float y3); + +void gfx_quads_text(float x, float y, float size, const char *text); + +// sound (client) +enum +{ + SND_PLAY_ONCE = 0, + SND_LOOP +}; + +bool snd_init(); +int snd_load_wav(const char *filename); +int snd_play(int sound, int loop = SND_PLAY_ONCE, float vol = 1.0f, float pan = 0.0f); +void snd_stop(int id); +void snd_set_vol(int id, float vol); +bool snd_shutdown(); + +/* + Group: Input +*/ + +/* + Function: inp_mouse_relative + Fetches the mouse movements. + + Arguments: + x - Pointer to the variable that should get the X movement. + y - Pointer to the variable that should get the Y movement. +*/ +void inp_mouse_relative(int *x, int *y); + +/* + Function: inp_mouse_button_pressed + Checks if a mouse button is pressed. + + Arguments: + button - Index to the button to check. + * 0 - Left mouse button. + * 1 - Right mouse button. + * 2 - Middle mouse button. + * Others over 2 is undefined mouse buttons. + + Returns: + Returns 1 if the button is pressed, otherwise 0. +*/ +int inp_mouse_button_pressed(int button); + +/* + Function: inp_key_pressed + Checks if a key is pressed. + + Arguments: + key - Index to the key to check + + Returns: + Returns 1 if the button is pressed, otherwise 0. + + Remarks: + Check baselib/include/baselib/keys.h for the keys. +*/ +int inp_key_pressed(int key); + +/* + Group: Map +*/ + +int map_load(const char *mapname); // NOT EXPOSED +void map_unload(); // NOT EXPOSED + +/* + Function: map_is_loaded + Checks if a map is loaded. + + Returns: + Returns 1 if the button is pressed, otherwise 0. +*/ +int map_is_loaded(); + +/* + Function: map_num_items + Checks the number of items in the loaded map. + + Returns: + Returns the number of items. 0 if no map is loaded. +*/ +int map_num_items(); + +/* + Function: map_find_item + Searches the map for an item. + + Arguments: + type - Item type. + id - Item ID. + + Returns: + Returns a pointer to the item if it exists, otherwise it returns NULL. +*/ +void *map_find_item(int type, int id); + +/* + Function: map_get_item + Gets an item from the loaded map from index. + + Arguments: + index - Item index. + type - Pointer that recives the item type (can be NULL). + id - Pointer that recives the item id (can be NULL). + + Returns: + Returns a pointer to the item if it exists, otherwise it returns NULL. +*/ +void *map_get_item(int index, int *type, int *id); + +/* + Function: map_get_type + Gets the index range of an item type. + + Arguments: + type - Item type to search for. + start - Pointer that recives the starting index. + num - Pointer that recives the number of items. + + Returns: + If the item type is not in the map, start and num will be set to 0. +*/ +void map_get_type(int type, int *start, int *num); + +/* + Function: map_get_data + Fetches a pointer to a raw data chunk in the map. + + Arguments: + index - Index to the data to fetch. + + Returns: + A pointer to the raw data, otherwise 0. +*/ +void *map_get_data(int index); + +/* + Group: Network (Server) +*/ +/* + Function: snap_new_item + Creates a new item that should be sent. + + Arguments: + type - Type of the item. + id - ID of the item. + size - Size of the item. + + Returns: + A pointer to the item data, otherwise 0. + + Remarks: + The item data should only consist pf 4 byte integers as + they are subject to byte swapping. This means that the size + argument should be dividable by 4. +*/ +void *snap_new_item(int type, int id, int size); + +/* + Group: Network (Client) +*/ +/* + Function: snap_num_items + Check the number of items in a snapshot. + + Arguments: + snapid - Snapshot ID to the data to fetch. + * SNAP_PREV for previous snapshot. + * SNAP_CUR for current snapshot. + + Returns: + The number of items in the snapshot. +*/ +int snap_num_items(int snapid); + +/* + Function: snap_get_item + Gets an item from a snapshot. + + Arguments: + snapid - Snapshot ID to the data to fetch. + * SNAP_PREV for previous snapshot. + * SNAP_CUR for current snapshot. + index - Index of the item. + item - Pointer that recives the item info. + + Returns: + Returns a pointer to the item if it exists, otherwise NULL. +*/ +void *snap_get_item(int snapid, int index, snap_item *item); + +/* + Function: snap_find_item + Searches a snapshot for an item. + + Arguments: + snapid - Snapshot ID to the data to fetch. + * SNAP_PREV for previous snapshot. + * SNAP_CUR for current snapshot. + type - Type of the item. + id - ID of the item. + + Returns: + Returns a pointer to the item if it exists, otherwise NULL. +*/ +void *snap_find_item(int snapid, int type, int id); + +/* + Function: snap_input + Sets the input data to send to the server. + + Arguments: + data - Pointer to the data. + size - Size of the data. + + Remarks: + The data should only consist of 4 bytes integer as they are + subject to byte swapping. +*/ +void snap_input(void *data, int size); + +/* + Function: snap_intratick + Returns the intra-tick mixing value. + + Returns: + Returns the mixing value between the previous snapshot + and the current snapshot. + + Remarks: + DOCTODO: Explain how to use it. +*/ +float snap_intratick(); + +/* + Group: Server Callbacks +*/ +/* + Function: mods_init + Called when the server is started. + + Remarks: + It's called after the map is loaded so all map items are available. +*/ +void mods_init(); + +/* + Function: mods_shutdown + Called when the server quits. + + Remarks: + Should be used to clean up all resources used. +*/ +void mods_shutdown(); + +/* + Function: mods_client_enter + Called when a client has joined the game. + + Arguments: + cid - Client ID. Is 0 - MAX_CLIENTS. + + Remarks: + It's called when the client is finished loading and should enter gameplay. +*/ +void mods_client_enter(int cid); + +/* + Function: mods_client_drop + Called when a client drops from the server. + + Arguments: + cid - Client ID. Is 0 - MAX_CLIENTS +*/ +void mods_client_drop(int cid); + +/* + Function: mods_client_input + Called when the server recives new input from a client. + + Arguments: + cid - Client ID. Is 0 - MAX_CLIENTS. + input - Pointer to the input data. + size - Size of the data. (NOT IMPLEMENTED YET) +*/ +void mods_client_input(int cid, void *input); + +/* + Function: mods_tick + Called with a regular interval to progress the gameplay. + + Remarks: + The SERVER_TICK_SPEED tells the number of ticks per second. +*/ +void mods_tick(); + +/* + Function: mods_presnap + Called before the server starts to construct snapshots for the clients. +*/ +void mods_presnap(); + +/* + Function: mods_snap + Called to create the snapshot for a client. + + Arguments: + cid - Client ID. Is 0 - MAX_CLIENTS. + + Remarks: + The game should make a series of calls to to construct + the snapshot for the client. +*/ +void mods_snap(int cid); + +/* + Function: mods_postsnap + Called after the server is done sending the snapshots. +*/ +void mods_postsnap(); + +/* + Group: Client Callbacks +*/ +/* + Function: modc_init + Called when the client starts. + + Remarks: + The game should load resources that are used during the entire + time of the game. No map is loaded. +*/ +void modc_init(); + +/* + Function: modc_newsnapshot + Called when the client progressed to a new snapshot. + + Remarks: + The client can check for items in the snapshot and perform one time + events like playing sounds, spawning client side effects etc. +*/ +void modc_newsnapshot(); + +/* + Function: modc_entergame + Called when the client has successfully connect to a server and + loaded a map. + + Remarks: + The client can check for items in the map and load them. +*/ +void modc_entergame(); + +/* + Function: modc_shutdown + Called when the client closes down. +*/ +void modc_shutdown(); + +/* + Function: modc_render + Called every frame to let the game render it self. +*/ +void modc_render(); + + + +/* + Group: Menu Callbacks +*/ +/* + Function: modmenu_init + Called when the menu starts. + + Remarks: + The menu should load resources that are used during the entire + time of the menu use. +*/ +void modmenu_init(); + +/* + Function: modmenu_shutdown + Called when the menu closes down. +*/ +void modmenu_shutdown(); + +/* + Function: modmenu_render + Called every frame to let the menu render it self. +*/ +int modmenu_render(void *server_address, char *name, int max_len); + +void snap_encode_string(const char *src, int *dst, int length, int max_length); +void snap_decode_string(const int *src, char *dst, int length); + +int server_getclientinfo(int client_id, client_info *info); +int server_tick(); +int server_tickspeed(); + +int inp_key_was_pressed(int key); +int inp_key_down(int key); +void inp_update(); +float client_frametime(); +float client_localtime(); + +#define MASTER_SERVER_ADDRESS "master.teewars.com" +#define MASTER_SERVER_PORT 8300 + + + +#endif diff --git a/src/engine/lzw.cpp b/src/engine/lzw.cpp new file mode 100644 index 00000000..80dd1c22 --- /dev/null +++ b/src/engine/lzw.cpp @@ -0,0 +1,223 @@ +#include + +// LZW Compressor +struct SYM +{ + unsigned char *data; + int size; + int next; +}; + +struct SYMBOLS +{ + SYM syms[512]; + int jumptable[256]; + int currentsym; +}; + +static SYMBOLS symbols; + +// symbol info +inline int sym_size(int i) { return symbols.syms[i].size; } +inline unsigned char *sym_data(int i) { return symbols.syms[i].data; } + +static void sym_index(int sym) +{ + int table = symbols.syms[sym].data[0]; + symbols.syms[sym].next = symbols.jumptable[table]; + symbols.jumptable[table] = sym; +} + +static void sym_unindex(int sym) +{ + int table = symbols.syms[sym].data[0]; + int prev = -1; + int current = symbols.jumptable[table]; + + while(current != -1) + { + if(current == sym) + { + if(prev != -1) + symbols.syms[prev].next = symbols.syms[current].next; + else + symbols.jumptable[table] = symbols.syms[current].next; + break; + } + + prev = current; + current = symbols.syms[current].next; + } +} + +static int sym_add(unsigned char *sym, long len) +{ + int i = 256+symbols.currentsym; + symbols.syms[i].data = sym; + symbols.syms[i].size = len; + symbols.currentsym = (symbols.currentsym+1)%255; + return i; +} + +static int sym_add_and_index(unsigned char *sym, long len) +{ + if(symbols.syms[256+symbols.currentsym].size) + sym_unindex(256+symbols.currentsym); + int s = sym_add(sym, len); + sym_index( s); + return s; +} + +static void sym_init() +{ + static unsigned char table[256]; + for(int i = 0; i < 256; i++) + { + table[i] = i; + symbols.syms[i].data = &table[i]; + symbols.syms[i].size = 1; + symbols.jumptable[i] = -1; + } + + for(int i = 0; i < 512; i++) + symbols.syms[i].next = -1; + + /* + // insert some symbols to start with + static unsigned char zeros[8] = {0,0,0,0,0,0,0,0}; + //static unsigned char one1[4] = {0,0,0,1}; + //static unsigned char one2[4] = {1,0,0,0}; + sym_add_and_index(zeros, 2); + sym_add_and_index(zeros, 3); + sym_add_and_index(zeros, 4); + sym_add_and_index(zeros, 5); + sym_add_and_index(zeros, 6); + sym_add_and_index(zeros, 7); + sym_add_and_index(zeros, 8); + + //sym_add_and_index(one1, 4); + //sym_add_and_index(one2, 4);*/ + + symbols.currentsym = 0; +} + +static int sym_find(unsigned char *data, int size, int avoid) +{ + int best = data[0]; + int bestlen = 1; + int current = symbols.jumptable[data[0]]; + + while(current != -1) + { + if(current != avoid && symbols.syms[current].size <= size && memcmp(data, symbols.syms[current].data, symbols.syms[current].size) == 0) + { + if(bestlen < symbols.syms[current].size) + { + bestlen = symbols.syms[current].size; + best = current; + } + } + + current = symbols.syms[current].next; + } + + return best; +} + +// +// compress +// +long lzw_compress(const void *src_, int size, void *dst_) +{ + unsigned char *src = (unsigned char *)src_; + unsigned char *end = (unsigned char *)src_+size; + unsigned char *dst = (unsigned char *)dst_; + long left = (end-src); + int lastsym = -1; + + // init symboltable + sym_init(); + + bool done = false; + while(!done) + { + unsigned char *flagptr = dst; + unsigned char flagbits = 0; + int b = 0; + + dst++; // skip a byte where the flags are + + for(; b < 8; b++) + { + if(left <= 0) // check for EOF + { + // write EOF symbol + flagbits |= 1< symsize+1) // create new symbol + lastsym = sym_add_and_index(src, symsize+1); + + src += symsize; // advance src + left -= symsize; + } + + // write the flags + *flagptr = flagbits; + } + + return (long)(dst-(unsigned char*)dst_); +} + +// +// decompress +// +long lzw_decompress(const void *src_, void *dst_) +{ + unsigned char *src = (unsigned char *)src_; + unsigned char *dst = (unsigned char *)dst_; + unsigned char *prevdst = 0; + int prevsize = -1; + int item; + + sym_init(); + + while(1) + { + unsigned char flagbits = 0; + flagbits = *src++; // read flags + + int b = 0; + for(; b < 8; b++) + { + item = *src++; + if(flagbits&(1< +#include + +#include "datafile.h" + +static datafile *map; + +void *map_get_data(int index) +{ + return datafile_get_data(map, index); +} + +void *map_get_item(int index, int *type, int *id) +{ + return datafile_get_item(map, index, type, id); +} + +void map_get_type(int type, int *start, int *num) +{ + datafile_get_type(map, type, start, num); +} + +void *map_find_item(int type, int id) +{ + return datafile_find_item(map, type, id); +} + +int map_num_items() +{ + return datafile_num_items(map); +} + +void map_unload() +{ + datafile_unload(map); + map = 0x0; +} + +int map_is_loaded() +{ + return map != 0; +} + +int map_load(const char *mapname) +{ + map = datafile_load(mapname); + return map != 0; +} diff --git a/src/engine/packet.h b/src/engine/packet.h new file mode 100644 index 00000000..6dc99043 --- /dev/null +++ b/src/engine/packet.h @@ -0,0 +1,288 @@ +#include +#include + +// TODO: this is not KISS +class packet +{ +protected: + enum + { + MAX_PACKET_SIZE = 1024, + }; + + // packet data + struct header + { + unsigned msg; + unsigned ack; + unsigned seq; + }; + + unsigned char packet_data[MAX_PACKET_SIZE]; + unsigned char *current; + + // these are used to prepend data in the packet + // used for debugging so we have checks that we + // pack and unpack the same way + enum + { + DEBUG_TYPE_INT=0x1, + DEBUG_TYPE_STR=0x2, + DEBUG_TYPE_RAW=0x3, + }; + + // writes an int to the packet + void write_int_raw(int i) + { + // TODO: check for overflow + *(int*)current = i; + current += sizeof(int); + } + + // reads an int from the packet + int read_int_raw() + { + // TODO: check for overflow + int i = *(int*)current; + current += sizeof(int); + return i; + } + + void debug_insert_mark(int type, int size) + { + write_int_raw((type<<16)|size); + } + + void debug_verify_mark(int type, int size) + { + if(read_int_raw() != ((type<<16) | size)) + dbg_assert(0, "error during packet disassembly"); + } + +public: + packet(unsigned msg=0) + { + current = packet_data; + current += sizeof(header); + ((header*)packet_data)->msg = msg; + } + + void set_header(unsigned ack, unsigned seq) + { + ((header*)packet_data)->ack = ack; + ((header*)packet_data)->seq = seq; + } + + // writes an int to the packet + void write_int(int i) + { + debug_insert_mark(DEBUG_TYPE_INT, 4); + write_int_raw(i); + } + + void write_raw(const char *raw, int size) + { + debug_insert_mark(DEBUG_TYPE_RAW, size); + while(size--) + *current++ = *raw++; + } + + // writes a string to the packet + void write_str(const char *str) + { + debug_insert_mark(DEBUG_TYPE_STR, 0); + int s = strlen(str)+1; + write_int_raw(s); + for(;*str; current++, str++) + *current = *str; + *current = 0; + current++; + } + + // reads an int from the packet + int read_int() + { + debug_verify_mark(DEBUG_TYPE_INT, 4); + return read_int_raw(); + } + + // reads a string from the packet + const char *read_str() + { + debug_verify_mark(DEBUG_TYPE_STR, 0); + int size = read_int_raw(); + const char *s = (const char *)current; + //dbg_msg("packet", "reading string '%s' (%d)", s, size); + current += size; + return s; + } + + const char *read_raw(int size) + { + debug_verify_mark(DEBUG_TYPE_RAW, size); + const char *d = (const char *)current; + current += size; + return d; + } + + // TODO: impelement this + bool is_good() const { return true; } + + unsigned msg() const { return ((header*)packet_data)->msg; } + unsigned seq() const { return ((header*)packet_data)->seq; } + unsigned ack() const { return ((header*)packet_data)->ack; } + + // access functions to get the size and data + int size() const { return (int)(current-(unsigned char*)packet_data); } + int max_size() const { return MAX_PACKET_SIZE; } + void *data() { return packet_data; } +}; + +// +class connection +{ + baselib::socket_udp4 *socket; + baselib::netaddr4 addr; + unsigned seq; + unsigned ack; + unsigned last_ack; + + unsigned counter_sent_bytes; + unsigned counter_recv_bytes; + +public: + void counter_reset() + { + counter_sent_bytes = 0; + counter_recv_bytes = 0; + } + + void counter_get(unsigned *sent, unsigned *recved) + { + *sent = counter_sent_bytes; + *recved = counter_recv_bytes; + } + + void init(baselib::socket_udp4 *socket, const baselib::netaddr4 *addr) + { + this->addr = *addr; + this->socket = socket; + last_ack = 0; + ack = 0; + seq = 0; + counter_reset(); + } + + void send(packet *p) + { + if(p->msg()&(31<<1)) + { + // vital packet + seq++; + // TODO: save packet, we might need to retransmit + } + + p->set_header(ack, seq); + socket->send(&address(), p->data(), p->size()); + counter_sent_bytes += p->size(); + } + + packet *feed(packet *p) + { + counter_recv_bytes += p->size(); + + if(p->msg()&(31<<1)) + { + if(p->seq() == ack+1) + { + // packet in seqence, ack it + ack++; + //dbg_msg("network/connection", "packet in sequence. seq/ack=%x", ack); + return p; + } + else if(p->seq() > ack) + { + // TODO: request resend + // packet loss + dbg_msg("network/connection", "packet loss! seq=%x ack=%x+1", p->seq(), ack); + return p; + } + else + { + // we already got this packet + return 0x0; + } + } + + if(last_ack != p->ack()) + { + // TODO: remove acked packets + } + + return p; + } + + const baselib::netaddr4 &address() const { return addr; } + + void update() + { + } +}; + +//const char *NETWORK_VERSION = "development"; + +enum +{ + NETMSG_VITAL=0x80000000, + NETMSG_CONTEXT_CONNECT=0x00010000, + NETMSG_CONTEXT_GAME=0x00020000, + NETMSG_CONTEXT_GLOBAL=0x00040000, + + // connection phase + NETMSG_CLIENT_CONNECT=NETMSG_CONTEXT_CONNECT|1, + // str32 name + // str32 clan + // str32 password + // str32 skin + + // TODO: These should be implemented to make the server send the map to the client on connect + // NETMSG_CLIENT_FETCH, + // NETMSG_SERVER_MAPDATA, + + NETMSG_SERVER_ACCEPT=NETMSG_CONTEXT_CONNECT|2, + // str32 mapname + + + NETMSG_CLIENT_DONE=NETMSG_VITAL|NETMSG_CONTEXT_CONNECT|3, + // nothing + + // game phase + NETMSG_SERVER_SNAP = NETMSG_CONTEXT_GAME|1, // server will spam these + // int num_parts + // int part + // int size + // data * + + NETMSG_CLIENT_INPUT = NETMSG_CONTEXT_GAME|1, // client will spam these + // int input[MAX_INPUTS] + + NETMSG_SERVER_EVENT = NETMSG_CONTEXT_GAME|NETMSG_VITAL|2, + NETMSG_CLIENT_EVENT = NETMSG_CONTEXT_GAME|NETMSG_VITAL|2, + + NETMSG_CLIENT_CHECKALIVE = NETMSG_CONTEXT_GAME|NETMSG_VITAL|3, // check if client is alive + + NETMSG_CLIENT_ERROR=0x0fffffff, + // str128 reason + + NETMSG_SERVER_ERROR=0x0fffffff, + // str128 reason +}; + +enum +{ + MAX_NAME_LENGTH=32, + MAX_CLANNAME_LENGTH=32, + MAX_INPUT_SIZE=128, + MAX_SNAPSHOT_SIZE=64*1024, + MAX_SNAPSHOT_PACKSIZE=768 +}; diff --git a/src/engine/server/server.cpp b/src/engine/server/server.cpp new file mode 100644 index 00000000..d9151a43 --- /dev/null +++ b/src/engine/server/server.cpp @@ -0,0 +1,5 @@ +#include #include #include #include //#include "socket.h" #include #include #include #include namespace baselib {} using namespace baselib; int net_addr4_cmp(const NETADDR4 *a, const NETADDR4 *b) { if( a->ip[0] != b->ip[0] || a->ip[1] != b->ip[1] || a->ip[2] != b->ip[2] || a->ip[3] != b->ip[3] || a->port != b->port ) return 1; return 0; } // --- string handling (MOVE THESE!!) --- void snap_encode_string(const char *src, int *dst, int length, int max_length) { const unsigned char *p = (const unsigned char *)src; // handle whole int for(int i = 0; i < length/4; i++) { *dst = (p[0]<<24|p[1]<<16|p[2]<<8|p[3]); p += 4; dst++; } // take care of the left overs int left = length%4; if(left) { unsigned last = 0; switch(left) { case 3: last |= p[2]<<8; case 2: last |= p[1]<<16; case 1: last |= p[0]<<24; } *dst = last; } } class snapshot_builder { public: static const int MAX_ITEMS = 512; //static const int MAX_DATA_SIZE=1*1024; char data[MAX_SNAPSHOT_SIZE]; int data_size; int offsets[MAX_ITEMS]; int num_items; int top_size; int top_items; int snapnum; snapshot_builder() { top_size = 0; top_items = 0; snapnum = 0; } void start() { data_size = 0; num_items = 0; } int finish(void *snapdata) { snapnum++; // collect some data /* int change = 0; if(data_size > top_size) { change++; top_size = data_size; } if(num_items > top_items) { change++; top_items = num_items; } if(change) { dbg_msg("snapshot", "new top, items=%d size=%d", top_items, top_size); }*/ // flattern and make the snapshot snapshot *snap = (snapshot *)snapdata; snap->num_items = num_items; int offset_size = sizeof(int)*num_items; mem_copy(snap->offsets, offsets, offset_size); mem_copy(snap->data_start(), data, data_size); return sizeof(int) + offset_size + data_size; } void *new_item(int type, int id, int size) { snapshot::item *obj = (snapshot::item *)(data+data_size); obj->type_and_id = (type<<16)|id; offsets[num_items] = data_size; data_size += sizeof(int) + size; num_items++; dbg_assert(data_size < MAX_SNAPSHOT_SIZE, "too much data"); dbg_assert(num_items < MAX_ITEMS, "too many items"); return &obj->data; } }; static snapshot_builder builder; void *snap_new_item(int type, int id, int size) { dbg_assert(type >= 0 && type <=0xffff, "incorrect type"); dbg_assert(id >= 0 && id <=0xffff, "incorrect id"); return builder.new_item(type, id, size); } // class client { public: enum { STATE_EMPTY = 0, STATE_CONNECTING = 1, STATE_INGAME = 2, }; // connection state info int state; // (ticks) if lastactivity > 5 seconds kick him int64 lastactivity; connection conn; char name[MAX_NAME_LENGTH]; char clan[MAX_CLANNAME_LENGTH]; /* client() { state = STATE_EMPTY; name[0] = 0; clan[0] = 0; } ~client() { dbg_assert(state == STATE_EMPTY, "client destoyed while in use"); }*/ bool is_empty() const { return state == STATE_EMPTY; } bool is_ingame() const { return state == STATE_INGAME; } const netaddr4 &address() const { return conn.address(); } }; static client clients[MAX_CLIENTS]; static int current_tick = 0; static int send_heartbeats = 1; int server_tick() { return current_tick; } int server_tickspeed() { return 50; } int server_init() { for(int i = 0; i < MAX_CLIENTS; i++) { clients[i].state = client::STATE_EMPTY; clients[i].name[0] = 0; clients[i].clan[0] = 0; clients[i].lastactivity = 0; } current_tick = 0; return 0; } int server_getclientinfo(int client_id, client_info *info) { dbg_assert(client_id >= 0 && client_id < MAX_CLIENTS, "client_id is not valid"); dbg_assert(info != 0, "info can not be null"); if(clients[client_id].is_ingame()) { info->name = clients[client_id].name; info->latency = 0; return 1; } return 0; } // class server { public: socket_udp4 game_socket; + + const char *map_name; const char *server_name; int64 lasttick; int64 lastheartbeat; netaddr4 master_server; int biggest_snapshot; bool run(const char *servername, const char *mapname) { biggest_snapshot = 0; net_init(); // For Windows compatibility. map_name = mapname; server_name = servername; // load map if(!map_load(mapname)) { dbg_msg("server", "failed to load map. mapname='%s'"); return false; } // start server if(!game_socket.open(8303)) { dbg_msg("network/server", "couldn't open socket"); return false; } for(int i = 0; i < MAX_CLIENTS; i++) dbg_msg("network/server", "\t%d: %d", i, clients[i].state); if (net_host_lookup(MASTER_SERVER_ADDRESS, MASTER_SERVER_PORT, &master_server) != 0) { // TODO: fix me //master_server = netaddr4(0, 0, 0, 0, 0); } mods_init(); int64 time_per_tick = time_freq()/SERVER_TICK_SPEED; int64 time_per_heartbeat = time_freq() * 30; int64 starttime = time_get(); //int64 lasttick = starttime; lasttick = starttime; lastheartbeat = 0; int64 reporttime = time_get(); int64 reportinterval = time_freq()*3; int64 simulationtime = 0; int64 snaptime = 0; int64 networktime = 0; while(1) { int64 t = time_get(); if(t-lasttick > time_per_tick) { { int64 start = time_get(); tick(); simulationtime += time_get()-start; } { int64 start = time_get(); snap(); snaptime += time_get()-start; } // Check for client timeouts for (int i = 0; i < MAX_CLIENTS; i++) { if (clients[i].state != client::STATE_EMPTY) { // check last activity time if (((lasttick - clients[i].lastactivity) / time_freq()) > SERVER_CLIENT_TIMEOUT) client_timeout(i); } } lasttick += time_per_tick; } if(send_heartbeats) { if (t > lastheartbeat+time_per_heartbeat) { if (master_server.port != 0) { int players = 0; for (int i = 0; i < MAX_CLIENTS; i++) if (!clients[i].is_empty()) players++; // TODO: fix me netaddr4 me(127, 0, 0, 0, 8303); send_heartbeat(0, &me, players, MAX_CLIENTS, server_name, mapname); } lastheartbeat = t+time_per_heartbeat; } } { int64 start = time_get(); pump_network(); networktime += time_get()-start; } if(reporttime < time_get()) { int64 totaltime = simulationtime+snaptime+networktime; dbg_msg("server/report", "sim=%.02fms snap=%.02fms net=%.02fms total=%.02fms load=%.02f%%", simulationtime/(float)reportinterval*1000, snaptime/(float)reportinterval*1000, networktime/(float)reportinterval*1000, totaltime/(float)reportinterval*1000, (simulationtime+snaptime+networktime)/(float)reportinterval*100.0f); unsigned sent_total=0, recv_total=0; for (int i = 0; i < MAX_CLIENTS; i++) if (!clients[i].is_empty()) { unsigned s,r; clients[i].conn.counter_get(&s,&r); clients[i].conn.counter_reset(); sent_total += s; recv_total += r; } dbg_msg("server/report", "biggestsnap=%d send=%d recv=%d", biggest_snapshot, sent_total/3, recv_total/3); simulationtime = 0; snaptime = 0; networktime = 0; reporttime += reportinterval; } thread_sleep(1); } mods_shutdown(); map_unload(); } void tick() { current_tick++; mods_tick(); } void snap() { mods_presnap(); for(int i = 0; i < MAX_CLIENTS; i++) { if(clients[i].is_ingame()) { char data[MAX_SNAPSHOT_SIZE]; char compdata[MAX_SNAPSHOT_SIZE]; builder.start(); mods_snap(i); // finish snapshot int snapshot_size = builder.finish(data); // compress it int compsize = lzw_compress(data, snapshot_size, compdata); snapshot_size = compsize; if(snapshot_size > biggest_snapshot) biggest_snapshot = snapshot_size; const int max_size = MAX_SNAPSHOT_PACKSIZE; int numpackets = (snapshot_size+max_size-1)/max_size; for(int n = 0, left = snapshot_size; left; n++) { int chunk = left < max_size ? left : max_size; left -= chunk; packet p(NETMSG_SERVER_SNAP); p.write_int(numpackets); p.write_int(n); p.write_int(chunk); p.write_raw(&compdata[n*max_size], chunk); clients[i].conn.send(&p); } } } mods_postsnap(); } void send_accept(client *client, const char *map) { packet p(NETMSG_SERVER_ACCEPT); p.write_str(map); client->conn.send(&p); } void drop(int cid, const char *reason) { if(clients[cid].state == client::STATE_EMPTY) return; clients[cid].state = client::STATE_EMPTY; mods_client_drop(cid); dbg_msg("game", "player dropped. reason='%s' cid=%x name='%s'", reason, cid, clients[cid].name); } int find_client(const netaddr4 *addr) { // fetch client for(int i = 0; i < MAX_CLIENTS; i++) { if(!clients[i].is_empty() && clients[i].address() == *addr) return i; } return -1; } void client_process_packet(int cid, packet *p) { clients[cid].lastactivity = lasttick; if(p->msg() == NETMSG_CLIENT_DONE) { dbg_msg("game", "player as entered the game. cid=%x", cid); clients[cid].state = client::STATE_INGAME; mods_client_enter(cid); } else if(p->msg() == NETMSG_CLIENT_INPUT) { int input[MAX_INPUT_SIZE]; int size = p->read_int(); for(int i = 0; i < size/4; i++) input[i] = p->read_int(); if(p->is_good()) { //dbg_msg("network/server", "applying input %d %d %d", input[0], input[1], input[2]); mods_client_input(cid, input); } } else if(p->msg() == NETMSG_CLIENT_ERROR) { const char *reason = p->read_str(); if(p->is_good()) dbg_msg("network/server", "client error. cid=%x reason='%s'", cid, reason); else dbg_msg("network/server", "client error. cid=%x", cid); drop(cid, "client error"); } else { dbg_msg("network/server", "invalid message. cid=%x msg=%x", cid, p->msg()); drop(cid, "invalid message"); } } void process_packet(packet *p, netaddr4 *from) { if(p->msg() == NETMSG_CLIENT_CONNECT) { // we got no state for this client yet const char *version; const char *name; const char *clan; const char *password; const char *skin; version = p->read_str(); name = p->read_str(); clan = p->read_str(); password = p->read_str(); skin = p->read_str(); if(p->is_good()) { // check version if(strcmp(version, TEEWARS_NETVERSION) != 0) { dbg_msg("network/server", "wrong version connecting '%s'", version); + // TODO: send error return; } // look for empty slot, linear search int id = -1; for(int i = 0; i < MAX_CLIENTS; i++) if(clients[i].is_empty()) { id = i; break; } if(id != -1) { // slot found // TODO: perform correct copy here mem_copy(clients[id].name, name, MAX_NAME_LENGTH); mem_copy(clients[id].clan, clan, MAX_CLANNAME_LENGTH); clients[id].state = client::STATE_CONNECTING; clients[id].conn.init(&game_socket, from); clients[id].lastactivity = lasttick; clients[id].name[MAX_NAME_LENGTH-1] = 0; clients[id].clan[MAX_CLANNAME_LENGTH-1] = 0; dbg_msg("network/server", "client connected. '%s' on slot %d", name, id); // TODO: return success send_accept(&clients[id], map_name); } else { // no slot found // TODO: send error dbg_msg("network/server", "client connected but server is full"); for(int i = 0; i < MAX_CLIENTS; i++) dbg_msg("network/server", "\t%d: %d", i, clients[i].state); } } } else { int cid = find_client(from); if(cid >= 0) { if(clients[cid].conn.feed(p)) { // packet is ok unsigned msg = p->msg(); // client found, check state if(((msg>>16)&0xff)&clients[cid].state) { // state is ok client_process_packet(cid, p); } else { // invalid state, disconnect the client drop(cid, "invalid message at this state"); } } else { drop(cid, "connection error"); } } else dbg_msg("network/server", "packet from strange address."); } } void client_timeout(int clientId) { drop(clientId, "client timedout"); } void pump_network() { while(1) { packet p; netaddr4 from; //int bytes = net_udp4_recv( int bytes = game_socket.recv(&from, p.data(), p.max_size()); //int bytes = game_socket.recv(&from, p.data(), p.max_size()); if(bytes <= 0) break; process_packet(&p, &from); } // TODO: check for client timeouts } char *write_int(char *buffer, int integer) { *buffer++ = integer >> 24; *buffer++ = integer >> 16; *buffer++ = integer >> 8; *buffer++ = integer; return buffer; } char *write_netaddr4(char *buffer, NETADDR4 *address) { *buffer++ = address->ip[0]; *buffer++ = address->ip[1]; *buffer++ = address->ip[2]; *buffer++ = address->ip[3]; return write_int(buffer, address->port); } void send_heartbeat(int version, netaddr4 *address, int players, int max_players, const char *name, const char *map_name) { char buffer[216] = {0}; char *d = buffer; d = write_int(d, 'TWHB'); d = write_int(d, version); d = write_netaddr4(d, address); d = write_int(d,players); d = write_int(d, max_players); int len = strlen(name); if (len > 128) len = 128; memcpy(d, name, len); d += 128; len = strlen(map_name); if (len > 64) len = 64; memcpy(d, map_name, len); d += 64; + game_socket.send(&master_server, buffer, sizeof(buffer)); } }; int main(int argc, char **argv) { dbg_msg("server", "starting..."); const char *mapname = "data/demo.map"; const char *servername = 0; // parse arguments for(int i = 1; i < argc; i++) { if(argv[i][0] == '-' && argv[i][1] == 'm' && argv[i][2] == 0 && argc - i > 1) { // -m map i++; mapname = argv[i]; } else if(argv[i][0] == '-' && argv[i][1] == 'n' && argv[i][2] == 0 && argc - i > 1) { // -n server name i++; servername = argv[i]; } else if(argv[i][0] == '-' && argv[i][1] == 'p' && argv[i][2] == 0) { // -p (private server) send_heartbeats = 0; } } if(!mapname) { dbg_msg("server", "no map given (-m MAPNAME)"); return 0; } if(!servername) { dbg_msg("server", "no server name given (-n \"server name\")"); return 0; } server_init(); server s; s.run(servername, mapname); return 0; } \ No newline at end of file diff --git a/src/engine/snapshot.h b/src/engine/snapshot.h new file mode 100644 index 00000000..9d803486 --- /dev/null +++ b/src/engine/snapshot.h @@ -0,0 +1,19 @@ + +struct snapshot +{ + int num_items; + int offsets[1]; + + struct item + { + int type_and_id; + char data[1]; + + int type() { return type_and_id>>16; } + int id() { return type_and_id&(0xffff); } + }; + + char *data_start() { return (char *)&offsets[num_items]; } + item *get_item(int index) { return (item *)(data_start() + offsets[index]); }; +}; + diff --git a/src/engine/versions.h b/src/engine/versions.h new file mode 100644 index 00000000..d70ee721 --- /dev/null +++ b/src/engine/versions.h @@ -0,0 +1,2 @@ +#define TEEWARS_NETVERSION "dev v2" +#define TEEWARS_VERSION "0.2.1-dev" diff --git a/src/game/client/game_client.cpp b/src/game/client/game_client.cpp new file mode 100644 index 00000000..f510e00e --- /dev/null +++ b/src/game/client/game_client.cpp @@ -0,0 +1,1993 @@ +#include +#include +#include "../game.h" +#include "mapres_image.h" +#include "mapres_tilemap.h" + +using namespace baselib; + +static int texture_char_default = 0; +static int texture_game = 0; +static int texture_weapon = 0; +static int texture_sun = 0; +static int texture_particles = 0; + +struct weapontexcell +{ + float x; + float y; + float w; + float h; +}; +struct renderparams +{ + float sizex; + float sizey; + float offsetx; + float offsety; +}; +int numcellsx = 32; +int numcellsy = 32; +renderparams weaponrenderparams[WEAPON_NUMWEAPONS]; +renderparams modifierrenderparams[WEAPON_NUMWEAPONS]; + +weapontexcell weaponprojtexcoord[WEAPON_NUMWEAPONS]; +weapontexcell weapontexcoord[WEAPON_NUMWEAPONS]; +weapontexcell weapontexcoordcursor[WEAPON_NUMWEAPONS]; + +weapontexcell poweruptexcoord[POWERUP_TYPE_NUMPOWERUPS]; + +weapontexcell modifiertexcoord[MODIFIER_NUMMODIFIERS]; +weapontexcell modifiertexcoordcursor[MODIFIER_NUMMODIFIERS]; + +int nummuzzletex[WEAPON_NUMWEAPONS]; +weapontexcell muzzletexcoord[WEAPON_NUMWEAPONS][3]; +renderparams muzzleparams[WEAPON_NUMWEAPONS]; + +#define NUMHADOKENS 6 +#define NUMSTARS 2 +#define NUMPARTICLES 9 +int particlesnumcellsx = 16; +int particlesnumcellsy = 16; +weapontexcell chaintexcoord; +weapontexcell chainheadtexcoord; +weapontexcell stars[NUMSTARS]; + +float lifemodifier[NUMPARTICLES]; +vec4 particlecolors[NUMPARTICLES]; +weapontexcell particlestexcoord[NUMPARTICLES]; + +int charnumcellsx = 8; +int charnumcellsy = 32; +int charoffsety = 2; +weapontexcell body[2]; +weapontexcell leye; +weapontexcell reye; +weapontexcell feet[2]; + +int charids[16] = { 2,10,0,4,12,6,14,1,9,15,13,11,7,5,8,3 }; + +renderparams hadokenparams[6]; +weapontexcell hadoken[6]; + +float recoils[WEAPON_NUMWEAPONS] = { 10.0f, 10.0f, 10.0f, 10.0f }; + +static int font_texture = 0; +static vec2 mouse_pos; + + +static vec2 local_player_pos; +static obj_player *local_player; + +float frandom() +{ + return rand()/(float)(RAND_MAX); +} + +float sign(float f) +{ + return f<0.0f?-1.0f:1.0f; +} + +// sound helpers +template +class sound_kit +{ +private: + int sounds[N]; + int last_id; +public: + sound_kit() : last_id(-1) { } + + int& operator[](int id) { return sounds[id]; } + + inline void play_random(float vol = 1.0f, float pan = 0.0f); +}; + +template<> +inline void sound_kit<1>::play_random(float vol, float pan) +{ + snd_play(sounds[0], SND_PLAY_ONCE, vol, pan); +} + +template +inline void sound_kit::play_random(float vol, float pan) +{ + int id; + do { + id = rand() % N; + } while(id == last_id); + snd_play(sounds[id], SND_PLAY_ONCE, vol, pan); + last_id = id; +} + + +// sound volume tweak +static const float stereo_separation = 0.01f; +static const float stereo_separation_deadzone = 512.0f; +static const float volume_distance_falloff = 100.0f; +static const float volume_distance_deadzone = 512.0f; +static const float volume_gun = 0.5f; +static const float volume_tee = 0.5f; +static const float volume_hit = 0.5f; +static const float volume_music = 0.8f; + +// sounds +sound_kit<3> sound_gun_fire; +sound_kit<3> sound_shotty_fire; +sound_kit<3> sound_flump_launch; +sound_kit<3> sound_hammer_swing; +sound_kit<3> sound_ninja_attack; + +sound_kit<3> sound_flump_explode; +sound_kit<4> sound_ninja_hit; + +sound_kit<3> sound_weapon_switch; + +sound_kit<12> sound_pain_short; +sound_kit<2> sound_pain_long; + +sound_kit<4> sound_body_jump; +sound_kit<4> sound_body_land; +sound_kit<2> sound_body_splat; + +sound_kit<7> sound_spawn; +sound_kit<2> sound_tee_cry; + +sound_kit<1> sound_hook_loop; +sound_kit<3> sound_hook_attach; + +void sound_vol_pan(const vec2& p, float *vol, float *pan) +{ + vec2 player_to_ev = p - local_player_pos; + *pan = 0.0f; + *vol = 1.0f; + + if(abs(player_to_ev.x) > stereo_separation_deadzone) + { + *pan = stereo_separation * (player_to_ev.x - sign(player_to_ev.x)*stereo_separation_deadzone); + if(*pan < -1.0f) *pan = -1.0f; + if(*pan > 1.0f) *pan = 1.0f; + } + + float len = length(player_to_ev); + if(len > volume_distance_deadzone) + { + *vol = volume_distance_falloff / (len - volume_distance_deadzone); + + if(*vol < 0.0f) *vol = 0.0f; + if(*vol > 1.0f) *vol = 1.0f; + } +} + +// TODO: we should do something nicer then this +static void cell_select_ex(int cx, int cy, float x, float y, float w, float h) +{ + gfx_quads_setsubset(x/(float)cx,y/(float)cy,(x+w)/(float)cx,(y+h)/(float)cy); +} + +static void cell_select_ex_flip_x(int cx, int cy, float x, float y, float w, float h) +{ + gfx_quads_setsubset((x+w)/(float)cx,y/(float)cy,x /(float)cx,(y+h)/(float)cy); +} + +static void cell_select_ex_flip_y(int cx, int cy, float x, float y, float w, float h) +{ + gfx_quads_setsubset(x/(float)cx,(y+h)/(float)cy,(x+w)/(float)cx,y/(float)cy); +} + +static void cell_select(int x, int y, int w, int h) +{ + gfx_quads_setsubset(x/16.0f,y/16.0f,(x+w)/16.0f,(y+h)/16.0f); +} + +inline void cell_select_flip_x(int x, int y, int w, int h) +{ + gfx_quads_setsubset((x+w)/16.0f,y/16.0f,(x)/16.0f,(y+h)/16.0f); +} + +inline void cell_select_flip_y(int x, int y, int w, int h) +{ + gfx_quads_setsubset(x/16.0f,(y+h)/16.0f,(x+w)/16.0f,(y)/16.0f); +} + +struct particle +{ + vec2 pos; + vec2 vel; + float life; + float max_life; + float size; + + float rot; + float rotspeed; + + float gravity; + float friction; + int iparticle; + + vec4 color; +}; + +void move_point(vec2 *inout_pos, vec2 *inout_vel, float elasticity) +{ + vec2 pos = *inout_pos; + vec2 vel = *inout_vel; + if(col_check_point(pos + vel)) + { + int affected = 0; + if(col_check_point(pos.x + vel.x, pos.y)) + { + inout_vel->x *= -elasticity; + affected++; + } + + if(col_check_point(pos.x, pos.y + vel.y)) + { + inout_vel->y *= -elasticity; + affected++; + } + + if(affected == 0) + { + inout_vel->x *= -elasticity; + inout_vel->y *= -elasticity; + } + } + else + { + *inout_pos = pos + vel; + } +} + +class health_texts +{ +public: + int64 lastupdate; + struct item + { + vec2 pos; + vec2 vel; + int amount; + int istar; + float life; + float startangle; + }; + + enum + { + MAX_ITEMS=16, + }; + + health_texts() + { + lastupdate = 0; + } + + item items[MAX_ITEMS]; + int num_items; + + item *create_i() + { + if (num_items < MAX_ITEMS) + { + item *p = &items[num_items]; + num_items++; + return p; + } + return 0; + } + + void destroy_i(item *i) + { + num_items--; + *i = items[num_items]; + } + + void create(vec2 pos, int amount) + { + amount = max(1,amount); + for (int j = 0; j < amount; j++) + { + float a = j/(float)amount-0.5f; + item *i = create_i(); + if (i) + { + i->pos = pos; + i->pos.y -= 20.0f; + i->pos.x += ((float)rand()/(float)RAND_MAX) * 5.0f; + i->amount = amount; + i->life = 1.5f; + i->istar = rand() % NUMSTARS; + i->vel = vec2(((float)rand()/(float)RAND_MAX) * 50.0f,-150.0f); + i->startangle = (( (float)rand()/(float)RAND_MAX) - 1.0f) * 2.0f * pi; + } + } + } + + void render() + { + if (!lastupdate) + lastupdate = time_get(); + + int64 lasttime = lastupdate; + lastupdate = time_get(); + + float delta = (float) (lastupdate - lasttime) / (float)time_freq(); + gfx_texture_set(texture_particles); + gfx_quads_begin(); + for(int i = 0; i < num_items;) + { + items[i].vel += vec2(0,500.0f) * delta; + items[i].pos += items[i].vel * delta; + items[i].life -= delta; + //items[i].pos.y -= frametime*15.0f; + if(items[i].life < 0.0f) + destroy_i(&items[i]); + else + { + gfx_quads_setcolor(1.0f,1.0f,1.0f, items[i].life / 1.5f); + gfx_quads_setrotation(items[i].startangle + items[i].life * 2.0f); + float size = 64.0f; + cell_select_ex(particlesnumcellsx,particlesnumcellsy, stars[items[i].istar].x,stars[items[i].istar].y, stars[items[i].istar].w, stars[items[i].istar].h); + gfx_quads_draw(items[i].pos.x-size/2, items[i].pos.y-size/2, size, size); + /*char buf[32]; + if(items[i].amount < 0) + { + sprintf(buf, "%d", items[i].amount*-1); + } + else + { + sprintf(buf, "%d", items[i].amount); + } + float size = 42.0f; + if(items[i].life > 1.25f) + size += 42.0f * ((items[i].life - 1.25f) * 4); + gfx_quads_text(items[i].pos.x-size/2, items[i].pos.y, size, buf);*/ + i++; + } + } + gfx_quads_end(); + } + +}; + +/*class texture_animator +{ +public: + + int texture; + int numframes; + float duration; + float* framereltime; + weapontexcell* params; + texture_animator() + { + texture = -1; + numframes = 0; + duration = 0; + framereltime = 0; + params = 0; + } + + ~texture_animator() + { + if (params) + mem_free(params); + if (framereltime) + mem_free(framereltime); + } + + void create_anim(int texture, int numframes, float duration) + { + framereltime = 0; + params = 0; + this->texture = texture; + this->numframes = numframes; + this->duration = duration; + if (numframes) + { + framereltime = (float*)mem_alloc(sizeof(float) * numframes,1); + params = (weapontexcell*)mem_alloc(sizeof(renderparams) * numframes,1); + float delta = 1.0f / (float)(numframes - 1); + for (int i = 0; i < numframes; i++) + { + framereltime[i] = delta * i; + } + } + } + + static void create_gunmuzzle(texture_animator& anim, int texture, float duration) + { + anim.create_anim(texture, 3, duration); + anim.params[0].x = 8; + anim.params[0].y = 4; + anim.params[0].w = 3; + anim.params[0].h = 2; + anim.params[1].x = 12; + anim.params[1].y = 4; + anim.params[1].w = 3; + anim.params[1].h = 2; + anim.params[2].x = 16; + anim.params[2].y = 4; + anim.params[2].w = 3; + anim.params[2].h = 2; + } + + static void create_shotgunmuzzle() + { + + } +};*/ + +class keyframe +{ +public: + vec2 pos; + float angle; + float relativetime; +}; + +class anim +{ +public: + keyframe* keyframes; + int numframes; + float duration; + anim() + { + numframes = 0; + keyframes = 0; + } + ~anim() + { + if (keyframes) + mem_free(keyframes); + } + + void create_anim(int numframes, float duration) + { + if (keyframes) + mem_free(keyframes); + + this->numframes = numframes; + this->duration = duration; + keyframes = (keyframe*)mem_alloc(sizeof(keyframe) * numframes,1); + float delta = 1.0f / (float) (numframes - 1); + for (int i = 0; i < numframes; i++) + { + keyframes[i].pos = vec2(0.0f,0.0f); + keyframes[i].angle = 0; + keyframes[i].relativetime = delta * (float)i; + } + } + + void getframes(float relativetime, keyframe*& frame1, keyframe*& frame2, float& blend) + { + for (int i = 1; i < numframes; i++) + { + if (keyframes[i-1].relativetime <= relativetime && keyframes[i].relativetime >= relativetime) + { + frame1 = &keyframes[i-1]; + frame2 = &keyframes[i]; + blend = (relativetime - frame1->relativetime) / (frame2->relativetime - frame1->relativetime); + } + } + } + + void evalanim(float time, vec2& pos, float& angle) + { + float reltime = max(0.0f, min(1.0f, time / duration)); + keyframe* frame1 = 0; + keyframe* frame2 = 0; + float blend = 0.0f; + getframes(reltime, frame1, frame2, blend); + + if (frame1 && frame2) + { + pos = mix(frame1->pos, frame2->pos, blend); + angle = LERP(frame1->angle, frame2->angle, blend); + } + } + + static void setup_hammer(anim& hammeranim) + { + // straight up = -0.25 + // frame0 = standard pose time 0 + // frame1 = back a little time 0.3 + // frame2 = over head time 0.4 + // frame3 = on ground smashed time 0.5 + // frame4 = back to standard pose time 1.0 + hammeranim.create_anim(5, 1.0f); + // only angles... (for now...) + hammeranim.keyframes[0].angle = -0.35f * pi * 2.0f; + hammeranim.keyframes[1].angle = -0.4f * pi * 2.0f; + hammeranim.keyframes[1].relativetime = 0.3f; + hammeranim.keyframes[2].angle = -0.25f; + hammeranim.keyframes[2].relativetime = 0.4f; + hammeranim.keyframes[3].angle = 0.0f * pi * 2.0f; + hammeranim.keyframes[3].relativetime = 0.5f; + hammeranim.keyframes[4].angle = -0.35f * pi * 2.0f; + hammeranim.keyframes[4].relativetime = 1.0f; + } + + static void setup_ninja(anim& ninjanim) + { + // (straight up = -0.25) + // frame0 = standard pose straight back time 0.0 + // frame1 = overhead attack frame 1 time 0.1 + // frame2 = attack end frame time 0.15 + // frame3 = attack hold frame (a bit up) time 0.4 + // frame4 = attack hold frame end time 0.7 + // frame5 = endframe time 1.0 + ninjanim.create_anim(6, 1.0f); + // only angles... (for now...) + ninjanim.keyframes[0].angle = -0.5f * pi * 2.0f; + + ninjanim.keyframes[1].angle = -0.3f * pi * 2.0f; + ninjanim.keyframes[1].relativetime = 0.1f; + + ninjanim.keyframes[2].angle = 0.1f * pi * 2.0f; + ninjanim.keyframes[2].relativetime = 0.15f; + + ninjanim.keyframes[3].angle = -0.05f * pi * 2.0f; + ninjanim.keyframes[3].relativetime = 0.42; + + ninjanim.keyframes[4].angle = -0.05f * pi * 2.0f; + ninjanim.keyframes[4].relativetime = 0.5f; + + ninjanim.keyframes[5].angle = -0.5f * pi * 2.0f; + ninjanim.keyframes[5].relativetime = 1.0f; + } +}; + +static anim hammeranim; +static anim ninjaanim; +static health_texts healthmods; + +class particle_system +{ +public: + enum + { + MAX_PARTICLES=1024, + }; + + particle particles[MAX_PARTICLES]; + int num_particles; + + particle_system() + { + num_particles = 0; + } + + void new_particle(vec2 pos, vec2 vel, float life, float size, float gravity, float friction) + { + if (num_particles >= MAX_PARTICLES) + return; + + particles[num_particles].iparticle = rand() % NUMPARTICLES; + particles[num_particles].pos = pos; + particles[num_particles].vel = vel; + particles[num_particles].life = life - lifemodifier[particles[num_particles].iparticle] * life; + particles[num_particles].size = size; + particles[num_particles].max_life = life; + particles[num_particles].gravity = gravity; + particles[num_particles].friction = friction; + particles[num_particles].rot = frandom()*pi*2; + particles[num_particles].rotspeed = frandom() * 10.0f; + num_particles++; + } + + void update(float time_passed) + { + for(int i = 0; i < num_particles; i++) + { + particles[i].vel.y += particles[i].gravity*time_passed; + particles[i].vel *= particles[i].friction; + vec2 vel = particles[i].vel*time_passed; + move_point(&particles[i].pos, &vel, 0.1f+0.9f*frandom()); + particles[i].vel = vel* (1.0f/time_passed); + particles[i].life += time_passed; + particles[i].rot += time_passed * particles[i].rotspeed; + + // check particle death + if(particles[i].life > particles[i].max_life) + { + num_particles--; + particles[i] = particles[num_particles]; + i--; + } + } + } + + void render() + { + gfx_blend_additive(); + gfx_texture_set(texture_particles); + gfx_quads_begin(); + //cell_select(4,1,1,1); + //cell_select(0,6,2,2); + //gfx_quads_setrotation(get_angle(vec2(proj->vx, proj->vy))); + for(int i = 0; i < num_particles; i++) + { + int type = particles[i].iparticle; + cell_select_ex(particlesnumcellsx,particlesnumcellsy,particlestexcoord[type].x, particlestexcoord[type].y, particlestexcoord[type].w, particlestexcoord[type].h); + float a = 1 - particles[i].life / particles[i].max_life; + vec2 p = particles[i].pos; + //a *= length(particles[i].vel) * 0.01f; + gfx_quads_setrotation(particles[i].rot); + gfx_quads_setcolor(particlecolors[type].x,particlecolors[type].y,particlecolors[type].z,pow(a,0.75f)); + //gfx_quads_setcolor(particlecolors[type].x * 0.5,particlecolors[type].y * 0.5,particlecolors[type].z* 0.5,pow(a,0.75f)); + //gfx_quads_setcolor(particlecolors[type].x * 0.0,particlecolors[type].y * 0.0,particlecolors[type].z* 0.0,pow(a,0.75f)); + //gfx_quads_setcolor(0.64f*2,0.28f*2,0.16f*2,pow(a,0.75f)); + gfx_quads_draw(p.x, p.y,particles[i].size,particles[i].size); + } + gfx_quads_end(); + gfx_blend_normal(); + } +}; + +static particle_system temp_system; + +void modc_init() +{ + // load textures + texture_weapon = gfx_load_texture_tga("data/tileset_weapons.tga"); + texture_game = gfx_load_texture_tga("data/game_main.tga"); + texture_char_default = gfx_load_texture_tga("data/char_teefault.tga"); + texture_sun = gfx_load_texture_tga("data/sun.tga"); + texture_particles = gfx_load_texture_tga("data/tileset_particles.tga"); + font_texture = gfx_load_texture_tga("data/debug_font.tga"); + + + // load sounds + sound_gun_fire[0] = snd_load_wav("data/audio/wp_gun_fire-01.wav"); + sound_gun_fire[0] = snd_load_wav("data/audio/wp_gun_fire-01.wav"); + sound_gun_fire[1] = snd_load_wav("data/audio/wp_gun_fire-02.wav"); + sound_shotty_fire[0] = snd_load_wav("data/audio/wp_shotty_fire-01.wav"); + sound_shotty_fire[1] = snd_load_wav("data/audio/wp_shotty_fire-02.wav"); + sound_shotty_fire[2] = snd_load_wav("data/audio/wp_shotty_fire-03.wav"); + sound_flump_launch[0] = snd_load_wav("data/audio/wp_flump_launch-01.wav"); + sound_flump_launch[1] = snd_load_wav("data/audio/wp_flump_launch-02.wav"); + sound_flump_launch[2] = snd_load_wav("data/audio/wp_flump_launch-03.wav"); + sound_hammer_swing[0] = snd_load_wav("data/audio/wp_hammer_swing-01.wav"); + sound_hammer_swing[1] = snd_load_wav("data/audio/wp_hammer_swing-02.wav"); + sound_hammer_swing[2] = snd_load_wav("data/audio/wp_hammer_swing-03.wav"); + sound_ninja_attack[0] = snd_load_wav("data/audio/wp_ninja_attack-01.wav"); + sound_ninja_attack[1] = snd_load_wav("data/audio/wp_ninja_attack-02.wav"); + sound_ninja_attack[2] = snd_load_wav("data/audio/wp_ninja_attack-03.wav"); + + sound_flump_explode[0] = snd_load_wav("data/audio/wp_flump_explo-01.wav"); + sound_flump_explode[1] = snd_load_wav("data/audio/wp_flump_explo-02.wav"); + sound_flump_explode[2] = snd_load_wav("data/audio/wp_flump_explo-03.wav"); + sound_ninja_hit[0] = snd_load_wav("data/audio/wp_ninja_hit-01.wav"); + sound_ninja_hit[1] = snd_load_wav("data/audio/wp_ninja_hit-02.wav"); + sound_ninja_hit[2] = snd_load_wav("data/audio/wp_ninja_hit-03.wav"); + sound_ninja_hit[3] = snd_load_wav("data/audio/wp_ninja_hit-04.wav"); + + sound_weapon_switch[0] = snd_load_wav("data/audio/wp_switch-01.wav"); + sound_weapon_switch[1] = snd_load_wav("data/audio/wp_switch-02.wav"); + sound_weapon_switch[2] = snd_load_wav("data/audio/wp_switch-03.wav"); + + sound_pain_short[0] = snd_load_wav("data/audio/vo_teefault_pain_short-01.wav"); + sound_pain_short[1] = snd_load_wav("data/audio/vo_teefault_pain_short-02.wav"); + sound_pain_short[2] = snd_load_wav("data/audio/vo_teefault_pain_short-03.wav"); + sound_pain_short[3] = snd_load_wav("data/audio/vo_teefault_pain_short-04.wav"); + sound_pain_short[4] = snd_load_wav("data/audio/vo_teefault_pain_short-05.wav"); + sound_pain_short[5] = snd_load_wav("data/audio/vo_teefault_pain_short-06.wav"); + sound_pain_short[6] = snd_load_wav("data/audio/vo_teefault_pain_short-07.wav"); + sound_pain_short[7] = snd_load_wav("data/audio/vo_teefault_pain_short-08.wav"); + sound_pain_short[8] = snd_load_wav("data/audio/vo_teefault_pain_short-09.wav"); + sound_pain_short[9] = snd_load_wav("data/audio/vo_teefault_pain_short-10.wav"); + sound_pain_short[10] = snd_load_wav("data/audio/vo_teefault_pain_short-11.wav"); + sound_pain_short[11] = snd_load_wav("data/audio/vo_teefault_pain_short-12.wav"); + + sound_pain_long[0] = snd_load_wav("data/audio/vo_teefault_pain_long-01.wav"); + sound_pain_long[1] = snd_load_wav("data/audio/vo_teefault_pain_long-02.wav"); + + sound_body_land[0] = snd_load_wav("data/audio/foley_land-01.wav"); + sound_body_land[1] = snd_load_wav("data/audio/foley_land-02.wav"); + sound_body_land[2] = snd_load_wav("data/audio/foley_land-03.wav"); + sound_body_land[3] = snd_load_wav("data/audio/foley_land-04.wav"); + sound_body_jump[0] = snd_load_wav("data/audio/foley_foot_left-01.wav"); + sound_body_jump[1] = snd_load_wav("data/audio/foley_foot_left-02.wav"); + sound_body_jump[2] = snd_load_wav("data/audio/foley_foot_left-03.wav"); + sound_body_jump[3] = snd_load_wav("data/audio/foley_foot_left-04.wav"); + sound_body_jump[4] = snd_load_wav("data/audio/foley_foot_right-01.wav"); + sound_body_jump[5] = snd_load_wav("data/audio/foley_foot_right-02.wav"); + sound_body_jump[6] = snd_load_wav("data/audio/foley_foot_right-03.wav"); + sound_body_jump[7] = snd_load_wav("data/audio/foley_foot_right-04.wav"); + + sound_body_splat[1] = snd_load_wav("data/audio/foley_body_splat-02.wav"); + sound_body_splat[2] = snd_load_wav("data/audio/foley_body_splat-03.wav"); + sound_body_splat[3] = snd_load_wav("data/audio/foley_body_splat-04.wav"); + + sound_spawn[0] = snd_load_wav("data/audio/vo_teefault_spawn-01.wav"); + sound_spawn[1] = snd_load_wav("data/audio/vo_teefault_spawn-02.wav"); + sound_spawn[2] = snd_load_wav("data/audio/vo_teefault_spawn-03.wav"); + sound_spawn[3] = snd_load_wav("data/audio/vo_teefault_spawn-04.wav"); + sound_spawn[4] = snd_load_wav("data/audio/vo_teefault_spawn-05.wav"); + sound_spawn[5] = snd_load_wav("data/audio/vo_teefault_spawn-06.wav"); + sound_spawn[6] = snd_load_wav("data/audio/vo_teefault_spawn-07.wav"); + + sound_tee_cry[0] = snd_load_wav("data/audio/vo_teefault_cry-01.wav"); + sound_tee_cry[1] = snd_load_wav("data/audio/vo_teefault_cry-02.wav"); + + //sound_hook_loop[0] = snd_load_wav("data/audio/hook_loop-01.wav"); + sound_hook_loop[0] = snd_load_wav("data/audio/hook_loop-02.wav"); + sound_hook_attach[0] = snd_load_wav("data/audio/hook_attach-01.wav"); + sound_hook_attach[1] = snd_load_wav("data/audio/hook_attach-02.wav"); + sound_hook_attach[2] = snd_load_wav("data/audio/hook_attach-03.wav"); + + poweruptexcoord[POWERUP_TYPE_HEALTH].x = 10; + poweruptexcoord[POWERUP_TYPE_HEALTH].y = 2; + poweruptexcoord[POWERUP_TYPE_HEALTH].w = 2; + poweruptexcoord[POWERUP_TYPE_HEALTH].h = 2; + + poweruptexcoord[POWERUP_TYPE_ARMOR].x = 12; + poweruptexcoord[POWERUP_TYPE_ARMOR].y = 2; + poweruptexcoord[POWERUP_TYPE_ARMOR].w = 2; + poweruptexcoord[POWERUP_TYPE_ARMOR].h = 2; + + poweruptexcoord[POWERUP_TYPE_WEAPON].x = 3; + poweruptexcoord[POWERUP_TYPE_WEAPON].y = 0; + poweruptexcoord[POWERUP_TYPE_WEAPON].w = 6; + poweruptexcoord[POWERUP_TYPE_WEAPON].h = 2; + + poweruptexcoord[POWERUP_TYPE_NINJA].x = 3; + poweruptexcoord[POWERUP_TYPE_NINJA].y = 10; + poweruptexcoord[POWERUP_TYPE_NINJA].w = 7; + poweruptexcoord[POWERUP_TYPE_NINJA].h = 2; + + poweruptexcoord[POWERUP_TYPE_TIMEFIELD].x = 3; + poweruptexcoord[POWERUP_TYPE_TIMEFIELD].y = 0; + poweruptexcoord[POWERUP_TYPE_TIMEFIELD].w = 6; + poweruptexcoord[POWERUP_TYPE_TIMEFIELD].h = 2; + + // Setup weapon cell coords + float sizemodifier = 1.0f; + weaponrenderparams[WEAPON_TYPE_GUN].sizex = 60.0f * sizemodifier; + weaponrenderparams[WEAPON_TYPE_GUN].sizey = 30.0f * sizemodifier; + weaponrenderparams[WEAPON_TYPE_GUN].offsetx = 32.0f; + weaponrenderparams[WEAPON_TYPE_GUN].offsety = 4.0f; + weapontexcoordcursor[WEAPON_TYPE_GUN].x = 0; + weapontexcoordcursor[WEAPON_TYPE_GUN].y = 4; + weapontexcoordcursor[WEAPON_TYPE_GUN].w = 2; + weapontexcoordcursor[WEAPON_TYPE_GUN].h = 2; + weapontexcoord[WEAPON_TYPE_GUN].x = 2; + weapontexcoord[WEAPON_TYPE_GUN].y = 4; + weapontexcoord[WEAPON_TYPE_GUN].w = 4; + weapontexcoord[WEAPON_TYPE_GUN].h = 2; + weaponprojtexcoord[WEAPON_TYPE_GUN].x = 6; + weaponprojtexcoord[WEAPON_TYPE_GUN].y = 4; + weaponprojtexcoord[WEAPON_TYPE_GUN].w = 2; + weaponprojtexcoord[WEAPON_TYPE_GUN].h = 2; + + nummuzzletex[WEAPON_TYPE_GUN] = 3; + muzzletexcoord[WEAPON_TYPE_GUN][0].x = 8; + muzzletexcoord[WEAPON_TYPE_GUN][0].y = 4; + muzzletexcoord[WEAPON_TYPE_GUN][0].w = 3; + muzzletexcoord[WEAPON_TYPE_GUN][0].h = 2; + muzzletexcoord[WEAPON_TYPE_GUN][1].x = 12; + muzzletexcoord[WEAPON_TYPE_GUN][1].y = 4; + muzzletexcoord[WEAPON_TYPE_GUN][1].w = 3; + muzzletexcoord[WEAPON_TYPE_GUN][1].h = 2; + muzzletexcoord[WEAPON_TYPE_GUN][2].x = 16; + muzzletexcoord[WEAPON_TYPE_GUN][2].y = 4; + muzzletexcoord[WEAPON_TYPE_GUN][2].w = 3; + muzzletexcoord[WEAPON_TYPE_GUN][2].h = 2; + + muzzleparams[WEAPON_TYPE_GUN].sizex = 60.0f * sizemodifier; + muzzleparams[WEAPON_TYPE_GUN].sizey = 40.0f * sizemodifier; + muzzleparams[WEAPON_TYPE_GUN].offsetx = 50.0f * sizemodifier; + muzzleparams[WEAPON_TYPE_GUN].offsety = 6.0f * sizemodifier; + + sizemodifier = 1.3f; + weaponrenderparams[WEAPON_TYPE_ROCKET].sizex = 70.0f * sizemodifier; + weaponrenderparams[WEAPON_TYPE_ROCKET].sizey = 20.0f * sizemodifier; + weaponrenderparams[WEAPON_TYPE_ROCKET].offsetx = 24.0f; + weaponrenderparams[WEAPON_TYPE_ROCKET].offsety = -2.0f; + weapontexcoordcursor[WEAPON_TYPE_ROCKET].x = 0; + weapontexcoordcursor[WEAPON_TYPE_ROCKET].y = 8; + weapontexcoordcursor[WEAPON_TYPE_ROCKET].w = 2; + weapontexcoordcursor[WEAPON_TYPE_ROCKET].h = 2; + weapontexcoord[WEAPON_TYPE_ROCKET].x = 2; + weapontexcoord[WEAPON_TYPE_ROCKET].y = 8; + weapontexcoord[WEAPON_TYPE_ROCKET].w = 7; + weapontexcoord[WEAPON_TYPE_ROCKET].h = 2; + weaponprojtexcoord[WEAPON_TYPE_ROCKET].x = 10; + weaponprojtexcoord[WEAPON_TYPE_ROCKET].y = 8; + weaponprojtexcoord[WEAPON_TYPE_ROCKET].w = 2; + weaponprojtexcoord[WEAPON_TYPE_ROCKET].h = 2; + + /*weaponrenderparams[WEAPON_TYPE_SNIPER].sizex = 60.0f; + weaponrenderparams[WEAPON_TYPE_SNIPER].sizey = 20.0f; + weaponrenderparams[WEAPON_TYPE_SNIPER].offsetx = 16.0f; + weaponrenderparams[WEAPON_TYPE_SNIPER].offsety = 4.0f; + weapontexcoordcursor[WEAPON_TYPE_SNIPER].x = 0; + weapontexcoordcursor[WEAPON_TYPE_SNIPER].y = 6; + weapontexcoordcursor[WEAPON_TYPE_SNIPER].w = 2; + weapontexcoordcursor[WEAPON_TYPE_SNIPER].h = 2; + weapontexcoord[WEAPON_TYPE_SNIPER].x = 3; + weapontexcoord[WEAPON_TYPE_SNIPER].y = 6; + weapontexcoord[WEAPON_TYPE_SNIPER].w = 6; + weapontexcoord[WEAPON_TYPE_SNIPER].h = 2; + weaponprojtexcoord[WEAPON_TYPE_SNIPER].x = 10; + weaponprojtexcoord[WEAPON_TYPE_SNIPER].y = 6; + weaponprojtexcoord[WEAPON_TYPE_SNIPER].w = 1; + weaponprojtexcoord[WEAPON_TYPE_SNIPER].h = 1;*/ + + weaponrenderparams[WEAPON_TYPE_SHOTGUN].sizex = 80.0f * sizemodifier; + weaponrenderparams[WEAPON_TYPE_SHOTGUN].sizey = 20.0f * sizemodifier; + weaponrenderparams[WEAPON_TYPE_SHOTGUN].offsetx = 24.0f; + weaponrenderparams[WEAPON_TYPE_SHOTGUN].offsety = -2.0f; + weapontexcoordcursor[WEAPON_TYPE_SHOTGUN].x = 0; + weapontexcoordcursor[WEAPON_TYPE_SHOTGUN].y = 6; + weapontexcoordcursor[WEAPON_TYPE_SHOTGUN].w = 2; + weapontexcoordcursor[WEAPON_TYPE_SHOTGUN].h = 2; + weapontexcoord[WEAPON_TYPE_SHOTGUN].x = 2; + weapontexcoord[WEAPON_TYPE_SHOTGUN].y = 6; + weapontexcoord[WEAPON_TYPE_SHOTGUN].w = 8; + weapontexcoord[WEAPON_TYPE_SHOTGUN].h = 2; + weaponprojtexcoord[WEAPON_TYPE_SHOTGUN].x = 10; + weaponprojtexcoord[WEAPON_TYPE_SHOTGUN].y = 6; + weaponprojtexcoord[WEAPON_TYPE_SHOTGUN].w = 2; + weaponprojtexcoord[WEAPON_TYPE_SHOTGUN].h = 2; + + nummuzzletex[WEAPON_TYPE_SHOTGUN] = 3; + muzzletexcoord[WEAPON_TYPE_SHOTGUN][0].x = 12; + muzzletexcoord[WEAPON_TYPE_SHOTGUN][0].y = 6; + muzzletexcoord[WEAPON_TYPE_SHOTGUN][0].w = 3; + muzzletexcoord[WEAPON_TYPE_SHOTGUN][0].h = 2; + muzzletexcoord[WEAPON_TYPE_SHOTGUN][1].x = 16; + muzzletexcoord[WEAPON_TYPE_SHOTGUN][1].y = 6; + muzzletexcoord[WEAPON_TYPE_SHOTGUN][1].w = 3; + muzzletexcoord[WEAPON_TYPE_SHOTGUN][1].h = 2; + muzzletexcoord[WEAPON_TYPE_SHOTGUN][2].x = 20; + muzzletexcoord[WEAPON_TYPE_SHOTGUN][2].y = 6; + muzzletexcoord[WEAPON_TYPE_SHOTGUN][2].w = 3; + muzzletexcoord[WEAPON_TYPE_SHOTGUN][2].h = 2; + + muzzleparams[WEAPON_TYPE_SHOTGUN].sizex = 60.0f * sizemodifier; + muzzleparams[WEAPON_TYPE_SHOTGUN].sizey = 40.0f * sizemodifier; + muzzleparams[WEAPON_TYPE_SHOTGUN].offsetx = 50.0f * sizemodifier; + muzzleparams[WEAPON_TYPE_SHOTGUN].offsety = 6.0f * sizemodifier; + + + + weaponrenderparams[WEAPON_TYPE_MELEE].sizex = 60.0f * sizemodifier; + weaponrenderparams[WEAPON_TYPE_MELEE].sizey = 50.0f * sizemodifier; + weaponrenderparams[WEAPON_TYPE_MELEE].offsetx = 20.0f; + weaponrenderparams[WEAPON_TYPE_MELEE].offsety = -4.0f; + weapontexcoordcursor[WEAPON_TYPE_MELEE].x = 0; + weapontexcoordcursor[WEAPON_TYPE_MELEE].y = 0; + weapontexcoordcursor[WEAPON_TYPE_MELEE].w = 2; + weapontexcoordcursor[WEAPON_TYPE_MELEE].h = 2; + weapontexcoord[WEAPON_TYPE_MELEE].x = 2; + weapontexcoord[WEAPON_TYPE_MELEE].y = 1; + weapontexcoord[WEAPON_TYPE_MELEE].w = 4; + weapontexcoord[WEAPON_TYPE_MELEE].h = 3; + weaponprojtexcoord[WEAPON_TYPE_MELEE].x = 0; + weaponprojtexcoord[WEAPON_TYPE_MELEE].y = 0; + weaponprojtexcoord[WEAPON_TYPE_MELEE].w = 0; + weaponprojtexcoord[WEAPON_TYPE_MELEE].h = 0; + + + // MODIFIERS + sizemodifier = 2.0; + modifierrenderparams[MODIFIER_TYPE_NINJA].sizex = 60.0f * sizemodifier; + modifierrenderparams[MODIFIER_TYPE_NINJA].sizey = 20.0f * sizemodifier; + modifierrenderparams[MODIFIER_TYPE_NINJA].offsetx = 20.0f; + modifierrenderparams[MODIFIER_TYPE_NINJA].offsety = 4.0f; + modifiertexcoord[MODIFIER_TYPE_NINJA].x = 2; + modifiertexcoord[MODIFIER_TYPE_NINJA].y = 10; + modifiertexcoord[MODIFIER_TYPE_NINJA].w = 7; + modifiertexcoord[MODIFIER_TYPE_NINJA].h = 2; + modifiertexcoordcursor[MODIFIER_TYPE_NINJA].x = 0; + modifiertexcoordcursor[MODIFIER_TYPE_NINJA].y = 10; + modifiertexcoordcursor[MODIFIER_TYPE_NINJA].w = 2; + modifiertexcoordcursor[MODIFIER_TYPE_NINJA].h = 2; + + modifierrenderparams[MODIFIER_TYPE_TIMEFIELD].sizex = 60.0f * sizemodifier; + modifierrenderparams[MODIFIER_TYPE_TIMEFIELD].sizey = 20.0f * sizemodifier; + modifierrenderparams[MODIFIER_TYPE_TIMEFIELD].offsetx = 16.0f; + modifierrenderparams[MODIFIER_TYPE_TIMEFIELD].offsety = 4.0f; + modifiertexcoord[MODIFIER_TYPE_TIMEFIELD].x = 0; + modifiertexcoord[MODIFIER_TYPE_TIMEFIELD].y = 0; + modifiertexcoord[MODIFIER_TYPE_TIMEFIELD].w = 0; + modifiertexcoord[MODIFIER_TYPE_TIMEFIELD].h = 0; + + stars[0].x = 0; + stars[0].y = 0; + stars[0].w = 2; + stars[0].h = 2; + + stars[1].x = 0; + stars[1].y = 2; + stars[1].w = 2; + stars[1].h = 2; + + particlecolors[0].x = 0.7f; + particlecolors[0].y = 0.7f; + particlecolors[0].z = 0.7f; + particlecolors[0].w = 1.0f; + particlestexcoord[0].x = 2; + particlestexcoord[0].y = 0; + particlestexcoord[0].w = 2; + particlestexcoord[0].h = 2; + particlecolors[1].x = 1.0f; + particlecolors[1].y = 1.0f; + particlecolors[1].z = 1.0f; + particlecolors[1].w = 1.0f; + particlestexcoord[1].x = 4; + particlestexcoord[1].y = 0; + particlestexcoord[1].w = 2; + particlestexcoord[1].h = 2; + particlecolors[2].x = 0.8f; + particlecolors[2].y = 0.8f; + particlecolors[2].z = 0.8f; + particlecolors[2].w = 1.0f; + particlestexcoord[2].x = 6; + particlestexcoord[2].y = 0; + particlestexcoord[2].w = 2; + particlestexcoord[2].h = 2; + particlecolors[3].x = 0.988f; + particlecolors[3].y = 1.0f; + particlecolors[3].z = 0.16f; + particlecolors[3].w = 1.0f; + particlestexcoord[3].x = 8; + particlestexcoord[3].y = 0; + particlestexcoord[3].w = 2; + particlestexcoord[3].h = 2; + particlecolors[4].x = 1.0f; + particlecolors[4].y = 1.0f; + particlecolors[4].z = 1.0f; + particlecolors[4].w = 1.0f; + particlestexcoord[4].x = 10; + particlestexcoord[4].y = 0; + particlestexcoord[4].w = 2; + particlestexcoord[4].h = 2; + particlecolors[5].x = 0.6f; + particlecolors[5].y = 0.6f; + particlecolors[5].z = 0.6f; + particlecolors[5].w = 1.0f; + particlestexcoord[5].x = 2; + particlestexcoord[5].y = 2; + particlestexcoord[5].w = 2; + particlestexcoord[5].h = 2; + particlecolors[6].x = 1.0f; + particlecolors[6].y = 1.0f; + particlecolors[6].z = 1.0f; + particlecolors[6].w = 1.0f; + particlestexcoord[6].x = 4; + particlestexcoord[6].y = 2; + particlestexcoord[6].w = 2; + particlestexcoord[6].h = 2; + particlecolors[5].x = 0.9f; + particlecolors[5].y = 0.9f; + particlecolors[5].z = 0.9f; + particlecolors[5].w = 1.0f; + particlestexcoord[7].x = 6; + particlestexcoord[7].y = 2; + particlestexcoord[7].w = 2; + particlestexcoord[7].h = 2; + particlecolors[8].x = 1.0f; + particlecolors[8].y = 1.0f; + particlecolors[8].z = 1.0f; + particlecolors[8].w = 1.0f; + particlestexcoord[8].x = 8; + particlestexcoord[8].y = 2; + particlestexcoord[8].w = 2; + particlestexcoord[8].h = 2; + lifemodifier[0] = 0.5f; + lifemodifier[1] = 0.5f; + lifemodifier[2] = 0.5f; + lifemodifier[3] = 0.7f; + lifemodifier[4] = 0.7f; + lifemodifier[5] = 1.0f; + lifemodifier[6] = 1.0f; + lifemodifier[7] = 1.5f; + lifemodifier[8] = 0.4f; + + chaintexcoord.x = 2; + chaintexcoord.y = 0; + chaintexcoord.w = 1; + chaintexcoord.h = 1; + + chainheadtexcoord.x = 3; + chainheadtexcoord.y = 0; + chainheadtexcoord.w = 2; + chainheadtexcoord.h = 1; + + + // anims + anim::setup_hammer(hammeranim); + anim::setup_ninja(ninjaanim); + + for (int i = 0; i < NUMHADOKENS; i++) + { + hadoken[i].x = 1; + hadoken[i].y = 12; + hadoken[i].w = 7; + hadoken[i].h = 4; + hadokenparams[i].sizex = 0.0f; + hadokenparams[i].sizey = 0.0f; + hadokenparams[i].offsetx = 0.0f; + hadokenparams[i].offsety = 0.0f;//-hadokenparams[0].sizey * 0.15f; + } + + // hadoken + hadoken[0].x = 1; + hadoken[0].y = 12; + hadoken[0].w = 7; + hadoken[0].h = 4; + hadokenparams[0].sizex = 70.0f * 2.5f; + hadokenparams[0].sizey = 40.0f * 2.5f; + hadokenparams[0].offsetx = -60.0f; + hadokenparams[0].offsety = 0;//-hadokenparams[0].sizey * 0.15f; + + hadoken[2].x = 8; + hadoken[2].y = 12; + hadoken[2].w = 8; + hadoken[2].h = 4; + hadokenparams[2].sizex = 80.0f * 2.5f; + hadokenparams[2].sizey = 40.0f * 2.5f; + hadokenparams[2].offsetx = -60.0f; + hadokenparams[2].offsety = 0;//-hadokenparams[1].sizey * 0.5f; + + hadoken[4].x = 17; + hadoken[4].y = 12; + hadoken[4].w = 7; + hadoken[4].h = 4; + hadokenparams[4].sizex = 70.0f * 2.5f; + hadokenparams[4].sizey = 40.0f * 2.5f; + hadokenparams[4].offsetx = -60.0f; + hadokenparams[4].offsety = 0;//-hadokenparams[2].sizey * 0.5f; + + // 0 = outline, 1 = body + body[0].x = 2; + body[0].y = 0; + body[0].w = 2; + body[0].h = 2; + body[1].x = 0; + body[1].y = 0; + body[1].w = 2; + body[1].h = 2; + + feet[0].x = 4; + feet[0].y = 1; + feet[0].w = 1; + feet[0].h = 0.5; + feet[1].x = 4; + feet[1].y = 1.52; + feet[1].w = 1; + feet[1].h = 0.48; + + leye.x = 5; + leye.y = 1; + leye.w = 0.5; + leye.h = 0.5; + + reye.x = 5; + reye.y = 1.0; + reye.w = 0.5; + reye.h = 0.5; +} + +void modc_entergame() +{ + col_init(32); + img_init(); + tilemap_init(); +} + +void modc_shutdown() +{ +} + +void modc_newsnapshot() +{ + int num = snap_num_items(SNAP_CURRENT); + for(int i = 0; i < num; i++) + { + snap_item item; + void *data = snap_get_item(SNAP_CURRENT, i, &item); + + if(item.type == EVENT_HEALTHMOD) + { + ev_healthmod *ev = (ev_healthmod *)data; + healthmods.create(vec2(ev->x, ev->y), ev->amount); + } + else if(item.type == EVENT_EXPLOSION) + { + ev_explosion *ev = (ev_explosion *)data; + vec2 p(ev->x, ev->y); + + // center explosion + temp_system.new_particle(p, vec2(0,0), 0.3f, 96.0f, 0, 0.95f); + temp_system.new_particle(p, vec2(0,0), 0.3f, 64.0f, 0, 0.95f); + temp_system.new_particle(p, vec2(0,0), 0.3f, 32.0f, 0, 0.95f); + temp_system.new_particle(p, vec2(0,0), 0.3f, 16.0f, 0, 0.95f); + + for(int i = 0; i < 16; i++) + { + vec2 v = normalize(vec2(frandom()-0.5f, frandom()-0.5f))*(128.0f+frandom()*128.0f); + temp_system.new_particle(p, v, 0.2f+0.25f*frandom(), 16.0f, 0, 0.985f); + } + + for(int i = 0; i < 16; i++) + { + vec2 v = normalize(vec2(frandom()-0.5f, frandom()-0.5f))*(256.0f+frandom()*512.0f); + temp_system.new_particle(p, v, 0.2f+0.25f*frandom(), 16.0f, 128.0f, 0.985f); + } + + for(int i = 0; i < 64; i++) + { + vec2 v = normalize(vec2(frandom()-0.5f, frandom()-0.5f))*(frandom()*256.0f); + temp_system.new_particle(p, v, 0.2f+0.25f*frandom(), 24.0f, 128.0f, 0.985f); + } + } + else if(item.type == EVENT_SMOKE) + { + ev_explosion *ev = (ev_explosion *)data; + vec2 p(ev->x, ev->y); + + // center explosion + vec2 v = normalize(vec2(frandom()-0.5f, -frandom()))*(32.0f+frandom()*32.0f); + temp_system.new_particle(p, v, 1.2f, 64.0f, 0, 0.95f); + v = normalize(vec2(frandom()-0.5f, -frandom()))*(128.0f+frandom()*128.0f); + temp_system.new_particle(p, v, 1.2f, 32.0f, 0, 0.95f); + v = normalize(vec2(frandom()-0.5f, -frandom()))*(128.0f+frandom()*128.0f); + temp_system.new_particle(p, v, 1.2f, 16.0f, 0, 0.95f); + + for(int i = 0; i < 8; i++) + { + vec2 v = normalize(vec2(frandom()-0.5f, frandom()-0.5f))*(64.0f+frandom()*64.0f); + temp_system.new_particle(p, v, 0.5f+0.5f*frandom(), 16.0f, 0, 0.985f); + } + + for(int i = 0; i < 8; i++) + { + vec2 v = normalize(vec2(frandom()-0.5f, frandom()-0.5f))*(128.0f+frandom()*256.0f); + temp_system.new_particle(p, v, 0.5f+0.5f*frandom(), 16.0f, 128.0f, 0.985f); + } + } + else if(item.type == EVENT_SOUND) + { + ev_sound *ev = (ev_sound *)data; + vec2 p(ev->x, ev->y); + int sound = (ev->sound & SOUND_MASK); + bool bstartloop = (ev->sound & SOUND_LOOPFLAG_STARTLOOP) != 0; + bool bstoploop = (ev->sound & SOUND_LOOPFLAG_STOPLOOP) != 0; + float vol, pan; + sound_vol_pan(p, &vol, &pan); + + switch(sound) + { + + // FIRE! + case SOUND_FIRE_GUN: + sound_gun_fire.play_random(volume_gun*vol, pan); + break; + case SOUND_FIRE_SHOTGUN: + sound_shotty_fire.play_random(volume_gun*vol, pan); + break; + case SOUND_FIRE_ROCKET: + sound_flump_launch.play_random(volume_gun*vol, pan); + break; + case SOUND_FIRE_MELEE: + sound_hammer_swing.play_random(volume_gun*vol, pan); + break; + case SOUND_FIRE_NINJA: + sound_ninja_attack.play_random(volume_gun*vol, pan); + break; + + // IMPACT + case SOUND_IMPACT_PROJECTILE_GUN: + break; + case SOUND_IMPACT_PROJECTILE_SHOTGUN: + break; + case SOUND_IMPACT_PROJECTILE_ROCKET: + sound_flump_explode.play_random(volume_hit*vol, pan); + break; + + // PLAYER + case SOUND_PLAYER_JUMP: + sound_body_jump.play_random(volume_tee*vol, pan); + break; + case SOUND_PLAYER_HURT_SHORT: + sound_pain_short.play_random(volume_tee*vol, pan); + break; + case SOUND_PLAYER_HURT_LONG: + sound_pain_long.play_random(volume_tee*vol, pan); + break; + case SOUND_PLAYER_SPAWN: + sound_spawn.play_random(volume_tee*vol, pan); + break; + case SOUND_PLAYER_CHAIN_LOOP: + sound_hook_loop.play_random(volume_gun*vol, pan); + break; + case SOUND_PLAYER_CHAIN_IMPACT: + sound_hook_attach.play_random(volume_gun*vol, pan); + break; + case SOUND_PLAYER_IMPACT: + sound_body_land.play_random(volume_hit*vol, pan); + break; + case SOUND_PLAYER_IMPACT_NINJA: + sound_ninja_hit.play_random(volume_hit*vol, pan); + break; + case SOUND_PLAYER_DIE: + sound_body_splat.play_random(volume_tee*vol, pan); + break; + case SOUND_PLAYER_SWITCHWEAPON: + sound_weapon_switch.play_random(volume_gun*vol, pan); + break; + case SOUND_PLAYER_EQUIP: + break; + case SOUND_PLAYER_LAND: + sound_body_land.play_random(volume_tee*vol, pan); + break; + } + } + } +} + +static void render_projectile(obj_projectile *prev, obj_projectile *current) +{ + gfx_texture_set(texture_weapon); + gfx_quads_begin(); + cell_select_ex(numcellsx,numcellsy,weaponprojtexcoord[current->type].x, weaponprojtexcoord[current->type].y, weaponprojtexcoord[current->type].w, weaponprojtexcoord[current->type].h); + vec2 vel(current->vx, current->vy); + + // TODO: interpolare angle aswell + if(length(vel) > 0.00001f) + gfx_quads_setrotation(get_angle(vel)); + else + gfx_quads_setrotation(0); + + vec2 pos = mix(vec2(prev->x, prev->y), vec2(current->x, current->y), snap_intratick()); + gfx_quads_draw(pos.x, pos.y,32,32); + gfx_quads_setrotation(0); + gfx_quads_end(); +} + +static void render_powerup(obj_powerup *prev, obj_powerup *current) +{ + //dbg_msg("client", "rendering powerup at %d,%d", current->x, current->y); + + gfx_texture_set(texture_weapon); + gfx_quads_begin(); + float angle = 0.0f; + float sizex = 64.0f; + float sizey = 64.0f; + if (current->type == POWERUP_TYPE_WEAPON) + { + angle = -0.25f * pi * 2.0f; + cell_select_ex(numcellsx,numcellsy,weapontexcoord[current->subtype].x, weapontexcoord[current->subtype].y, weapontexcoord[current->subtype].w, weapontexcoord[current->subtype].h); + sizex = weaponrenderparams[current->subtype].sizex; + sizey = weaponrenderparams[current->subtype].sizey; + } + else + cell_select_ex(numcellsx,numcellsy,poweruptexcoord[current->type].x, poweruptexcoord[current->type].y, poweruptexcoord[current->type].w, poweruptexcoord[current->type].h); + vec2 vel(current->vx, current->vy); + + gfx_quads_setrotation(angle); + // TODO: interpolare angle aswell + /*if(length(vel) > 0.00001f) + gfx_quads_setrotation(get_angle(vel)); + else + gfx_quads_setrotation(0);*/ + + vec2 pos = mix(vec2(prev->x, prev->y), vec2(current->x, current->y), snap_intratick()); + float offset = pos.y/32.0f + pos.x/32.0f; + gfx_quads_draw(pos.x+cosf(client_localtime()*2.0f+offset)*2.5f, pos.y+sinf(client_localtime()*2.0f+offset)*2.5f,sizex * 0.65f,sizey * 0.65f); + gfx_quads_setrotation(0); + gfx_quads_end(); +} + +float getmeleeangle(vec2 direction, obj_player* prev, obj_player* player) +{ + vec2 meleedir(0.53, -0.84); + meleedir = normalize(meleedir); + vec2 meleedirattack(0.95, -0.3); + meleedirattack = normalize(meleedirattack); + + if(direction.x < 0) + { + meleedir.x = -meleedir.x; + meleedirattack.x = -meleedirattack.x; + } + + // 0 -> visualtimeattack go to end pose, (len - visualime) -> go back to normal pose + + float angle = get_angle(meleedir); + if (prev->attackticks) + { + float angleattack = get_angle(meleedirattack); + int phase1tick = (player->attacklen - player->attackticks); + if (phase1tick < player->visualtimeattack) + { + float intratick = snap_intratick(); + float t = ((((float)phase1tick) + intratick)/(float)player->visualtimeattack); + angle = LERP(angle, angleattack, min(1.0f,max(0.0f,t))); + } + else + { + // go back to normal pose + int phase2tick = (player->attacklen - player->visualtimeattack - player->attackticks); + float intratick = snap_intratick(); + float t = ((((float)phase2tick) + intratick)/(float)player->visualtimeattack); + angle = LERP(angleattack, angle, min(1.0f,max(0.0f,t))); + } + } + /*if (prev->attackticks && !player->attackticks) + { + // blend back to normal + float angleattack = get_angle(meleedirattack); + angle = LERP(angleattack, angle, min(1.0f,max(0.0f,snap_intratick()))); + } + else if (player->attackticks) + { + float angleattack = get_angle(meleedirattack); + float intratick = snap_intratick(); + float t = ((((float)player->attackticks) - intratick)/(float)player->attacklen); + angle = LERP(angleattack, angle, min(1.0f,max(0.0f,t))); + }*/ + + return angle; +} + +float gethammereangle(vec2 direction, obj_player* prev, obj_player* player) +{ + float t = 0.0f; + if (prev->attackticks) + t = 1.0f - ((((float)player->attackticks) - snap_intratick())/(float)player->attacklen); + + vec2 pos; + float angle = 0.0f; + hammeranim.evalanim(t,pos,angle); + if(direction.x < 0) + angle = pi -angle;// + ; + //dbg_msg("anim", "Time: %f", t); + return angle; +} + +float getninjaangle(vec2 direction, obj_player* prev, obj_player* player) +{ + float t = 0.0f; + if (prev->attackticks) + t = 1.0f - ((((float)player->attackticks) - snap_intratick())/(float)player->attacklen); + + vec2 pos; + float angle = 0.0f; + ninjaanim.evalanim(t,pos,angle); + if(direction.x < 0) + angle = pi -angle;// + ; + //dbg_msg("anim", "Time: %f", t); + return angle; +} + + +float getrecoil(obj_player* prev, obj_player* player) +{ + // attack = -10 + float recoil = 0.0f; + if (prev->attackticks) + { + float attackrecoil = recoils[player->weapon]; + int phase1tick = (player->attacklen - player->attackticks); + if (phase1tick < player->visualtimeattack) + { + float intratick = snap_intratick(); + float t = ((((float)phase1tick) + intratick)/(float)player->visualtimeattack); + recoil = LERP(0, attackrecoil, min(1.0f,max(0.0f,t))); + } + else + { + // go back to normal pose + int phase2tick = (player->attacklen - player->visualtimeattack - player->attackticks); + float intratick = snap_intratick(); + float t = ((((float)phase2tick) + intratick)/(float)(player->attacklen - player->visualtimeattack)); + recoil = LERP(attackrecoil, 0.0f, min(1.0f,max(0.0f,t))); + } + } + return recoil; +} + +static void render_player(obj_player *prev, obj_player *player) +{ + vec2 direction = get_direction(player->angle); + float angle = player->angle/256.0f; + vec2 position = mix(vec2(prev->x, prev->y), vec2(player->x, player->y), snap_intratick()); + + // draw hook + if(player->hook_active) + { + gfx_texture_set(texture_weapon); + gfx_quads_begin(); + //gfx_quads_begin(); + + vec2 pos = position; + + vec2 hook_pos = mix(vec2(prev->hook_x, prev->hook_y), vec2(player->hook_x, player->hook_y), snap_intratick()); + + float d = distance(pos, hook_pos); + vec2 dir = normalize(pos-hook_pos); + + gfx_quads_setrotation(get_angle(dir)+pi); + + // render head + cell_select_ex(numcellsx,numcellsy, chainheadtexcoord.x,chainheadtexcoord.y, chainheadtexcoord.w, chainheadtexcoord.h); + gfx_quads_draw(hook_pos.x, hook_pos.y, 24,16); + + // render chain + cell_select_ex(numcellsx,numcellsy, chaintexcoord.x, chaintexcoord.y, chaintexcoord.w, chaintexcoord.h); + for(float f = 24; f < d; f += 24) + { + vec2 p = hook_pos + dir*f; + gfx_quads_draw(p.x, p.y,24,16); + } + + gfx_quads_setrotation(0); + gfx_quads_end(); + } + + // draw gun + { + gfx_texture_set(texture_weapon); + gfx_quads_begin(); + gfx_quads_setrotation(angle); + + if (player->modifier & (1 << MODIFIER_TYPE_NINJA)) + { + float playerangle = angle; + // render NINJA!!! (0.53, 0.84) when idle to -> (0.95, 0.3) at the end of attack + if(direction.x < 0) + cell_select_ex_flip_y(numcellsx, numcellsy, modifiertexcoord[MODIFIER_TYPE_NINJA].x, modifiertexcoord[MODIFIER_TYPE_NINJA].y, modifiertexcoord[MODIFIER_TYPE_NINJA].w, modifiertexcoord[MODIFIER_TYPE_NINJA].h); + else + cell_select_ex(numcellsx, numcellsy, modifiertexcoord[MODIFIER_TYPE_NINJA].x, modifiertexcoord[MODIFIER_TYPE_NINJA].y, modifiertexcoord[MODIFIER_TYPE_NINJA].w, modifiertexcoord[MODIFIER_TYPE_NINJA].h); + + angle = getninjaangle(direction, prev, player);//getmeleeangle(direction, prev, player); + vec2 ninjadir = get_direction(angle * 256.0f); + gfx_quads_setrotation(angle); + vec2 p = position + vec2(0,modifierrenderparams[MODIFIER_TYPE_NINJA].offsety)+ ninjadir * modifierrenderparams[MODIFIER_TYPE_NINJA].offsetx; + // if attack is active hold it differently and draw speedlines behind us? + gfx_quads_draw(p.x,p.y/*+bob*/,modifierrenderparams[MODIFIER_TYPE_NINJA].sizex, modifierrenderparams[MODIFIER_TYPE_NINJA].sizey); + + if ((player->attacklen - player->attackticks) <= (SERVER_TICK_SPEED / 5)) + { + gfx_quads_setrotation(playerangle); + int ihadoken = rand() % NUMHADOKENS; + cell_select_ex(numcellsx, numcellsy, hadoken[ihadoken].x, hadoken[ihadoken].y, hadoken[ihadoken].w, hadoken[ihadoken].h); + vec2 p = position + vec2(0,hadokenparams[ihadoken].offsety)+ direction * hadokenparams[ihadoken].offsetx; + gfx_quads_draw(p.x,p.y/*+bob*/,hadokenparams[ihadoken].sizex, hadokenparams[ihadoken].sizey); + } + } + else + { + // normal weapons + if(direction.x < 0) + cell_select_ex_flip_y(numcellsx, numcellsy, weapontexcoord[player->weapon].x, weapontexcoord[player->weapon].y, weapontexcoord[player->weapon].w, weapontexcoord[player->weapon].h); + else + cell_select_ex(numcellsx, numcellsy, weapontexcoord[player->weapon].x, weapontexcoord[player->weapon].y, weapontexcoord[player->weapon].w, weapontexcoord[player->weapon].h); + + vec2 dir = direction; + float recoil = 0.0f; + if (player->weapon == WEAPON_TYPE_MELEE) + { + // if attack is under way, bash stuffs + //angle = getmeleeangle(direction, prev, player); + angle = gethammereangle(direction, prev, player); + gfx_quads_setrotation(angle); + dir = get_direction(angle * 256.0f); + } + else + { + recoil = getrecoil(prev, player); + } + + vec2 p = position + vec2(0,weaponrenderparams[player->weapon].offsety) + dir * weaponrenderparams[player->weapon].offsetx - dir * recoil; + gfx_quads_draw(p.x,p.y/*+bob*/,weaponrenderparams[player->weapon].sizex, weaponrenderparams[player->weapon].sizey); + // draw muzzleflare + if (player->weapon == WEAPON_TYPE_GUN || player->weapon == WEAPON_TYPE_SHOTGUN) + { + // check if we're firing stuff + if (true)///prev->attackticks) + { + float alpha = 0.0f; + int phase1tick = (player->attacklen - player->attackticks); + if (phase1tick < (player->visualtimeattack + 3)) + { + float intratick = snap_intratick(); + float t = ((((float)phase1tick) + intratick)/(float)player->visualtimeattack); + alpha = LERP(2.0, 0.0f, min(1.0f,max(0.0f,t))); + } + + if (alpha > 0.0f) + { + float offsety = -muzzleparams[player->weapon].offsety; + int itex = rand() % nummuzzletex[player->weapon]; + if(direction.x < 0) + { + offsety = -offsety; + cell_select_ex_flip_y(numcellsx, numcellsy, muzzletexcoord[player->weapon][itex].x, muzzletexcoord[player->weapon][itex].y, muzzletexcoord[player->weapon][itex].w, muzzletexcoord[player->weapon][itex].h); + } + else + cell_select_ex(numcellsx, numcellsy, muzzletexcoord[player->weapon][itex].x, muzzletexcoord[player->weapon][itex].y, muzzletexcoord[player->weapon][itex].w, muzzletexcoord[player->weapon][itex].h); + + gfx_quads_setcolor(1.0f,1.0f,1.0f,alpha); + vec2 diry(-dir.y,dir.x); + p += dir * muzzleparams[player->weapon].offsetx + diry * offsety; + gfx_quads_draw(p.x,p.y/*+bob*/,muzzleparams[player->weapon].sizex, muzzleparams[player->weapon].sizey); + } + } + } + } + /*else + { + // minigun + if(direction.x < 0) + cell_select_flip_y(4,4,8,2); + else + cell_select(4,4,8,2); + vec2 p = position + vec2(0,3); + gfx_quads_draw(p.x,p.y,8*8,8*2); + }*/ + + gfx_quads_setrotation(0); + gfx_quads_end(); + } + + + gfx_texture_set(texture_char_default); + gfx_quads_begin(); + + float bob = 0; + + // draw foots + const float cyclelength = 128.0f; + const float steplength = 26; + const float lift = 4.0f; + bool stationary = player->vx < 1 && player->vx > -1; + bool inair = col_check_point(player->x, player->y+16) == 0; + + for(int p = 0; p < 2; p++) + { + // first pass we draw the outline + // second pass we draw the filling + + //int v_offset = p?0:5; + int outline = p;// ? 1 : 0; + float offsety = charids[player->clientid % 16] * 2.0f; + + for(int f = 0; f < 2; f++) + { + float basesize = 10.0f; + if(f == 1) + { + // draw body + float t = fmod(position.x, cyclelength/2)/(cyclelength/2); + bob = -sinf(pow(t,2)*pi) * 3; + cell_select_ex(charnumcellsx,charnumcellsy, body[outline].x,body[outline].y + offsety,body[outline].w,body[outline].h); + //cell_select_ex(16,16, 0,0+v_offset,4,4); + //const float size = 64.0f; + if(stationary || inair) + bob = 0; + gfx_quads_draw(position.x, position.y-5+bob, 4*basesize, 4*basesize); + + // draw eyes + if(p == 1) + { + //cell_select_ex(16,16, 8,3,1,1); + vec2 md = get_direction(player->angle); + float mouse_dir_x = md.x; + float mouse_dir_y = md.y; + + // normal + cell_select_ex(charnumcellsx,charnumcellsy, leye.x,leye.y + offsety,leye.w,leye.h); + gfx_quads_draw(position.x-4+mouse_dir_x*4, position.y-8+mouse_dir_y*3+bob, basesize, basesize); + cell_select_ex(charnumcellsx,charnumcellsy, reye.x,reye.y + offsety,reye.w,reye.h); + gfx_quads_draw(position.x+4+mouse_dir_x*4, position.y-8+mouse_dir_y*3+bob, basesize, basesize); + } + } + + // draw feet + //cell_select_ex(16,16, 5,2+v_offset, 2,2); + cell_select_ex(charnumcellsx,charnumcellsy, feet[outline].x,feet[outline].y + offsety, feet[outline].w,feet[outline].h); + float w = basesize*2.5f; + float h = basesize*1.425f; + if(inair) + { + float r = 0.0f; + if(player->vy < 0.0f) + r = player->vy/3.0f; + else + r = player->vy/15.0f; + + // clamp the rotation + if(r > 0.5f) r = 0.5f; + if(r < -0.5f) r = -0.5f; + + if(player->vx > 0.0f) + r *= -1.0f; + gfx_quads_setrotation(r); + gfx_quads_drawTL(position.x-4+f*7-w/2, position.y+16 - h, w, h); + gfx_quads_setrotation(0); + } + else if(stationary) + { + // stationary + gfx_quads_drawTL(position.x-7+f*14-w/2, position.y+16 - h, w, h); + } + else + { + /* + The walk cycle, 2 parts + + 111 + 1 1 + 2 1 + 2 1 + 2222221 + GROUND GROUND GROUND + */ + + // moving + float tx = position.x+f*(cyclelength/2); + float t = fmod(tx, cyclelength) / cyclelength; + if(player->vx < 0) + t = 1.0f-t; + + float y; + float x = 0; + float r = 0; + float r_back = 1.5f; + + if(t < 0.5f) + { + // stomp down foot (part 1) + float st = t*2; + y = 1.0f-pow(st, 0.5f) + sinf(pow(st,2)*pi)*0.5f; + x = -steplength/2 + st*steplength; + r = r_back*(1-st) + sinf(pow(st,1.5f)*pi*2); + } + else + { + // lift foot up again (part 2) + float st = (t-0.5f)*2; + y = pow(st, 5.0f); + x = steplength/2 - st*steplength; + r = y*r_back; + } + + + if(player->vx > 0) + { + gfx_quads_setrotation(r); + gfx_quads_drawTL(position.x+x-w/2, position.y+16-y*lift - h, w, h); + } + else + { + gfx_quads_setrotation(-r); + gfx_quads_drawTL(position.x-x-w/2, position.y+16-y*lift - h, w, h); + } + gfx_quads_setrotation(0); + } + + } + } + + gfx_quads_end(); + + +} + +static player_input oldinput; +static bool bfirst = true; +void modc_render() +{ + if (bfirst) + { + bfirst = false; + oldinput.activeweapon = 0; + oldinput.angle = 0; + oldinput.blink = 0; + oldinput.fire = 0; + oldinput.hook = 0; + oldinput.jump = 0; + oldinput.left = 0; + oldinput.right = 0; + } + // fetch new input + { + int x, y; + inp_mouse_relative(&x, &y); + mouse_pos += vec2(x, y); + float l = length(mouse_pos); + if(l > 600.0f) + mouse_pos = normalize(mouse_pos)*600.0f; + } + + // snap input + { + player_input input; + input.left = inp_key_pressed('A'); + input.right = inp_key_pressed('D'); + float a = atan((float)mouse_pos.y/(float)mouse_pos.x); + if(mouse_pos.x < 0) + a = a+pi; + input.angle = (int)(a*256.0f); + input.jump = inp_key_pressed(baselib::keys::space) || inp_key_pressed('W'); + + input.fire = inp_mouse_button_pressed(0);// | (oldinput.fire << 16); + //oldinput.fire = input.fire & 0x0000ffff; + + input.hook = inp_mouse_button_pressed(1) || inp_key_pressed(baselib::keys::lctrl); // be nice to mac users O.o + input.blink = inp_key_pressed('S'); + + // Weapon switching + input.activeweapon = inp_key_pressed('1') ? 0x80000000 : 0; + if (!input.activeweapon) + input.activeweapon = inp_key_pressed('2') ? 0x80000000 | 1 : 0; + if (!input.activeweapon) + input.activeweapon = inp_key_pressed('3') ? 0x80000000 | 2 : 0; + if (!input.activeweapon) + input.activeweapon = inp_key_pressed('4') ? 0x80000000 | 3 : 0; + /*if (!input.activeweapon) + input.activeweapon = inp_key_pressed('5') ? 0x80000000 | 4 : 0;*/ + + snap_input(&input, sizeof(input)); + } + + // setup world view + { + // 1. fetch local player + // 2. set him to the center + + int num = snap_num_items(SNAP_CURRENT); + for(int i = 0; i < num; i++) + { + snap_item item; + void *data = snap_get_item(SNAP_CURRENT, i, &item); + + if(item.type == OBJTYPE_PLAYER) + { + obj_player *player = (obj_player *)data; + if(player->local) + { + local_player = player; + local_player_pos = vec2(player->x, player->y); + + void *p = snap_find_item(SNAP_PREV, item.type, item.id); + if(p) + local_player_pos = mix(vec2(((obj_player *)p)->x, ((obj_player *)p)->y), local_player_pos, snap_intratick()); + break; + } + } + } + } + + // pseudo format + float zoom = inp_key_pressed('T') ? 1.0 : 3.0f; + + float width = 400*zoom; + float height = 300*zoom; + float screen_x = 0; + float screen_y = 0; + + // center at char but can be moved when mouse is far away + float offx = 0, offy = 0; + int deadzone = 300; + if(mouse_pos.x > deadzone) offx = mouse_pos.x-deadzone; + if(mouse_pos.x <-deadzone) offx = mouse_pos.x+deadzone; + if(mouse_pos.y > deadzone) offy = mouse_pos.y-deadzone; + if(mouse_pos.y <-deadzone) offy = mouse_pos.y+deadzone; + offx = offx*2/3; + offy = offy*2/3; + + screen_x = local_player_pos.x+offx; + screen_y = local_player_pos.y+offy; + + gfx_mapscreen(screen_x-width/2, screen_y-height/2, screen_x+width/2, screen_y+height/2); + + // draw background + gfx_clear(0.65f,0.78f,0.9f); + + { + + vec2 pos(local_player_pos.x*0.5f, local_player_pos.y*0.5f); + + gfx_texture_set(-1); + gfx_blend_additive(); + gfx_quads_begin(); + const int rays = 10; + gfx_quads_setcolor(1.0f,1.0f,1.0f,0.025f); + for(int r = 0; r < rays; r++) + { + float a = r/(float)rays + client_localtime()*0.05f; + float size = (1.0f/(float)rays)*0.25f; + vec2 dir0(sinf((a-size)*pi*2.0f), cosf((a-size)*pi*2.0f)); + vec2 dir1(sinf((a+size)*pi*2.0f), cosf((a+size)*pi*2.0f)); + + //gfx_quads_draw_freeform(0,0, -100,0, -100,-100, 0,-100); + + gfx_quads_setcolorvertex(0, 1.0f,1.0f,1.0f,0.025f); + gfx_quads_setcolorvertex(1, 1.0f,1.0f,1.0f,0.025f); + gfx_quads_setcolorvertex(2, 1.0f,1.0f,1.0f,0.0f); + gfx_quads_setcolorvertex(3, 1.0f,1.0f,1.0f,0.0f); + const float range = 1000.0f; + gfx_quads_draw_freeform( + pos.x+dir0.x, pos.y+dir0.y, + pos.x+dir1.x, pos.y+dir1.y, + pos.x+dir0.x*range, pos.y+dir0.y*range, + pos.x+dir1.x*range, pos.y+dir1.y*range); + } + gfx_quads_end(); + gfx_blend_normal(); + + gfx_texture_set(texture_sun); + gfx_quads_begin(); + gfx_quads_draw(pos.x, pos.y, 256, 256); + gfx_quads_end(); + } + + // render map + tilemap_render(32.0f, 0); +#ifdef _DEBUG + float speed = 0.0f; +#endif + // render items + int num = snap_num_items(SNAP_CURRENT); + for(int i = 0; i < num; i++) + { + snap_item item; + void *data = snap_get_item(SNAP_CURRENT, i, &item); + + if(item.type == OBJTYPE_PLAYER) + { + void *prev = snap_find_item(SNAP_PREV, item.type, item.id); + if(prev) + { + render_player((obj_player *)prev, (obj_player *)data); +/*#ifdef _DEBUG + { + obj_player *p = (obj_player *)prev; + obj_player *c = (obj_player *)data; + vec2 positionold = vec2(p->x, p->y); + vec2 poscur = vec2(c->x, c->y); + speed = distance(positionold,poscur); + } +#endif*/ + } + } + else if(item.type == OBJTYPE_PROJECTILE) + { + void *prev = snap_find_item(SNAP_PREV, item.type, item.id); + if(prev) + render_projectile((obj_projectile *)prev, (obj_projectile *)data); + } + else if(item.type == OBJTYPE_POWERUP) + { + void *prev = snap_find_item(SNAP_PREV, item.type, item.id); + if(prev) + render_powerup((obj_powerup*)prev, (obj_powerup *)data); + } + } + + // render particles + temp_system.update(client_frametime()); + temp_system.render(); + + tilemap_render(32.0f, 1); + + // render health mods + healthmods.render(); + + // render cursor + // FIXME CURSOR!!! + + if(local_player) + { + gfx_texture_set(texture_weapon); + gfx_quads_begin(); + if (local_player->modifier & (1 << MODIFIER_TYPE_NINJA)) + cell_select_ex(numcellsx,numcellsy, modifiertexcoordcursor[MODIFIER_TYPE_NINJA].x, modifiertexcoordcursor[MODIFIER_TYPE_NINJA].y, modifiertexcoordcursor[MODIFIER_TYPE_NINJA].w, modifiertexcoordcursor[MODIFIER_TYPE_NINJA].h); + else + cell_select_ex(numcellsx,numcellsy, weapontexcoordcursor[local_player->weapon].x, weapontexcoordcursor[local_player->weapon].y, weapontexcoordcursor[local_player->weapon].w, weapontexcoordcursor[local_player->weapon].h); + float cursorsize = 64; + gfx_quads_draw(local_player_pos.x+mouse_pos.x, local_player_pos.y+mouse_pos.y,cursorsize,cursorsize); + + + // render ammo count + // render gui stuff + gfx_quads_end(); + gfx_quads_begin(); + gfx_mapscreen(0,0,400,300); + cell_select_ex(numcellsx,numcellsy, weaponprojtexcoord[local_player->weapon].x, weaponprojtexcoord[local_player->weapon].y, weaponprojtexcoord[local_player->weapon].w, weaponprojtexcoord[local_player->weapon].h); + for (int i = 0; i < local_player->ammocount; i++) + { + gfx_quads_drawTL(10+i*12,34,10,10); + } + gfx_quads_end(); + + gfx_texture_set(texture_game); + gfx_quads_begin(); + int h = 0; + cell_select_ex(32,16, 0,0, 4,4); + for(; h < local_player->health; h++) + gfx_quads_drawTL(10+h*12,10,10,10); + + cell_select_ex(32,16, 5,0, 4,4); + for(; h < 10; h++) + gfx_quads_drawTL(10+h*12,10,10,10); + + h = 0; + cell_select_ex(32,16, 0,5, 4,4); + for(; h < local_player->armor; h++) + gfx_quads_drawTL(10+h*12,22,10,10); + + cell_select_ex(32,16, 5,5, 4,4); + for(; h < 10; h++) + gfx_quads_drawTL(10+h*12,22,10,10); + gfx_quads_end(); + + // render speed +/*#ifdef _DEBUG + gfx_texture_set(font_texture); + char text[256]; + sprintf(text,"speed: %f",speed); + gfx_quads_text(300,20,10,text); +#endif*/ + } + // render gui stuff + gfx_mapscreen(0,0,400,300); + // render score board + if(inp_key_pressed(baselib::keys::tab)) + { + gfx_texture_set(font_texture); + gfx_quads_text(10, 50, 8, "Score Board"); + + int num = snap_num_items(SNAP_CURRENT); + int row = 1; + for(int i = 0; i < num; i++) + { + snap_item item; + void *data = snap_get_item(SNAP_CURRENT, i, &item); + + if(item.type == OBJTYPE_PLAYER) + { + obj_player *player = (obj_player *)data; + if(player) + { + char buf[128]; + char name[32]; + snap_decode_string(player->name, name, 32); + sprintf(buf, "%4d %s", player->score, name); + gfx_quads_text(10, 50 + 10 * row, 8, buf); + row++; + } + } + } + } +} diff --git a/src/game/client/mapres_image.cpp b/src/game/client/mapres_image.cpp new file mode 100644 index 00000000..1c81b309 --- /dev/null +++ b/src/game/client/mapres_image.cpp @@ -0,0 +1,41 @@ +#include +#include "../../engine/interface.h" +#include "mapres_image.h" +#include "../mapres.h" + +static int map_textures[64] = {0}; +static int count = 0; + +int img_init() +{ + int start, count; + map_get_type(MAPRES_IMAGE, &start, &count); + dbg_msg("mapres_image", "start=%d count=%d", start, count); + for(int i = 0; i < 64; i++) + { + if(map_textures[i]) + { + gfx_unload_texture(map_textures[i]); + map_textures[i] = 0; + } + } + + for(int i = 0; i < count; i++) + { + mapres_image *img = (mapres_image *)map_get_item(start+i, 0, 0); + void *data = map_get_data(img->image_data); + map_textures[i] = gfx_load_texture_raw(img->width, img->height, data); + } + + return count; +} + +int img_num() +{ + return count; +} + +int img_get(int index) +{ + return map_textures[index]; +} diff --git a/src/game/client/mapres_image.h b/src/game/client/mapres_image.h new file mode 100644 index 00000000..eab1559a --- /dev/null +++ b/src/game/client/mapres_image.h @@ -0,0 +1,18 @@ + +// loads images from the map to textures +int img_init(); + +// returns the number of images in the map +int img_num(); + +// fetches the texture id for the image +int img_get(int index); + + +class mapres_image +{ +public: + int width; + int height; + int image_data; +}; diff --git a/src/game/client/mapres_tilemap.cpp b/src/game/client/mapres_tilemap.cpp new file mode 100644 index 00000000..36302d0a --- /dev/null +++ b/src/game/client/mapres_tilemap.cpp @@ -0,0 +1,54 @@ +#include "../../engine/interface.h" +#include "mapres_tilemap.h" +#include "mapres_image.h" +#include "../mapres.h" + +int tilemap_init() +{ + return 0; +} + +void tilemap_render(float scale, int fg) +{ + if(!map_is_loaded()) + return; + + // fetch indecies + int start, num; + map_get_type(MAPRES_TILEMAP, &start, &num); + + // render tilemaps + int passed_main = 0; + for(int t = 0; t < num; t++) + { + mapres_tilemap *tmap = (mapres_tilemap *)map_get_item(start+t,0,0); + unsigned char *data = (unsigned char *)map_get_data(tmap->data); + + if(tmap->main) + passed_main = 1; + + if((fg && passed_main) || (!fg && !passed_main)) + { + gfx_texture_set(img_get(tmap->image)); + gfx_quads_begin(); + + int c = 0; + float frac = (1.0f/1024.0f); //2.0f; + for(int y = 0; y < tmap->height; y++) + for(int x = 0; x < tmap->width; x++, c++) + { + unsigned char d = data[c*2]; + if(d) + { + gfx_quads_setsubset( + (d%16)/16.0f+frac, + (d/16)/16.0f+frac, + (d%16)/16.0f+1.0f/16.0f-frac, + (d/16)/16.0f+1.0f/16.0f-frac); + gfx_quads_drawTL(x*scale, y*scale, scale, scale); + } + } + gfx_quads_end(); + } + } +} diff --git a/src/game/client/mapres_tilemap.h b/src/game/client/mapres_tilemap.h new file mode 100644 index 00000000..6e9d81be --- /dev/null +++ b/src/game/client/mapres_tilemap.h @@ -0,0 +1,19 @@ + +// dependencies: image + +// +int tilemap_init(); + +// renders the tilemaps +void tilemap_render(float scale, int fg); + +struct mapres_tilemap +{ + int image; + int width; + int height; + int x, y; + int scale; + int data; + int main; +}; diff --git a/src/game/client/menu.cpp b/src/game/client/menu.cpp new file mode 100644 index 00000000..88c87526 --- /dev/null +++ b/src/game/client/menu.cpp @@ -0,0 +1,600 @@ +#include +#include +#include +#include +#include +#include + +#include +#include +#include "../mapres.h" + +#include +#include "mapres_image.h" +#include "mapres_tilemap.h" + +using namespace baselib; + +/******************************************************** + MENU +*********************************************************/ + +struct pretty_font +{ + char m_CharStartTable[256]; + char m_CharEndTable[256]; + int font_texture; +}; + +extern pretty_font *current_font; +void gfx_pretty_text(float x, float y, float size, const char *text); +float gfx_pretty_text_width(float size, const char *text); + +void draw_scrolling_background(int id, float w, float h, float t) +{ + float tx = w/256.0f; + float ty = h/256.0f; + + float start_x = fmod(t, 1.0f); + float start_y = 1.0f - fmod(t*0.8f, 1.0f); + + gfx_blend_normal(); + gfx_texture_set(id); + gfx_quads_begin(); + gfx_quads_setcolor(1,1,1,1); + gfx_quads_setsubset( + start_x, // startx + start_y, // starty + start_x+tx, // endx + start_y+ty); // endy + gfx_quads_drawTL(0.0f,0.0f,w,h); + gfx_quads_end(); +} + +int background_texture; +int not_empty_item_texture; +int empty_item_texture; +int active_item_texture; +int selected_item_texture; +int join_button_texture; +int join_button_hot_texture; +int join_button_active_texture; +int join_button_grey_texture; +int quit_button_texture; +int quit_button_hot_texture; +int quit_button_active_texture; +int up_button_texture; +int up_button_active_texture; +int down_button_texture; +int down_button_active_texture; +int teewars_banner_texture; +int scroll_indicator_texture; +int connect_localhost_texture; +int refresh_button_texture; +int refresh_button_hot_texture; +int refresh_button_active_texture; +int input_box_texture; + +int music_menu; +int music_menu_id = -1; + +struct button_textures +{ + int *normal_texture; + int *hot_texture; + int *active_texture; +}; + +button_textures connect_localhost_button = { &connect_localhost_texture, &connect_localhost_texture, &connect_localhost_texture }; +button_textures join_button = { &join_button_texture, &join_button_hot_texture, &join_button_active_texture }; +button_textures quit_button = { &quit_button_texture, &quit_button_hot_texture, &quit_button_active_texture }; +button_textures scroll_up_button = { &up_button_texture, &up_button_texture, &up_button_active_texture }; +button_textures scroll_down_button = { &down_button_texture, &down_button_texture, &down_button_active_texture }; +button_textures list_item_button = { ¬_empty_item_texture, &active_item_texture, &active_item_texture }; +button_textures selected_item_button = { &selected_item_texture, &selected_item_texture, &selected_item_texture }; +button_textures refresh_button = { &refresh_button_texture, &refresh_button_hot_texture, &refresh_button_active_texture }; + +void draw_menu_button(void *id, const char *text, int checked, float x, float y, float w, float h, void *extra) +{ + button_textures *tx = (button_textures *)extra; + + gfx_blend_normal(); + + if (ui_active_item() == id && ui_hot_item() == id) + gfx_texture_set(*tx->active_texture); + else if (ui_hot_item() == id) + gfx_texture_set(*tx->hot_texture); + else + gfx_texture_set(*tx->normal_texture); + + gfx_quads_begin(); + + gfx_quads_setcolor(1,1,1,1); + + gfx_quads_drawTL(x,y,w,h); + gfx_quads_end(); + + gfx_texture_set(current_font->font_texture); + gfx_pretty_text(x + 4, y - 3.5f, 18.f, text); +} + +void draw_image_button(void *id, const char *text, int checked, float x, float y, float w, float h, void *extra) +{ + ui_do_image(*(int *)id, x, y, w, h); +} + +struct server_info +{ + int version; + int players; + int max_players; + netaddr4 address; + char name[129]; + char map[65]; +}; + +struct server_list +{ + server_info infos[10]; + int active_count, info_count; + int scroll_index; + int selected_index; +}; +#include + +int ui_do_edit_box(void *id, float x, float y, float w, float h, char *str, int str_size) +{ + int inside = ui_mouse_inside(x, y, w, h); + int r = 0; + + if(inside) + { + ui_set_hot_item(id); + + if(ui_mouse_button(0)) + ui_set_active_item(id); + } + + if (ui_active_item() == id) + { + char c = keys::last_char(); + int k = keys::last_key(); + int len = strlen(str); + + if (c >= 32 && c < 128) + { + if (len < str_size - 1) + { + str[len] = c; + str[len+1] = 0; + } + } + + if (k == keys::backspace) + { + if (len > 0) + str[len-1] = 0; + } + r = 1; + } + + ui_do_label(x + 4, y - 3.5f, str); + + if (ui_active_item() == id) + { + float w = gfx_pretty_text_width(18.0f, str); + ui_do_label(x + 4 + w, y - 3.5f, "_"); + } + + return r; +} + +int do_scroll_bar(void *id, float x, float y, float height, int steps, int last_index) +{ + int r = last_index; + + if (ui_do_button(&up_button_texture, "", 0, x, y, 8, 8, draw_menu_button, &scroll_up_button)) + { + if (r > 0) + --r; + } + else if (ui_do_button(&down_button_texture, "", 0, x, y + height - 8, 8, 8, draw_menu_button, &scroll_down_button)) + { + if (r < steps) + ++r; + } + else if (steps > 0) // only if there's actually stuff to scroll through + { + int inside = ui_mouse_inside(x, y + 8, 8, height - 16); + if (inside && (!ui_active_item() || ui_active_item() == id)) + ui_set_hot_item(id); + + if(ui_active_item() == id) + { + if (ui_mouse_button(0)) + { + float pos = ui_mouse_y() - y - 8; + float perc = pos / (height - 16); + + r = (steps + 1) * perc; + if (r < 0) + r = 0; + else if (r > steps) + r = steps; + } + else + ui_set_active_item(0); + } + else if (ui_hot_item() == id && ui_mouse_button(0)) + ui_set_active_item(id); + else if (inside && (!ui_active_item() || ui_active_item() == id)) + ui_set_hot_item(id); + } + + ui_do_image(scroll_indicator_texture, x, y + 8 + r * ((height - 32) / steps), 8, 16); + + return r; +} + +int do_server_list(server_list *list, float x, float y, int visible_items) +{ + const float spacing = 1.5f; + const float item_height = 14; + const float item_width = 364; + const float real_width = item_width + 10; + const float real_height = item_height * visible_items + spacing * (visible_items - 1); + + int r = -1; + + for (int i = 0; i < visible_items; i++) + { + int item_index = i + list->scroll_index; + if (item_index >= list->active_count) + ui_do_image(empty_item_texture, x, y + i * item_height + i * spacing, item_width, item_height); + else + { + server_info *item = &list->infos[item_index]; + + bool clicked = false; + if (list->selected_index == item_index) + clicked = ui_do_button(item, item->name, 0, x, y + i * item_height + i * spacing, item_width, item_height, draw_menu_button, &selected_item_button); + else + clicked = ui_do_button(item, item->name, 0, x, y + i * item_height + i * spacing, item_width, item_height, draw_menu_button, &list_item_button); + + char temp[64]; // plenty of extra room so we don't get sad :o + sprintf(temp, "%i/%i", item->players, item->max_players); + + gfx_texture_set(current_font->font_texture); + gfx_pretty_text(x + 300, y + i * item_height + i * spacing - 3.5f, 18.f, temp); + gfx_pretty_text(x + 180, y + i * item_height + i * spacing - 3.5f, 18.f, item->map); + + if (clicked) + { + r = item_index; + list->selected_index = item_index; + } + } + } + + list->scroll_index = do_scroll_bar(&list->scroll_index, x + real_width - 8, y, real_height, list->active_count - visible_items, list->scroll_index); + + return r; +} + +#include + +char *read_int(char *buffer, int *value) +{ + *value = buffer[0] << 24; + *value |= buffer[1] << 16; + *value |= buffer[2] << 8; + *value |= buffer[3]; + + return buffer + 4; +} + +char *read_netaddr(char *buffer, netaddr4 *addr) +{ + addr->ip[0] = *buffer++; + addr->ip[1] = *buffer++; + addr->ip[2] = *buffer++; + addr->ip[3] = *buffer++; + + int port; + buffer = read_int(buffer, &port); + + addr->port = port; + + return buffer; +} + +void refresh_list(server_list *list) +{ + netaddr4 addr; + netaddr4 me(0, 0, 0, 0, 0); + + list->selected_index = -1; + + if (net_host_lookup(MASTER_SERVER_ADDRESS, MASTER_SERVER_PORT, &addr) == 0) + { + socket_tcp4 sock; + sock.open(&me); + + //sock.set_non_blocking(); + + // try and connect with a timeout of 1 second + if (sock.connect_non_blocking(&addr)) + { + char data[256]; + int total_received = 0; + int pointer = 0; + int received; + + int master_server_version = -1; + int server_count = -1; + + // read header + while (total_received < 12 && (received = sock.recv(data + total_received, 12 - total_received)) > 0) + total_received += received; + + // see if we have the header + if (total_received == 12) + { + int signature; + read_int(data, &signature); + + // check signature + if (signature == 'TWSL') + { + read_int(data + 4, &master_server_version); + read_int(data + 8, &server_count); + + // TODO: handle master server version O.o + + const int server_info_size = 212; + const int wanted_data_count = server_count * server_info_size; + + list->active_count = 0; + + for (int i = 0; i < server_count; i++) + { + total_received = 0; + + // read data for a server + while (sock.is_connected() && total_received < server_info_size && (received = sock.recv(data + total_received, server_info_size - total_received)) > 0) + total_received += received; + + // check if we got enough data + if (total_received == server_info_size) + { + char *d = data; + + server_info *info = &list->infos[i]; + + d = read_int(d, &info->version); + d = read_netaddr(d, &info->address); + + //dbg_msg("menu/got_serverinfo", "IP: %i.%i.%i.%i:%i", (int)info->address.ip[0], (int)info->address.ip[1], (int)info->address.ip[2], (int)info->address.ip[3], info->address.port); + + d = read_int(d, &info->players); + d = read_int(d, &info->max_players); + memcpy(info->name, d, 128); + d += 128; + memcpy(info->map, d, 64); + + // let's be safe. + info->name[128] = 0; + info->map[64] = 0; + + ++list->active_count; + } + else + break; + } + + if (list->scroll_index >= list->active_count) + list->scroll_index = list->active_count - 1; + + if (list->scroll_index < 0) + list->scroll_index = 0; + } + } + + sock.close(); + } + } +} + +static int menu_render(netaddr4 *server_address, char *str, int max_len) +{ + // background color + gfx_clear(89/255.f,122/255.f,0.0); + + // GRADIENT: top to bottom + // top color: 60, 80, 0 + // bottom color: 90, 120, 0 + + // world coordsys + float zoom = 3.0f; + gfx_mapscreen(0,0,400.0f*zoom,300.0f*zoom); + + // GUI coordsys + gfx_mapscreen(0,0,400.0f,300.0f); + + static server_list list; + static bool inited = false; + + if (!inited) + { + list.info_count = 256; + + list.scroll_index = 0; + list.selected_index = -1; + + inited = true; + + refresh_list(&list); + } + + static int64 start = time_get(); + + float t = double(time_get() - start) / double(time_freq()); + draw_scrolling_background(background_texture, 400, 300, t * 0.01); + + ui_do_image(teewars_banner_texture, 70, 10, 256, 64); + + do_server_list(&list, 10, 80, 8); + + /* + if (ui_do_button(&connect_localhost_button, "", 0, 15, 250, 64, 24, draw_menu_button, &connect_localhost_button)) + { + *server_address = netaddr4(127, 0, 0, 1, 8303); + return 1; + }*/ + + if (ui_do_button(&refresh_button, "", 0, 220, 210, 64, 24, draw_menu_button, &refresh_button)) + { + refresh_list(&list); + } + + if (list.selected_index == -1) + { + ui_do_image(join_button_grey_texture, 290, 210, 64, 24); + } + else if (ui_do_button(&join_button, "", 0, 290, 210, 64, 24, draw_menu_button, &join_button)) + { + *server_address = list.infos[list.selected_index].address; + + dbg_msg("menu/join_button", "IP: %i.%i.%i.%i:%i", (int)server_address->ip[0], (int)server_address->ip[1], (int)server_address->ip[2], (int)server_address->ip[3], server_address->port); + + return 1; + } + + const float name_x = 10, name_y = 215; + + ui_do_label(name_x + 4, name_y - 3.5f, "Name:"); + ui_do_image(input_box_texture, name_x + 50 - 5, name_y - 5, 150 + 10, 14 + 10); + ui_do_edit_box(str, name_x + 50, name_y, 150, 14, str, max_len); + + if (ui_do_button(&quit_button, "", 0, 290, 250, 69, 25, draw_menu_button, &quit_button)) + return -1; + + ui_do_label(10.0f, 300.0f-20.0f, "Version: " TEEWARS_VERSION); + + return 0; +} + +void modmenu_init() +{ + keys::enable_char_cache(); + keys::enable_key_cache(); + + current_font->font_texture = gfx_load_texture_tga("data/big_font.tga"); + background_texture = gfx_load_texture_tga("data/gui_bg.tga"); + not_empty_item_texture = gfx_load_texture_tga("data/gui/game_list_item_not_empty.tga"); + empty_item_texture = gfx_load_texture_tga("data/gui/game_list_item_empty.tga"); + active_item_texture = gfx_load_texture_tga("data/gui/game_list_item_active.tga"); + selected_item_texture = gfx_load_texture_tga("data/gui/game_list_item_selected.tga"); + + join_button_texture = gfx_load_texture_tga("data/gui/join_button.tga"); + join_button_hot_texture = gfx_load_texture_tga("data/gui/join_button_hot.tga"); + join_button_active_texture = gfx_load_texture_tga("data/gui/join_button_active.tga"); + join_button_grey_texture = gfx_load_texture_tga("data/gui/join_button_greyed.tga"); + + +// button_not_hilighted_texture = gfx_load_texture_tga("data/gui/game_list_join_button.tga"); +// button_hilighted_texture = gfx_load_texture_tga("data/gui/button_hilighted.tga"); +// button_active_texture = gfx_load_texture_tga("data/gui/button_active.tga"); + + quit_button_texture = gfx_load_texture_tga("data/gui/quit_button.tga"); + quit_button_hot_texture = gfx_load_texture_tga("data/gui/quit_button_hot.tga"); + quit_button_active_texture = gfx_load_texture_tga("data/gui/quit_button_active.tga"); + + up_button_texture = gfx_load_texture_tga("data/gui/scroll_arrow_up.tga"); + up_button_active_texture = gfx_load_texture_tga("data/gui/scroll_arrow_up_active.tga"); + + down_button_texture = gfx_load_texture_tga("data/gui/scroll_arrow_down.tga"); + down_button_active_texture = gfx_load_texture_tga("data/gui/scroll_arrow_down_active.tga"); + + teewars_banner_texture = gfx_load_texture_tga("data/gui_logo.tga"); + scroll_indicator_texture = gfx_load_texture_tga("data/gui/scroll_drag.tga"); + connect_localhost_texture = gfx_load_texture_tga("data/gui/game_list_connect_localhost.tga"); + + refresh_button_texture = gfx_load_texture_tga("data/gui/refresh_button.tga"); + refresh_button_hot_texture = gfx_load_texture_tga("data/gui/refresh_button_hot.tga"); + refresh_button_active_texture = gfx_load_texture_tga("data/gui/refresh_button_active.tga"); + + input_box_texture = gfx_load_texture_tga("data/gui/input_box.tga"); + + music_menu = snd_load_wav("data/audio/Music_Menu.wav"); +} + +void modmenu_shutdown() +{ +} + +int modmenu_render(void *ptr, char *str, int max_len) +{ + static int mouse_x = 0; + static int mouse_y = 0; + + if (music_menu_id == -1) + { + dbg_msg("menu", "no music is playing, so let's play some tunes!"); + music_menu_id = snd_play(music_menu, SND_LOOP); + } + + netaddr4 *server_address = (netaddr4 *)ptr; + + // handle mouse movement + float mx, my, mwx, mwy; + { + int rx, ry; + inp_mouse_relative(&rx, &ry); + mouse_x += rx; + mouse_y += ry; + if(mouse_x < 0) mouse_x = 0; + if(mouse_y < 0) mouse_y = 0; + if(mouse_x > gfx_screenwidth()) mouse_x = gfx_screenwidth(); + if(mouse_y > gfx_screenheight()) mouse_y = gfx_screenheight(); + + // update the ui + mx = (mouse_x/(float)gfx_screenwidth())*400.0f; + my = (mouse_y/(float)gfx_screenheight())*300.0f; + mwx = mx*3.0f; // adjust to zoom and offset + mwy = mx*3.0f; // adjust to zoom and offset + + int buttons = 0; + if(inp_mouse_button_pressed(0)) buttons |= 1; + if(inp_mouse_button_pressed(1)) buttons |= 2; + if(inp_mouse_button_pressed(2)) buttons |= 4; + + ui_update(mx,my,mx*3.0f,my*3.0f,buttons); + } + + int r = menu_render(server_address, str, max_len); + + // render butt ugly mouse cursor + gfx_texture_set(-1); + gfx_quads_begin(); + gfx_quads_setcolor(0,0,0,1); + gfx_quads_draw_freeform(mx,my,mx,my, + mx+7,my, + mx,my+7); + gfx_quads_setcolor(1,1,1,1); + gfx_quads_draw_freeform(mx+1,my+1,mx+1,my+1, + mx+5,my+1, + mx+1,my+5); + gfx_quads_end(); + + if (r) + { + snd_stop(music_menu_id); + music_menu_id = -1; + } + + keys::clear_char(); + keys::clear_key(); + + return r; +} diff --git a/src/game/game.h b/src/game/game.h index a1817eea..2696b74d 100644 --- a/src/game/game.h +++ b/src/game/game.h @@ -1,7 +1,7 @@ #include #include #include -#include "../interface.h" +#include "../engine/interface.h" #include "mapres_col.h" // Don't tweak :) diff --git a/src/game/game_client.cpp b/src/game/game_client.cpp deleted file mode 100644 index 8bbdf0b3..00000000 --- a/src/game/game_client.cpp +++ /dev/null @@ -1,1993 +0,0 @@ -#include -#include -#include "game.h" -#include "mapres_image.h" -#include "mapres_tilemap.h" - -using namespace baselib; - -static int texture_char_default = 0; -static int texture_game = 0; -static int texture_weapon = 0; -static int texture_sun = 0; -static int texture_particles = 0; - -struct weapontexcell -{ - float x; - float y; - float w; - float h; -}; -struct renderparams -{ - float sizex; - float sizey; - float offsetx; - float offsety; -}; -int numcellsx = 32; -int numcellsy = 32; -renderparams weaponrenderparams[WEAPON_NUMWEAPONS]; -renderparams modifierrenderparams[WEAPON_NUMWEAPONS]; - -weapontexcell weaponprojtexcoord[WEAPON_NUMWEAPONS]; -weapontexcell weapontexcoord[WEAPON_NUMWEAPONS]; -weapontexcell weapontexcoordcursor[WEAPON_NUMWEAPONS]; - -weapontexcell poweruptexcoord[POWERUP_TYPE_NUMPOWERUPS]; - -weapontexcell modifiertexcoord[MODIFIER_NUMMODIFIERS]; -weapontexcell modifiertexcoordcursor[MODIFIER_NUMMODIFIERS]; - -int nummuzzletex[WEAPON_NUMWEAPONS]; -weapontexcell muzzletexcoord[WEAPON_NUMWEAPONS][3]; -renderparams muzzleparams[WEAPON_NUMWEAPONS]; - -#define NUMHADOKENS 6 -#define NUMSTARS 2 -#define NUMPARTICLES 9 -int particlesnumcellsx = 16; -int particlesnumcellsy = 16; -weapontexcell chaintexcoord; -weapontexcell chainheadtexcoord; -weapontexcell stars[NUMSTARS]; - -float lifemodifier[NUMPARTICLES]; -vec4 particlecolors[NUMPARTICLES]; -weapontexcell particlestexcoord[NUMPARTICLES]; - -int charnumcellsx = 8; -int charnumcellsy = 32; -int charoffsety = 2; -weapontexcell body[2]; -weapontexcell leye; -weapontexcell reye; -weapontexcell feet[2]; - -int charids[16] = { 2,10,0,4,12,6,14,1,9,15,13,11,7,5,8,3 }; - -renderparams hadokenparams[6]; -weapontexcell hadoken[6]; - -float recoils[WEAPON_NUMWEAPONS] = { 10.0f, 10.0f, 10.0f, 10.0f }; - -static int font_texture = 0; -static vec2 mouse_pos; - - -static vec2 local_player_pos; -static obj_player *local_player; - -float frandom() -{ - return rand()/(float)(RAND_MAX); -} - -float sign(float f) -{ - return f<0.0f?-1.0f:1.0f; -} - -// sound helpers -template -class sound_kit -{ -private: - int sounds[N]; - int last_id; -public: - sound_kit() : last_id(-1) { } - - int& operator[](int id) { return sounds[id]; } - - inline void play_random(float vol = 1.0f, float pan = 0.0f); -}; - -template<> -inline void sound_kit<1>::play_random(float vol, float pan) -{ - snd_play(sounds[0], SND_PLAY_ONCE, vol, pan); -} - -template -inline void sound_kit::play_random(float vol, float pan) -{ - int id; - do { - id = rand() % N; - } while(id == last_id); - snd_play(sounds[id], SND_PLAY_ONCE, vol, pan); - last_id = id; -} - - -// sound volume tweak -static const float stereo_separation = 0.01f; -static const float stereo_separation_deadzone = 512.0f; -static const float volume_distance_falloff = 100.0f; -static const float volume_distance_deadzone = 512.0f; -static const float volume_gun = 0.5f; -static const float volume_tee = 0.5f; -static const float volume_hit = 0.5f; -static const float volume_music = 0.8f; - -// sounds -sound_kit<3> sound_gun_fire; -sound_kit<3> sound_shotty_fire; -sound_kit<3> sound_flump_launch; -sound_kit<3> sound_hammer_swing; -sound_kit<3> sound_ninja_attack; - -sound_kit<3> sound_flump_explode; -sound_kit<4> sound_ninja_hit; - -sound_kit<3> sound_weapon_switch; - -sound_kit<12> sound_pain_short; -sound_kit<2> sound_pain_long; - -sound_kit<4> sound_body_jump; -sound_kit<4> sound_body_land; -sound_kit<2> sound_body_splat; - -sound_kit<7> sound_spawn; -sound_kit<2> sound_tee_cry; - -sound_kit<1> sound_hook_loop; -sound_kit<3> sound_hook_attach; - -void sound_vol_pan(const vec2& p, float *vol, float *pan) -{ - vec2 player_to_ev = p - local_player_pos; - *pan = 0.0f; - *vol = 1.0f; - - if(abs(player_to_ev.x) > stereo_separation_deadzone) - { - *pan = stereo_separation * (player_to_ev.x - sign(player_to_ev.x)*stereo_separation_deadzone); - if(*pan < -1.0f) *pan = -1.0f; - if(*pan > 1.0f) *pan = 1.0f; - } - - float len = length(player_to_ev); - if(len > volume_distance_deadzone) - { - *vol = volume_distance_falloff / (len - volume_distance_deadzone); - - if(*vol < 0.0f) *vol = 0.0f; - if(*vol > 1.0f) *vol = 1.0f; - } -} - -// TODO: we should do something nicer then this -static void cell_select_ex(int cx, int cy, float x, float y, float w, float h) -{ - gfx_quads_setsubset(x/(float)cx,y/(float)cy,(x+w)/(float)cx,(y+h)/(float)cy); -} - -static void cell_select_ex_flip_x(int cx, int cy, float x, float y, float w, float h) -{ - gfx_quads_setsubset((x+w)/(float)cx,y/(float)cy,x /(float)cx,(y+h)/(float)cy); -} - -static void cell_select_ex_flip_y(int cx, int cy, float x, float y, float w, float h) -{ - gfx_quads_setsubset(x/(float)cx,(y+h)/(float)cy,(x+w)/(float)cx,y/(float)cy); -} - -static void cell_select(int x, int y, int w, int h) -{ - gfx_quads_setsubset(x/16.0f,y/16.0f,(x+w)/16.0f,(y+h)/16.0f); -} - -inline void cell_select_flip_x(int x, int y, int w, int h) -{ - gfx_quads_setsubset((x+w)/16.0f,y/16.0f,(x)/16.0f,(y+h)/16.0f); -} - -inline void cell_select_flip_y(int x, int y, int w, int h) -{ - gfx_quads_setsubset(x/16.0f,(y+h)/16.0f,(x+w)/16.0f,(y)/16.0f); -} - -struct particle -{ - vec2 pos; - vec2 vel; - float life; - float max_life; - float size; - - float rot; - float rotspeed; - - float gravity; - float friction; - int iparticle; - - vec4 color; -}; - -void move_point(vec2 *inout_pos, vec2 *inout_vel, float elasticity) -{ - vec2 pos = *inout_pos; - vec2 vel = *inout_vel; - if(col_check_point(pos + vel)) - { - int affected = 0; - if(col_check_point(pos.x + vel.x, pos.y)) - { - inout_vel->x *= -elasticity; - affected++; - } - - if(col_check_point(pos.x, pos.y + vel.y)) - { - inout_vel->y *= -elasticity; - affected++; - } - - if(affected == 0) - { - inout_vel->x *= -elasticity; - inout_vel->y *= -elasticity; - } - } - else - { - *inout_pos = pos + vel; - } -} - -class health_texts -{ -public: - int64 lastupdate; - struct item - { - vec2 pos; - vec2 vel; - int amount; - int istar; - float life; - float startangle; - }; - - enum - { - MAX_ITEMS=16, - }; - - health_texts() - { - lastupdate = 0; - } - - item items[MAX_ITEMS]; - int num_items; - - item *create_i() - { - if (num_items < MAX_ITEMS) - { - item *p = &items[num_items]; - num_items++; - return p; - } - return 0; - } - - void destroy_i(item *i) - { - num_items--; - *i = items[num_items]; - } - - void create(vec2 pos, int amount) - { - amount = max(1,amount); - for (int j = 0; j < amount; j++) - { - float a = j/(float)amount-0.5f; - item *i = create_i(); - if (i) - { - i->pos = pos; - i->pos.y -= 20.0f; - i->pos.x += ((float)rand()/(float)RAND_MAX) * 5.0f; - i->amount = amount; - i->life = 1.5f; - i->istar = rand() % NUMSTARS; - i->vel = vec2(((float)rand()/(float)RAND_MAX) * 50.0f,-150.0f); - i->startangle = (( (float)rand()/(float)RAND_MAX) - 1.0f) * 2.0f * pi; - } - } - } - - void render() - { - if (!lastupdate) - lastupdate = time_get(); - - int64 lasttime = lastupdate; - lastupdate = time_get(); - - float delta = (float) (lastupdate - lasttime) / (float)time_freq(); - gfx_texture_set(texture_particles); - gfx_quads_begin(); - for(int i = 0; i < num_items;) - { - items[i].vel += vec2(0,500.0f) * delta; - items[i].pos += items[i].vel * delta; - items[i].life -= delta; - //items[i].pos.y -= frametime*15.0f; - if(items[i].life < 0.0f) - destroy_i(&items[i]); - else - { - gfx_quads_setcolor(1.0f,1.0f,1.0f, items[i].life / 1.5f); - gfx_quads_setrotation(items[i].startangle + items[i].life * 2.0f); - float size = 64.0f; - cell_select_ex(particlesnumcellsx,particlesnumcellsy, stars[items[i].istar].x,stars[items[i].istar].y, stars[items[i].istar].w, stars[items[i].istar].h); - gfx_quads_draw(items[i].pos.x-size/2, items[i].pos.y-size/2, size, size); - /*char buf[32]; - if(items[i].amount < 0) - { - sprintf(buf, "%d", items[i].amount*-1); - } - else - { - sprintf(buf, "%d", items[i].amount); - } - float size = 42.0f; - if(items[i].life > 1.25f) - size += 42.0f * ((items[i].life - 1.25f) * 4); - gfx_quads_text(items[i].pos.x-size/2, items[i].pos.y, size, buf);*/ - i++; - } - } - gfx_quads_end(); - } - -}; - -/*class texture_animator -{ -public: - - int texture; - int numframes; - float duration; - float* framereltime; - weapontexcell* params; - texture_animator() - { - texture = -1; - numframes = 0; - duration = 0; - framereltime = 0; - params = 0; - } - - ~texture_animator() - { - if (params) - mem_free(params); - if (framereltime) - mem_free(framereltime); - } - - void create_anim(int texture, int numframes, float duration) - { - framereltime = 0; - params = 0; - this->texture = texture; - this->numframes = numframes; - this->duration = duration; - if (numframes) - { - framereltime = (float*)mem_alloc(sizeof(float) * numframes,1); - params = (weapontexcell*)mem_alloc(sizeof(renderparams) * numframes,1); - float delta = 1.0f / (float)(numframes - 1); - for (int i = 0; i < numframes; i++) - { - framereltime[i] = delta * i; - } - } - } - - static void create_gunmuzzle(texture_animator& anim, int texture, float duration) - { - anim.create_anim(texture, 3, duration); - anim.params[0].x = 8; - anim.params[0].y = 4; - anim.params[0].w = 3; - anim.params[0].h = 2; - anim.params[1].x = 12; - anim.params[1].y = 4; - anim.params[1].w = 3; - anim.params[1].h = 2; - anim.params[2].x = 16; - anim.params[2].y = 4; - anim.params[2].w = 3; - anim.params[2].h = 2; - } - - static void create_shotgunmuzzle() - { - - } -};*/ - -class keyframe -{ -public: - vec2 pos; - float angle; - float relativetime; -}; - -class anim -{ -public: - keyframe* keyframes; - int numframes; - float duration; - anim() - { - numframes = 0; - keyframes = 0; - } - ~anim() - { - if (keyframes) - mem_free(keyframes); - } - - void create_anim(int numframes, float duration) - { - if (keyframes) - mem_free(keyframes); - - this->numframes = numframes; - this->duration = duration; - keyframes = (keyframe*)mem_alloc(sizeof(keyframe) * numframes,1); - float delta = 1.0f / (float) (numframes - 1); - for (int i = 0; i < numframes; i++) - { - keyframes[i].pos = vec2(0.0f,0.0f); - keyframes[i].angle = 0; - keyframes[i].relativetime = delta * (float)i; - } - } - - void getframes(float relativetime, keyframe*& frame1, keyframe*& frame2, float& blend) - { - for (int i = 1; i < numframes; i++) - { - if (keyframes[i-1].relativetime <= relativetime && keyframes[i].relativetime >= relativetime) - { - frame1 = &keyframes[i-1]; - frame2 = &keyframes[i]; - blend = (relativetime - frame1->relativetime) / (frame2->relativetime - frame1->relativetime); - } - } - } - - void evalanim(float time, vec2& pos, float& angle) - { - float reltime = max(0.0f, min(1.0f, time / duration)); - keyframe* frame1 = 0; - keyframe* frame2 = 0; - float blend = 0.0f; - getframes(reltime, frame1, frame2, blend); - - if (frame1 && frame2) - { - pos = mix(frame1->pos, frame2->pos, blend); - angle = LERP(frame1->angle, frame2->angle, blend); - } - } - - static void setup_hammer(anim& hammeranim) - { - // straight up = -0.25 - // frame0 = standard pose time 0 - // frame1 = back a little time 0.3 - // frame2 = over head time 0.4 - // frame3 = on ground smashed time 0.5 - // frame4 = back to standard pose time 1.0 - hammeranim.create_anim(5, 1.0f); - // only angles... (for now...) - hammeranim.keyframes[0].angle = -0.35f * pi * 2.0f; - hammeranim.keyframes[1].angle = -0.4f * pi * 2.0f; - hammeranim.keyframes[1].relativetime = 0.3f; - hammeranim.keyframes[2].angle = -0.25f; - hammeranim.keyframes[2].relativetime = 0.4f; - hammeranim.keyframes[3].angle = 0.0f * pi * 2.0f; - hammeranim.keyframes[3].relativetime = 0.5f; - hammeranim.keyframes[4].angle = -0.35f * pi * 2.0f; - hammeranim.keyframes[4].relativetime = 1.0f; - } - - static void setup_ninja(anim& ninjanim) - { - // (straight up = -0.25) - // frame0 = standard pose straight back time 0.0 - // frame1 = overhead attack frame 1 time 0.1 - // frame2 = attack end frame time 0.15 - // frame3 = attack hold frame (a bit up) time 0.4 - // frame4 = attack hold frame end time 0.7 - // frame5 = endframe time 1.0 - ninjanim.create_anim(6, 1.0f); - // only angles... (for now...) - ninjanim.keyframes[0].angle = -0.5f * pi * 2.0f; - - ninjanim.keyframes[1].angle = -0.3f * pi * 2.0f; - ninjanim.keyframes[1].relativetime = 0.1f; - - ninjanim.keyframes[2].angle = 0.1f * pi * 2.0f; - ninjanim.keyframes[2].relativetime = 0.15f; - - ninjanim.keyframes[3].angle = -0.05f * pi * 2.0f; - ninjanim.keyframes[3].relativetime = 0.42; - - ninjanim.keyframes[4].angle = -0.05f * pi * 2.0f; - ninjanim.keyframes[4].relativetime = 0.5f; - - ninjanim.keyframes[5].angle = -0.5f * pi * 2.0f; - ninjanim.keyframes[5].relativetime = 1.0f; - } -}; - -static anim hammeranim; -static anim ninjaanim; -static health_texts healthmods; - -class particle_system -{ -public: - enum - { - MAX_PARTICLES=1024, - }; - - particle particles[MAX_PARTICLES]; - int num_particles; - - particle_system() - { - num_particles = 0; - } - - void new_particle(vec2 pos, vec2 vel, float life, float size, float gravity, float friction) - { - if (num_particles >= MAX_PARTICLES) - return; - - particles[num_particles].iparticle = rand() % NUMPARTICLES; - particles[num_particles].pos = pos; - particles[num_particles].vel = vel; - particles[num_particles].life = life - lifemodifier[particles[num_particles].iparticle] * life; - particles[num_particles].size = size; - particles[num_particles].max_life = life; - particles[num_particles].gravity = gravity; - particles[num_particles].friction = friction; - particles[num_particles].rot = frandom()*pi*2; - particles[num_particles].rotspeed = frandom() * 10.0f; - num_particles++; - } - - void update(float time_passed) - { - for(int i = 0; i < num_particles; i++) - { - particles[i].vel.y += particles[i].gravity*time_passed; - particles[i].vel *= particles[i].friction; - vec2 vel = particles[i].vel*time_passed; - move_point(&particles[i].pos, &vel, 0.1f+0.9f*frandom()); - particles[i].vel = vel* (1.0f/time_passed); - particles[i].life += time_passed; - particles[i].rot += time_passed * particles[i].rotspeed; - - // check particle death - if(particles[i].life > particles[i].max_life) - { - num_particles--; - particles[i] = particles[num_particles]; - i--; - } - } - } - - void render() - { - gfx_blend_additive(); - gfx_texture_set(texture_particles); - gfx_quads_begin(); - //cell_select(4,1,1,1); - //cell_select(0,6,2,2); - //gfx_quads_setrotation(get_angle(vec2(proj->vx, proj->vy))); - for(int i = 0; i < num_particles; i++) - { - int type = particles[i].iparticle; - cell_select_ex(particlesnumcellsx,particlesnumcellsy,particlestexcoord[type].x, particlestexcoord[type].y, particlestexcoord[type].w, particlestexcoord[type].h); - float a = 1 - particles[i].life / particles[i].max_life; - vec2 p = particles[i].pos; - //a *= length(particles[i].vel) * 0.01f; - gfx_quads_setrotation(particles[i].rot); - gfx_quads_setcolor(particlecolors[type].x,particlecolors[type].y,particlecolors[type].z,pow(a,0.75f)); - //gfx_quads_setcolor(particlecolors[type].x * 0.5,particlecolors[type].y * 0.5,particlecolors[type].z* 0.5,pow(a,0.75f)); - //gfx_quads_setcolor(particlecolors[type].x * 0.0,particlecolors[type].y * 0.0,particlecolors[type].z* 0.0,pow(a,0.75f)); - //gfx_quads_setcolor(0.64f*2,0.28f*2,0.16f*2,pow(a,0.75f)); - gfx_quads_draw(p.x, p.y,particles[i].size,particles[i].size); - } - gfx_quads_end(); - gfx_blend_normal(); - } -}; - -static particle_system temp_system; - -void modc_init() -{ - // load textures - texture_weapon = gfx_load_texture_tga("data/tileset_weapons.tga"); - texture_game = gfx_load_texture_tga("data/game_main.tga"); - texture_char_default = gfx_load_texture_tga("data/char_teefault.tga"); - texture_sun = gfx_load_texture_tga("data/sun.tga"); - texture_particles = gfx_load_texture_tga("data/tileset_particles.tga"); - font_texture = gfx_load_texture_tga("data/debug_font.tga"); - - - // load sounds - sound_gun_fire[0] = snd_load_wav("data/audio/wp_gun_fire-01.wav"); - sound_gun_fire[0] = snd_load_wav("data/audio/wp_gun_fire-01.wav"); - sound_gun_fire[1] = snd_load_wav("data/audio/wp_gun_fire-02.wav"); - sound_shotty_fire[0] = snd_load_wav("data/audio/wp_shotty_fire-01.wav"); - sound_shotty_fire[1] = snd_load_wav("data/audio/wp_shotty_fire-02.wav"); - sound_shotty_fire[2] = snd_load_wav("data/audio/wp_shotty_fire-03.wav"); - sound_flump_launch[0] = snd_load_wav("data/audio/wp_flump_launch-01.wav"); - sound_flump_launch[1] = snd_load_wav("data/audio/wp_flump_launch-02.wav"); - sound_flump_launch[2] = snd_load_wav("data/audio/wp_flump_launch-03.wav"); - sound_hammer_swing[0] = snd_load_wav("data/audio/wp_hammer_swing-01.wav"); - sound_hammer_swing[1] = snd_load_wav("data/audio/wp_hammer_swing-02.wav"); - sound_hammer_swing[2] = snd_load_wav("data/audio/wp_hammer_swing-03.wav"); - sound_ninja_attack[0] = snd_load_wav("data/audio/wp_ninja_attack-01.wav"); - sound_ninja_attack[1] = snd_load_wav("data/audio/wp_ninja_attack-02.wav"); - sound_ninja_attack[2] = snd_load_wav("data/audio/wp_ninja_attack-03.wav"); - - sound_flump_explode[0] = snd_load_wav("data/audio/wp_flump_explo-01.wav"); - sound_flump_explode[1] = snd_load_wav("data/audio/wp_flump_explo-02.wav"); - sound_flump_explode[2] = snd_load_wav("data/audio/wp_flump_explo-03.wav"); - sound_ninja_hit[0] = snd_load_wav("data/audio/wp_ninja_hit-01.wav"); - sound_ninja_hit[1] = snd_load_wav("data/audio/wp_ninja_hit-02.wav"); - sound_ninja_hit[2] = snd_load_wav("data/audio/wp_ninja_hit-03.wav"); - sound_ninja_hit[3] = snd_load_wav("data/audio/wp_ninja_hit-04.wav"); - - sound_weapon_switch[0] = snd_load_wav("data/audio/wp_switch-01.wav"); - sound_weapon_switch[1] = snd_load_wav("data/audio/wp_switch-02.wav"); - sound_weapon_switch[2] = snd_load_wav("data/audio/wp_switch-03.wav"); - - sound_pain_short[0] = snd_load_wav("data/audio/vo_teefault_pain_short-01.wav"); - sound_pain_short[1] = snd_load_wav("data/audio/vo_teefault_pain_short-02.wav"); - sound_pain_short[2] = snd_load_wav("data/audio/vo_teefault_pain_short-03.wav"); - sound_pain_short[3] = snd_load_wav("data/audio/vo_teefault_pain_short-04.wav"); - sound_pain_short[4] = snd_load_wav("data/audio/vo_teefault_pain_short-05.wav"); - sound_pain_short[5] = snd_load_wav("data/audio/vo_teefault_pain_short-06.wav"); - sound_pain_short[6] = snd_load_wav("data/audio/vo_teefault_pain_short-07.wav"); - sound_pain_short[7] = snd_load_wav("data/audio/vo_teefault_pain_short-08.wav"); - sound_pain_short[8] = snd_load_wav("data/audio/vo_teefault_pain_short-09.wav"); - sound_pain_short[9] = snd_load_wav("data/audio/vo_teefault_pain_short-10.wav"); - sound_pain_short[10] = snd_load_wav("data/audio/vo_teefault_pain_short-11.wav"); - sound_pain_short[11] = snd_load_wav("data/audio/vo_teefault_pain_short-12.wav"); - - sound_pain_long[0] = snd_load_wav("data/audio/vo_teefault_pain_long-01.wav"); - sound_pain_long[1] = snd_load_wav("data/audio/vo_teefault_pain_long-02.wav"); - - sound_body_land[0] = snd_load_wav("data/audio/foley_land-01.wav"); - sound_body_land[1] = snd_load_wav("data/audio/foley_land-02.wav"); - sound_body_land[2] = snd_load_wav("data/audio/foley_land-03.wav"); - sound_body_land[3] = snd_load_wav("data/audio/foley_land-04.wav"); - sound_body_jump[0] = snd_load_wav("data/audio/foley_foot_left-01.wav"); - sound_body_jump[1] = snd_load_wav("data/audio/foley_foot_left-02.wav"); - sound_body_jump[2] = snd_load_wav("data/audio/foley_foot_left-03.wav"); - sound_body_jump[3] = snd_load_wav("data/audio/foley_foot_left-04.wav"); - sound_body_jump[4] = snd_load_wav("data/audio/foley_foot_right-01.wav"); - sound_body_jump[5] = snd_load_wav("data/audio/foley_foot_right-02.wav"); - sound_body_jump[6] = snd_load_wav("data/audio/foley_foot_right-03.wav"); - sound_body_jump[7] = snd_load_wav("data/audio/foley_foot_right-04.wav"); - - sound_body_splat[1] = snd_load_wav("data/audio/foley_body_splat-02.wav"); - sound_body_splat[2] = snd_load_wav("data/audio/foley_body_splat-03.wav"); - sound_body_splat[3] = snd_load_wav("data/audio/foley_body_splat-04.wav"); - - sound_spawn[0] = snd_load_wav("data/audio/vo_teefault_spawn-01.wav"); - sound_spawn[1] = snd_load_wav("data/audio/vo_teefault_spawn-02.wav"); - sound_spawn[2] = snd_load_wav("data/audio/vo_teefault_spawn-03.wav"); - sound_spawn[3] = snd_load_wav("data/audio/vo_teefault_spawn-04.wav"); - sound_spawn[4] = snd_load_wav("data/audio/vo_teefault_spawn-05.wav"); - sound_spawn[5] = snd_load_wav("data/audio/vo_teefault_spawn-06.wav"); - sound_spawn[6] = snd_load_wav("data/audio/vo_teefault_spawn-07.wav"); - - sound_tee_cry[0] = snd_load_wav("data/audio/vo_teefault_cry-01.wav"); - sound_tee_cry[1] = snd_load_wav("data/audio/vo_teefault_cry-02.wav"); - - //sound_hook_loop[0] = snd_load_wav("data/audio/hook_loop-01.wav"); - sound_hook_loop[0] = snd_load_wav("data/audio/hook_loop-02.wav"); - sound_hook_attach[0] = snd_load_wav("data/audio/hook_attach-01.wav"); - sound_hook_attach[1] = snd_load_wav("data/audio/hook_attach-02.wav"); - sound_hook_attach[2] = snd_load_wav("data/audio/hook_attach-03.wav"); - - poweruptexcoord[POWERUP_TYPE_HEALTH].x = 10; - poweruptexcoord[POWERUP_TYPE_HEALTH].y = 2; - poweruptexcoord[POWERUP_TYPE_HEALTH].w = 2; - poweruptexcoord[POWERUP_TYPE_HEALTH].h = 2; - - poweruptexcoord[POWERUP_TYPE_ARMOR].x = 12; - poweruptexcoord[POWERUP_TYPE_ARMOR].y = 2; - poweruptexcoord[POWERUP_TYPE_ARMOR].w = 2; - poweruptexcoord[POWERUP_TYPE_ARMOR].h = 2; - - poweruptexcoord[POWERUP_TYPE_WEAPON].x = 3; - poweruptexcoord[POWERUP_TYPE_WEAPON].y = 0; - poweruptexcoord[POWERUP_TYPE_WEAPON].w = 6; - poweruptexcoord[POWERUP_TYPE_WEAPON].h = 2; - - poweruptexcoord[POWERUP_TYPE_NINJA].x = 3; - poweruptexcoord[POWERUP_TYPE_NINJA].y = 10; - poweruptexcoord[POWERUP_TYPE_NINJA].w = 7; - poweruptexcoord[POWERUP_TYPE_NINJA].h = 2; - - poweruptexcoord[POWERUP_TYPE_TIMEFIELD].x = 3; - poweruptexcoord[POWERUP_TYPE_TIMEFIELD].y = 0; - poweruptexcoord[POWERUP_TYPE_TIMEFIELD].w = 6; - poweruptexcoord[POWERUP_TYPE_TIMEFIELD].h = 2; - - // Setup weapon cell coords - float sizemodifier = 1.0f; - weaponrenderparams[WEAPON_TYPE_GUN].sizex = 60.0f * sizemodifier; - weaponrenderparams[WEAPON_TYPE_GUN].sizey = 30.0f * sizemodifier; - weaponrenderparams[WEAPON_TYPE_GUN].offsetx = 32.0f; - weaponrenderparams[WEAPON_TYPE_GUN].offsety = 4.0f; - weapontexcoordcursor[WEAPON_TYPE_GUN].x = 0; - weapontexcoordcursor[WEAPON_TYPE_GUN].y = 4; - weapontexcoordcursor[WEAPON_TYPE_GUN].w = 2; - weapontexcoordcursor[WEAPON_TYPE_GUN].h = 2; - weapontexcoord[WEAPON_TYPE_GUN].x = 2; - weapontexcoord[WEAPON_TYPE_GUN].y = 4; - weapontexcoord[WEAPON_TYPE_GUN].w = 4; - weapontexcoord[WEAPON_TYPE_GUN].h = 2; - weaponprojtexcoord[WEAPON_TYPE_GUN].x = 6; - weaponprojtexcoord[WEAPON_TYPE_GUN].y = 4; - weaponprojtexcoord[WEAPON_TYPE_GUN].w = 2; - weaponprojtexcoord[WEAPON_TYPE_GUN].h = 2; - - nummuzzletex[WEAPON_TYPE_GUN] = 3; - muzzletexcoord[WEAPON_TYPE_GUN][0].x = 8; - muzzletexcoord[WEAPON_TYPE_GUN][0].y = 4; - muzzletexcoord[WEAPON_TYPE_GUN][0].w = 3; - muzzletexcoord[WEAPON_TYPE_GUN][0].h = 2; - muzzletexcoord[WEAPON_TYPE_GUN][1].x = 12; - muzzletexcoord[WEAPON_TYPE_GUN][1].y = 4; - muzzletexcoord[WEAPON_TYPE_GUN][1].w = 3; - muzzletexcoord[WEAPON_TYPE_GUN][1].h = 2; - muzzletexcoord[WEAPON_TYPE_GUN][2].x = 16; - muzzletexcoord[WEAPON_TYPE_GUN][2].y = 4; - muzzletexcoord[WEAPON_TYPE_GUN][2].w = 3; - muzzletexcoord[WEAPON_TYPE_GUN][2].h = 2; - - muzzleparams[WEAPON_TYPE_GUN].sizex = 60.0f * sizemodifier; - muzzleparams[WEAPON_TYPE_GUN].sizey = 40.0f * sizemodifier; - muzzleparams[WEAPON_TYPE_GUN].offsetx = 50.0f * sizemodifier; - muzzleparams[WEAPON_TYPE_GUN].offsety = 6.0f * sizemodifier; - - sizemodifier = 1.3f; - weaponrenderparams[WEAPON_TYPE_ROCKET].sizex = 70.0f * sizemodifier; - weaponrenderparams[WEAPON_TYPE_ROCKET].sizey = 20.0f * sizemodifier; - weaponrenderparams[WEAPON_TYPE_ROCKET].offsetx = 24.0f; - weaponrenderparams[WEAPON_TYPE_ROCKET].offsety = -2.0f; - weapontexcoordcursor[WEAPON_TYPE_ROCKET].x = 0; - weapontexcoordcursor[WEAPON_TYPE_ROCKET].y = 8; - weapontexcoordcursor[WEAPON_TYPE_ROCKET].w = 2; - weapontexcoordcursor[WEAPON_TYPE_ROCKET].h = 2; - weapontexcoord[WEAPON_TYPE_ROCKET].x = 2; - weapontexcoord[WEAPON_TYPE_ROCKET].y = 8; - weapontexcoord[WEAPON_TYPE_ROCKET].w = 7; - weapontexcoord[WEAPON_TYPE_ROCKET].h = 2; - weaponprojtexcoord[WEAPON_TYPE_ROCKET].x = 10; - weaponprojtexcoord[WEAPON_TYPE_ROCKET].y = 8; - weaponprojtexcoord[WEAPON_TYPE_ROCKET].w = 2; - weaponprojtexcoord[WEAPON_TYPE_ROCKET].h = 2; - - /*weaponrenderparams[WEAPON_TYPE_SNIPER].sizex = 60.0f; - weaponrenderparams[WEAPON_TYPE_SNIPER].sizey = 20.0f; - weaponrenderparams[WEAPON_TYPE_SNIPER].offsetx = 16.0f; - weaponrenderparams[WEAPON_TYPE_SNIPER].offsety = 4.0f; - weapontexcoordcursor[WEAPON_TYPE_SNIPER].x = 0; - weapontexcoordcursor[WEAPON_TYPE_SNIPER].y = 6; - weapontexcoordcursor[WEAPON_TYPE_SNIPER].w = 2; - weapontexcoordcursor[WEAPON_TYPE_SNIPER].h = 2; - weapontexcoord[WEAPON_TYPE_SNIPER].x = 3; - weapontexcoord[WEAPON_TYPE_SNIPER].y = 6; - weapontexcoord[WEAPON_TYPE_SNIPER].w = 6; - weapontexcoord[WEAPON_TYPE_SNIPER].h = 2; - weaponprojtexcoord[WEAPON_TYPE_SNIPER].x = 10; - weaponprojtexcoord[WEAPON_TYPE_SNIPER].y = 6; - weaponprojtexcoord[WEAPON_TYPE_SNIPER].w = 1; - weaponprojtexcoord[WEAPON_TYPE_SNIPER].h = 1;*/ - - weaponrenderparams[WEAPON_TYPE_SHOTGUN].sizex = 80.0f * sizemodifier; - weaponrenderparams[WEAPON_TYPE_SHOTGUN].sizey = 20.0f * sizemodifier; - weaponrenderparams[WEAPON_TYPE_SHOTGUN].offsetx = 24.0f; - weaponrenderparams[WEAPON_TYPE_SHOTGUN].offsety = -2.0f; - weapontexcoordcursor[WEAPON_TYPE_SHOTGUN].x = 0; - weapontexcoordcursor[WEAPON_TYPE_SHOTGUN].y = 6; - weapontexcoordcursor[WEAPON_TYPE_SHOTGUN].w = 2; - weapontexcoordcursor[WEAPON_TYPE_SHOTGUN].h = 2; - weapontexcoord[WEAPON_TYPE_SHOTGUN].x = 2; - weapontexcoord[WEAPON_TYPE_SHOTGUN].y = 6; - weapontexcoord[WEAPON_TYPE_SHOTGUN].w = 8; - weapontexcoord[WEAPON_TYPE_SHOTGUN].h = 2; - weaponprojtexcoord[WEAPON_TYPE_SHOTGUN].x = 10; - weaponprojtexcoord[WEAPON_TYPE_SHOTGUN].y = 6; - weaponprojtexcoord[WEAPON_TYPE_SHOTGUN].w = 2; - weaponprojtexcoord[WEAPON_TYPE_SHOTGUN].h = 2; - - nummuzzletex[WEAPON_TYPE_SHOTGUN] = 3; - muzzletexcoord[WEAPON_TYPE_SHOTGUN][0].x = 12; - muzzletexcoord[WEAPON_TYPE_SHOTGUN][0].y = 6; - muzzletexcoord[WEAPON_TYPE_SHOTGUN][0].w = 3; - muzzletexcoord[WEAPON_TYPE_SHOTGUN][0].h = 2; - muzzletexcoord[WEAPON_TYPE_SHOTGUN][1].x = 16; - muzzletexcoord[WEAPON_TYPE_SHOTGUN][1].y = 6; - muzzletexcoord[WEAPON_TYPE_SHOTGUN][1].w = 3; - muzzletexcoord[WEAPON_TYPE_SHOTGUN][1].h = 2; - muzzletexcoord[WEAPON_TYPE_SHOTGUN][2].x = 20; - muzzletexcoord[WEAPON_TYPE_SHOTGUN][2].y = 6; - muzzletexcoord[WEAPON_TYPE_SHOTGUN][2].w = 3; - muzzletexcoord[WEAPON_TYPE_SHOTGUN][2].h = 2; - - muzzleparams[WEAPON_TYPE_SHOTGUN].sizex = 60.0f * sizemodifier; - muzzleparams[WEAPON_TYPE_SHOTGUN].sizey = 40.0f * sizemodifier; - muzzleparams[WEAPON_TYPE_SHOTGUN].offsetx = 50.0f * sizemodifier; - muzzleparams[WEAPON_TYPE_SHOTGUN].offsety = 6.0f * sizemodifier; - - - - weaponrenderparams[WEAPON_TYPE_MELEE].sizex = 60.0f * sizemodifier; - weaponrenderparams[WEAPON_TYPE_MELEE].sizey = 50.0f * sizemodifier; - weaponrenderparams[WEAPON_TYPE_MELEE].offsetx = 20.0f; - weaponrenderparams[WEAPON_TYPE_MELEE].offsety = -4.0f; - weapontexcoordcursor[WEAPON_TYPE_MELEE].x = 0; - weapontexcoordcursor[WEAPON_TYPE_MELEE].y = 0; - weapontexcoordcursor[WEAPON_TYPE_MELEE].w = 2; - weapontexcoordcursor[WEAPON_TYPE_MELEE].h = 2; - weapontexcoord[WEAPON_TYPE_MELEE].x = 2; - weapontexcoord[WEAPON_TYPE_MELEE].y = 1; - weapontexcoord[WEAPON_TYPE_MELEE].w = 4; - weapontexcoord[WEAPON_TYPE_MELEE].h = 3; - weaponprojtexcoord[WEAPON_TYPE_MELEE].x = 0; - weaponprojtexcoord[WEAPON_TYPE_MELEE].y = 0; - weaponprojtexcoord[WEAPON_TYPE_MELEE].w = 0; - weaponprojtexcoord[WEAPON_TYPE_MELEE].h = 0; - - - // MODIFIERS - sizemodifier = 2.0; - modifierrenderparams[MODIFIER_TYPE_NINJA].sizex = 60.0f * sizemodifier; - modifierrenderparams[MODIFIER_TYPE_NINJA].sizey = 20.0f * sizemodifier; - modifierrenderparams[MODIFIER_TYPE_NINJA].offsetx = 20.0f; - modifierrenderparams[MODIFIER_TYPE_NINJA].offsety = 4.0f; - modifiertexcoord[MODIFIER_TYPE_NINJA].x = 2; - modifiertexcoord[MODIFIER_TYPE_NINJA].y = 10; - modifiertexcoord[MODIFIER_TYPE_NINJA].w = 7; - modifiertexcoord[MODIFIER_TYPE_NINJA].h = 2; - modifiertexcoordcursor[MODIFIER_TYPE_NINJA].x = 0; - modifiertexcoordcursor[MODIFIER_TYPE_NINJA].y = 10; - modifiertexcoordcursor[MODIFIER_TYPE_NINJA].w = 2; - modifiertexcoordcursor[MODIFIER_TYPE_NINJA].h = 2; - - modifierrenderparams[MODIFIER_TYPE_TIMEFIELD].sizex = 60.0f * sizemodifier; - modifierrenderparams[MODIFIER_TYPE_TIMEFIELD].sizey = 20.0f * sizemodifier; - modifierrenderparams[MODIFIER_TYPE_TIMEFIELD].offsetx = 16.0f; - modifierrenderparams[MODIFIER_TYPE_TIMEFIELD].offsety = 4.0f; - modifiertexcoord[MODIFIER_TYPE_TIMEFIELD].x = 0; - modifiertexcoord[MODIFIER_TYPE_TIMEFIELD].y = 0; - modifiertexcoord[MODIFIER_TYPE_TIMEFIELD].w = 0; - modifiertexcoord[MODIFIER_TYPE_TIMEFIELD].h = 0; - - stars[0].x = 0; - stars[0].y = 0; - stars[0].w = 2; - stars[0].h = 2; - - stars[1].x = 0; - stars[1].y = 2; - stars[1].w = 2; - stars[1].h = 2; - - particlecolors[0].x = 0.7f; - particlecolors[0].y = 0.7f; - particlecolors[0].z = 0.7f; - particlecolors[0].w = 1.0f; - particlestexcoord[0].x = 2; - particlestexcoord[0].y = 0; - particlestexcoord[0].w = 2; - particlestexcoord[0].h = 2; - particlecolors[1].x = 1.0f; - particlecolors[1].y = 1.0f; - particlecolors[1].z = 1.0f; - particlecolors[1].w = 1.0f; - particlestexcoord[1].x = 4; - particlestexcoord[1].y = 0; - particlestexcoord[1].w = 2; - particlestexcoord[1].h = 2; - particlecolors[2].x = 0.8f; - particlecolors[2].y = 0.8f; - particlecolors[2].z = 0.8f; - particlecolors[2].w = 1.0f; - particlestexcoord[2].x = 6; - particlestexcoord[2].y = 0; - particlestexcoord[2].w = 2; - particlestexcoord[2].h = 2; - particlecolors[3].x = 0.988f; - particlecolors[3].y = 1.0f; - particlecolors[3].z = 0.16f; - particlecolors[3].w = 1.0f; - particlestexcoord[3].x = 8; - particlestexcoord[3].y = 0; - particlestexcoord[3].w = 2; - particlestexcoord[3].h = 2; - particlecolors[4].x = 1.0f; - particlecolors[4].y = 1.0f; - particlecolors[4].z = 1.0f; - particlecolors[4].w = 1.0f; - particlestexcoord[4].x = 10; - particlestexcoord[4].y = 0; - particlestexcoord[4].w = 2; - particlestexcoord[4].h = 2; - particlecolors[5].x = 0.6f; - particlecolors[5].y = 0.6f; - particlecolors[5].z = 0.6f; - particlecolors[5].w = 1.0f; - particlestexcoord[5].x = 2; - particlestexcoord[5].y = 2; - particlestexcoord[5].w = 2; - particlestexcoord[5].h = 2; - particlecolors[6].x = 1.0f; - particlecolors[6].y = 1.0f; - particlecolors[6].z = 1.0f; - particlecolors[6].w = 1.0f; - particlestexcoord[6].x = 4; - particlestexcoord[6].y = 2; - particlestexcoord[6].w = 2; - particlestexcoord[6].h = 2; - particlecolors[5].x = 0.9f; - particlecolors[5].y = 0.9f; - particlecolors[5].z = 0.9f; - particlecolors[5].w = 1.0f; - particlestexcoord[7].x = 6; - particlestexcoord[7].y = 2; - particlestexcoord[7].w = 2; - particlestexcoord[7].h = 2; - particlecolors[8].x = 1.0f; - particlecolors[8].y = 1.0f; - particlecolors[8].z = 1.0f; - particlecolors[8].w = 1.0f; - particlestexcoord[8].x = 8; - particlestexcoord[8].y = 2; - particlestexcoord[8].w = 2; - particlestexcoord[8].h = 2; - lifemodifier[0] = 0.5f; - lifemodifier[1] = 0.5f; - lifemodifier[2] = 0.5f; - lifemodifier[3] = 0.7f; - lifemodifier[4] = 0.7f; - lifemodifier[5] = 1.0f; - lifemodifier[6] = 1.0f; - lifemodifier[7] = 1.5f; - lifemodifier[8] = 0.4f; - - chaintexcoord.x = 2; - chaintexcoord.y = 0; - chaintexcoord.w = 1; - chaintexcoord.h = 1; - - chainheadtexcoord.x = 3; - chainheadtexcoord.y = 0; - chainheadtexcoord.w = 2; - chainheadtexcoord.h = 1; - - - // anims - anim::setup_hammer(hammeranim); - anim::setup_ninja(ninjaanim); - - for (int i = 0; i < NUMHADOKENS; i++) - { - hadoken[i].x = 1; - hadoken[i].y = 12; - hadoken[i].w = 7; - hadoken[i].h = 4; - hadokenparams[i].sizex = 0.0f; - hadokenparams[i].sizey = 0.0f; - hadokenparams[i].offsetx = 0.0f; - hadokenparams[i].offsety = 0.0f;//-hadokenparams[0].sizey * 0.15f; - } - - // hadoken - hadoken[0].x = 1; - hadoken[0].y = 12; - hadoken[0].w = 7; - hadoken[0].h = 4; - hadokenparams[0].sizex = 70.0f * 2.5f; - hadokenparams[0].sizey = 40.0f * 2.5f; - hadokenparams[0].offsetx = -60.0f; - hadokenparams[0].offsety = 0;//-hadokenparams[0].sizey * 0.15f; - - hadoken[2].x = 8; - hadoken[2].y = 12; - hadoken[2].w = 8; - hadoken[2].h = 4; - hadokenparams[2].sizex = 80.0f * 2.5f; - hadokenparams[2].sizey = 40.0f * 2.5f; - hadokenparams[2].offsetx = -60.0f; - hadokenparams[2].offsety = 0;//-hadokenparams[1].sizey * 0.5f; - - hadoken[4].x = 17; - hadoken[4].y = 12; - hadoken[4].w = 7; - hadoken[4].h = 4; - hadokenparams[4].sizex = 70.0f * 2.5f; - hadokenparams[4].sizey = 40.0f * 2.5f; - hadokenparams[4].offsetx = -60.0f; - hadokenparams[4].offsety = 0;//-hadokenparams[2].sizey * 0.5f; - - // 0 = outline, 1 = body - body[0].x = 2; - body[0].y = 0; - body[0].w = 2; - body[0].h = 2; - body[1].x = 0; - body[1].y = 0; - body[1].w = 2; - body[1].h = 2; - - feet[0].x = 4; - feet[0].y = 1; - feet[0].w = 1; - feet[0].h = 0.5; - feet[1].x = 4; - feet[1].y = 1.52; - feet[1].w = 1; - feet[1].h = 0.48; - - leye.x = 5; - leye.y = 1; - leye.w = 0.5; - leye.h = 0.5; - - reye.x = 5; - reye.y = 1.0; - reye.w = 0.5; - reye.h = 0.5; -} - -void modc_entergame() -{ - col_init(32); - img_init(); - tilemap_init(); -} - -void modc_shutdown() -{ -} - -void modc_newsnapshot() -{ - int num = snap_num_items(SNAP_CURRENT); - for(int i = 0; i < num; i++) - { - snap_item item; - void *data = snap_get_item(SNAP_CURRENT, i, &item); - - if(item.type == EVENT_HEALTHMOD) - { - ev_healthmod *ev = (ev_healthmod *)data; - healthmods.create(vec2(ev->x, ev->y), ev->amount); - } - else if(item.type == EVENT_EXPLOSION) - { - ev_explosion *ev = (ev_explosion *)data; - vec2 p(ev->x, ev->y); - - // center explosion - temp_system.new_particle(p, vec2(0,0), 0.3f, 96.0f, 0, 0.95f); - temp_system.new_particle(p, vec2(0,0), 0.3f, 64.0f, 0, 0.95f); - temp_system.new_particle(p, vec2(0,0), 0.3f, 32.0f, 0, 0.95f); - temp_system.new_particle(p, vec2(0,0), 0.3f, 16.0f, 0, 0.95f); - - for(int i = 0; i < 16; i++) - { - vec2 v = normalize(vec2(frandom()-0.5f, frandom()-0.5f))*(128.0f+frandom()*128.0f); - temp_system.new_particle(p, v, 0.2f+0.25f*frandom(), 16.0f, 0, 0.985f); - } - - for(int i = 0; i < 16; i++) - { - vec2 v = normalize(vec2(frandom()-0.5f, frandom()-0.5f))*(256.0f+frandom()*512.0f); - temp_system.new_particle(p, v, 0.2f+0.25f*frandom(), 16.0f, 128.0f, 0.985f); - } - - for(int i = 0; i < 64; i++) - { - vec2 v = normalize(vec2(frandom()-0.5f, frandom()-0.5f))*(frandom()*256.0f); - temp_system.new_particle(p, v, 0.2f+0.25f*frandom(), 24.0f, 128.0f, 0.985f); - } - } - else if(item.type == EVENT_SMOKE) - { - ev_explosion *ev = (ev_explosion *)data; - vec2 p(ev->x, ev->y); - - // center explosion - vec2 v = normalize(vec2(frandom()-0.5f, -frandom()))*(32.0f+frandom()*32.0f); - temp_system.new_particle(p, v, 1.2f, 64.0f, 0, 0.95f); - v = normalize(vec2(frandom()-0.5f, -frandom()))*(128.0f+frandom()*128.0f); - temp_system.new_particle(p, v, 1.2f, 32.0f, 0, 0.95f); - v = normalize(vec2(frandom()-0.5f, -frandom()))*(128.0f+frandom()*128.0f); - temp_system.new_particle(p, v, 1.2f, 16.0f, 0, 0.95f); - - for(int i = 0; i < 8; i++) - { - vec2 v = normalize(vec2(frandom()-0.5f, frandom()-0.5f))*(64.0f+frandom()*64.0f); - temp_system.new_particle(p, v, 0.5f+0.5f*frandom(), 16.0f, 0, 0.985f); - } - - for(int i = 0; i < 8; i++) - { - vec2 v = normalize(vec2(frandom()-0.5f, frandom()-0.5f))*(128.0f+frandom()*256.0f); - temp_system.new_particle(p, v, 0.5f+0.5f*frandom(), 16.0f, 128.0f, 0.985f); - } - } - else if(item.type == EVENT_SOUND) - { - ev_sound *ev = (ev_sound *)data; - vec2 p(ev->x, ev->y); - int sound = (ev->sound & SOUND_MASK); - bool bstartloop = (ev->sound & SOUND_LOOPFLAG_STARTLOOP) != 0; - bool bstoploop = (ev->sound & SOUND_LOOPFLAG_STOPLOOP) != 0; - float vol, pan; - sound_vol_pan(p, &vol, &pan); - - switch(sound) - { - - // FIRE! - case SOUND_FIRE_GUN: - sound_gun_fire.play_random(volume_gun*vol, pan); - break; - case SOUND_FIRE_SHOTGUN: - sound_shotty_fire.play_random(volume_gun*vol, pan); - break; - case SOUND_FIRE_ROCKET: - sound_flump_launch.play_random(volume_gun*vol, pan); - break; - case SOUND_FIRE_MELEE: - sound_hammer_swing.play_random(volume_gun*vol, pan); - break; - case SOUND_FIRE_NINJA: - sound_ninja_attack.play_random(volume_gun*vol, pan); - break; - - // IMPACT - case SOUND_IMPACT_PROJECTILE_GUN: - break; - case SOUND_IMPACT_PROJECTILE_SHOTGUN: - break; - case SOUND_IMPACT_PROJECTILE_ROCKET: - sound_flump_explode.play_random(volume_hit*vol, pan); - break; - - // PLAYER - case SOUND_PLAYER_JUMP: - sound_body_jump.play_random(volume_tee*vol, pan); - break; - case SOUND_PLAYER_HURT_SHORT: - sound_pain_short.play_random(volume_tee*vol, pan); - break; - case SOUND_PLAYER_HURT_LONG: - sound_pain_long.play_random(volume_tee*vol, pan); - break; - case SOUND_PLAYER_SPAWN: - sound_spawn.play_random(volume_tee*vol, pan); - break; - case SOUND_PLAYER_CHAIN_LOOP: - sound_hook_loop.play_random(volume_gun*vol, pan); - break; - case SOUND_PLAYER_CHAIN_IMPACT: - sound_hook_attach.play_random(volume_gun*vol, pan); - break; - case SOUND_PLAYER_IMPACT: - sound_body_land.play_random(volume_hit*vol, pan); - break; - case SOUND_PLAYER_IMPACT_NINJA: - sound_ninja_hit.play_random(volume_hit*vol, pan); - break; - case SOUND_PLAYER_DIE: - sound_body_splat.play_random(volume_tee*vol, pan); - break; - case SOUND_PLAYER_SWITCHWEAPON: - sound_weapon_switch.play_random(volume_gun*vol, pan); - break; - case SOUND_PLAYER_EQUIP: - break; - case SOUND_PLAYER_LAND: - sound_body_land.play_random(volume_tee*vol, pan); - break; - } - } - } -} - -static void render_projectile(obj_projectile *prev, obj_projectile *current) -{ - gfx_texture_set(texture_weapon); - gfx_quads_begin(); - cell_select_ex(numcellsx,numcellsy,weaponprojtexcoord[current->type].x, weaponprojtexcoord[current->type].y, weaponprojtexcoord[current->type].w, weaponprojtexcoord[current->type].h); - vec2 vel(current->vx, current->vy); - - // TODO: interpolare angle aswell - if(length(vel) > 0.00001f) - gfx_quads_setrotation(get_angle(vel)); - else - gfx_quads_setrotation(0); - - vec2 pos = mix(vec2(prev->x, prev->y), vec2(current->x, current->y), snap_intratick()); - gfx_quads_draw(pos.x, pos.y,32,32); - gfx_quads_setrotation(0); - gfx_quads_end(); -} - -static void render_powerup(obj_powerup *prev, obj_powerup *current) -{ - //dbg_msg("client", "rendering powerup at %d,%d", current->x, current->y); - - gfx_texture_set(texture_weapon); - gfx_quads_begin(); - float angle = 0.0f; - float sizex = 64.0f; - float sizey = 64.0f; - if (current->type == POWERUP_TYPE_WEAPON) - { - angle = -0.25f * pi * 2.0f; - cell_select_ex(numcellsx,numcellsy,weapontexcoord[current->subtype].x, weapontexcoord[current->subtype].y, weapontexcoord[current->subtype].w, weapontexcoord[current->subtype].h); - sizex = weaponrenderparams[current->subtype].sizex; - sizey = weaponrenderparams[current->subtype].sizey; - } - else - cell_select_ex(numcellsx,numcellsy,poweruptexcoord[current->type].x, poweruptexcoord[current->type].y, poweruptexcoord[current->type].w, poweruptexcoord[current->type].h); - vec2 vel(current->vx, current->vy); - - gfx_quads_setrotation(angle); - // TODO: interpolare angle aswell - /*if(length(vel) > 0.00001f) - gfx_quads_setrotation(get_angle(vel)); - else - gfx_quads_setrotation(0);*/ - - vec2 pos = mix(vec2(prev->x, prev->y), vec2(current->x, current->y), snap_intratick()); - float offset = pos.y/32.0f + pos.x/32.0f; - gfx_quads_draw(pos.x+cosf(client_localtime()*2.0f+offset)*2.5f, pos.y+sinf(client_localtime()*2.0f+offset)*2.5f,sizex * 0.65f,sizey * 0.65f); - gfx_quads_setrotation(0); - gfx_quads_end(); -} - -float getmeleeangle(vec2 direction, obj_player* prev, obj_player* player) -{ - vec2 meleedir(0.53, -0.84); - meleedir = normalize(meleedir); - vec2 meleedirattack(0.95, -0.3); - meleedirattack = normalize(meleedirattack); - - if(direction.x < 0) - { - meleedir.x = -meleedir.x; - meleedirattack.x = -meleedirattack.x; - } - - // 0 -> visualtimeattack go to end pose, (len - visualime) -> go back to normal pose - - float angle = get_angle(meleedir); - if (prev->attackticks) - { - float angleattack = get_angle(meleedirattack); - int phase1tick = (player->attacklen - player->attackticks); - if (phase1tick < player->visualtimeattack) - { - float intratick = snap_intratick(); - float t = ((((float)phase1tick) + intratick)/(float)player->visualtimeattack); - angle = LERP(angle, angleattack, min(1.0f,max(0.0f,t))); - } - else - { - // go back to normal pose - int phase2tick = (player->attacklen - player->visualtimeattack - player->attackticks); - float intratick = snap_intratick(); - float t = ((((float)phase2tick) + intratick)/(float)player->visualtimeattack); - angle = LERP(angleattack, angle, min(1.0f,max(0.0f,t))); - } - } - /*if (prev->attackticks && !player->attackticks) - { - // blend back to normal - float angleattack = get_angle(meleedirattack); - angle = LERP(angleattack, angle, min(1.0f,max(0.0f,snap_intratick()))); - } - else if (player->attackticks) - { - float angleattack = get_angle(meleedirattack); - float intratick = snap_intratick(); - float t = ((((float)player->attackticks) - intratick)/(float)player->attacklen); - angle = LERP(angleattack, angle, min(1.0f,max(0.0f,t))); - }*/ - - return angle; -} - -float gethammereangle(vec2 direction, obj_player* prev, obj_player* player) -{ - float t = 0.0f; - if (prev->attackticks) - t = 1.0f - ((((float)player->attackticks) - snap_intratick())/(float)player->attacklen); - - vec2 pos; - float angle = 0.0f; - hammeranim.evalanim(t,pos,angle); - if(direction.x < 0) - angle = pi -angle;// + ; - //dbg_msg("anim", "Time: %f", t); - return angle; -} - -float getninjaangle(vec2 direction, obj_player* prev, obj_player* player) -{ - float t = 0.0f; - if (prev->attackticks) - t = 1.0f - ((((float)player->attackticks) - snap_intratick())/(float)player->attacklen); - - vec2 pos; - float angle = 0.0f; - ninjaanim.evalanim(t,pos,angle); - if(direction.x < 0) - angle = pi -angle;// + ; - //dbg_msg("anim", "Time: %f", t); - return angle; -} - - -float getrecoil(obj_player* prev, obj_player* player) -{ - // attack = -10 - float recoil = 0.0f; - if (prev->attackticks) - { - float attackrecoil = recoils[player->weapon]; - int phase1tick = (player->attacklen - player->attackticks); - if (phase1tick < player->visualtimeattack) - { - float intratick = snap_intratick(); - float t = ((((float)phase1tick) + intratick)/(float)player->visualtimeattack); - recoil = LERP(0, attackrecoil, min(1.0f,max(0.0f,t))); - } - else - { - // go back to normal pose - int phase2tick = (player->attacklen - player->visualtimeattack - player->attackticks); - float intratick = snap_intratick(); - float t = ((((float)phase2tick) + intratick)/(float)(player->attacklen - player->visualtimeattack)); - recoil = LERP(attackrecoil, 0.0f, min(1.0f,max(0.0f,t))); - } - } - return recoil; -} - -static void render_player(obj_player *prev, obj_player *player) -{ - vec2 direction = get_direction(player->angle); - float angle = player->angle/256.0f; - vec2 position = mix(vec2(prev->x, prev->y), vec2(player->x, player->y), snap_intratick()); - - // draw hook - if(player->hook_active) - { - gfx_texture_set(texture_weapon); - gfx_quads_begin(); - //gfx_quads_begin(); - - vec2 pos = position; - - vec2 hook_pos = mix(vec2(prev->hook_x, prev->hook_y), vec2(player->hook_x, player->hook_y), snap_intratick()); - - float d = distance(pos, hook_pos); - vec2 dir = normalize(pos-hook_pos); - - gfx_quads_setrotation(get_angle(dir)+pi); - - // render head - cell_select_ex(numcellsx,numcellsy, chainheadtexcoord.x,chainheadtexcoord.y, chainheadtexcoord.w, chainheadtexcoord.h); - gfx_quads_draw(hook_pos.x, hook_pos.y, 24,16); - - // render chain - cell_select_ex(numcellsx,numcellsy, chaintexcoord.x, chaintexcoord.y, chaintexcoord.w, chaintexcoord.h); - for(float f = 24; f < d; f += 24) - { - vec2 p = hook_pos + dir*f; - gfx_quads_draw(p.x, p.y,24,16); - } - - gfx_quads_setrotation(0); - gfx_quads_end(); - } - - // draw gun - { - gfx_texture_set(texture_weapon); - gfx_quads_begin(); - gfx_quads_setrotation(angle); - - if (player->modifier & (1 << MODIFIER_TYPE_NINJA)) - { - float playerangle = angle; - // render NINJA!!! (0.53, 0.84) when idle to -> (0.95, 0.3) at the end of attack - if(direction.x < 0) - cell_select_ex_flip_y(numcellsx, numcellsy, modifiertexcoord[MODIFIER_TYPE_NINJA].x, modifiertexcoord[MODIFIER_TYPE_NINJA].y, modifiertexcoord[MODIFIER_TYPE_NINJA].w, modifiertexcoord[MODIFIER_TYPE_NINJA].h); - else - cell_select_ex(numcellsx, numcellsy, modifiertexcoord[MODIFIER_TYPE_NINJA].x, modifiertexcoord[MODIFIER_TYPE_NINJA].y, modifiertexcoord[MODIFIER_TYPE_NINJA].w, modifiertexcoord[MODIFIER_TYPE_NINJA].h); - - angle = getninjaangle(direction, prev, player);//getmeleeangle(direction, prev, player); - vec2 ninjadir = get_direction(angle * 256.0f); - gfx_quads_setrotation(angle); - vec2 p = position + vec2(0,modifierrenderparams[MODIFIER_TYPE_NINJA].offsety)+ ninjadir * modifierrenderparams[MODIFIER_TYPE_NINJA].offsetx; - // if attack is active hold it differently and draw speedlines behind us? - gfx_quads_draw(p.x,p.y/*+bob*/,modifierrenderparams[MODIFIER_TYPE_NINJA].sizex, modifierrenderparams[MODIFIER_TYPE_NINJA].sizey); - - if ((player->attacklen - player->attackticks) <= (SERVER_TICK_SPEED / 5)) - { - gfx_quads_setrotation(playerangle); - int ihadoken = rand() % NUMHADOKENS; - cell_select_ex(numcellsx, numcellsy, hadoken[ihadoken].x, hadoken[ihadoken].y, hadoken[ihadoken].w, hadoken[ihadoken].h); - vec2 p = position + vec2(0,hadokenparams[ihadoken].offsety)+ direction * hadokenparams[ihadoken].offsetx; - gfx_quads_draw(p.x,p.y/*+bob*/,hadokenparams[ihadoken].sizex, hadokenparams[ihadoken].sizey); - } - } - else - { - // normal weapons - if(direction.x < 0) - cell_select_ex_flip_y(numcellsx, numcellsy, weapontexcoord[player->weapon].x, weapontexcoord[player->weapon].y, weapontexcoord[player->weapon].w, weapontexcoord[player->weapon].h); - else - cell_select_ex(numcellsx, numcellsy, weapontexcoord[player->weapon].x, weapontexcoord[player->weapon].y, weapontexcoord[player->weapon].w, weapontexcoord[player->weapon].h); - - vec2 dir = direction; - float recoil = 0.0f; - if (player->weapon == WEAPON_TYPE_MELEE) - { - // if attack is under way, bash stuffs - //angle = getmeleeangle(direction, prev, player); - angle = gethammereangle(direction, prev, player); - gfx_quads_setrotation(angle); - dir = get_direction(angle * 256.0f); - } - else - { - recoil = getrecoil(prev, player); - } - - vec2 p = position + vec2(0,weaponrenderparams[player->weapon].offsety) + dir * weaponrenderparams[player->weapon].offsetx - dir * recoil; - gfx_quads_draw(p.x,p.y/*+bob*/,weaponrenderparams[player->weapon].sizex, weaponrenderparams[player->weapon].sizey); - // draw muzzleflare - if (player->weapon == WEAPON_TYPE_GUN || player->weapon == WEAPON_TYPE_SHOTGUN) - { - // check if we're firing stuff - if (true)///prev->attackticks) - { - float alpha = 0.0f; - int phase1tick = (player->attacklen - player->attackticks); - if (phase1tick < (player->visualtimeattack + 3)) - { - float intratick = snap_intratick(); - float t = ((((float)phase1tick) + intratick)/(float)player->visualtimeattack); - alpha = LERP(2.0, 0.0f, min(1.0f,max(0.0f,t))); - } - - if (alpha > 0.0f) - { - float offsety = -muzzleparams[player->weapon].offsety; - int itex = rand() % nummuzzletex[player->weapon]; - if(direction.x < 0) - { - offsety = -offsety; - cell_select_ex_flip_y(numcellsx, numcellsy, muzzletexcoord[player->weapon][itex].x, muzzletexcoord[player->weapon][itex].y, muzzletexcoord[player->weapon][itex].w, muzzletexcoord[player->weapon][itex].h); - } - else - cell_select_ex(numcellsx, numcellsy, muzzletexcoord[player->weapon][itex].x, muzzletexcoord[player->weapon][itex].y, muzzletexcoord[player->weapon][itex].w, muzzletexcoord[player->weapon][itex].h); - - gfx_quads_setcolor(1.0f,1.0f,1.0f,alpha); - vec2 diry(-dir.y,dir.x); - p += dir * muzzleparams[player->weapon].offsetx + diry * offsety; - gfx_quads_draw(p.x,p.y/*+bob*/,muzzleparams[player->weapon].sizex, muzzleparams[player->weapon].sizey); - } - } - } - } - /*else - { - // minigun - if(direction.x < 0) - cell_select_flip_y(4,4,8,2); - else - cell_select(4,4,8,2); - vec2 p = position + vec2(0,3); - gfx_quads_draw(p.x,p.y,8*8,8*2); - }*/ - - gfx_quads_setrotation(0); - gfx_quads_end(); - } - - - gfx_texture_set(texture_char_default); - gfx_quads_begin(); - - float bob = 0; - - // draw foots - const float cyclelength = 128.0f; - const float steplength = 26; - const float lift = 4.0f; - bool stationary = player->vx < 1 && player->vx > -1; - bool inair = col_check_point(player->x, player->y+16) == 0; - - for(int p = 0; p < 2; p++) - { - // first pass we draw the outline - // second pass we draw the filling - - //int v_offset = p?0:5; - int outline = p;// ? 1 : 0; - float offsety = charids[player->clientid % 16] * 2.0f; - - for(int f = 0; f < 2; f++) - { - float basesize = 10.0f; - if(f == 1) - { - // draw body - float t = fmod(position.x, cyclelength/2)/(cyclelength/2); - bob = -sinf(pow(t,2)*pi) * 3; - cell_select_ex(charnumcellsx,charnumcellsy, body[outline].x,body[outline].y + offsety,body[outline].w,body[outline].h); - //cell_select_ex(16,16, 0,0+v_offset,4,4); - //const float size = 64.0f; - if(stationary || inair) - bob = 0; - gfx_quads_draw(position.x, position.y-5+bob, 4*basesize, 4*basesize); - - // draw eyes - if(p == 1) - { - //cell_select_ex(16,16, 8,3,1,1); - vec2 md = get_direction(player->angle); - float mouse_dir_x = md.x; - float mouse_dir_y = md.y; - - // normal - cell_select_ex(charnumcellsx,charnumcellsy, leye.x,leye.y + offsety,leye.w,leye.h); - gfx_quads_draw(position.x-4+mouse_dir_x*4, position.y-8+mouse_dir_y*3+bob, basesize, basesize); - cell_select_ex(charnumcellsx,charnumcellsy, reye.x,reye.y + offsety,reye.w,reye.h); - gfx_quads_draw(position.x+4+mouse_dir_x*4, position.y-8+mouse_dir_y*3+bob, basesize, basesize); - } - } - - // draw feet - //cell_select_ex(16,16, 5,2+v_offset, 2,2); - cell_select_ex(charnumcellsx,charnumcellsy, feet[outline].x,feet[outline].y + offsety, feet[outline].w,feet[outline].h); - float w = basesize*2.5f; - float h = basesize*1.425f; - if(inair) - { - float r = 0.0f; - if(player->vy < 0.0f) - r = player->vy/3.0f; - else - r = player->vy/15.0f; - - // clamp the rotation - if(r > 0.5f) r = 0.5f; - if(r < -0.5f) r = -0.5f; - - if(player->vx > 0.0f) - r *= -1.0f; - gfx_quads_setrotation(r); - gfx_quads_drawTL(position.x-4+f*7-w/2, position.y+16 - h, w, h); - gfx_quads_setrotation(0); - } - else if(stationary) - { - // stationary - gfx_quads_drawTL(position.x-7+f*14-w/2, position.y+16 - h, w, h); - } - else - { - /* - The walk cycle, 2 parts - - 111 - 1 1 - 2 1 - 2 1 - 2222221 - GROUND GROUND GROUND - */ - - // moving - float tx = position.x+f*(cyclelength/2); - float t = fmod(tx, cyclelength) / cyclelength; - if(player->vx < 0) - t = 1.0f-t; - - float y; - float x = 0; - float r = 0; - float r_back = 1.5f; - - if(t < 0.5f) - { - // stomp down foot (part 1) - float st = t*2; - y = 1.0f-pow(st, 0.5f) + sinf(pow(st,2)*pi)*0.5f; - x = -steplength/2 + st*steplength; - r = r_back*(1-st) + sinf(pow(st,1.5f)*pi*2); - } - else - { - // lift foot up again (part 2) - float st = (t-0.5f)*2; - y = pow(st, 5.0f); - x = steplength/2 - st*steplength; - r = y*r_back; - } - - - if(player->vx > 0) - { - gfx_quads_setrotation(r); - gfx_quads_drawTL(position.x+x-w/2, position.y+16-y*lift - h, w, h); - } - else - { - gfx_quads_setrotation(-r); - gfx_quads_drawTL(position.x-x-w/2, position.y+16-y*lift - h, w, h); - } - gfx_quads_setrotation(0); - } - - } - } - - gfx_quads_end(); - - -} - -static player_input oldinput; -static bool bfirst = true; -void modc_render() -{ - if (bfirst) - { - bfirst = false; - oldinput.activeweapon = 0; - oldinput.angle = 0; - oldinput.blink = 0; - oldinput.fire = 0; - oldinput.hook = 0; - oldinput.jump = 0; - oldinput.left = 0; - oldinput.right = 0; - } - // fetch new input - { - int x, y; - inp_mouse_relative(&x, &y); - mouse_pos += vec2(x, y); - float l = length(mouse_pos); - if(l > 600.0f) - mouse_pos = normalize(mouse_pos)*600.0f; - } - - // snap input - { - player_input input; - input.left = inp_key_pressed('A'); - input.right = inp_key_pressed('D'); - float a = atan((float)mouse_pos.y/(float)mouse_pos.x); - if(mouse_pos.x < 0) - a = a+pi; - input.angle = (int)(a*256.0f); - input.jump = inp_key_pressed(baselib::keys::space) || inp_key_pressed('W'); - - input.fire = inp_mouse_button_pressed(0);// | (oldinput.fire << 16); - //oldinput.fire = input.fire & 0x0000ffff; - - input.hook = inp_mouse_button_pressed(1) || inp_key_pressed(baselib::keys::lctrl); // be nice to mac users O.o - input.blink = inp_key_pressed('S'); - - // Weapon switching - input.activeweapon = inp_key_pressed('1') ? 0x80000000 : 0; - if (!input.activeweapon) - input.activeweapon = inp_key_pressed('2') ? 0x80000000 | 1 : 0; - if (!input.activeweapon) - input.activeweapon = inp_key_pressed('3') ? 0x80000000 | 2 : 0; - if (!input.activeweapon) - input.activeweapon = inp_key_pressed('4') ? 0x80000000 | 3 : 0; - /*if (!input.activeweapon) - input.activeweapon = inp_key_pressed('5') ? 0x80000000 | 4 : 0;*/ - - snap_input(&input, sizeof(input)); - } - - // setup world view - { - // 1. fetch local player - // 2. set him to the center - - int num = snap_num_items(SNAP_CURRENT); - for(int i = 0; i < num; i++) - { - snap_item item; - void *data = snap_get_item(SNAP_CURRENT, i, &item); - - if(item.type == OBJTYPE_PLAYER) - { - obj_player *player = (obj_player *)data; - if(player->local) - { - local_player = player; - local_player_pos = vec2(player->x, player->y); - - void *p = snap_find_item(SNAP_PREV, item.type, item.id); - if(p) - local_player_pos = mix(vec2(((obj_player *)p)->x, ((obj_player *)p)->y), local_player_pos, snap_intratick()); - break; - } - } - } - } - - // pseudo format - float zoom = inp_key_pressed('T') ? 1.0 : 3.0f; - - float width = 400*zoom; - float height = 300*zoom; - float screen_x = 0; - float screen_y = 0; - - // center at char but can be moved when mouse is far away - float offx = 0, offy = 0; - int deadzone = 300; - if(mouse_pos.x > deadzone) offx = mouse_pos.x-deadzone; - if(mouse_pos.x <-deadzone) offx = mouse_pos.x+deadzone; - if(mouse_pos.y > deadzone) offy = mouse_pos.y-deadzone; - if(mouse_pos.y <-deadzone) offy = mouse_pos.y+deadzone; - offx = offx*2/3; - offy = offy*2/3; - - screen_x = local_player_pos.x+offx; - screen_y = local_player_pos.y+offy; - - gfx_mapscreen(screen_x-width/2, screen_y-height/2, screen_x+width/2, screen_y+height/2); - - // draw background - gfx_clear(0.65f,0.78f,0.9f); - - { - - vec2 pos(local_player_pos.x*0.5f, local_player_pos.y*0.5f); - - gfx_texture_set(-1); - gfx_blend_additive(); - gfx_quads_begin(); - const int rays = 10; - gfx_quads_setcolor(1.0f,1.0f,1.0f,0.025f); - for(int r = 0; r < rays; r++) - { - float a = r/(float)rays + client_localtime()*0.05f; - float size = (1.0f/(float)rays)*0.25f; - vec2 dir0(sinf((a-size)*pi*2.0f), cosf((a-size)*pi*2.0f)); - vec2 dir1(sinf((a+size)*pi*2.0f), cosf((a+size)*pi*2.0f)); - - //gfx_quads_draw_freeform(0,0, -100,0, -100,-100, 0,-100); - - gfx_quads_setcolorvertex(0, 1.0f,1.0f,1.0f,0.025f); - gfx_quads_setcolorvertex(1, 1.0f,1.0f,1.0f,0.025f); - gfx_quads_setcolorvertex(2, 1.0f,1.0f,1.0f,0.0f); - gfx_quads_setcolorvertex(3, 1.0f,1.0f,1.0f,0.0f); - const float range = 1000.0f; - gfx_quads_draw_freeform( - pos.x+dir0.x, pos.y+dir0.y, - pos.x+dir1.x, pos.y+dir1.y, - pos.x+dir0.x*range, pos.y+dir0.y*range, - pos.x+dir1.x*range, pos.y+dir1.y*range); - } - gfx_quads_end(); - gfx_blend_normal(); - - gfx_texture_set(texture_sun); - gfx_quads_begin(); - gfx_quads_draw(pos.x, pos.y, 256, 256); - gfx_quads_end(); - } - - // render map - tilemap_render(32.0f, 0); -#ifdef _DEBUG - float speed = 0.0f; -#endif - // render items - int num = snap_num_items(SNAP_CURRENT); - for(int i = 0; i < num; i++) - { - snap_item item; - void *data = snap_get_item(SNAP_CURRENT, i, &item); - - if(item.type == OBJTYPE_PLAYER) - { - void *prev = snap_find_item(SNAP_PREV, item.type, item.id); - if(prev) - { - render_player((obj_player *)prev, (obj_player *)data); -/*#ifdef _DEBUG - { - obj_player *p = (obj_player *)prev; - obj_player *c = (obj_player *)data; - vec2 positionold = vec2(p->x, p->y); - vec2 poscur = vec2(c->x, c->y); - speed = distance(positionold,poscur); - } -#endif*/ - } - } - else if(item.type == OBJTYPE_PROJECTILE) - { - void *prev = snap_find_item(SNAP_PREV, item.type, item.id); - if(prev) - render_projectile((obj_projectile *)prev, (obj_projectile *)data); - } - else if(item.type == OBJTYPE_POWERUP) - { - void *prev = snap_find_item(SNAP_PREV, item.type, item.id); - if(prev) - render_powerup((obj_powerup*)prev, (obj_powerup *)data); - } - } - - // render particles - temp_system.update(client_frametime()); - temp_system.render(); - - tilemap_render(32.0f, 1); - - // render health mods - healthmods.render(); - - // render cursor - // FIXME CURSOR!!! - - if(local_player) - { - gfx_texture_set(texture_weapon); - gfx_quads_begin(); - if (local_player->modifier & (1 << MODIFIER_TYPE_NINJA)) - cell_select_ex(numcellsx,numcellsy, modifiertexcoordcursor[MODIFIER_TYPE_NINJA].x, modifiertexcoordcursor[MODIFIER_TYPE_NINJA].y, modifiertexcoordcursor[MODIFIER_TYPE_NINJA].w, modifiertexcoordcursor[MODIFIER_TYPE_NINJA].h); - else - cell_select_ex(numcellsx,numcellsy, weapontexcoordcursor[local_player->weapon].x, weapontexcoordcursor[local_player->weapon].y, weapontexcoordcursor[local_player->weapon].w, weapontexcoordcursor[local_player->weapon].h); - float cursorsize = 64; - gfx_quads_draw(local_player_pos.x+mouse_pos.x, local_player_pos.y+mouse_pos.y,cursorsize,cursorsize); - - - // render ammo count - // render gui stuff - gfx_quads_end(); - gfx_quads_begin(); - gfx_mapscreen(0,0,400,300); - cell_select_ex(numcellsx,numcellsy, weaponprojtexcoord[local_player->weapon].x, weaponprojtexcoord[local_player->weapon].y, weaponprojtexcoord[local_player->weapon].w, weaponprojtexcoord[local_player->weapon].h); - for (int i = 0; i < local_player->ammocount; i++) - { - gfx_quads_drawTL(10+i*12,34,10,10); - } - gfx_quads_end(); - - gfx_texture_set(texture_game); - gfx_quads_begin(); - int h = 0; - cell_select_ex(32,16, 0,0, 4,4); - for(; h < local_player->health; h++) - gfx_quads_drawTL(10+h*12,10,10,10); - - cell_select_ex(32,16, 5,0, 4,4); - for(; h < 10; h++) - gfx_quads_drawTL(10+h*12,10,10,10); - - h = 0; - cell_select_ex(32,16, 0,5, 4,4); - for(; h < local_player->armor; h++) - gfx_quads_drawTL(10+h*12,22,10,10); - - cell_select_ex(32,16, 5,5, 4,4); - for(; h < 10; h++) - gfx_quads_drawTL(10+h*12,22,10,10); - gfx_quads_end(); - - // render speed -/*#ifdef _DEBUG - gfx_texture_set(font_texture); - char text[256]; - sprintf(text,"speed: %f",speed); - gfx_quads_text(300,20,10,text); -#endif*/ - } - // render gui stuff - gfx_mapscreen(0,0,400,300); - // render score board - if(inp_key_pressed(baselib::keys::tab)) - { - gfx_texture_set(font_texture); - gfx_quads_text(10, 50, 8, "Score Board"); - - int num = snap_num_items(SNAP_CURRENT); - int row = 1; - for(int i = 0; i < num; i++) - { - snap_item item; - void *data = snap_get_item(SNAP_CURRENT, i, &item); - - if(item.type == OBJTYPE_PLAYER) - { - obj_player *player = (obj_player *)data; - if(player) - { - char buf[128]; - char name[32]; - snap_decode_string(player->name, name, 32); - sprintf(buf, "%4d %s", player->score, name); - gfx_quads_text(10, 50 + 10 * row, 8, buf); - row++; - } - } - } - } -} diff --git a/src/game/game_server.cpp b/src/game/game_server.cpp deleted file mode 100644 index 5e93165a..00000000 --- a/src/game/game_server.cpp +++ /dev/null @@ -1,2122 +0,0 @@ -#include -#include -#include "game.h" - -using namespace baselib; - -// --------- PHYSICS TWEAK! -------- -const float ground_control_speed = 7.0f; -const float ground_control_accel = 2.0f; -const float ground_friction = 0.5f; -const float ground_jump_speed = 12.0f; -const float air_control_speed = 3.5f; -const float air_control_accel = 1.2f; -const float air_friction = 0.95f; -const float hook_length = 32*10.0f; -const float hook_fire_speed = 45.0f; -const float hook_drag_accel = 3.0f; -const float hook_drag_speed = 15.0f; -const float gravity = 0.5f; - -class player* get_player(int index); -void create_healthmod(vec2 p, int amount); -void create_explosion(vec2 p, int owner = -1, bool bnodamage = false); -void create_smoke(vec2 p); -void create_sound(vec2 pos, int sound, int loopflags = 0); -class player* intersect_player(vec2 pos0, vec2 pos1, vec2& new_pos, class entity* notthis = 0); - -// TODO: rewrite this smarter! -void move_box(vec2 *inout_pos, vec2 *inout_vel, vec2 size, float elasticity) -{ - // do the move - vec2 pos = *inout_pos; - vec2 vel = *inout_vel; - - float distance = length(vel); - int max = (int)distance; - - vec2 offsets[4] = { vec2(-size.x/2, -size.y/2), vec2( size.x/2, -size.y/2), - vec2(-size.x/2, size.y/2), vec2( size.x/2, size.y/2)}; - - if(distance > 0.00001f) - { - vec2 old_pos = pos; - for(int i = 0; i <= max; i++) - { - float amount = i/(float)max; - if(max == 0) - amount = 0; - - vec2 new_pos = pos + vel*amount; // TODO: this row is not nice - - for(int p = 0; p < 4; p++) - { - vec2 np = new_pos+offsets[p]; - vec2 op = old_pos+offsets[p]; - if(col_check_point(np)) - { - int affected = 0; - if(col_check_point(np.x, op.y)) - { - vel.x = -vel.x*elasticity; - pos.x = old_pos.x; - new_pos.x = old_pos.x; - affected++; - } - - if(col_check_point(op.x, np.y)) - { - vel.y = -vel.y*elasticity; - pos.y = old_pos.y; - new_pos.y = old_pos.y; - affected++; - } - - if(!affected) - { - new_pos = old_pos; - pos = old_pos; - vel *= -elasticity; - } - } - } - - old_pos = new_pos; - } - - pos = old_pos; - } - - *inout_pos = pos; - *inout_vel = vel; -} - -// TODO: rewrite this smarter! -bool intersect_line(vec2 pos0, vec2 pos1, vec2 *out) -{ - float d = distance(pos0, pos1); - - for(float f = 0; f < d; f++) - { - float a = f/d; - vec2 pos = mix(pos0, pos1, a); - if(col_check_point(pos)) - { - if(out) - *out = pos; - return true; - } - } - if(out) - *out = pos1; - return false; -} - -// -class event_handler -{ - static const int MAX_EVENTS = 128; - static const int MAX_DATASIZE = 128*4; - - int types[MAX_EVENTS]; // TODO: remove some of these arrays - int offsets[MAX_EVENTS]; - int sizes[MAX_EVENTS]; - char data[MAX_DATASIZE]; - - int current_offset; - int num_events; -public: - event_handler() - { - num_events = 0; - } - - void *create(int type, int size) - { - void *p = &data[current_offset]; - offsets[num_events] = current_offset; - types[num_events] = type; - sizes[num_events] = size; - current_offset += size; - num_events++; - return p; - } - - void clear() - { - num_events = 0; - current_offset = 0; - } - - void snap(int snapping_client) - { - for(int i = 0; i < num_events; i++) - { - void *d = snap_new_item(types[i], i, sizes[i]); - mem_copy(d, &data[offsets[i]], sizes[i]); - } - } -}; - -static event_handler events; -/* -template -class pool -{ - struct element - { - int next_free; - T data; - }; - - element elements[SIZE]; - int first_free; -public: - pool() - { - first_free = 0; - for(int i = 0; i < SIZE; i++) - elements[i].next_free = i+1; - elements[SIZE-1].next_free = -1; - } -};*/ - -// a basic entity -class entity -{ -private: - friend class game_world; - friend class player; - entity *prev_entity; - entity *next_entity; - int index; - -public: - vec2 pos; - float proximity_radius; - unsigned flags; - int objtype; - - enum - { - FLAG_DESTROY=0x00000001, - }; - - entity(int objtype) - { - this->objtype = objtype; - pos = vec2(0,0); - flags = 0; - proximity_radius = 0; - } - - virtual ~entity() - { - } - - virtual void destroy() { delete this; } - virtual void tick() {} - virtual void snap(int snapping_client) {} - - virtual bool take_damage(vec2 force, int dmg, int from) { return true; } -}; - -class powerup : public entity -{ -public: - static const int phys_size = 14; - enum - { - POWERUP_FLAG_HOOKABLE = 1 << 0, - }; - vec2 vel; - class player* playerhooked; - int type; - int id; - int subtype; // weapon type for instance? - int numitems; // number off powerup items - int flags; - int spawntick; - powerup(int _type, int _subtype = 0, int _numitems = 0, int _flags = 0); - - static void spawnrandom(int _lifespan); - - void tick(); - - void snap(int snapping_client); -}; - -// game world. handles all entities -class game_world -{ -public: - entity *first_entity; - game_world() - { - first_entity = 0x0; - } - - int find_entities(vec2 pos, float radius, entity **ents, int max) - { - int num = 0; - for(entity *ent = first_entity; ent; ent = ent->next_entity) - { - if(distance(ent->pos, pos) < radius+ent->proximity_radius) - { - ents[num] = ent; - num++; - if(num == max) - break; - } - } - - return num; - } - - int find_entities(vec2 pos, float radius, entity **ents, int max, const int* types, int maxtypes) - { - int num = 0; - for(entity *ent = first_entity; ent; ent = ent->next_entity) - { - for (int i = 0; i < maxtypes; i++) - { - if (ent->objtype != types[i]) - continue; - - if(distance(ent->pos, pos) < radius+ent->proximity_radius) - { - ents[num] = ent; - num++; - if(num == max) - break; - } - } - } - - return num; - } - - void insert_entity(entity *ent) - { - // insert it - if(first_entity) - first_entity->prev_entity = ent; - ent->next_entity = first_entity; - ent->prev_entity = 0x0; - first_entity = ent; - } - - void destroy_entity(entity *ent) - { - ent->flags |= entity::FLAG_DESTROY; - // call destroy - //remove_entity(ent); - //ent->destroy(); - } - - void remove_entity(entity *ent) - { - // remove - if(ent->prev_entity) - ent->prev_entity->next_entity = ent->next_entity; - else - first_entity = ent->next_entity; - if(ent->next_entity) - ent->next_entity->prev_entity = ent->prev_entity; - } - - // - void snap(int snapping_client) - { - for(entity *ent = first_entity; ent; ent = ent->next_entity) - ent->snap(snapping_client); - } - - void tick() - { - // update all objects - for(entity *ent = first_entity; ent; ent = ent->next_entity) - ent->tick(); - - // destroy objects marked for destruction - entity *ent = first_entity; - while(ent) - { - entity *next = ent->next_entity; - if(ent->flags&entity::FLAG_DESTROY) - { - remove_entity(ent); - ent->destroy(); - } - ent = next; - } - } -}; - -static game_world world; - -// projectile entity -class projectile : public entity -{ -public: - enum - { - PROJECTILE_FLAGS_EXPLODE = 1 << 0, - }; - vec2 vel; - entity* powner; - int lifespan; - int id; - int owner; - int type; - int flags; - int damage; - int sound_impact; - float force; - - projectile(int type, int owner, vec2 pos, vec2 vel, int span, entity* powner, int damage, int flags = 0, float force = 0.0f, int sound_impact = -1) : - entity(OBJTYPE_PROJECTILE) - { - static int current_id = 0; - this->id = current_id++; - this->type = type; - this->pos = pos; - this->vel = vel; - this->lifespan = span; - this->owner = owner; - this->powner = powner; - this->flags = flags; - this->force = force; - this->damage = damage; - this->sound_impact = sound_impact; - world.insert_entity(this); - } - - void tick() - { - vec2 oldpos = pos; - vel.y += 0.25f; - pos += vel; - lifespan--; - // check player intersection as well - entity* targetplayer = (entity*)intersect_player(oldpos, pos, oldpos, powner); - if(targetplayer || lifespan < 0 || col_check_point((int)pos.x, (int)pos.y)) - { - if (lifespan >= 0) - create_sound(pos, sound_impact); - if (flags & PROJECTILE_FLAGS_EXPLODE) - { - create_explosion(oldpos, owner); - } - else if (targetplayer) - { - targetplayer->take_damage(normalize(vel) * force, damage, owner); - } - world.destroy_entity(this); - } - } - - void snap(int snapping_client) - { - obj_projectile *proj = (obj_projectile *)snap_new_item(OBJTYPE_PROJECTILE, id, sizeof(obj_projectile)); - proj->x = (int)pos.x; - proj->y = (int)pos.y; - proj->vx = (int)vel.x; - proj->vy = (int)vel.y; - proj->type = type; - } -}; - -// player entity -class player : public entity -{ -public: - static const int phys_size = 28; - enum - { - WEAPON_NEEDRELOAD = 1 << 0, - WEAPON_DROPONUNEQUIP = 1 << 1, - WEAPON_DRAWSAMMO = 1 << 2, - WEAPON_HASSECONDARY = 1 << 3, - WEAPON_ISACTIVE = 1 << 4, // has the item - WEAPON_AUTOFIRE = 1 << 5, - - WEAPON_PROJECTILETYPE_GUN = 0, - WEAPON_PROJECTILETYPE_ROCKET = 1, - WEAPON_PROJECTILETYPE_SHOTGUN = 2, - - // Gun - - - // modifiers - MODIFIER_HASACTIVATIONS = 1 << 0, - MODIFIER_TIMELIMITED = 1 << 1, - MODIFIER_ISACTIVE = 1 << 2, - MODIFIER_NEEDSACTIVATION = 1 << 3, - - MODIFIER_RETURNFLAGS_OVERRIDEWEAPON = 1 << 0, - MODIFIER_RETURNFLAGS_OVERRIDEVELOCITY = 1 << 1, - MODIFIER_RETURNFLAGS_OVERRIDEPOSITION = 1 << 2, - MODIFIER_RETURNFLAGS_OVERRIDEGRAVITY = 1 << 3, - }; - class weapon - { - public: - entity* hitobjects[10]; - int numobjectshit; // for melee, so we don't hit the same object more than once per bash - int weapontype; - int equiptime; - int unequiptime; - int numammo; - int magsize; - int nummagazines; - int flags; - int firetime; - int reloadtime; - int projectileclass; - int damage; - int sound_fire; - int sound_equip; - int sound_impact; - int sound_impact_projectile; - int visualtimeattack; - float projectilevel; - float projectilespan; - float reach; // for melee - float force; - float recoilforce; - float projoffsety; - float projoffsetx; - - weapon() - { - weapontype = 0; - numammo = 0; - flags = 0; - reloadtime = 0; - projectileclass = 0; - numobjectshit = 0; - reach = 0.0f; - force = 5.0f; - damage = 1; - sound_fire = -1; - sound_equip = -1; - sound_impact = -1; - sound_impact_projectile = -1, - visualtimeattack = 3; - recoilforce = 0.0f; - projoffsety = 0.0f; - projoffsetx = 0.0f; - } - - void setgun(int ammo = 10) - { - weapontype = WEAPON_TYPE_GUN; - flags = 0;//WEAPON_DRAWSAMMO; - numammo = ammo; - projectileclass = WEAPON_PROJECTILETYPE_GUN; - firetime = SERVER_TICK_SPEED/10; - magsize = 0; - projectilevel = 30.0f; - projectilespan = 50.0f * 1.0f; - sound_fire = SOUND_FIRE_GUN; - sound_equip = SOUND_EQUIP_GUN; - sound_impact_projectile = SOUND_IMPACT_PROJECTILE_GUN; - projoffsety = -10.0f; - projoffsetx = 24.0f; - } - - void setrocket(int ammo = 10) - { - weapontype = WEAPON_TYPE_ROCKET; - flags = WEAPON_DRAWSAMMO; - numammo = ammo; - projectileclass = WEAPON_PROJECTILETYPE_ROCKET; - projectilevel = 15.0f; - projectilespan = 50.0f * 5.0f; - firetime = SERVER_TICK_SPEED * 4/5; - magsize = 0; - recoilforce = 5.0f; - sound_fire = SOUND_FIRE_ROCKET; - sound_equip = SOUND_EQUIP_ROCKET; - sound_impact_projectile = SOUND_IMPACT_PROJECTILE_ROCKET; - projoffsety = -17.0f; - projoffsetx = 24.0f; - } - - /*void setsniper(int ammo = 10) - { - weapontype = WEAPON_TYPE_SNIPER; - flags = WEAPON_DRAWSAMMO | WEAPON_HASSECONDARY | WEAPON_NEEDRELOAD; - numammo = ammo; - projectileclass = WEAPON_PROJECTILETYPE_SNIPER; - projectilevel = 30.0f; - projectilespan = 50.0f * 5.0f; - firetime = SERVER_TICK_SPEED; - reloadtime = SERVER_TICK_SPEED/2; - magsize = 2; - recoilforce = 20.0f; - }*/ - - void setshotgun(int ammo = 10) - { - weapontype = WEAPON_TYPE_SHOTGUN; - flags = WEAPON_DRAWSAMMO | WEAPON_NEEDRELOAD; - numammo = ammo; - projectileclass = WEAPON_PROJECTILETYPE_SHOTGUN; - projectilevel = 30.0f; - projectilespan = 50.0f * 5.0f; - firetime = SERVER_TICK_SPEED/2; - reloadtime = SERVER_TICK_SPEED/2; - magsize = 2; - damage = 3; - recoilforce = 5.0f; - sound_fire = SOUND_FIRE_SHOTGUN; - sound_equip = SOUND_EQUIP_SHOTGUN; - sound_impact_projectile = SOUND_IMPACT_PROJECTILE_SHOTGUN; - projoffsety = -17.0f; - projoffsetx = 24.0f; - } - - void setmelee(int ammo = 10) - { - weapontype = WEAPON_TYPE_MELEE; - flags = 0;//WEAPON_AUTOFIRE; - numammo = ammo; - projectileclass = -1; - firetime = SERVER_TICK_SPEED/5; - reloadtime = 0; - magsize = 2; - numobjectshit = 0; - reach = 15.0f; - damage = 1; - sound_fire = SOUND_FIRE_MELEE; - sound_equip = SOUND_EQUIP_MELEE; - sound_impact = SOUND_PLAYER_IMPACT; - } - - void settype() - { - switch(weapontype) - { - case WEAPON_TYPE_GUN: - { - setgun(); - break; - } - case WEAPON_TYPE_ROCKET: - { - setrocket(); - break; - } - /*case WEAPON_TYPE_SNIPER: - { - setsniper(); - break; - }*/ - case WEAPON_TYPE_SHOTGUN: - { - setshotgun(); - break; - } - case WEAPON_TYPE_MELEE: - { - setmelee(); - break; - } - default: - break; - } - } - - int activate(player* player) - { - // create sound event for fire - int projectileflags = 0; - create_sound(player->pos, sound_fire); - - switch (weapontype) - { - case WEAPON_TYPE_ROCKET: - projectileflags |= projectile::PROJECTILE_FLAGS_EXPLODE; - case WEAPON_TYPE_GUN: - //case WEAPON_TYPE_SNIPER: - case WEAPON_TYPE_SHOTGUN: - { - if (flags & WEAPON_DRAWSAMMO) - numammo--; - // Create projectile - new projectile(projectileclass, player->client_id, player->pos+vec2(0,projoffsety)+player->direction*projoffsetx, player->direction*projectilevel, projectilespan, player, damage, projectileflags, force, sound_impact_projectile); - // recoil force if any - if (recoilforce > 0.0f) - { - vec2 dir(player->direction.x,0.5); - if (dir.x == 0.0f) - dir.x = 0.5f; - else - dir = normalize(dir); - player->vel -= dir * recoilforce; - } - return firetime; - } - case WEAPON_TYPE_MELEE: - { - // Start bash sequence - numobjectshit = 0; - return firetime; - } - default: - return 0; - } - } - - void update(player* owner, int fire_timeout) - { - switch(weapontype) - { - case WEAPON_TYPE_MELEE: - { - // No more melee - if (fire_timeout <= 0) - return; - - // only one that needs update (for now) - // do selection for the weapon and bash anything in it - // check if we hit anything along the way - int type = OBJTYPE_PLAYER; - entity *ents[64]; - vec2 dir = owner->pos + owner->direction * reach; - float radius = length(dir * 0.5f); - vec2 center = owner->pos + dir * 0.5f; - int num = world.find_entities(center, radius, ents, 64, &type, 1); - - for (int i = 0; i < num; i++) - { - // Check if entity is a player - if (ents[i] == owner) - continue; - // make sure we haven't hit this object before - bool balreadyhit = false; - for (int j = 0; j < numobjectshit; j++) - { - if (hitobjects[j] == ents[i]) - balreadyhit = true; - } - if (balreadyhit) - continue; - - // check so we are sufficiently close - if (distance(ents[i]->pos, owner->pos) > (owner->phys_size * 2.0f)) - continue; - - // hit a player, give him damage and stuffs... - // create sound for bash - create_sound(ents[i]->pos, sound_impact); - - // set his velocity to fast upward (for now) - create_smoke(ents[i]->pos); - hitobjects[numobjectshit++] = ents[i]; - ents[i]->take_damage(vec2(0,10.0f), damage, owner->client_id); - player* target = (player*)ents[i]; - vec2 dir; - if (length(target->pos - owner->pos) > 0.0f) - dir = normalize(target->pos - owner->pos); - else - dir = vec2(0,-1); - target->vel += dir * 10.0f + vec2(0,-10.0f); - } - break; - } - default: - break; - } - } - }; - - class modifier - { - public: - vec2 activationdir; - entity* hitobjects[10]; - int numobjectshit; - float velocity; - int modifiertype; - int duration; - int numactivations; - int activationtime; - int cooldown; - int movetime; - int visualtimeattack; - int currentactivation; - int currentmovetime; - int currentcooldown; - int damage; - int flags; - int sound_impact; - int sound_activate; - - modifier() - { - modifiertype = 0; - duration = 0; - numobjectshit = 0; - numactivations = 0; - activationtime = 0; - cooldown = 0; - movetime = 0; - currentactivation = 0; - currentmovetime = 0; - currentcooldown =0; - damage = 0; - flags = 0; - activationdir = vec2(0.0f, 1.0f); - velocity = 0.0f; - visualtimeattack = 0; - sound_impact = -1; - } - - void setninja() - { - modifiertype = MODIFIER_TYPE_NINJA; - duration = SERVER_TICK_SPEED * 15; - numactivations = -1; - movetime = SERVER_TICK_SPEED / 5; - activationtime = SERVER_TICK_SPEED / 2; - cooldown = SERVER_TICK_SPEED; - currentactivation = 0; - currentmovetime = 0; - numobjectshit = 0; - damage = 3; - flags = MODIFIER_TIMELIMITED | MODIFIER_NEEDSACTIVATION; - velocity = 50.0f; - visualtimeattack = 3; - sound_impact = SOUND_PLAYER_IMPACT_NINJA; - sound_activate = SOUND_FIRE_NINJA; - } - - void settimefield() - { - modifiertype = MODIFIER_TYPE_TIMEFIELD; - duration = SERVER_TICK_SPEED * 10; - numactivations = -1; - activationtime = SERVER_TICK_SPEED; - numobjectshit = 0; - currentactivation = 0; - flags = MODIFIER_TIMELIMITED; - velocity = 0.0f; - } - - void settype() - { - switch (modifiertype) - { - case MODIFIER_TYPE_NINJA: - { - setninja(); - break; - } - case MODIFIER_TYPE_TIMEFIELD: - { - settimefield(); - break; - } - default: - break; - } - } - - int updateninja(player* player) - { - if (currentactivation <= 0) - return MODIFIER_RETURNFLAGS_OVERRIDEWEAPON; - currentactivation--; - currentmovetime--; - - if (currentmovetime == 0) - { - // reset player velocity - player->vel *= 0.2f; - //return MODIFIER_RETURNFLAGS_OVERRIDEWEAPON; - } - - if (currentmovetime > 0) - { - // Set player velocity - player->vel = activationdir * velocity; - vec2 oldpos = player->pos; - move_box(&player->pos, &player->vel, vec2(player->phys_size, player->phys_size), 0.0f); - // reset velocity so the client doesn't predict stuff - player->vel = vec2(0.0f,0.0f); - if ((currentmovetime % 2) == 0) - { - create_smoke(player->pos); - } - - // check if we hit anything along the way - { - int type = OBJTYPE_PLAYER; - entity *ents[64]; - vec2 dir = player->pos - oldpos; - float radius = length(dir * 0.5f); - vec2 center = oldpos + dir * 0.5f; - int num = world.find_entities(center, radius, ents, 64, &type, 1); - - for (int i = 0; i < num; i++) - { - // Check if entity is a player - if (ents[i] == player) - continue; - // make sure we haven't hit this object before - bool balreadyhit = false; - for (int j = 0; j < numobjectshit; j++) - { - if (hitobjects[j] == ents[i]) - balreadyhit = true; - } - if (balreadyhit) - continue; - - // check so we are sufficiently close - if (distance(ents[i]->pos, player->pos) > (player->phys_size * 2.0f)) - continue; - - // hit a player, give him damage and stuffs... - create_sound(ents[i]->pos, sound_impact); - // set his velocity to fast upward (for now) - hitobjects[numobjectshit++] = ents[i]; - ents[i]->take_damage(vec2(0,10.0f), damage, player->client_id); - } - } - return MODIFIER_RETURNFLAGS_OVERRIDEWEAPON | MODIFIER_RETURNFLAGS_OVERRIDEVELOCITY | MODIFIER_RETURNFLAGS_OVERRIDEPOSITION|MODIFIER_RETURNFLAGS_OVERRIDEGRAVITY; - } - - - // move char, and check intersection from us to target - return MODIFIER_RETURNFLAGS_OVERRIDEWEAPON | MODIFIER_RETURNFLAGS_OVERRIDEVELOCITY; - } - - int activateninja(player* player) - { - // ok then, activate ninja - activationdir = player->direction; - currentactivation = activationtime; - currentmovetime = movetime; - currentcooldown = cooldown; - // reset hit objects - numobjectshit = 0; - - create_sound(player->pos, SOUND_FIRE_NINJA); - - return MODIFIER_RETURNFLAGS_OVERRIDEWEAPON; - } - - int activate(player* player) - { - if (flags & MODIFIER_NEEDSACTIVATION) - { - if (!numactivations) - return 0; - numactivations--; - } - - switch (modifiertype) - { - case MODIFIER_TYPE_NINJA: - { - return activateninja(player); - } - /*case MODIFIER_TYPE_TIMEFIELD: - { - updatetimefield(); - break; - }*/ - default: - return 0; - } - } - int update(player* player) - { - switch (modifiertype) - { - case MODIFIER_TYPE_NINJA: - { - return updateninja(player); - } - /*case MODIFIER_TYPE_TIMEFIELD: - { - updatetimefield(); - break; - }*/ - default: - return 0; - } - } - }; - - enum - { - PLAYER_FLAGS_ISRELOADING = 1 << 0, - PLAYER_FLAGS_ISEQUIPPING = 1 << 1, - }; - - weapon lweapons[WEAPON_NUMWEAPONS]; - modifier modifiers[MODIFIER_NUMMODIFIERS]; - int iactiveweapon; - int inextweapon; - int equip_time; - - int client_id; - int flags; - - char name[32]; - player_input previnput; - player_input input; - int tick_count; - int damage_taken_tick; - - vec2 vel; - vec2 direction; - - int jumped; - int airjumped; - - //int firing; - int hooking; - - int fire_timeout; - int reload_timeout; - - int health; - int armor; - - int score; - - // sounds - int sound_player_jump; - int sound_player_land; - int sound_player_hurt_short; - int sound_player_hurt_long; - int sound_player_spawn; - int sound_player_chain_loop; - int sound_player_chain_impact; - int sound_player_impact; - int sound_player_impact_ninja; - int sound_player_die; - int sound_player_switchweapon; - - player* phookedplayer; - powerup* phookedpowerup; - int numhooked; - vec2 hook_pos; - vec2 hook_dir; - - player() : - entity(OBJTYPE_PLAYER) - { - reset(); - - //firing = 0; - // setup weaponflags and stuff - lweapons[WEAPON_TYPE_GUN].setgun(); - lweapons[WEAPON_TYPE_ROCKET].setrocket(); - //lweapons[WEAPON_TYPE_SNIPER].setsniper(); - lweapons[WEAPON_TYPE_SHOTGUN].setshotgun(); - lweapons[WEAPON_TYPE_MELEE].setmelee(); - - modifiers[MODIFIER_TYPE_NINJA].setninja(); - modifiers[MODIFIER_TYPE_TIMEFIELD].settimefield(); - //modifiers[MODIFIER_TYPE_NINJA].flags |= MODIFIER_ISACTIVE; - - sound_player_jump = SOUND_PLAYER_JUMP; - sound_player_hurt_short = SOUND_PLAYER_HURT_SHORT; - sound_player_hurt_long = SOUND_PLAYER_HURT_LONG; - sound_player_spawn = SOUND_PLAYER_SPAWN; - sound_player_chain_loop = SOUND_PLAYER_CHAIN_LOOP; - sound_player_chain_impact = SOUND_PLAYER_CHAIN_IMPACT; - sound_player_impact = SOUND_PLAYER_IMPACT; - sound_player_impact_ninja = SOUND_PLAYER_IMPACT_NINJA; - sound_player_die = SOUND_PLAYER_DIE; - sound_player_switchweapon = SOUND_PLAYER_SWITCHWEAPON; - sound_player_land = SOUND_PLAYER_LAND; - } - - void reset() - { - equip_time = 0; - phookedplayer = 0; - numhooked = 0; - proximity_radius = phys_size; - name[0] = 'n'; - name[1] = 'o'; - name[2] = 'o'; - name[3] = 'b'; - name[4] = 0; - - pos = vec2(100.0f, 0.0f); - vel = vec2(0.0f, 0.0f); - direction = vec2(0.0f, 1.0f); - client_id = -1; - tick_count = 0; - score = 0; - flags = 0; - } - - virtual void destroy() { flags = 0; } - - void respawn() - { - health = PLAYER_MAXHEALTH; - armor = 0; - - hooking = 0; - phookedplayer = 0; - phookedpowerup = 0; - numhooked = 0; - fire_timeout = 0; - reload_timeout = 0; - iactiveweapon = 0; - inextweapon = -1; - equip_time = 0; - jumped = 0; - airjumped = 0; - mem_zero(&input, sizeof(input)); - vel = vec2(0.0f, 0.0f); - - int start, num; - map_get_type(1, &start, &num); - - if(num) - { - mapres_spawnpoint *sp = (mapres_spawnpoint*)map_get_item(start + (rand()%num), NULL, NULL); - pos = vec2(sp->x, sp->y); - } - else - pos = vec2(100.0f, -60.0f); - - // reset active flags - for (int i = 0; i < WEAPON_NUMWEAPONS; i++) - { - // reset and remove - lweapons[i].settype(); - lweapons[i].flags &= ~WEAPON_ISACTIVE; - } - - - // TEMP REMOVE - - /*for (int i = 0; i < WEAPON_NUMWEAPONS; i++) - { - lweapons[i].settype(); - lweapons[i].flags |= WEAPON_ISACTIVE; - }*/ - lweapons[WEAPON_TYPE_MELEE].flags |= WEAPON_ISACTIVE; - // Add gun as default weapon - iactiveweapon = WEAPON_TYPE_GUN; - lweapons[WEAPON_TYPE_GUN].numammo = 10; - lweapons[WEAPON_TYPE_GUN].flags |= WEAPON_ISACTIVE; - - create_sound(pos, sound_player_spawn); - } - - bool is_grounded() - { - if(col_check_point((int)(pos.x+phys_size/2), (int)(pos.y+phys_size/2+5))) - return true; - if(col_check_point((int)(pos.x-phys_size/2), (int)(pos.y+phys_size/2+5))) - return true; - return false; - } - - // Disable weapon activation if this returns true - int handlemodifiers() - { - int returnflags = 0; - for (int i = 0; i < MODIFIER_NUMMODIFIERS; i++) - { - if (modifiers[i].flags & MODIFIER_ISACTIVE) - { - modifiers[i].duration--; - modifiers[i].currentcooldown--; - - // Check if it should activate - if (modifiers[i].currentcooldown <= 0 && - (modifiers[i].flags & MODIFIER_NEEDSACTIVATION) && - input.fire && !(previnput.fire)) - { - returnflags |= modifiers[i].activate(this); - } - - returnflags |= modifiers[i].update(this); - - // remove active if timed out - if (modifiers[i].duration <= 0 && modifiers[i].currentactivation <= 0) - modifiers[i].flags &= ~MODIFIER_ISACTIVE; - } - } - return returnflags; - } - - void handleweapon() - { - // handle weapon - if(input.fire && (!previnput.fire || lweapons[iactiveweapon].flags & WEAPON_AUTOFIRE) && - !(flags & PLAYER_FLAGS_ISEQUIPPING) && !reload_timeout) - { - if(fire_timeout == 0) - { - if (lweapons[iactiveweapon].numammo || !(lweapons[iactiveweapon].flags & WEAPON_DRAWSAMMO)) - { - // Decrease ammo - fire_timeout = lweapons[iactiveweapon].activate(this); - } - else if ((lweapons[iactiveweapon].flags & WEAPON_NEEDRELOAD) && lweapons[iactiveweapon].nummagazines) - { - // reload - reload_timeout = lweapons[iactiveweapon].reloadtime; - lweapons[iactiveweapon].nummagazines--; - lweapons[iactiveweapon].numammo = lweapons[iactiveweapon].magsize; - } - } - } - - // update active weapon - lweapons[iactiveweapon].update(this, fire_timeout); - } - - void handlehook() - { - // handle hook - if(input.hook) - { - if(hooking == 0) - { - hooking = 1; - hook_pos = pos; - hook_dir = direction; - // Sound - create_sound(pos, sound_player_chain_loop, SOUND_LOOPFLAG_STARTLOOP); - } - else if(hooking == 1) - { - vec2 new_pos = hook_pos+hook_dir*hook_fire_speed; - - // Check against other players and powerups first - player* targetplayer = 0; - powerup* targetpowerup = 0; - { - static const int typelist[2] = { OBJTYPE_PLAYER, OBJTYPE_POWERUP}; - entity *ents[64]; - vec2 dir = new_pos - hook_pos; - float radius = length(dir * 0.5f); - vec2 center = hook_pos + dir * 0.5f; - int num = world.find_entities(center, radius, ents, 64,typelist,2); - - for (int i = 0; i < num; i++) - { - // Check if entity is a player - if (ents[i] == this || (targetplayer && targetpowerup)) - continue; - - if (!targetplayer && ents[i]->objtype == OBJTYPE_PLAYER) - { - // temp, set hook pos to our position - if (((player*)ents[i])->phookedplayer != this) - targetplayer = (player*)ents[i]; - } - else if (!targetpowerup && ents[i]->objtype == OBJTYPE_POWERUP && - (((powerup*)ents[i])->flags & powerup::POWERUP_FLAG_HOOKABLE)) - { - targetpowerup = (powerup*)ents[i]; - } - } - } - - //player* targetplayer = intersect_player(hook_pos, hook_pos, new_pos, this); - if (targetplayer) - { - // So he can't move "normally" - new_pos = targetplayer->pos; - phookedplayer = targetplayer; - targetplayer->numhooked++; - hooking = 3; - create_sound(pos, sound_player_chain_impact); - - // stop looping chain sound - create_sound(pos, sound_player_chain_loop, SOUND_LOOPFLAG_STOPLOOP); - } - else if (targetpowerup) - { - new_pos = targetpowerup->pos; - phookedpowerup = targetpowerup; - phookedpowerup->playerhooked = this; - hooking = 4; - create_sound(pos, sound_player_chain_impact); - - // stop looping chain sound - create_sound(pos, sound_player_chain_loop, SOUND_LOOPFLAG_STOPLOOP); - } - else if(intersect_line(hook_pos, new_pos, &new_pos)) - { - hooking = 2; - create_sound(pos, sound_player_chain_impact); - - // stop looping chain sound - create_sound(pos, sound_player_chain_loop, SOUND_LOOPFLAG_STOPLOOP); - } - else if(distance(pos, new_pos) > hook_length) - { - hooking = -1; - create_sound(pos, sound_player_chain_loop, SOUND_LOOPFLAG_STOPLOOP); - } - - hook_pos = new_pos; - } - else if(hooking == 2) - { - vec2 hookvel = normalize(hook_pos-pos)*hook_drag_accel; - // the hook as more power to drag you up then down. - // this makes it easier to get on top of an platform - if(hookvel.y > 0) - hookvel.y *= 0.3f; - - // the hook will boost it's power if the player wants to move - // in that direction. otherwise it will dampen everything abit - if((hookvel.x < 0 && input.left) || (hookvel.x > 0 && input.right)) - hookvel.x *= 0.95f; - else - hookvel.x *= 0.75f; - vec2 new_vel = vel+hookvel; - - // check if we are under the legal limit for the hook - if(length(new_vel) < hook_drag_speed || length(new_vel) < length(vel)) - vel = new_vel; // no problem. apply - } - else if (hooking == 3) - { - // hmm, force the targetplayer towards us if possible, otherwise us towards them if they are already hooked - if (phookedplayer) - { - if (phookedplayer->hooking > 1) - { - // Drag us towards target player - vec2 hookvel = normalize(hook_pos-pos)*hook_drag_accel; - if((hookvel.x < 0 && input.left) || (hookvel.x > 0 && input.right)) - hookvel.x *= 0.95f; - else - hookvel.x *= 0.75f; - - // Apply the velocity - // the hook will boost it's power if the player wants to move - // in that direction. otherwise it will dampen everything abit - vec2 new_vel = vel+hookvel; - - // check if we are under the legal limit for the hook - if(length(new_vel) < hook_drag_speed || length(new_vel) < length(vel)) - vel = new_vel; // no problem. apply - } - else - { - // Drag targetplayer towards us - vec2 hookvel = normalize(pos-hook_pos)*hook_drag_accel; - - // Apply the velocity - // the hook will boost it's power if the player wants to move - // in that direction. otherwise it will dampen everything abit - vec2 new_vel = phookedplayer->vel+hookvel; - - // check if we are under the legal limit for the hook - if(length(new_vel) < hook_drag_speed || length(new_vel) < length(vel)) - phookedplayer->vel = new_vel; // no problem. apply - } - hook_pos = phookedplayer->pos; - // if hooked player dies, release the hook - } - else - { - hooking = -1; - phookedplayer = 0; - } - } - else if (hooking == 4) - { - // Have a powerup, drag it towards us - vec2 hookvel = normalize(pos-hook_pos)*hook_drag_accel; - - // Apply the velocity - // the hook will boost it's power if the player wants to move - // in that direction. otherwise it will dampen everything abit - vec2 new_vel = phookedpowerup->vel+hookvel; - - // check if we are under the legal limit for the hook - if(length(new_vel) < hook_drag_speed || length(new_vel) < length(vel)) - phookedpowerup->vel = new_vel; // no problem. apply - hook_pos = phookedpowerup->pos; - } - } - else - { - hooking = 0; - hook_pos = pos; - if (phookedplayer) - { - phookedplayer->numhooked--; - phookedplayer = 0; - } - } - } - - void getattackticks(int& curattack, int& attacklen, int& visualtimeattack) - { - // time left from current attack (if any) - // first check modifiers (ninja...) - for (int i = 0; i < MODIFIER_NUMMODIFIERS; i++) - { - if ((modifiers[i].flags & (MODIFIER_ISACTIVE | MODIFIER_NEEDSACTIVATION)) == (MODIFIER_ISACTIVE | MODIFIER_NEEDSACTIVATION)) - { - curattack = modifiers[i].currentactivation; - attacklen = modifiers[i].activationtime; - visualtimeattack = modifiers[i].visualtimeattack; - return; - } - } - - // otherwise current fire timeout - curattack = fire_timeout; - attacklen = lweapons[iactiveweapon].firetime; - visualtimeattack = lweapons[iactiveweapon].visualtimeattack; - } - - virtual void tick() - { - tick_count++; - - // fetch some info - bool grounded = is_grounded(); - direction = get_direction(input.angle); - - // decrease reload timer - if(fire_timeout) - fire_timeout--; - if (reload_timeout) - reload_timeout--; - - // Switch weapons - if (flags & PLAYER_FLAGS_ISEQUIPPING) - { - equip_time--; - if (equip_time <= 0) - { - if (inextweapon >= 0) - { - equip_time = SERVER_TICK_SPEED * lweapons[inextweapon].equiptime; - iactiveweapon = inextweapon; - inextweapon = -1; - - // Send switch weapon event to client? - } - else - { - flags &= ~PLAYER_FLAGS_ISEQUIPPING; - } - } - } - else if (input.activeweapon && iactiveweapon != (input.activeweapon & ~0x80000000)) - { - input.activeweapon &= ~0x80000000; - // check which weapon to activate - if (input.activeweapon >= 0 && input.activeweapon < WEAPON_NUMWEAPONS && - (lweapons[input.activeweapon].flags & WEAPON_ISACTIVE)) - { - inextweapon = input.activeweapon; - equip_time = SERVER_TICK_SPEED * lweapons[iactiveweapon].unequiptime; - // unequip current - flags |= PLAYER_FLAGS_ISEQUIPPING; - - create_sound(pos, sound_player_switchweapon); - } - } - - // don't do any weapon activations if modifier is currently overriding - int modifierflags = handlemodifiers(); - if (!(modifierflags & MODIFIER_RETURNFLAGS_OVERRIDEWEAPON)) - handleweapon(); - - handlehook(); - - // handle movement - if(grounded) - { - if (airjumped) - create_sound(pos, SOUND_PLAYER_LAND); - airjumped = 0; - } - - float elast = 0.0f; - - if (!numhooked) - { - // I'm hooked by someone, so don't do any movement plz (temp) - if (!(modifierflags & MODIFIER_RETURNFLAGS_OVERRIDEVELOCITY)) - { - if(grounded) - { - // ground movement - if(input.left) - { - if(vel.x > -ground_control_speed) - { - vel.x -= ground_control_accel; - if(vel.x < -ground_control_speed) - vel.x = -ground_control_speed; - } - } - else if(input.right) - { - if(vel.x < ground_control_speed) - { - vel.x += ground_control_accel; - if(vel.x > ground_control_speed) - vel.x = ground_control_speed; - } - } - else - vel.x *= ground_friction; // ground fiction - } - else - { - // air movement - if(input.left) - { - if(vel.x > -air_control_speed) - vel.x -= air_control_accel; - } - else if(input.right) - { - if(vel.x < air_control_speed) - vel.x += air_control_accel; - } - else - vel.x *= air_friction; // air fiction - } - - if(input.jump) - { - if(jumped == 0) - { - if(grounded) - { - create_sound(pos, sound_player_jump); - vel.y = -ground_jump_speed; - jumped++; - } - /* - else if(airjumped == 0) - { - vel.y = -12; - airjumped++; - jumped++; - }*/ - } - else if (!grounded) - { - airjumped++; - } - } - else - jumped = 0; - } - - // meh, go through all players and stop their hook on me - /* - for(entity *ent = world.first_entity; ent; ent = ent->next_entity) - { - if (ent && ent->objtype == OBJTYPE_PLAYER) - { - player *p = (player*)ent; - if(p != this) - { - float d = distance(pos, p->pos); - vec2 dir = normalize(pos - p->pos); - if(d < phys_size*1.5f) - { - float a = phys_size*1.5f - d; - vel = vel + dir*a; - } - - if(p->phookedplayer == this) - { - if(d > phys_size*2.5f) - { - elast = 0.0f; - vel = vel - dir*2.5f; - } - } - } - } - }*/ - - // gravity - if (!(modifierflags & MODIFIER_RETURNFLAGS_OVERRIDEGRAVITY)) - vel.y += gravity; - } - - if (!(modifierflags & MODIFIER_RETURNFLAGS_OVERRIDEPOSITION)) - move_box(&pos, &vel, vec2(phys_size, phys_size), elast); - } - - void die() - { - create_sound(pos, sound_player_die); - // release our hooked player - if (phookedplayer) - { - phookedplayer->numhooked--; - phookedplayer = 0; - hooking = -1; - numhooked = 0; - } - respawn(); - - // meh, go through all players and stop their hook on me - for(entity *ent = world.first_entity; ent; ent = ent->next_entity) - { - if (ent && ent->objtype == OBJTYPE_PLAYER) - { - player* p = (player*)ent; - if (p->phookedplayer == this) - { - p->phookedplayer = 0; - p->hooking = -1; - //p->numhooked--; - } - } - } - } - - virtual bool take_damage(vec2 force, int dmg, int from) - { - vel += force; - - if(armor) - { - armor -= 1; - dmg--; - } - - if(dmg > armor) - { - dmg -= armor; - armor = 0; - health -= dmg; - } - else - armor -= dmg; - /* - int armordmg = (dmg+1)/2; - int healthdmg = dmg-armordmg; - if(armor < armordmg) - { - healthdmg += armordmg - armor; - armor = 0; - } - else - armor -= armordmg; - - health -= healthdmg; - */ - - // create healthmod indicator - create_healthmod(pos, dmg); - - damage_taken_tick = tick_count+50; - - // check for death - if(health <= 0) - { - // apply score - if(from != -1) - { - if(from == client_id) - score--; - else - { - player *p = get_player(from); - p->score++; - } - } - - die(); - return false; - } - - if (dmg > 2) - create_sound(pos, sound_player_hurt_long); - else - create_sound(pos, sound_player_hurt_short); - - // spawn blood? - - return true; - } - - virtual void snap(int snaping_client) - { - obj_player *player = (obj_player *)snap_new_item(OBJTYPE_PLAYER, client_id, sizeof(obj_player)); - - client_info info; - if(server_getclientinfo(client_id, &info)) - snap_encode_string(info.name, player->name, strlen(info.name), 32); - - player->x = (int)pos.x; - player->y = (int)pos.y; - player->vx = (int)vel.x; - player->vy = (int)vel.y; - player->emote = EMOTE_NORMAL; - - player->ammocount = lweapons[iactiveweapon].numammo; - player->health = 0; - player->armor = 0; - player->local = 0; - player->clientid = client_id; - player->weapon = iactiveweapon; - player->modifier = 0; - for (int i = 0; i < MODIFIER_NUMMODIFIERS; i++) - { - // add active modifiers - if (modifiers[i].flags & MODIFIER_ISACTIVE) - player->modifier |= 1 << i; - } - // get current attack ticks - getattackticks(player->attackticks, player->attacklen, player->visualtimeattack); - - - if(client_id == snaping_client) - { - player->local = 1; - player->health = health; - player->armor = armor; - } - - if(length(vel) > 15.0f) - player->emote = EMOTE_HAPPY; - - if(damage_taken_tick > tick_count) - player->emote = EMOTE_PAIN; - - if(player->emote == EMOTE_NORMAL) - { - if((tick_count%(50*5)) < 10) - player->emote = EMOTE_BLINK; - } - - player->hook_active = hooking>0?1:0; - player->hook_x = (int)hook_pos.x; - player->hook_y = (int)hook_pos.y; - - player->angle = input.angle; - player->score = score; - } -}; - -// POWERUP /////////////////////// - -powerup::powerup(int _type, int _subtype, int _numitems, int _flags) : - entity(OBJTYPE_POWERUP) -{ - static int current_id = 0; - playerhooked = 0; - id = current_id++; - vel = vec2(0.0f,0.0f); - type = _type; - subtype = _subtype; - numitems = _numitems; - flags = _flags; - // set radius (so it can collide and be hooked and stuff) - proximity_radius = phys_size; - spawntick = -1; - world.insert_entity(this); -} - -void powerup::spawnrandom(int _lifespan) -{ - return; - /* - vec2 pos; - int start, num; - map_get_type(1, &start, &num); - - if(!num) - return; - - mapres_spawnpoint *sp = (mapres_spawnpoint*)map_get_item(start + (rand()%num), NULL, NULL); - pos = vec2(sp->x, sp->y); - - // Check if there already is a powerup at that location - { - int type = OBJTYPE_POWERUP; - entity *ents[64]; - int num = world.find_entities(pos, 5.0f, ents, 64,&type,1); - for (int i = 0; i < num; i++) - { - if (ents[i]->objtype == OBJTYPE_POWERUP) - { - // location busy - return; - } - } - } - - powerup* ppower = new powerup(rand() % POWERUP_TYPE_NUMPOWERUPS,_lifespan); - ppower->pos = pos; - if (ppower->type == POWERUP_TYPE_WEAPON) - { - ppower->subtype = rand() % WEAPON_NUMWEAPONS; - ppower->numitems = 10; - } - else if (ppower->type == POWERUP_TYPE_HEALTH || ppower->type == POWERUP_TYPE_ARMOR) - { - ppower->numitems = rand() % 5; - } - ppower->flags |= POWERUP_FLAG_HOOKABLE;*/ -} - -void powerup::tick() -{ - // wait for respawn - if(spawntick > 0) - { - if(server_tick() > spawntick) - spawntick = -1; - else - return; - } - - vec2 oldpos = pos; - //vel.y += 0.25f; - pos += vel; - move_box(&pos, &vel, vec2(phys_size, phys_size), 0.0f); - - // Check if a player intersected us - vec2 meh; - player* pplayer = intersect_player(pos, pos + vec2(0,16), meh, 0); - if (pplayer) - { - // player picked us up, is someone was hooking us, let them go - if (playerhooked) - playerhooked->hooking = -1; - int respawntime = -1; - switch (type) - { - case POWERUP_TYPE_HEALTH: - { - if(pplayer->health < PLAYER_MAXHEALTH) - { - pplayer->health = min(PLAYER_MAXHEALTH, pplayer->health + numitems); - respawntime = 20; - } - break; - } - case POWERUP_TYPE_ARMOR: - { - if(pplayer->armor < PLAYER_MAXARMOR) - { - pplayer->armor = min(PLAYER_MAXARMOR, pplayer->armor + numitems); - respawntime = 20; - } - break; - } - case POWERUP_TYPE_WEAPON: - { - if (pplayer->lweapons[subtype].flags & player::WEAPON_ISACTIVE) - { - // add ammo - /* - if (pplayer->lweapons[subtype].flags & player::WEAPON_NEEDRELOAD) - { - int numtoadd = min(numitems, pplayer->lweapons[subtype].magsize); - pplayer->lweapons[subtype].numammo = min(10, pplayer->lweapons[subtype].numammo + numtoadd); - pplayer->lweapons[subtype].nummagazines += (numitems - numtoadd) % pplayer->lweapons[subtype].magsize; - } - else*/ - if(pplayer->lweapons[subtype].numammo < 10) - { - respawntime = 20; - pplayer->lweapons[subtype].numammo = min(10, pplayer->lweapons[subtype].numammo + numitems); - } - } - else - { - pplayer->lweapons[subtype].settype(); - pplayer->lweapons[subtype].flags |= player::WEAPON_ISACTIVE; - respawntime = 20; - } - break; - } - case POWERUP_TYPE_NINJA: - { - respawntime = 60; - // reset and activate - pplayer->modifiers[MODIFIER_TYPE_NINJA].settype(); - pplayer->modifiers[MODIFIER_TYPE_NINJA].flags |= player::MODIFIER_ISACTIVE; - break; - } - //POWERUP_TYPE_TIMEFIELD = 4, - default: - break; - }; - - if(respawntime >= 0) - spawntick = server_tick() + server_tickspeed() * respawntime; - //world.destroy_entity(this); - } -} - -void powerup::snap(int snapping_client) -{ - if(spawntick != -1) - return; - - obj_powerup *powerup = (obj_powerup *)snap_new_item(OBJTYPE_POWERUP, id, sizeof(obj_powerup)); - powerup->x = (int)pos.x; - powerup->y = (int)pos.y; - powerup->vx = (int)vel.x; - powerup->vy = (int)vel.y; - powerup->type = type; - powerup->subtype = subtype; -} - -// POWERUP END /////////////////////// - -static player players[MAX_CLIENTS]; - -player *get_player(int index) -{ - return &players[index]; -} - -void create_healthmod(vec2 p, int amount) -{ - ev_healthmod *ev = (ev_healthmod *)events.create(EVENT_HEALTHMOD, sizeof(ev_healthmod)); - ev->x = (int)p.x; - ev->y = (int)p.y; - ev->amount = amount; -} - -void create_explosion(vec2 p, int owner, bool bnodamage) -{ - // create the event - ev_explosion *ev = (ev_explosion *)events.create(EVENT_EXPLOSION, sizeof(ev_explosion)); - ev->x = (int)p.x; - ev->y = (int)p.y; - - if (!bnodamage) - { - // deal damage - entity *ents[64]; - const float radius = 128.0f; - int num = world.find_entities(p, radius, ents, 64); - for(int i = 0; i < num; i++) - { - vec2 diff = ents[i]->pos - p; - vec2 forcedir(0,1); - if (length(diff)) - forcedir = normalize(diff); - float l = length(diff); - float dmg = 5 * (1 - (l/radius)); - if((int)dmg) - { - ents[i]->take_damage(forcedir*dmg*2, (int)dmg, owner);/* && - ents[i]->objtype == OBJTYPE_PLAYER && - owner >= 0) - { - player *p = (player*)ents[i]; - if(p->client_id == owner) - p->score--; - else - ((player*)ents[owner])->score++; - - }*/ - } - } - } -} - -void create_smoke(vec2 p) -{ - // create the event - ev_explosion *ev = (ev_explosion *)events.create(EVENT_SMOKE, sizeof(ev_explosion)); - ev->x = (int)p.x; - ev->y = (int)p.y; -} - -void create_sound(vec2 pos, int sound, int loopingflags) -{ - if (sound < 0) - return; - - // create a sound - ev_sound *ev = (ev_sound *)events.create(EVENT_SOUND, sizeof(ev_sound)); - ev->x = (int)pos.x; - ev->y = (int)pos.y; - ev->sound = sound | loopingflags; -} - -player* intersect_player(vec2 pos0, vec2 pos1, vec2& new_pos, entity* notthis) -{ - // Find other players - entity *ents[64]; - vec2 dir = pos1 - pos0; - float radius = length(dir * 0.5f); - vec2 center = pos0 + dir * 0.5f; - int num = world.find_entities(center, radius, ents, 64); - for (int i = 0; i < num; i++) - { - // Check if entity is a player - if (ents[i] != notthis && ents[i]->objtype == OBJTYPE_PLAYER) - { - // temp, set hook pos to our position - new_pos = ents[i]->pos; - return (player*)ents[i]; - } - } - - return 0; -} - -// Server hooks -static int addtick = SERVER_TICK_SPEED * 5; -void mods_tick() -{ - // clear all events - events.clear(); - world.tick(); - - if (addtick <= 0) - { - powerup::spawnrandom(SERVER_TICK_SPEED * 5); - addtick = SERVER_TICK_SPEED * 5; - } - addtick--; -} - -void mods_snap(int client_id) -{ - world.snap(client_id); - events.snap(client_id); -} - -void mods_client_input(int client_id, void *input) -{ - players[client_id].previnput = players[client_id].input; - players[client_id].input = *(player_input*)input; -} - -void mods_client_enter(int client_id) -{ - players[client_id].reset(); - players[client_id].client_id = client_id; - players[client_id].respawn(); - world.insert_entity(&players[client_id]); - -} - -void mods_client_drop(int client_id) -{ - players[client_id].client_id = -1; - world.remove_entity(&players[client_id]); -} - -void mods_init() -{ - col_init(32); - - int start, num; - map_get_type(MAPRES_ITEM, &start, &num); - - for(int i = 0; i < num; i++) - { - mapres_item *it = (mapres_item *)map_get_item(start+i, 0, 0); - - int type = -1; - int subtype = -1; - int numitems = 1; - - switch(it->type) - { - case ITEM_WEAPON_GUN: - type = POWERUP_TYPE_WEAPON; - subtype = WEAPON_TYPE_GUN; - break; - case ITEM_WEAPON_SHOTGUN: - type = POWERUP_TYPE_WEAPON; - subtype = WEAPON_TYPE_SHOTGUN; - numitems = 5; - break; - case ITEM_WEAPON_ROCKET: - type = POWERUP_TYPE_WEAPON; - subtype = WEAPON_TYPE_ROCKET; - numitems = 5; - break; - /*case ITEM_WEAPON_SNIPER: - type = POWERUP_TYPE_WEAPON; - subtype = WEAPON_TYPE_ROCKET; - break;*/ - case ITEM_WEAPON_HAMMER: - type = POWERUP_TYPE_WEAPON; - subtype = WEAPON_TYPE_MELEE; - break; - - case ITEM_HEALTH_1: - type = POWERUP_TYPE_HEALTH; - numitems = 1; - break; - case ITEM_HEALTH_5: - type = POWERUP_TYPE_HEALTH; - numitems = 5; - break; - case ITEM_HEALTH_10: - type = POWERUP_TYPE_HEALTH; - numitems = 10; - break; - - case ITEM_ARMOR_1: - type = POWERUP_TYPE_ARMOR; - numitems = 1; - break; - case ITEM_ARMOR_5: - type = POWERUP_TYPE_ARMOR; - numitems = 5; - break; - case ITEM_ARMOR_10: - type = POWERUP_TYPE_ARMOR; - numitems = 10; - break; - }; - - powerup* ppower = new powerup(type, subtype, numitems); - ppower->pos.x = it->x; - ppower->pos.y = it->y; - } - - - /* - powerup* ppower = new powerup(rand() % POWERUP_TYPE_NUMPOWERUPS,_lifespan); - ppower->pos = pos; - if (ppower->type == POWERUP_TYPE_WEAPON) - { - ppower->subtype = rand() % WEAPON_NUMWEAPONS; - ppower->numitems = 10; - } - else if (ppower->type == POWERUP_TYPE_HEALTH || ppower->type == POWERUP_TYPE_ARMOR) - { - ppower->numitems = rand() % 5; - } - ppower->flags |= POWERUP_FLAG_HOOKABLE; - */ - - //powerup::spawnrandom(SERVER_TICK_SPEED * 5); -} - -void mods_shutdown() {} -void mods_presnap() {} -void mods_postsnap() {} diff --git a/src/game/mapres_col.cpp b/src/game/mapres_col.cpp index 0cf71986..29215d7d 100644 --- a/src/game/mapres_col.cpp +++ b/src/game/mapres_col.cpp @@ -1,5 +1,5 @@ #include -#include "../interface.h" +#include "../engine/interface.h" #include "mapres_col.h" #include "mapres.h" diff --git a/src/game/mapres_image.cpp b/src/game/mapres_image.cpp deleted file mode 100644 index baf7f09b..00000000 --- a/src/game/mapres_image.cpp +++ /dev/null @@ -1,41 +0,0 @@ -#include -#include "../interface.h" -#include "mapres_image.h" -#include "mapres.h" - -static int map_textures[64] = {0}; -static int count = 0; - -int img_init() -{ - int start, count; - map_get_type(MAPRES_IMAGE, &start, &count); - dbg_msg("mapres_image", "start=%d count=%d", start, count); - for(int i = 0; i < 64; i++) - { - if(map_textures[i]) - { - gfx_unload_texture(map_textures[i]); - map_textures[i] = 0; - } - } - - for(int i = 0; i < count; i++) - { - mapres_image *img = (mapres_image *)map_get_item(start+i, 0, 0); - void *data = map_get_data(img->image_data); - map_textures[i] = gfx_load_texture_raw(img->width, img->height, data); - } - - return count; -} - -int img_num() -{ - return count; -} - -int img_get(int index) -{ - return map_textures[index]; -} diff --git a/src/game/mapres_image.h b/src/game/mapres_image.h deleted file mode 100644 index eab1559a..00000000 --- a/src/game/mapres_image.h +++ /dev/null @@ -1,18 +0,0 @@ - -// loads images from the map to textures -int img_init(); - -// returns the number of images in the map -int img_num(); - -// fetches the texture id for the image -int img_get(int index); - - -class mapres_image -{ -public: - int width; - int height; - int image_data; -}; diff --git a/src/game/mapres_tilemap.cpp b/src/game/mapres_tilemap.cpp deleted file mode 100644 index 0868d2e4..00000000 --- a/src/game/mapres_tilemap.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include "../interface.h" -#include "mapres_tilemap.h" -#include "mapres_image.h" -#include "mapres.h" - -int tilemap_init() -{ - return 0; -} - -void tilemap_render(float scale, int fg) -{ - if(!map_is_loaded()) - return; - - // fetch indecies - int start, num; - map_get_type(MAPRES_TILEMAP, &start, &num); - - // render tilemaps - int passed_main = 0; - for(int t = 0; t < num; t++) - { - mapres_tilemap *tmap = (mapres_tilemap *)map_get_item(start+t,0,0); - unsigned char *data = (unsigned char *)map_get_data(tmap->data); - - if(tmap->main) - passed_main = 1; - - if((fg && passed_main) || (!fg && !passed_main)) - { - gfx_texture_set(img_get(tmap->image)); - gfx_quads_begin(); - - int c = 0; - float frac = (1.0f/1024.0f); //2.0f; - for(int y = 0; y < tmap->height; y++) - for(int x = 0; x < tmap->width; x++, c++) - { - unsigned char d = data[c*2]; - if(d) - { - gfx_quads_setsubset( - (d%16)/16.0f+frac, - (d/16)/16.0f+frac, - (d%16)/16.0f+1.0f/16.0f-frac, - (d/16)/16.0f+1.0f/16.0f-frac); - gfx_quads_drawTL(x*scale, y*scale, scale, scale); - } - } - gfx_quads_end(); - } - } -} diff --git a/src/game/mapres_tilemap.h b/src/game/mapres_tilemap.h deleted file mode 100644 index 6e9d81be..00000000 --- a/src/game/mapres_tilemap.h +++ /dev/null @@ -1,19 +0,0 @@ - -// dependencies: image - -// -int tilemap_init(); - -// renders the tilemaps -void tilemap_render(float scale, int fg); - -struct mapres_tilemap -{ - int image; - int width; - int height; - int x, y; - int scale; - int data; - int main; -}; diff --git a/src/game/server/game_server.cpp b/src/game/server/game_server.cpp new file mode 100644 index 00000000..769a250f --- /dev/null +++ b/src/game/server/game_server.cpp @@ -0,0 +1,2131 @@ +#include +#include +#include "../game.h" + +using namespace baselib; + +// --------- PHYSICS TWEAK! -------- +const float ground_control_speed = 7.0f; +const float ground_control_accel = 2.0f; +const float ground_friction = 0.5f; +const float ground_jump_speed = 12.0f; +const float air_control_speed = 3.5f; +const float air_control_accel = 1.2f; +const float air_friction = 0.95f; +const float hook_length = 32*10.0f; +const float hook_fire_speed = 45.0f; +const float hook_drag_accel = 3.0f; +const float hook_drag_speed = 15.0f; +const float gravity = 0.5f; + +class player* get_player(int index); +void create_healthmod(vec2 p, int amount); +void create_explosion(vec2 p, int owner = -1, bool bnodamage = false); +void create_smoke(vec2 p); +void create_sound(vec2 pos, int sound, int loopflags = 0); +class player* intersect_player(vec2 pos0, vec2 pos1, vec2& new_pos, class entity* notthis = 0); + +// TODO: rewrite this smarter! +void move_box(vec2 *inout_pos, vec2 *inout_vel, vec2 size, float elasticity) +{ + // do the move + vec2 pos = *inout_pos; + vec2 vel = *inout_vel; + + float distance = length(vel); + int max = (int)distance; + + vec2 offsets[4] = { vec2(-size.x/2, -size.y/2), vec2( size.x/2, -size.y/2), + vec2(-size.x/2, size.y/2), vec2( size.x/2, size.y/2)}; + + if(distance > 0.00001f) + { + vec2 old_pos = pos; + for(int i = 0; i <= max; i++) + { + float amount = i/(float)max; + if(max == 0) + amount = 0; + + vec2 new_pos = pos + vel*amount; // TODO: this row is not nice + + for(int p = 0; p < 4; p++) + { + vec2 np = new_pos+offsets[p]; + vec2 op = old_pos+offsets[p]; + if(col_check_point(np)) + { + int affected = 0; + if(col_check_point(np.x, op.y)) + { + vel.x = -vel.x*elasticity; + pos.x = old_pos.x; + new_pos.x = old_pos.x; + affected++; + } + + if(col_check_point(op.x, np.y)) + { + vel.y = -vel.y*elasticity; + pos.y = old_pos.y; + new_pos.y = old_pos.y; + affected++; + } + + if(!affected) + { + new_pos = old_pos; + pos = old_pos; + vel *= -elasticity; + } + } + } + + old_pos = new_pos; + } + + pos = old_pos; + } + + *inout_pos = pos; + *inout_vel = vel; +} + +// TODO: rewrite this smarter! +bool intersect_line(vec2 pos0, vec2 pos1, vec2 *out) +{ + float d = distance(pos0, pos1); + + for(float f = 0; f < d; f++) + { + float a = f/d; + vec2 pos = mix(pos0, pos1, a); + if(col_check_point(pos)) + { + if(out) + *out = pos; + return true; + } + } + if(out) + *out = pos1; + return false; +} + +// +class event_handler +{ + static const int MAX_EVENTS = 128; + static const int MAX_DATASIZE = 128*4; + + int types[MAX_EVENTS]; // TODO: remove some of these arrays + int offsets[MAX_EVENTS]; + int sizes[MAX_EVENTS]; + char data[MAX_DATASIZE]; + + int current_offset; + int num_events; +public: + event_handler() + { + num_events = 0; + } + + void *create(int type, int size) + { + void *p = &data[current_offset]; + offsets[num_events] = current_offset; + types[num_events] = type; + sizes[num_events] = size; + current_offset += size; + num_events++; + return p; + } + + void clear() + { + num_events = 0; + current_offset = 0; + } + + void snap(int snapping_client) + { + for(int i = 0; i < num_events; i++) + { + void *d = snap_new_item(types[i], i, sizes[i]); + mem_copy(d, &data[offsets[i]], sizes[i]); + } + } +}; + +static event_handler events; +/* +template +class pool +{ + struct element + { + int next_free; + T data; + }; + + element elements[SIZE]; + int first_free; +public: + pool() + { + first_free = 0; + for(int i = 0; i < SIZE; i++) + elements[i].next_free = i+1; + elements[SIZE-1].next_free = -1; + } +};*/ + +// a basic entity +class entity +{ +private: + friend class game_world; + friend class player; + entity *prev_entity; + entity *next_entity; + int index; + +public: + vec2 pos; + float proximity_radius; + unsigned flags; + int objtype; + + enum + { + FLAG_DESTROY=0x00000001, + }; + + entity(int objtype) + { + this->objtype = objtype; + pos = vec2(0,0); + flags = 0; + proximity_radius = 0; + } + + virtual ~entity() + { + } + + virtual void destroy() { delete this; } + virtual void tick() {} + virtual void snap(int snapping_client) {} + + virtual bool take_damage(vec2 force, int dmg, int from) { return true; } +}; + +class powerup : public entity +{ +public: + static const int phys_size = 14; + enum + { + POWERUP_FLAG_HOOKABLE = 1 << 0, + }; + vec2 vel; + class player* playerhooked; + int type; + int id; + int subtype; // weapon type for instance? + int numitems; // number off powerup items + int flags; + int spawntick; + powerup(int _type, int _subtype = 0, int _numitems = 0, int _flags = 0); + + static void spawnrandom(int _lifespan); + + void tick(); + + void snap(int snapping_client); +}; + +// game world. handles all entities +class game_world +{ +public: + entity *first_entity; + game_world() + { + first_entity = 0x0; + } + + int find_entities(vec2 pos, float radius, entity **ents, int max) + { + int num = 0; + for(entity *ent = first_entity; ent; ent = ent->next_entity) + { + if(distance(ent->pos, pos) < radius+ent->proximity_radius) + { + ents[num] = ent; + num++; + if(num == max) + break; + } + } + + return num; + } + + int find_entities(vec2 pos, float radius, entity **ents, int max, const int* types, int maxtypes) + { + int num = 0; + for(entity *ent = first_entity; ent; ent = ent->next_entity) + { + for (int i = 0; i < maxtypes; i++) + { + if (ent->objtype != types[i]) + continue; + + if(distance(ent->pos, pos) < radius+ent->proximity_radius) + { + ents[num] = ent; + num++; + if(num == max) + break; + } + } + } + + return num; + } + + void insert_entity(entity *ent) + { + // insert it + if(first_entity) + first_entity->prev_entity = ent; + ent->next_entity = first_entity; + ent->prev_entity = 0x0; + first_entity = ent; + } + + void destroy_entity(entity *ent) + { + ent->flags |= entity::FLAG_DESTROY; + // call destroy + //remove_entity(ent); + //ent->destroy(); + } + + void remove_entity(entity *ent) + { + // remove + if(ent->prev_entity) + ent->prev_entity->next_entity = ent->next_entity; + else + first_entity = ent->next_entity; + if(ent->next_entity) + ent->next_entity->prev_entity = ent->prev_entity; + } + + // + void snap(int snapping_client) + { + for(entity *ent = first_entity; ent; ent = ent->next_entity) + ent->snap(snapping_client); + } + + void tick() + { + // update all objects + for(entity *ent = first_entity; ent; ent = ent->next_entity) + ent->tick(); + + // destroy objects marked for destruction + entity *ent = first_entity; + while(ent) + { + entity *next = ent->next_entity; + if(ent->flags&entity::FLAG_DESTROY) + { + remove_entity(ent); + ent->destroy(); + } + ent = next; + } + } +}; + +static game_world world; + +// projectile entity +class projectile : public entity +{ +public: + enum + { + PROJECTILE_FLAGS_EXPLODE = 1 << 0, + }; + vec2 vel; + entity* powner; + int lifespan; + int id; + int owner; + int type; + int flags; + int damage; + int sound_impact; + float force; + + projectile(int type, int owner, vec2 pos, vec2 vel, int span, entity* powner, int damage, int flags = 0, float force = 0.0f, int sound_impact = -1) : + entity(OBJTYPE_PROJECTILE) + { + static int current_id = 0; + this->id = current_id++; + this->type = type; + this->pos = pos; + this->vel = vel; + this->lifespan = span; + this->owner = owner; + this->powner = powner; + this->flags = flags; + this->force = force; + this->damage = damage; + this->sound_impact = sound_impact; + world.insert_entity(this); + } + + void tick() + { + vec2 oldpos = pos; + vel.y += 0.25f; + pos += vel; + lifespan--; + // check player intersection as well + entity* targetplayer = (entity*)intersect_player(oldpos, pos, oldpos, powner); + if(targetplayer || lifespan < 0 || col_check_point((int)pos.x, (int)pos.y)) + { + if (lifespan >= 0) + create_sound(pos, sound_impact); + if (flags & PROJECTILE_FLAGS_EXPLODE) + { + create_explosion(oldpos, owner); + } + else if (targetplayer) + { + targetplayer->take_damage(normalize(vel) * force, damage, owner); + } + world.destroy_entity(this); + } + } + + void snap(int snapping_client) + { + obj_projectile *proj = (obj_projectile *)snap_new_item(OBJTYPE_PROJECTILE, id, sizeof(obj_projectile)); + proj->x = (int)pos.x; + proj->y = (int)pos.y; + proj->vx = (int)vel.x; + proj->vy = (int)vel.y; + proj->type = type; + } +}; + +// player entity +class player : public entity +{ +public: + static const int phys_size = 28; + enum + { + WEAPON_NEEDRELOAD = 1 << 0, + WEAPON_DROPONUNEQUIP = 1 << 1, + WEAPON_DRAWSAMMO = 1 << 2, + WEAPON_HASSECONDARY = 1 << 3, + WEAPON_ISACTIVE = 1 << 4, // has the item + WEAPON_AUTOFIRE = 1 << 5, + + WEAPON_PROJECTILETYPE_GUN = 0, + WEAPON_PROJECTILETYPE_ROCKET = 1, + WEAPON_PROJECTILETYPE_SHOTGUN = 2, + + // Gun + + + // modifiers + MODIFIER_HASACTIVATIONS = 1 << 0, + MODIFIER_TIMELIMITED = 1 << 1, + MODIFIER_ISACTIVE = 1 << 2, + MODIFIER_NEEDSACTIVATION = 1 << 3, + + MODIFIER_RETURNFLAGS_OVERRIDEWEAPON = 1 << 0, + MODIFIER_RETURNFLAGS_OVERRIDEVELOCITY = 1 << 1, + MODIFIER_RETURNFLAGS_OVERRIDEPOSITION = 1 << 2, + MODIFIER_RETURNFLAGS_OVERRIDEGRAVITY = 1 << 3, + }; + class weapon + { + public: + entity* hitobjects[10]; + int numobjectshit; // for melee, so we don't hit the same object more than once per bash + int weapontype; + int equiptime; + int unequiptime; + int numammo; + int magsize; + int nummagazines; + int flags; + int firetime; + int reloadtime; + int projectileclass; + int damage; + int sound_fire; + int sound_equip; + int sound_impact; + int sound_impact_projectile; + int visualtimeattack; + float projectilevel; + float projectilespan; + float reach; // for melee + float force; + float recoilforce; + float projoffsety; + float projoffsetx; + + weapon() + { + weapontype = 0; + numammo = 0; + flags = 0; + reloadtime = 0; + projectileclass = 0; + numobjectshit = 0; + reach = 0.0f; + force = 5.0f; + damage = 1; + sound_fire = -1; + sound_equip = -1; + sound_impact = -1; + sound_impact_projectile = -1, + visualtimeattack = 3; + recoilforce = 0.0f; + projoffsety = 0.0f; + projoffsetx = 0.0f; + } + + void setgun(int ammo = 10) + { + weapontype = WEAPON_TYPE_GUN; + flags = 0;//WEAPON_DRAWSAMMO; + numammo = ammo; + projectileclass = WEAPON_PROJECTILETYPE_GUN; + firetime = SERVER_TICK_SPEED/10; + magsize = 0; + projectilevel = 30.0f; + projectilespan = 50.0f * 1.0f; + sound_fire = SOUND_FIRE_GUN; + sound_equip = SOUND_EQUIP_GUN; + sound_impact_projectile = SOUND_IMPACT_PROJECTILE_GUN; + projoffsety = -10.0f; + projoffsetx = 24.0f; + } + + void setrocket(int ammo = 10) + { + weapontype = WEAPON_TYPE_ROCKET; + flags = WEAPON_DRAWSAMMO; + numammo = ammo; + projectileclass = WEAPON_PROJECTILETYPE_ROCKET; + projectilevel = 15.0f; + projectilespan = 50.0f * 5.0f; + firetime = SERVER_TICK_SPEED * 4/5; + magsize = 0; + recoilforce = 5.0f; + sound_fire = SOUND_FIRE_ROCKET; + sound_equip = SOUND_EQUIP_ROCKET; + sound_impact_projectile = SOUND_IMPACT_PROJECTILE_ROCKET; + projoffsety = -17.0f; + projoffsetx = 24.0f; + } + + /*void setsniper(int ammo = 10) + { + weapontype = WEAPON_TYPE_SNIPER; + flags = WEAPON_DRAWSAMMO | WEAPON_HASSECONDARY | WEAPON_NEEDRELOAD; + numammo = ammo; + projectileclass = WEAPON_PROJECTILETYPE_SNIPER; + projectilevel = 30.0f; + projectilespan = 50.0f * 5.0f; + firetime = SERVER_TICK_SPEED; + reloadtime = SERVER_TICK_SPEED/2; + magsize = 2; + recoilforce = 20.0f; + }*/ + + void setshotgun(int ammo = 10) + { + weapontype = WEAPON_TYPE_SHOTGUN; + flags = WEAPON_DRAWSAMMO | WEAPON_NEEDRELOAD; + numammo = ammo; + projectileclass = WEAPON_PROJECTILETYPE_SHOTGUN; + projectilevel = 30.0f; + projectilespan = 50.0f * 5.0f; + firetime = SERVER_TICK_SPEED/2; + reloadtime = SERVER_TICK_SPEED/2; + magsize = 2; + damage = 3; + recoilforce = 5.0f; + sound_fire = SOUND_FIRE_SHOTGUN; + sound_equip = SOUND_EQUIP_SHOTGUN; + sound_impact_projectile = SOUND_IMPACT_PROJECTILE_SHOTGUN; + projoffsety = -17.0f; + projoffsetx = 24.0f; + } + + void setmelee(int ammo = 10) + { + weapontype = WEAPON_TYPE_MELEE; + flags = 0;//WEAPON_AUTOFIRE; + numammo = ammo; + projectileclass = -1; + firetime = SERVER_TICK_SPEED/5; + reloadtime = 0; + magsize = 2; + numobjectshit = 0; + reach = 15.0f; + damage = 1; + sound_fire = SOUND_FIRE_MELEE; + sound_equip = SOUND_EQUIP_MELEE; + sound_impact = SOUND_PLAYER_IMPACT; + } + + void settype() + { + switch(weapontype) + { + case WEAPON_TYPE_GUN: + { + setgun(); + break; + } + case WEAPON_TYPE_ROCKET: + { + setrocket(); + break; + } + /*case WEAPON_TYPE_SNIPER: + { + setsniper(); + break; + }*/ + case WEAPON_TYPE_SHOTGUN: + { + setshotgun(); + break; + } + case WEAPON_TYPE_MELEE: + { + setmelee(); + break; + } + default: + break; + } + } + + int activate(player* player) + { + // create sound event for fire + int projectileflags = 0; + create_sound(player->pos, sound_fire); + + switch (weapontype) + { + case WEAPON_TYPE_ROCKET: + projectileflags |= projectile::PROJECTILE_FLAGS_EXPLODE; + case WEAPON_TYPE_GUN: + //case WEAPON_TYPE_SNIPER: + case WEAPON_TYPE_SHOTGUN: + { + if (flags & WEAPON_DRAWSAMMO) + numammo--; + // Create projectile + new projectile(projectileclass, + player->client_id, + player->pos+vec2(0,projoffsety)+player->direction*projoffsetx, + player->direction*projectilevel, + (int)projectilespan, + player, + damage, + projectileflags, + force, + sound_impact_projectile); + // recoil force if any + if (recoilforce > 0.0f) + { + vec2 dir(player->direction.x,0.5); + if (dir.x == 0.0f) + dir.x = 0.5f; + else + dir = normalize(dir); + player->vel -= dir * recoilforce; + } + return firetime; + } + case WEAPON_TYPE_MELEE: + { + // Start bash sequence + numobjectshit = 0; + return firetime; + } + default: + return 0; + } + } + + void update(player* owner, int fire_timeout) + { + switch(weapontype) + { + case WEAPON_TYPE_MELEE: + { + // No more melee + if (fire_timeout <= 0) + return; + + // only one that needs update (for now) + // do selection for the weapon and bash anything in it + // check if we hit anything along the way + int type = OBJTYPE_PLAYER; + entity *ents[64]; + vec2 dir = owner->pos + owner->direction * reach; + float radius = length(dir * 0.5f); + vec2 center = owner->pos + dir * 0.5f; + int num = world.find_entities(center, radius, ents, 64, &type, 1); + + for (int i = 0; i < num; i++) + { + // Check if entity is a player + if (ents[i] == owner) + continue; + // make sure we haven't hit this object before + bool balreadyhit = false; + for (int j = 0; j < numobjectshit; j++) + { + if (hitobjects[j] == ents[i]) + balreadyhit = true; + } + if (balreadyhit) + continue; + + // check so we are sufficiently close + if (distance(ents[i]->pos, owner->pos) > (owner->phys_size * 2.0f)) + continue; + + // hit a player, give him damage and stuffs... + // create sound for bash + create_sound(ents[i]->pos, sound_impact); + + // set his velocity to fast upward (for now) + create_smoke(ents[i]->pos); + hitobjects[numobjectshit++] = ents[i]; + ents[i]->take_damage(vec2(0,10.0f), damage, owner->client_id); + player* target = (player*)ents[i]; + vec2 dir; + if (length(target->pos - owner->pos) > 0.0f) + dir = normalize(target->pos - owner->pos); + else + dir = vec2(0,-1); + target->vel += dir * 10.0f + vec2(0,-10.0f); + } + break; + } + default: + break; + } + } + }; + + class modifier + { + public: + vec2 activationdir; + entity* hitobjects[10]; + int numobjectshit; + float velocity; + int modifiertype; + int duration; + int numactivations; + int activationtime; + int cooldown; + int movetime; + int visualtimeattack; + int currentactivation; + int currentmovetime; + int currentcooldown; + int damage; + int flags; + int sound_impact; + int sound_activate; + + modifier() + { + modifiertype = 0; + duration = 0; + numobjectshit = 0; + numactivations = 0; + activationtime = 0; + cooldown = 0; + movetime = 0; + currentactivation = 0; + currentmovetime = 0; + currentcooldown =0; + damage = 0; + flags = 0; + activationdir = vec2(0.0f, 1.0f); + velocity = 0.0f; + visualtimeattack = 0; + sound_impact = -1; + } + + void setninja() + { + modifiertype = MODIFIER_TYPE_NINJA; + duration = SERVER_TICK_SPEED * 15; + numactivations = -1; + movetime = SERVER_TICK_SPEED / 5; + activationtime = SERVER_TICK_SPEED / 2; + cooldown = SERVER_TICK_SPEED; + currentactivation = 0; + currentmovetime = 0; + numobjectshit = 0; + damage = 3; + flags = MODIFIER_TIMELIMITED | MODIFIER_NEEDSACTIVATION; + velocity = 50.0f; + visualtimeattack = 3; + sound_impact = SOUND_PLAYER_IMPACT_NINJA; + sound_activate = SOUND_FIRE_NINJA; + } + + void settimefield() + { + modifiertype = MODIFIER_TYPE_TIMEFIELD; + duration = SERVER_TICK_SPEED * 10; + numactivations = -1; + activationtime = SERVER_TICK_SPEED; + numobjectshit = 0; + currentactivation = 0; + flags = MODIFIER_TIMELIMITED; + velocity = 0.0f; + } + + void settype() + { + switch (modifiertype) + { + case MODIFIER_TYPE_NINJA: + { + setninja(); + break; + } + case MODIFIER_TYPE_TIMEFIELD: + { + settimefield(); + break; + } + default: + break; + } + } + + int updateninja(player* player) + { + if (currentactivation <= 0) + return MODIFIER_RETURNFLAGS_OVERRIDEWEAPON; + currentactivation--; + currentmovetime--; + + if (currentmovetime == 0) + { + // reset player velocity + player->vel *= 0.2f; + //return MODIFIER_RETURNFLAGS_OVERRIDEWEAPON; + } + + if (currentmovetime > 0) + { + // Set player velocity + player->vel = activationdir * velocity; + vec2 oldpos = player->pos; + move_box(&player->pos, &player->vel, vec2(player->phys_size, player->phys_size), 0.0f); + // reset velocity so the client doesn't predict stuff + player->vel = vec2(0.0f,0.0f); + if ((currentmovetime % 2) == 0) + { + create_smoke(player->pos); + } + + // check if we hit anything along the way + { + int type = OBJTYPE_PLAYER; + entity *ents[64]; + vec2 dir = player->pos - oldpos; + float radius = length(dir * 0.5f); + vec2 center = oldpos + dir * 0.5f; + int num = world.find_entities(center, radius, ents, 64, &type, 1); + + for (int i = 0; i < num; i++) + { + // Check if entity is a player + if (ents[i] == player) + continue; + // make sure we haven't hit this object before + bool balreadyhit = false; + for (int j = 0; j < numobjectshit; j++) + { + if (hitobjects[j] == ents[i]) + balreadyhit = true; + } + if (balreadyhit) + continue; + + // check so we are sufficiently close + if (distance(ents[i]->pos, player->pos) > (player->phys_size * 2.0f)) + continue; + + // hit a player, give him damage and stuffs... + create_sound(ents[i]->pos, sound_impact); + // set his velocity to fast upward (for now) + hitobjects[numobjectshit++] = ents[i]; + ents[i]->take_damage(vec2(0,10.0f), damage, player->client_id); + } + } + return MODIFIER_RETURNFLAGS_OVERRIDEWEAPON | MODIFIER_RETURNFLAGS_OVERRIDEVELOCITY | MODIFIER_RETURNFLAGS_OVERRIDEPOSITION|MODIFIER_RETURNFLAGS_OVERRIDEGRAVITY; + } + + + // move char, and check intersection from us to target + return MODIFIER_RETURNFLAGS_OVERRIDEWEAPON | MODIFIER_RETURNFLAGS_OVERRIDEVELOCITY; + } + + int activateninja(player* player) + { + // ok then, activate ninja + activationdir = player->direction; + currentactivation = activationtime; + currentmovetime = movetime; + currentcooldown = cooldown; + // reset hit objects + numobjectshit = 0; + + create_sound(player->pos, SOUND_FIRE_NINJA); + + return MODIFIER_RETURNFLAGS_OVERRIDEWEAPON; + } + + int activate(player* player) + { + if (flags & MODIFIER_NEEDSACTIVATION) + { + if (!numactivations) + return 0; + numactivations--; + } + + switch (modifiertype) + { + case MODIFIER_TYPE_NINJA: + { + return activateninja(player); + } + /*case MODIFIER_TYPE_TIMEFIELD: + { + updatetimefield(); + break; + }*/ + default: + return 0; + } + } + int update(player* player) + { + switch (modifiertype) + { + case MODIFIER_TYPE_NINJA: + { + return updateninja(player); + } + /*case MODIFIER_TYPE_TIMEFIELD: + { + updatetimefield(); + break; + }*/ + default: + return 0; + } + } + }; + + enum + { + PLAYER_FLAGS_ISRELOADING = 1 << 0, + PLAYER_FLAGS_ISEQUIPPING = 1 << 1, + }; + + weapon lweapons[WEAPON_NUMWEAPONS]; + modifier modifiers[MODIFIER_NUMMODIFIERS]; + int iactiveweapon; + int inextweapon; + int equip_time; + + int client_id; + int flags; + + char name[32]; + player_input previnput; + player_input input; + int tick_count; + int damage_taken_tick; + + vec2 vel; + vec2 direction; + + int jumped; + int airjumped; + + //int firing; + int hooking; + + int fire_timeout; + int reload_timeout; + + int health; + int armor; + + int score; + + // sounds + int sound_player_jump; + int sound_player_land; + int sound_player_hurt_short; + int sound_player_hurt_long; + int sound_player_spawn; + int sound_player_chain_loop; + int sound_player_chain_impact; + int sound_player_impact; + int sound_player_impact_ninja; + int sound_player_die; + int sound_player_switchweapon; + + player* phookedplayer; + powerup* phookedpowerup; + int numhooked; + vec2 hook_pos; + vec2 hook_dir; + + player() : + entity(OBJTYPE_PLAYER) + { + reset(); + + //firing = 0; + // setup weaponflags and stuff + lweapons[WEAPON_TYPE_GUN].setgun(); + lweapons[WEAPON_TYPE_ROCKET].setrocket(); + //lweapons[WEAPON_TYPE_SNIPER].setsniper(); + lweapons[WEAPON_TYPE_SHOTGUN].setshotgun(); + lweapons[WEAPON_TYPE_MELEE].setmelee(); + + modifiers[MODIFIER_TYPE_NINJA].setninja(); + modifiers[MODIFIER_TYPE_TIMEFIELD].settimefield(); + //modifiers[MODIFIER_TYPE_NINJA].flags |= MODIFIER_ISACTIVE; + + sound_player_jump = SOUND_PLAYER_JUMP; + sound_player_hurt_short = SOUND_PLAYER_HURT_SHORT; + sound_player_hurt_long = SOUND_PLAYER_HURT_LONG; + sound_player_spawn = SOUND_PLAYER_SPAWN; + sound_player_chain_loop = SOUND_PLAYER_CHAIN_LOOP; + sound_player_chain_impact = SOUND_PLAYER_CHAIN_IMPACT; + sound_player_impact = SOUND_PLAYER_IMPACT; + sound_player_impact_ninja = SOUND_PLAYER_IMPACT_NINJA; + sound_player_die = SOUND_PLAYER_DIE; + sound_player_switchweapon = SOUND_PLAYER_SWITCHWEAPON; + sound_player_land = SOUND_PLAYER_LAND; + } + + void reset() + { + equip_time = 0; + phookedplayer = 0; + numhooked = 0; + proximity_radius = phys_size; + name[0] = 'n'; + name[1] = 'o'; + name[2] = 'o'; + name[3] = 'b'; + name[4] = 0; + + pos = vec2(100.0f, 0.0f); + vel = vec2(0.0f, 0.0f); + direction = vec2(0.0f, 1.0f); + client_id = -1; + tick_count = 0; + score = 0; + flags = 0; + } + + virtual void destroy() { flags = 0; } + + void respawn() + { + health = PLAYER_MAXHEALTH; + armor = 0; + + hooking = 0; + phookedplayer = 0; + phookedpowerup = 0; + numhooked = 0; + fire_timeout = 0; + reload_timeout = 0; + iactiveweapon = 0; + inextweapon = -1; + equip_time = 0; + jumped = 0; + airjumped = 0; + mem_zero(&input, sizeof(input)); + vel = vec2(0.0f, 0.0f); + + int start, num; + map_get_type(1, &start, &num); + + if(num) + { + mapres_spawnpoint *sp = (mapres_spawnpoint*)map_get_item(start + (rand()%num), NULL, NULL); + pos = vec2(sp->x, sp->y); + } + else + pos = vec2(100.0f, -60.0f); + + // reset active flags + for (int i = 0; i < WEAPON_NUMWEAPONS; i++) + { + // reset and remove + lweapons[i].settype(); + lweapons[i].flags &= ~WEAPON_ISACTIVE; + } + + + // TEMP REMOVE + + /*for (int i = 0; i < WEAPON_NUMWEAPONS; i++) + { + lweapons[i].settype(); + lweapons[i].flags |= WEAPON_ISACTIVE; + }*/ + lweapons[WEAPON_TYPE_MELEE].flags |= WEAPON_ISACTIVE; + // Add gun as default weapon + iactiveweapon = WEAPON_TYPE_GUN; + lweapons[WEAPON_TYPE_GUN].numammo = 10; + lweapons[WEAPON_TYPE_GUN].flags |= WEAPON_ISACTIVE; + + create_sound(pos, sound_player_spawn); + } + + bool is_grounded() + { + if(col_check_point((int)(pos.x+phys_size/2), (int)(pos.y+phys_size/2+5))) + return true; + if(col_check_point((int)(pos.x-phys_size/2), (int)(pos.y+phys_size/2+5))) + return true; + return false; + } + + // Disable weapon activation if this returns true + int handlemodifiers() + { + int returnflags = 0; + for (int i = 0; i < MODIFIER_NUMMODIFIERS; i++) + { + if (modifiers[i].flags & MODIFIER_ISACTIVE) + { + modifiers[i].duration--; + modifiers[i].currentcooldown--; + + // Check if it should activate + if (modifiers[i].currentcooldown <= 0 && + (modifiers[i].flags & MODIFIER_NEEDSACTIVATION) && + input.fire && !(previnput.fire)) + { + returnflags |= modifiers[i].activate(this); + } + + returnflags |= modifiers[i].update(this); + + // remove active if timed out + if (modifiers[i].duration <= 0 && modifiers[i].currentactivation <= 0) + modifiers[i].flags &= ~MODIFIER_ISACTIVE; + } + } + return returnflags; + } + + void handleweapon() + { + // handle weapon + if(input.fire && (!previnput.fire || lweapons[iactiveweapon].flags & WEAPON_AUTOFIRE) && + !(flags & PLAYER_FLAGS_ISEQUIPPING) && !reload_timeout) + { + if(fire_timeout == 0) + { + if (lweapons[iactiveweapon].numammo || !(lweapons[iactiveweapon].flags & WEAPON_DRAWSAMMO)) + { + // Decrease ammo + fire_timeout = lweapons[iactiveweapon].activate(this); + } + else if ((lweapons[iactiveweapon].flags & WEAPON_NEEDRELOAD) && lweapons[iactiveweapon].nummagazines) + { + // reload + reload_timeout = lweapons[iactiveweapon].reloadtime; + lweapons[iactiveweapon].nummagazines--; + lweapons[iactiveweapon].numammo = lweapons[iactiveweapon].magsize; + } + } + } + + // update active weapon + lweapons[iactiveweapon].update(this, fire_timeout); + } + + void handlehook() + { + // handle hook + if(input.hook) + { + if(hooking == 0) + { + hooking = 1; + hook_pos = pos; + hook_dir = direction; + // Sound + create_sound(pos, sound_player_chain_loop, SOUND_LOOPFLAG_STARTLOOP); + } + else if(hooking == 1) + { + vec2 new_pos = hook_pos+hook_dir*hook_fire_speed; + + // Check against other players and powerups first + player* targetplayer = 0; + powerup* targetpowerup = 0; + { + static const int typelist[2] = { OBJTYPE_PLAYER, OBJTYPE_POWERUP}; + entity *ents[64]; + vec2 dir = new_pos - hook_pos; + float radius = length(dir * 0.5f); + vec2 center = hook_pos + dir * 0.5f; + int num = world.find_entities(center, radius, ents, 64,typelist,2); + + for (int i = 0; i < num; i++) + { + // Check if entity is a player + if (ents[i] == this || (targetplayer && targetpowerup)) + continue; + + if (!targetplayer && ents[i]->objtype == OBJTYPE_PLAYER) + { + // temp, set hook pos to our position + if (((player*)ents[i])->phookedplayer != this) + targetplayer = (player*)ents[i]; + } + else if (!targetpowerup && ents[i]->objtype == OBJTYPE_POWERUP && + (((powerup*)ents[i])->flags & powerup::POWERUP_FLAG_HOOKABLE)) + { + targetpowerup = (powerup*)ents[i]; + } + } + } + + //player* targetplayer = intersect_player(hook_pos, hook_pos, new_pos, this); + if (targetplayer) + { + // So he can't move "normally" + new_pos = targetplayer->pos; + phookedplayer = targetplayer; + targetplayer->numhooked++; + hooking = 3; + create_sound(pos, sound_player_chain_impact); + + // stop looping chain sound + create_sound(pos, sound_player_chain_loop, SOUND_LOOPFLAG_STOPLOOP); + } + else if (targetpowerup) + { + new_pos = targetpowerup->pos; + phookedpowerup = targetpowerup; + phookedpowerup->playerhooked = this; + hooking = 4; + create_sound(pos, sound_player_chain_impact); + + // stop looping chain sound + create_sound(pos, sound_player_chain_loop, SOUND_LOOPFLAG_STOPLOOP); + } + else if(intersect_line(hook_pos, new_pos, &new_pos)) + { + hooking = 2; + create_sound(pos, sound_player_chain_impact); + + // stop looping chain sound + create_sound(pos, sound_player_chain_loop, SOUND_LOOPFLAG_STOPLOOP); + } + else if(distance(pos, new_pos) > hook_length) + { + hooking = -1; + create_sound(pos, sound_player_chain_loop, SOUND_LOOPFLAG_STOPLOOP); + } + + hook_pos = new_pos; + } + else if(hooking == 2) + { + vec2 hookvel = normalize(hook_pos-pos)*hook_drag_accel; + // the hook as more power to drag you up then down. + // this makes it easier to get on top of an platform + if(hookvel.y > 0) + hookvel.y *= 0.3f; + + // the hook will boost it's power if the player wants to move + // in that direction. otherwise it will dampen everything abit + if((hookvel.x < 0 && input.left) || (hookvel.x > 0 && input.right)) + hookvel.x *= 0.95f; + else + hookvel.x *= 0.75f; + vec2 new_vel = vel+hookvel; + + // check if we are under the legal limit for the hook + if(length(new_vel) < hook_drag_speed || length(new_vel) < length(vel)) + vel = new_vel; // no problem. apply + } + else if (hooking == 3) + { + // hmm, force the targetplayer towards us if possible, otherwise us towards them if they are already hooked + if (phookedplayer) + { + if (phookedplayer->hooking > 1) + { + // Drag us towards target player + vec2 hookvel = normalize(hook_pos-pos)*hook_drag_accel; + if((hookvel.x < 0 && input.left) || (hookvel.x > 0 && input.right)) + hookvel.x *= 0.95f; + else + hookvel.x *= 0.75f; + + // Apply the velocity + // the hook will boost it's power if the player wants to move + // in that direction. otherwise it will dampen everything abit + vec2 new_vel = vel+hookvel; + + // check if we are under the legal limit for the hook + if(length(new_vel) < hook_drag_speed || length(new_vel) < length(vel)) + vel = new_vel; // no problem. apply + } + else + { + // Drag targetplayer towards us + vec2 hookvel = normalize(pos-hook_pos)*hook_drag_accel; + + // Apply the velocity + // the hook will boost it's power if the player wants to move + // in that direction. otherwise it will dampen everything abit + vec2 new_vel = phookedplayer->vel+hookvel; + + // check if we are under the legal limit for the hook + if(length(new_vel) < hook_drag_speed || length(new_vel) < length(vel)) + phookedplayer->vel = new_vel; // no problem. apply + } + hook_pos = phookedplayer->pos; + // if hooked player dies, release the hook + } + else + { + hooking = -1; + phookedplayer = 0; + } + } + else if (hooking == 4) + { + // Have a powerup, drag it towards us + vec2 hookvel = normalize(pos-hook_pos)*hook_drag_accel; + + // Apply the velocity + // the hook will boost it's power if the player wants to move + // in that direction. otherwise it will dampen everything abit + vec2 new_vel = phookedpowerup->vel+hookvel; + + // check if we are under the legal limit for the hook + if(length(new_vel) < hook_drag_speed || length(new_vel) < length(vel)) + phookedpowerup->vel = new_vel; // no problem. apply + hook_pos = phookedpowerup->pos; + } + } + else + { + hooking = 0; + hook_pos = pos; + if (phookedplayer) + { + phookedplayer->numhooked--; + phookedplayer = 0; + } + } + } + + void getattackticks(int& curattack, int& attacklen, int& visualtimeattack) + { + // time left from current attack (if any) + // first check modifiers (ninja...) + for (int i = 0; i < MODIFIER_NUMMODIFIERS; i++) + { + if ((modifiers[i].flags & (MODIFIER_ISACTIVE | MODIFIER_NEEDSACTIVATION)) == (MODIFIER_ISACTIVE | MODIFIER_NEEDSACTIVATION)) + { + curattack = modifiers[i].currentactivation; + attacklen = modifiers[i].activationtime; + visualtimeattack = modifiers[i].visualtimeattack; + return; + } + } + + // otherwise current fire timeout + curattack = fire_timeout; + attacklen = lweapons[iactiveweapon].firetime; + visualtimeattack = lweapons[iactiveweapon].visualtimeattack; + } + + virtual void tick() + { + tick_count++; + + // fetch some info + bool grounded = is_grounded(); + direction = get_direction(input.angle); + + // decrease reload timer + if(fire_timeout) + fire_timeout--; + if (reload_timeout) + reload_timeout--; + + // Switch weapons + if (flags & PLAYER_FLAGS_ISEQUIPPING) + { + equip_time--; + if (equip_time <= 0) + { + if (inextweapon >= 0) + { + equip_time = SERVER_TICK_SPEED * lweapons[inextweapon].equiptime; + iactiveweapon = inextweapon; + inextweapon = -1; + + // Send switch weapon event to client? + } + else + { + flags &= ~PLAYER_FLAGS_ISEQUIPPING; + } + } + } + else if (input.activeweapon && iactiveweapon != (input.activeweapon & ~0x80000000)) + { + input.activeweapon &= ~0x80000000; + // check which weapon to activate + if (input.activeweapon >= 0 && input.activeweapon < WEAPON_NUMWEAPONS && + (lweapons[input.activeweapon].flags & WEAPON_ISACTIVE)) + { + inextweapon = input.activeweapon; + equip_time = SERVER_TICK_SPEED * lweapons[iactiveweapon].unequiptime; + // unequip current + flags |= PLAYER_FLAGS_ISEQUIPPING; + + create_sound(pos, sound_player_switchweapon); + } + } + + // don't do any weapon activations if modifier is currently overriding + int modifierflags = handlemodifiers(); + if (!(modifierflags & MODIFIER_RETURNFLAGS_OVERRIDEWEAPON)) + handleweapon(); + + handlehook(); + + // handle movement + if(grounded) + { + if (airjumped) + create_sound(pos, SOUND_PLAYER_LAND); + airjumped = 0; + } + + float elast = 0.0f; + + if (!numhooked) + { + // I'm hooked by someone, so don't do any movement plz (temp) + if (!(modifierflags & MODIFIER_RETURNFLAGS_OVERRIDEVELOCITY)) + { + if(grounded) + { + // ground movement + if(input.left) + { + if(vel.x > -ground_control_speed) + { + vel.x -= ground_control_accel; + if(vel.x < -ground_control_speed) + vel.x = -ground_control_speed; + } + } + else if(input.right) + { + if(vel.x < ground_control_speed) + { + vel.x += ground_control_accel; + if(vel.x > ground_control_speed) + vel.x = ground_control_speed; + } + } + else + vel.x *= ground_friction; // ground fiction + } + else + { + // air movement + if(input.left) + { + if(vel.x > -air_control_speed) + vel.x -= air_control_accel; + } + else if(input.right) + { + if(vel.x < air_control_speed) + vel.x += air_control_accel; + } + else + vel.x *= air_friction; // air fiction + } + + if(input.jump) + { + if(jumped == 0) + { + if(grounded) + { + create_sound(pos, sound_player_jump); + vel.y = -ground_jump_speed; + jumped++; + } + /* + else if(airjumped == 0) + { + vel.y = -12; + airjumped++; + jumped++; + }*/ + } + else if (!grounded) + { + airjumped++; + } + } + else + jumped = 0; + } + + // meh, go through all players and stop their hook on me + /* + for(entity *ent = world.first_entity; ent; ent = ent->next_entity) + { + if (ent && ent->objtype == OBJTYPE_PLAYER) + { + player *p = (player*)ent; + if(p != this) + { + float d = distance(pos, p->pos); + vec2 dir = normalize(pos - p->pos); + if(d < phys_size*1.5f) + { + float a = phys_size*1.5f - d; + vel = vel + dir*a; + } + + if(p->phookedplayer == this) + { + if(d > phys_size*2.5f) + { + elast = 0.0f; + vel = vel - dir*2.5f; + } + } + } + } + }*/ + + // gravity + if (!(modifierflags & MODIFIER_RETURNFLAGS_OVERRIDEGRAVITY)) + vel.y += gravity; + } + + if (!(modifierflags & MODIFIER_RETURNFLAGS_OVERRIDEPOSITION)) + move_box(&pos, &vel, vec2(phys_size, phys_size), elast); + } + + void die() + { + create_sound(pos, sound_player_die); + // release our hooked player + if (phookedplayer) + { + phookedplayer->numhooked--; + phookedplayer = 0; + hooking = -1; + numhooked = 0; + } + respawn(); + + // meh, go through all players and stop their hook on me + for(entity *ent = world.first_entity; ent; ent = ent->next_entity) + { + if (ent && ent->objtype == OBJTYPE_PLAYER) + { + player* p = (player*)ent; + if (p->phookedplayer == this) + { + p->phookedplayer = 0; + p->hooking = -1; + //p->numhooked--; + } + } + } + } + + virtual bool take_damage(vec2 force, int dmg, int from) + { + vel += force; + + if(armor) + { + armor -= 1; + dmg--; + } + + if(dmg > armor) + { + dmg -= armor; + armor = 0; + health -= dmg; + } + else + armor -= dmg; + /* + int armordmg = (dmg+1)/2; + int healthdmg = dmg-armordmg; + if(armor < armordmg) + { + healthdmg += armordmg - armor; + armor = 0; + } + else + armor -= armordmg; + + health -= healthdmg; + */ + + // create healthmod indicator + create_healthmod(pos, dmg); + + damage_taken_tick = tick_count+50; + + // check for death + if(health <= 0) + { + // apply score + if(from != -1) + { + if(from == client_id) + score--; + else + { + player *p = get_player(from); + p->score++; + } + } + + die(); + return false; + } + + if (dmg > 2) + create_sound(pos, sound_player_hurt_long); + else + create_sound(pos, sound_player_hurt_short); + + // spawn blood? + + return true; + } + + virtual void snap(int snaping_client) + { + obj_player *player = (obj_player *)snap_new_item(OBJTYPE_PLAYER, client_id, sizeof(obj_player)); + + client_info info; + if(server_getclientinfo(client_id, &info)) + snap_encode_string(info.name, player->name, strlen(info.name), 32); + + player->x = (int)pos.x; + player->y = (int)pos.y; + player->vx = (int)vel.x; + player->vy = (int)vel.y; + player->emote = EMOTE_NORMAL; + + player->ammocount = lweapons[iactiveweapon].numammo; + player->health = 0; + player->armor = 0; + player->local = 0; + player->clientid = client_id; + player->weapon = iactiveweapon; + player->modifier = 0; + for (int i = 0; i < MODIFIER_NUMMODIFIERS; i++) + { + // add active modifiers + if (modifiers[i].flags & MODIFIER_ISACTIVE) + player->modifier |= 1 << i; + } + // get current attack ticks + getattackticks(player->attackticks, player->attacklen, player->visualtimeattack); + + + if(client_id == snaping_client) + { + player->local = 1; + player->health = health; + player->armor = armor; + } + + if(length(vel) > 15.0f) + player->emote = EMOTE_HAPPY; + + if(damage_taken_tick > tick_count) + player->emote = EMOTE_PAIN; + + if(player->emote == EMOTE_NORMAL) + { + if((tick_count%(50*5)) < 10) + player->emote = EMOTE_BLINK; + } + + player->hook_active = hooking>0?1:0; + player->hook_x = (int)hook_pos.x; + player->hook_y = (int)hook_pos.y; + + player->angle = input.angle; + player->score = score; + } +}; + +// POWERUP /////////////////////// + +powerup::powerup(int _type, int _subtype, int _numitems, int _flags) : + entity(OBJTYPE_POWERUP) +{ + static int current_id = 0; + playerhooked = 0; + id = current_id++; + vel = vec2(0.0f,0.0f); + type = _type; + subtype = _subtype; + numitems = _numitems; + flags = _flags; + // set radius (so it can collide and be hooked and stuff) + proximity_radius = phys_size; + spawntick = -1; + world.insert_entity(this); +} + +void powerup::spawnrandom(int _lifespan) +{ + return; + /* + vec2 pos; + int start, num; + map_get_type(1, &start, &num); + + if(!num) + return; + + mapres_spawnpoint *sp = (mapres_spawnpoint*)map_get_item(start + (rand()%num), NULL, NULL); + pos = vec2(sp->x, sp->y); + + // Check if there already is a powerup at that location + { + int type = OBJTYPE_POWERUP; + entity *ents[64]; + int num = world.find_entities(pos, 5.0f, ents, 64,&type,1); + for (int i = 0; i < num; i++) + { + if (ents[i]->objtype == OBJTYPE_POWERUP) + { + // location busy + return; + } + } + } + + powerup* ppower = new powerup(rand() % POWERUP_TYPE_NUMPOWERUPS,_lifespan); + ppower->pos = pos; + if (ppower->type == POWERUP_TYPE_WEAPON) + { + ppower->subtype = rand() % WEAPON_NUMWEAPONS; + ppower->numitems = 10; + } + else if (ppower->type == POWERUP_TYPE_HEALTH || ppower->type == POWERUP_TYPE_ARMOR) + { + ppower->numitems = rand() % 5; + } + ppower->flags |= POWERUP_FLAG_HOOKABLE;*/ +} + +void powerup::tick() +{ + // wait for respawn + if(spawntick > 0) + { + if(server_tick() > spawntick) + spawntick = -1; + else + return; + } + + vec2 oldpos = pos; + //vel.y += 0.25f; + pos += vel; + move_box(&pos, &vel, vec2(phys_size, phys_size), 0.0f); + + // Check if a player intersected us + vec2 meh; + player* pplayer = intersect_player(pos, pos + vec2(0,16), meh, 0); + if (pplayer) + { + // player picked us up, is someone was hooking us, let them go + if (playerhooked) + playerhooked->hooking = -1; + int respawntime = -1; + switch (type) + { + case POWERUP_TYPE_HEALTH: + { + if(pplayer->health < PLAYER_MAXHEALTH) + { + pplayer->health = min(PLAYER_MAXHEALTH, pplayer->health + numitems); + respawntime = 20; + } + break; + } + case POWERUP_TYPE_ARMOR: + { + if(pplayer->armor < PLAYER_MAXARMOR) + { + pplayer->armor = min(PLAYER_MAXARMOR, pplayer->armor + numitems); + respawntime = 20; + } + break; + } + case POWERUP_TYPE_WEAPON: + { + if (pplayer->lweapons[subtype].flags & player::WEAPON_ISACTIVE) + { + // add ammo + /* + if (pplayer->lweapons[subtype].flags & player::WEAPON_NEEDRELOAD) + { + int numtoadd = min(numitems, pplayer->lweapons[subtype].magsize); + pplayer->lweapons[subtype].numammo = min(10, pplayer->lweapons[subtype].numammo + numtoadd); + pplayer->lweapons[subtype].nummagazines += (numitems - numtoadd) % pplayer->lweapons[subtype].magsize; + } + else*/ + if(pplayer->lweapons[subtype].numammo < 10) + { + respawntime = 20; + pplayer->lweapons[subtype].numammo = min(10, pplayer->lweapons[subtype].numammo + numitems); + } + } + else + { + pplayer->lweapons[subtype].settype(); + pplayer->lweapons[subtype].flags |= player::WEAPON_ISACTIVE; + respawntime = 20; + } + break; + } + case POWERUP_TYPE_NINJA: + { + respawntime = 60; + // reset and activate + pplayer->modifiers[MODIFIER_TYPE_NINJA].settype(); + pplayer->modifiers[MODIFIER_TYPE_NINJA].flags |= player::MODIFIER_ISACTIVE; + break; + } + //POWERUP_TYPE_TIMEFIELD = 4, + default: + break; + }; + + if(respawntime >= 0) + spawntick = server_tick() + server_tickspeed() * respawntime; + //world.destroy_entity(this); + } +} + +void powerup::snap(int snapping_client) +{ + if(spawntick != -1) + return; + + obj_powerup *powerup = (obj_powerup *)snap_new_item(OBJTYPE_POWERUP, id, sizeof(obj_powerup)); + powerup->x = (int)pos.x; + powerup->y = (int)pos.y; + powerup->vx = (int)vel.x; + powerup->vy = (int)vel.y; + powerup->type = type; + powerup->subtype = subtype; +} + +// POWERUP END /////////////////////// + +static player players[MAX_CLIENTS]; + +player *get_player(int index) +{ + return &players[index]; +} + +void create_healthmod(vec2 p, int amount) +{ + ev_healthmod *ev = (ev_healthmod *)events.create(EVENT_HEALTHMOD, sizeof(ev_healthmod)); + ev->x = (int)p.x; + ev->y = (int)p.y; + ev->amount = amount; +} + +void create_explosion(vec2 p, int owner, bool bnodamage) +{ + // create the event + ev_explosion *ev = (ev_explosion *)events.create(EVENT_EXPLOSION, sizeof(ev_explosion)); + ev->x = (int)p.x; + ev->y = (int)p.y; + + if (!bnodamage) + { + // deal damage + entity *ents[64]; + const float radius = 128.0f; + int num = world.find_entities(p, radius, ents, 64); + for(int i = 0; i < num; i++) + { + vec2 diff = ents[i]->pos - p; + vec2 forcedir(0,1); + if (length(diff)) + forcedir = normalize(diff); + float l = length(diff); + float dmg = 5 * (1 - (l/radius)); + if((int)dmg) + { + ents[i]->take_damage(forcedir*dmg*2, (int)dmg, owner);/* && + ents[i]->objtype == OBJTYPE_PLAYER && + owner >= 0) + { + player *p = (player*)ents[i]; + if(p->client_id == owner) + p->score--; + else + ((player*)ents[owner])->score++; + + }*/ + } + } + } +} + +void create_smoke(vec2 p) +{ + // create the event + ev_explosion *ev = (ev_explosion *)events.create(EVENT_SMOKE, sizeof(ev_explosion)); + ev->x = (int)p.x; + ev->y = (int)p.y; +} + +void create_sound(vec2 pos, int sound, int loopingflags) +{ + if (sound < 0) + return; + + // create a sound + ev_sound *ev = (ev_sound *)events.create(EVENT_SOUND, sizeof(ev_sound)); + ev->x = (int)pos.x; + ev->y = (int)pos.y; + ev->sound = sound | loopingflags; +} + +player* intersect_player(vec2 pos0, vec2 pos1, vec2& new_pos, entity* notthis) +{ + // Find other players + entity *ents[64]; + vec2 dir = pos1 - pos0; + float radius = length(dir * 0.5f); + vec2 center = pos0 + dir * 0.5f; + int num = world.find_entities(center, radius, ents, 64); + for (int i = 0; i < num; i++) + { + // Check if entity is a player + if (ents[i] != notthis && ents[i]->objtype == OBJTYPE_PLAYER) + { + // temp, set hook pos to our position + new_pos = ents[i]->pos; + return (player*)ents[i]; + } + } + + return 0; +} + +// Server hooks +static int addtick = SERVER_TICK_SPEED * 5; +void mods_tick() +{ + // clear all events + events.clear(); + world.tick(); + + if (addtick <= 0) + { + powerup::spawnrandom(SERVER_TICK_SPEED * 5); + addtick = SERVER_TICK_SPEED * 5; + } + addtick--; +} + +void mods_snap(int client_id) +{ + world.snap(client_id); + events.snap(client_id); +} + +void mods_client_input(int client_id, void *input) +{ + players[client_id].previnput = players[client_id].input; + players[client_id].input = *(player_input*)input; +} + +void mods_client_enter(int client_id) +{ + players[client_id].reset(); + players[client_id].client_id = client_id; + players[client_id].respawn(); + world.insert_entity(&players[client_id]); + +} + +void mods_client_drop(int client_id) +{ + players[client_id].client_id = -1; + world.remove_entity(&players[client_id]); +} + +void mods_init() +{ + col_init(32); + + int start, num; + map_get_type(MAPRES_ITEM, &start, &num); + + for(int i = 0; i < num; i++) + { + mapres_item *it = (mapres_item *)map_get_item(start+i, 0, 0); + + int type = -1; + int subtype = -1; + int numitems = 1; + + switch(it->type) + { + case ITEM_WEAPON_GUN: + type = POWERUP_TYPE_WEAPON; + subtype = WEAPON_TYPE_GUN; + break; + case ITEM_WEAPON_SHOTGUN: + type = POWERUP_TYPE_WEAPON; + subtype = WEAPON_TYPE_SHOTGUN; + numitems = 5; + break; + case ITEM_WEAPON_ROCKET: + type = POWERUP_TYPE_WEAPON; + subtype = WEAPON_TYPE_ROCKET; + numitems = 5; + break; + /*case ITEM_WEAPON_SNIPER: + type = POWERUP_TYPE_WEAPON; + subtype = WEAPON_TYPE_ROCKET; + break;*/ + case ITEM_WEAPON_HAMMER: + type = POWERUP_TYPE_WEAPON; + subtype = WEAPON_TYPE_MELEE; + break; + + case ITEM_HEALTH_1: + type = POWERUP_TYPE_HEALTH; + numitems = 1; + break; + case ITEM_HEALTH_5: + type = POWERUP_TYPE_HEALTH; + numitems = 5; + break; + case ITEM_HEALTH_10: + type = POWERUP_TYPE_HEALTH; + numitems = 10; + break; + + case ITEM_ARMOR_1: + type = POWERUP_TYPE_ARMOR; + numitems = 1; + break; + case ITEM_ARMOR_5: + type = POWERUP_TYPE_ARMOR; + numitems = 5; + break; + case ITEM_ARMOR_10: + type = POWERUP_TYPE_ARMOR; + numitems = 10; + break; + }; + + powerup* ppower = new powerup(type, subtype, numitems); + ppower->pos.x = it->x; + ppower->pos.y = it->y; + } + + + /* + powerup* ppower = new powerup(rand() % POWERUP_TYPE_NUMPOWERUPS,_lifespan); + ppower->pos = pos; + if (ppower->type == POWERUP_TYPE_WEAPON) + { + ppower->subtype = rand() % WEAPON_NUMWEAPONS; + ppower->numitems = 10; + } + else if (ppower->type == POWERUP_TYPE_HEALTH || ppower->type == POWERUP_TYPE_ARMOR) + { + ppower->numitems = rand() % 5; + } + ppower->flags |= POWERUP_FLAG_HOOKABLE; + */ + + //powerup::spawnrandom(SERVER_TICK_SPEED * 5); +} + +void mods_shutdown() {} +void mods_presnap() {} +void mods_postsnap() {} diff --git a/src/gfx.cpp b/src/gfx.cpp deleted file mode 100644 index c1e05c9c..00000000 --- a/src/gfx.cpp +++ /dev/null @@ -1,581 +0,0 @@ -#include -#include -#include - -#include "interface.h" - -using namespace baselib; - -static opengl::context context; - -struct custom_vertex -{ - vec3 pos; - vec2 tex; - vec4 color; -}; - -const int vertexBufferSize = 2048; -//static custom_vertex vertices[4]; -static custom_vertex* g_pVertices = 0; -static int g_iVertexStart = 0; -static int g_iVertexEnd = 0; -static vec4 g_QuadColor[4]; -static vec2 g_QuadTexture[4]; - -static opengl::vertex_buffer vertex_buffer; -//static int screen_width = 800; -//static int screen_height = 600; -static int screen_width = 1024; -static int screen_height = 768; -static float rotation = 0; -static int quads_drawing = 0; - - -struct texture_holder -{ - opengl::texture tex; - int flags; - int next; -}; - -static const int MAX_TEXTURES = 128; - -static texture_holder textures[MAX_TEXTURES]; -static int first_free_texture; - -static const char null_texture_data[] = { - 0xff,0x00,0x00,0xff, 0xff,0x00,0x00,0xff, 0x00,0xff,0x00,0xff, 0x00,0xff,0x00,0xff, - 0xff,0x00,0x00,0xff, 0xff,0x00,0x00,0xff, 0x00,0xff,0x00,0xff, 0x00,0xff,0x00,0xff, - 0x00,0x00,0xff,0xff, 0x00,0x00,0xff,0xff, 0xff,0xff,0x00,0xff, 0xff,0xff,0x00,0xff, - 0x00,0x00,0xff,0xff, 0x00,0x00,0xff,0xff, 0xff,0xff,0x00,0xff, 0xff,0xff,0x00,0xff, -}; - -static void draw_quad(bool _bflush = false) -{ - if (!_bflush && ((g_iVertexEnd + 4) < vertexBufferSize)) - { - // Just add - g_iVertexEnd += 4; - } - else if (g_iVertexEnd) - { - if (!_bflush) - g_iVertexEnd += 4; - if(GLEW_VERSION_2_0) - { - // set the data - vertex_buffer.data(g_pVertices, vertexBufferSize * sizeof(custom_vertex), GL_DYNAMIC_DRAW); - opengl::stream_vertex(&vertex_buffer, 3, GL_FLOAT, sizeof(custom_vertex), 0); - opengl::stream_texcoord(&vertex_buffer, 0, 2, GL_FLOAT, - sizeof(custom_vertex), - sizeof(vec3)); - opengl::stream_color(&vertex_buffer, 4, GL_FLOAT, - sizeof(custom_vertex), - sizeof(vec3)+sizeof(vec2)); - opengl::draw_arrays(GL_QUADS, 0, g_iVertexEnd); - } - else - { - glVertexPointer(3, GL_FLOAT, - sizeof(custom_vertex), - (char*)g_pVertices); - glTexCoordPointer(2, GL_FLOAT, - sizeof(custom_vertex), - (char*)g_pVertices + sizeof(vec3)); - glColorPointer(4, GL_FLOAT, - sizeof(custom_vertex), - (char*)g_pVertices + sizeof(vec3) + sizeof(vec2)); - glEnableClientState(GL_VERTEX_ARRAY); - glEnableClientState(GL_TEXTURE_COORD_ARRAY); - glEnableClientState(GL_COLOR_ARRAY); - glDrawArrays(GL_QUADS, 0, g_iVertexEnd); - } - // Reset pointer - g_iVertexEnd = 0; - } -} - -bool gfx_init(bool fullscreen) -{ - if(!context.create(screen_width, screen_height, 24, 8, 16, 0, fullscreen?opengl::context::FLAG_FULLSCREEN:0)) - { - dbg_msg("game", "failed to create gl context"); - return false; - } - // Init vertices - if (g_pVertices) - mem_free(g_pVertices); - g_pVertices = (custom_vertex*)mem_alloc(sizeof(custom_vertex) * vertexBufferSize, 1); - g_iVertexStart = 0; - g_iVertexEnd = 0; - - context.set_title("---"); - - /* - dbg_msg("gfx", "OpenGL version %d.%d.%d", context.version_major(), - context.version_minor(), - context.version_rev());*/ - - gfx_mapscreen(0,0,screen_width, screen_height); - - // TODO: make wrappers for this - glEnable(GL_BLEND); - - // model - mat4 mat = mat4::identity; - opengl::matrix_modelview(&mat); - - // Set all z to -5.0f - for (int i = 0; i < vertexBufferSize; i++) - g_pVertices[i].pos.z = -5.0f; - - if(GLEW_VERSION_2_0) - { - // set the streams - vertex_buffer.data(g_pVertices, sizeof(vertexBufferSize), GL_DYNAMIC_DRAW); - opengl::stream_vertex(&vertex_buffer, 3, GL_FLOAT, sizeof(custom_vertex), 0); - opengl::stream_texcoord(&vertex_buffer, 0, 2, GL_FLOAT, - sizeof(custom_vertex), - sizeof(vec3)); - opengl::stream_color(&vertex_buffer, 4, GL_FLOAT, - sizeof(custom_vertex), - sizeof(vec3)+sizeof(vec2)); - } - - // init textures - first_free_texture = 0; - for(int i = 0; i < MAX_TEXTURES; i++) - textures[i].next = i+1; - textures[MAX_TEXTURES-1].next = -1; - - // create null texture, will get id=0 - gfx_load_texture_raw(4,4,null_texture_data); - - return true; -} - -int gfx_unload_texture(int index) -{ - textures[index].tex.clear(); - textures[index].next = first_free_texture; - first_free_texture = index; - return 0; -} - -void gfx_blend_normal() -{ - // TODO: wrapper for this - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); -} - -void gfx_blend_additive() -{ - // TODO: wrapper for this - glBlendFunc(GL_SRC_ALPHA, GL_ONE); -} - -int gfx_load_texture_raw(int w, int h, const void *data) -{ - // grab texture - int tex = first_free_texture; - first_free_texture = textures[tex].next; - textures[tex].next = -1; - - // set data and return - // TODO: should be RGBA, not BGRA - dbg_msg("gfx", "%d = %dx%d", tex, w, h); - textures[tex].tex.data2d(w, h, GL_RGBA, GL_BGRA, GL_UNSIGNED_BYTE, data); - return tex; -} - -// simple uncompressed RGBA loaders -int gfx_load_texture_tga(const char *filename) -{ - image_info img; - - if(gfx_load_tga(&img, filename)) - { - int id = gfx_load_texture_raw(img.width, img.height, img.data); - mem_free(img.data); - return id; - } - - return 0; -} - -int gfx_load_tga(image_info *img, const char *filename) -{ - // open file for reading - file_stream file; - if(!file.open_r(filename)) - { - dbg_msg("game/tga", "failed to open file. filename='%s'", filename); - return 0; - } - - // read header - unsigned char headers[18]; - file.read(headers, sizeof(headers)); - img->width = headers[12]+(headers[13]<<8); - img->height = headers[14]+(headers[15]<<8); - - bool flipx = (headers[17] >> 4) & 1; - bool flipy = !((headers[17] >> 5) & 1); - - if(headers[2] != 2) // needs to be uncompressed RGB - { - dbg_msg("game/tga", "tga not uncompressed rgb. filename='%s'", filename); - return 0; - } - - if(headers[16] != 32) // needs to be RGBA - { - dbg_msg("game/tga", "tga is 32bit. filename='%s'", filename); - return 0; - } - - // read data - int data_size = img->width*img->height*4; - img->data = mem_alloc(data_size, 1); - - if (flipy) - { - for (int i = 0; i < img->height; i++) - { - file.read((char *)img->data + (img->height-i-1)*img->width*4, img->width*4); - } - } - else - file.read(img->data, data_size); - file.close(); - - return 1; -} - -void gfx_shutdown() -{ - if (g_pVertices) - mem_free(g_pVertices); - context.destroy(); -} - -void gfx_swap() -{ - context.swap(); -} - -int gfx_screenwidth() -{ - return screen_width; -} - -int gfx_screenheight() -{ - return screen_height; -} - -void gfx_texture_set(int slot) -{ - dbg_assert(quads_drawing == 0, "called gfx_texture_set within quads_begin"); - if(slot == -1) - opengl::texture_disable(0); - else - opengl::texture_2d(0, &textures[slot].tex); -} - -void gfx_clear(float r, float g, float b) -{ - glClearColor(r,g,b,1.0f); - opengl::clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); -} - -void gfx_mapscreen(float tl_x, float tl_y, float br_x, float br_y) -{ - mat4 mat; - mat.ortho(tl_x, br_x, br_y, tl_y, 1.0f, 10.f); - opengl::matrix_projection(&mat); -} - -void gfx_setoffset(float x, float y) -{ - //const float scale = 0.75f; - const float scale = 1.0f; - mat4 mat = mat4::identity; - mat.m[0] = scale; - mat.m[5] = scale; - mat.m[10] = scale; - mat.m[12] = x*scale; - mat.m[13] = y*scale; - opengl::matrix_modelview(&mat); -} - -void gfx_quads_begin() -{ - dbg_assert(quads_drawing == 0, "called quads_begin twice"); - quads_drawing++; - - gfx_quads_setsubset(0,0,1,1); - gfx_quads_setrotation(0); - gfx_quads_setcolor(1,1,1,1); -} - -void gfx_quads_end() -{ - dbg_assert(quads_drawing == 1, "called quads_end without quads_begin"); - draw_quad(true); - quads_drawing--; -} - - -void gfx_quads_setrotation(float angle) -{ - dbg_assert(quads_drawing == 1, "called gfx_quads_setrotation without quads_begin"); - rotation = angle; -} - -void gfx_quads_setcolorvertex(int i, float r, float g, float b, float a) -{ - dbg_assert(quads_drawing == 1, "called gfx_quads_setcolorvertex without quads_begin"); - g_QuadColor[i].r = r; - g_QuadColor[i].g = g; - g_QuadColor[i].b = b; - g_QuadColor[i].a = a; -} - -void gfx_quads_setcolor(float r, float g, float b, float a) -{ - dbg_assert(quads_drawing == 1, "called gfx_quads_setcolor without quads_begin"); - g_QuadColor[0] = vec4(r,g,b,a); - g_QuadColor[1] = vec4(r,g,b,a); - g_QuadColor[2] = vec4(r,g,b,a); - g_QuadColor[3] = vec4(r,g,b,a); - /*gfx_quads_setcolorvertex(0,r,g,b,a); - gfx_quads_setcolorvertex(1,r,g,b,a); - gfx_quads_setcolorvertex(2,r,g,b,a); - gfx_quads_setcolorvertex(3,r,g,b,a);*/ -} - -void gfx_quads_setsubset(float tl_u, float tl_v, float br_u, float br_v) -{ - dbg_assert(quads_drawing == 1, "called gfx_quads_setsubset without quads_begin"); - - g_QuadTexture[0].x = tl_u; - g_QuadTexture[0].y = tl_v; - //g_pVertices[g_iVertexEnd].tex.u = tl_u; - //g_pVertices[g_iVertexEnd].tex.v = tl_v; - - g_QuadTexture[1].x = br_u; - g_QuadTexture[1].y = tl_v; - //g_pVertices[g_iVertexEnd + 2].tex.u = br_u; - //g_pVertices[g_iVertexEnd + 2].tex.v = tl_v; - - g_QuadTexture[2].x = br_u; - g_QuadTexture[2].y = br_v; - //g_pVertices[g_iVertexEnd + 1].tex.u = tl_u; - //g_pVertices[g_iVertexEnd + 1].tex.v = br_v; - - g_QuadTexture[3].x = tl_u; - g_QuadTexture[3].y = br_v; - //g_pVertices[g_iVertexEnd + 3].tex.u = br_u; - //g_pVertices[g_iVertexEnd + 3].tex.v = br_v; -} - -static void rotate(vec3 ¢er, vec3 &point) -{ - vec3 p = point-center; - point.x = p.x * cosf(rotation) - p.y * sinf(rotation) + center.x; - point.y = p.x * sinf(rotation) + p.y * cosf(rotation) + center.y; -} - -void gfx_quads_draw(float x, float y, float w, float h) -{ - gfx_quads_drawTL(x-w/2, y-h/2,w,h); -} - -void gfx_quads_drawTL(float x, float y, float width, float height) -{ - dbg_assert(quads_drawing == 1, "called quads_draw without quads_begin"); - - vec3 center; - center.x = x + width/2; - center.y = y + height/2; - center.z = 0; - - g_pVertices[g_iVertexEnd].pos.x = x; - g_pVertices[g_iVertexEnd].pos.y = y; - g_pVertices[g_iVertexEnd].tex.u = g_QuadTexture[0].x; - g_pVertices[g_iVertexEnd].tex.v = g_QuadTexture[0].y; - g_pVertices[g_iVertexEnd].color = g_QuadColor[0]; - rotate(center, g_pVertices[g_iVertexEnd].pos); - - g_pVertices[g_iVertexEnd + 1].pos.x = x+width; - g_pVertices[g_iVertexEnd + 1].pos.y = y; - g_pVertices[g_iVertexEnd + 1].tex.u = g_QuadTexture[1].x; - g_pVertices[g_iVertexEnd + 1].tex.v = g_QuadTexture[1].y; - g_pVertices[g_iVertexEnd + 1].color = g_QuadColor[1]; - rotate(center, g_pVertices[g_iVertexEnd + 1].pos); - - g_pVertices[g_iVertexEnd + 2].pos.x = x + width; - g_pVertices[g_iVertexEnd + 2].pos.y = y+height; - g_pVertices[g_iVertexEnd + 2].tex.u = g_QuadTexture[2].x; - g_pVertices[g_iVertexEnd + 2].tex.v = g_QuadTexture[2].y; - g_pVertices[g_iVertexEnd + 2].color = g_QuadColor[2]; - rotate(center, g_pVertices[g_iVertexEnd + 2].pos); - - g_pVertices[g_iVertexEnd + 3].pos.x = x; - g_pVertices[g_iVertexEnd + 3].pos.y = y+height; - g_pVertices[g_iVertexEnd + 3].tex.u = g_QuadTexture[3].x; - g_pVertices[g_iVertexEnd + 3].tex.v = g_QuadTexture[3].y; - g_pVertices[g_iVertexEnd + 3].color = g_QuadColor[3]; - rotate(center, g_pVertices[g_iVertexEnd + 3].pos); - - draw_quad(); -} - -void gfx_quads_draw_freeform( - float x0, float y0, - float x1, float y1, - float x2, float y2, - float x3, float y3) -{ - dbg_assert(quads_drawing == 1, "called quads_draw_freeform without quads_begin"); - - g_pVertices[g_iVertexEnd].pos.x = x0; - g_pVertices[g_iVertexEnd].pos.y = y0; - g_pVertices[g_iVertexEnd].tex.u = g_QuadTexture[0].x; - g_pVertices[g_iVertexEnd].tex.v = g_QuadTexture[0].y; - g_pVertices[g_iVertexEnd].color = g_QuadColor[0]; - - g_pVertices[g_iVertexEnd + 1].pos.x = x1; - g_pVertices[g_iVertexEnd + 1].pos.y = y1; - g_pVertices[g_iVertexEnd + 1].tex.u = g_QuadTexture[1].x; - g_pVertices[g_iVertexEnd + 1].tex.v = g_QuadTexture[1].y; - g_pVertices[g_iVertexEnd + 1].color = g_QuadColor[1]; - - g_pVertices[g_iVertexEnd + 2].pos.x = x3; - g_pVertices[g_iVertexEnd + 2].pos.y = y3; - g_pVertices[g_iVertexEnd + 2].tex.u = g_QuadTexture[2].x; - g_pVertices[g_iVertexEnd + 2].tex.v = g_QuadTexture[2].y; - g_pVertices[g_iVertexEnd + 2].color = g_QuadColor[2]; - - g_pVertices[g_iVertexEnd + 3].pos.x = x2; - g_pVertices[g_iVertexEnd + 3].pos.y = y2; - g_pVertices[g_iVertexEnd + 3].tex.u = g_QuadTexture[3].x; - g_pVertices[g_iVertexEnd + 3].tex.v = g_QuadTexture[3].y; - g_pVertices[g_iVertexEnd + 3].color = g_QuadColor[3]; - - draw_quad(); -} - -void gfx_quads_text(float x, float y, float size, const char *text) -{ - gfx_quads_begin(); - while(*text) - { - char c = *text; - text++; - - gfx_quads_setsubset( - (c%16)/16.0f, - (c/16)/16.0f, - (c%16)/16.0f+1.0f/16.0f, - (c/16)/16.0f+1.0f/16.0f); - - gfx_quads_drawTL(x,y,size,size); - x += size/2; - } - - gfx_quads_end(); -} - -struct pretty_font -{ - float m_CharStartTable[256]; - float m_CharEndTable[256]; - int font_texture; -}; - -pretty_font default_font = -{ -{ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0.421875, 0.359375, 0.265625, 0.25, 0.1875, 0.25, 0.4375, 0.390625, 0.390625, 0.34375, 0.28125, 0.421875, 0.390625, 0.4375, 0.203125, - 0.265625, 0.28125, 0.28125, 0.265625, 0.25, 0.28125, 0.28125, 0.265625, 0.28125, 0.265625, 0.4375, 0.421875, 0.3125, 0.28125, 0.3125, 0.3125, - 0.25, 0.234375, 0.28125, 0.265625, 0.265625, 0.296875, 0.3125, 0.25, 0.25, 0.421875, 0.28125, 0.265625, 0.328125, 0.171875, 0.234375, 0.25, - 0.28125, 0.234375, 0.265625, 0.265625, 0.28125, 0.265625, 0.234375, 0.09375, 0.234375, 0.234375, 0.265625, 0.390625, 0.203125, 0.390625, 0.296875, 0.28125, - 0.375, 0.3125, 0.3125, 0.3125, 0.296875, 0.3125, 0.359375, 0.296875, 0.3125, 0.4375, 0.390625, 0.328125, 0.4375, 0.203125, 0.3125, 0.296875, - 0.3125, 0.296875, 0.359375, 0.3125, 0.328125, 0.3125, 0.296875, 0.203125, 0.296875, 0.296875, 0.328125, 0.375, 0.421875, 0.375, 0.28125, 0.3125, - 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, - 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, - 0, 0.421875, 0.3125, 0.265625, 0.25, 0.25, 0.421875, 0.265625, 0.375, 0.21875, 0.375, 0.328125, 0.3125, 0, 0.21875, 0.28125, - 0.359375, 0.28125, 0.34375, 0.34375, 0.421875, 0.3125, 0.265625, 0.421875, 0.421875, 0.34375, 0.375, 0.328125, 0.125, 0.125, 0.125, 0.296875, - 0.234375, 0.234375, 0.234375, 0.234375, 0.234375, 0.234375, 0.109375, 0.265625, 0.296875, 0.296875, 0.296875, 0.296875, 0.375, 0.421875, 0.359375, 0.390625, - 0.21875, 0.234375, 0.25, 0.25, 0.25, 0.25, 0.25, 0.296875, 0.21875, 0.265625, 0.265625, 0.265625, 0.265625, 0.234375, 0.28125, 0.3125, - 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.1875, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.375, 0.421875, 0.359375, 0.390625, - 0.3125, 0.3125, 0.296875, 0.296875, 0.296875, 0.296875, 0.296875, 0.28125, 0.28125, 0.3125, 0.3125, 0.3125, 0.3125, 0.296875, 0.3125, 0.296875, -}, -{ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0.2, 0.5625, 0.625, 0.71875, 0.734375, 0.796875, 0.765625, 0.546875, 0.59375, 0.59375, 0.65625, 0.703125, 0.546875, 0.59375, 0.5625, 0.6875, - 0.71875, 0.609375, 0.703125, 0.703125, 0.71875, 0.703125, 0.703125, 0.6875, 0.703125, 0.703125, 0.5625, 0.546875, 0.671875, 0.703125, 0.671875, 0.671875, - 0.734375, 0.75, 0.734375, 0.734375, 0.734375, 0.6875, 0.6875, 0.734375, 0.71875, 0.5625, 0.65625, 0.765625, 0.703125, 0.8125, 0.75, 0.734375, - 0.734375, 0.765625, 0.71875, 0.71875, 0.703125, 0.71875, 0.75, 0.890625, 0.75, 0.75, 0.71875, 0.59375, 0.6875, 0.59375, 0.6875, 0.703125, - 0.5625, 0.671875, 0.6875, 0.671875, 0.671875, 0.671875, 0.625, 0.671875, 0.671875, 0.5625, 0.546875, 0.703125, 0.5625, 0.78125, 0.671875, 0.671875, - 0.6875, 0.671875, 0.65625, 0.671875, 0.65625, 0.671875, 0.6875, 0.78125, 0.6875, 0.671875, 0.65625, 0.609375, 0.546875, 0.609375, 0.703125, 0.671875, - 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, - 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, - 0, 0.5625, 0.671875, 0.734375, 0.734375, 0.734375, 0.546875, 0.71875, 0.609375, 0.765625, 0.609375, 0.65625, 0.671875, 0, 0.765625, 0.703125, - 0.625, 0.703125, 0.640625, 0.640625, 0.609375, 0.671875, 0.703125, 0.546875, 0.5625, 0.578125, 0.609375, 0.65625, 0.859375, 0.859375, 0.859375, 0.671875, - 0.75, 0.75, 0.75, 0.75, 0.75, 0.75, 0.84375, 0.734375, 0.6875, 0.6875, 0.6875, 0.6875, 0.5625, 0.609375, 0.640625, 0.59375, - 0.734375, 0.75, 0.734375, 0.734375, 0.734375, 0.734375, 0.734375, 0.6875, 0.765625, 0.71875, 0.71875, 0.71875, 0.71875, 0.75, 0.734375, 0.6875, - 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.796875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.5625, 0.609375, 0.625, 0.59375, - 0.6875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.703125, 0.703125, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.6875, 0.671875, -}, -0 -}; - -pretty_font *current_font = &default_font; - -void gfx_pretty_text(float x, float y, float size, const char *text) -{ - const float spacing = 0.05f; - - gfx_quads_begin(); - - while (*text) - { - const char c = *text; - const float width = current_font->m_CharEndTable[c] - current_font->m_CharStartTable[c]; - - text++; - - gfx_quads_setsubset( - (c%16)/16.0f + current_font->m_CharStartTable[c]/16.0f, // startx - (c/16)/16.0f, // starty - (c%16)/16.0f + current_font->m_CharEndTable[c]/16.0f + 0.001, // endx - (c/16)/16.0f+1.0f/16.0f); // endy - - gfx_quads_drawTL(x, y, width * size, size); - - x += (width + spacing) * size; - } - - gfx_quads_end(); -} - -float gfx_pretty_text_width(float size, const char *text) -{ - const float spacing = 0.05f; - float width = 0.0f; - - while (*text) - { - const char c = *text++; - width += size * (current_font->m_CharEndTable[c] - current_font->m_CharStartTable[c] + spacing); - } - - return width; -} diff --git a/src/interface.h b/src/interface.h deleted file mode 100644 index 95ea0252..00000000 --- a/src/interface.h +++ /dev/null @@ -1,711 +0,0 @@ -#ifndef FILE_INTERFACE_H -#define FILE_INTERFACE_H - -/* - Title: Engine Interface -*/ - -// TODO: Move the definitions of these keys here -#include - -enum -{ - MAX_CLIENTS=8, - SERVER_TICK_SPEED=50, - SERVER_CLIENT_TIMEOUT=5, - SNAP_CURRENT=0, - SNAP_PREV=1, -}; - -struct snap_item -{ - int type; - int id; -}; - -struct client_info -{ -public: - const char *name; - int latency; -}; - -struct image_info -{ - int width, height; - void *data; -}; - -int gfx_load_tga(image_info *img, const char *filename); - - -/* - Group: Graphics -*/ - -// graphics -bool gfx_init(bool fullscreen); // NOT EXPOSED -void gfx_shutdown(); // NOT EXPOSED -void gfx_swap(); // NOT EXPOSED - -// textures -/* - Function: gfx_load_texture_tga - Loads a TGA from file. - - Arguments: - filename - Null terminated string to the file to load. - - Returns: - An ID to the texture. -1 on failure. - - See Also: - -*/ -int gfx_load_texture_tga(const char *filename); - -/* - Function: gfx_load_texture_raw - Loads a texture from memory. - - Arguments: - w - Width of the texture. - h - Height of the texture. - data - Pointer to the pixel data. - - Returns: - An ID to the texture. -1 on failure. - - Remarks: - The pixel data should be in RGBA format with 8 bit per component. - So the total size of the data should be w*h*4. - - See Also: - -*/ -int gfx_load_texture_raw(int w, int h, const void *data); - -/* - Function: gfx_texture_set - Sets the active texture. - - Arguments: - id - ID to the texture to set. -*/ -void gfx_texture_set(int id); - -/* - Function: gfx_unload_texture - Unloads a texture. - - Arguments: - id - ID to the texture to unload. - - See Also: - , - - Remarks: - NOT IMPLEMENTED -*/ -int gfx_unload_texture(int id); // NOT IMPLEMENTED - -void gfx_clear(float r, float g, float b); - -/* - Function: gfx_screenwidth - Returns the screen width. - - See Also: - -*/ -int gfx_screenwidth(); - -/* - Function: gfx_screenheight - Returns the screen height. - - See Also: - -*/ -int gfx_screenheight(); - -/* - Function: gfx_mapscreen - Specifies the coordinate system for the screen. - - Arguments: - tl_x - Top-left X - tl_y - Top-left Y - br_x - Bottom-right X - br_y - Bottom-right y -*/ -void gfx_mapscreen(float tl_x, float tl_y, float br_x, float br_y); - -/* - Function: gfx_blend_normal - Set the active blending mode to normal (src, 1-src). - - Remarks: - This must be used before calling . - This is equal to glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA). - - See Also: - -*/ -void gfx_blend_normal(); - -/* - Function: gfx_blend_additive - Set the active blending mode to additive (src, one). - - Remarks: - This must be used before calling . - This is equal to glBlendFunc(GL_SRC_ALPHA, GL_ONE). - - See Also: - -*/ -void gfx_blend_additive(); - -/* - Function: gfx_quads_begin - Begins a quad drawing session. - - Remarks: - This functions resets the rotation, color and subset. - End the session by using . - You can't change texture or blending mode during a session. - - See Also: - -*/ -void gfx_quads_begin(); - -/* - Function: gfx_quads_end - Ends a quad session. - - See Also: - -*/ -void gfx_quads_end(); - -/* - Function: gfx_quads_setrotation - Sets the rotation to use when drawing a quad. - - Arguments: - angle - Angle in radians. - - Remarks: - The angle is reset when is called. -*/ -void gfx_quads_setrotation(float angle); - -/* - Function: gfx_quads_setcolorvertex - Sets the color of a vertex. - - Arguments: - i - Index to the vertex. - r - Red value. - g - Green value. - b - Blue value. - a - Alpha value. - - Remarks: - The color values are from 0.0 to 1.0. - The color is reset when is called. -*/ -void gfx_quads_setcolorvertex(int i, float r, float g, float b, float a); - -/* - Function: gfx_quads_setcolor - Sets the color of all the vertices. - - Arguments: - r - Red value. - g - Green value. - b - Blue value. - a - Alpha value. - - Remarks: - The color values are from 0.0 to 1.0. - The color is reset when is called. -*/ -void gfx_quads_setcolor(float r, float g, float b, float a); - -/* - Function: gfx_quads_setsubset - Sets the uv coordinates to use. - - Arguments: - tl_u - Top-left U value. - tl_v - Top-left V value. - br_u - Bottom-right U value. - br_v - Bottom-right V value. - - Remarks: - O,0 is top-left of the texture and 1,1 is bottom-right. - The color is reset when is called. -*/ -void gfx_quads_setsubset(float tl_u, float tl_v, float br_u, float br_v); - -/* - Function: gfx_quads_drawTL - Draws a quad by specifying the top-left point. - - Arguments: - x - X coordinate of the top-left corner. - y - Y coordinate of the top-left corner. - width - Width of the quad. - height - Height of the quad. - - Remarks: - Rotation still occurs from the center of the quad. - You must call before calling this function. - - See Also: - -*/ -void gfx_quads_drawTL(float x, float y, float width, float height); - -/* - Function: gfx_quads_draw - Draws a quad by specifying the center point. - - Arguments: - x - X coordinate of the center. - y - Y coordinate of the center. - width - Width of the quad. - height - Height of the quad. - - Remarks: - You must call before calling this function. - - See Also: - -*/ -void gfx_quads_draw(float x, float y, float w, float h); - -void gfx_quads_draw_freeform( - float x0, float y0, - float x1, float y1, - float x2, float y2, - float x3, float y3); - -void gfx_quads_text(float x, float y, float size, const char *text); - -// sound (client) -enum -{ - SND_PLAY_ONCE = 0, - SND_LOOP -}; - -bool snd_init(); -int snd_load_wav(const char *filename); -int snd_play(int sound, int loop = SND_PLAY_ONCE, float vol = 1.0f, float pan = 0.0f); -void snd_stop(int id); -void snd_set_vol(int id, float vol); -bool snd_shutdown(); - -/* - Group: Input -*/ - -/* - Function: inp_mouse_relative - Fetches the mouse movements. - - Arguments: - x - Pointer to the variable that should get the X movement. - y - Pointer to the variable that should get the Y movement. -*/ -void inp_mouse_relative(int *x, int *y); - -/* - Function: inp_mouse_button_pressed - Checks if a mouse button is pressed. - - Arguments: - button - Index to the button to check. - * 0 - Left mouse button. - * 1 - Right mouse button. - * 2 - Middle mouse button. - * Others over 2 is undefined mouse buttons. - - Returns: - Returns 1 if the button is pressed, otherwise 0. -*/ -int inp_mouse_button_pressed(int button); - -/* - Function: inp_key_pressed - Checks if a key is pressed. - - Arguments: - key - Index to the key to check - - Returns: - Returns 1 if the button is pressed, otherwise 0. - - Remarks: - Check baselib/include/baselib/keys.h for the keys. -*/ -int inp_key_pressed(int key); - -/* - Group: Map -*/ - -int map_load(const char *mapname); // NOT EXPOSED -void map_unload(); // NOT EXPOSED - -/* - Function: map_is_loaded - Checks if a map is loaded. - - Returns: - Returns 1 if the button is pressed, otherwise 0. -*/ -int map_is_loaded(); - -/* - Function: map_num_items - Checks the number of items in the loaded map. - - Returns: - Returns the number of items. 0 if no map is loaded. -*/ -int map_num_items(); - -/* - Function: map_find_item - Searches the map for an item. - - Arguments: - type - Item type. - id - Item ID. - - Returns: - Returns a pointer to the item if it exists, otherwise it returns NULL. -*/ -void *map_find_item(int type, int id); - -/* - Function: map_get_item - Gets an item from the loaded map from index. - - Arguments: - index - Item index. - type - Pointer that recives the item type (can be NULL). - id - Pointer that recives the item id (can be NULL). - - Returns: - Returns a pointer to the item if it exists, otherwise it returns NULL. -*/ -void *map_get_item(int index, int *type, int *id); - -/* - Function: map_get_type - Gets the index range of an item type. - - Arguments: - type - Item type to search for. - start - Pointer that recives the starting index. - num - Pointer that recives the number of items. - - Returns: - If the item type is not in the map, start and num will be set to 0. -*/ -void map_get_type(int type, int *start, int *num); - -/* - Function: map_get_data - Fetches a pointer to a raw data chunk in the map. - - Arguments: - index - Index to the data to fetch. - - Returns: - A pointer to the raw data, otherwise 0. -*/ -void *map_get_data(int index); - -/* - Group: Network (Server) -*/ -/* - Function: snap_new_item - Creates a new item that should be sent. - - Arguments: - type - Type of the item. - id - ID of the item. - size - Size of the item. - - Returns: - A pointer to the item data, otherwise 0. - - Remarks: - The item data should only consist pf 4 byte integers as - they are subject to byte swapping. This means that the size - argument should be dividable by 4. -*/ -void *snap_new_item(int type, int id, int size); - -/* - Group: Network (Client) -*/ -/* - Function: snap_num_items - Check the number of items in a snapshot. - - Arguments: - snapid - Snapshot ID to the data to fetch. - * SNAP_PREV for previous snapshot. - * SNAP_CUR for current snapshot. - - Returns: - The number of items in the snapshot. -*/ -int snap_num_items(int snapid); - -/* - Function: snap_get_item - Gets an item from a snapshot. - - Arguments: - snapid - Snapshot ID to the data to fetch. - * SNAP_PREV for previous snapshot. - * SNAP_CUR for current snapshot. - index - Index of the item. - item - Pointer that recives the item info. - - Returns: - Returns a pointer to the item if it exists, otherwise NULL. -*/ -void *snap_get_item(int snapid, int index, snap_item *item); - -/* - Function: snap_find_item - Searches a snapshot for an item. - - Arguments: - snapid - Snapshot ID to the data to fetch. - * SNAP_PREV for previous snapshot. - * SNAP_CUR for current snapshot. - type - Type of the item. - id - ID of the item. - - Returns: - Returns a pointer to the item if it exists, otherwise NULL. -*/ -void *snap_find_item(int snapid, int type, int id); - -/* - Function: snap_input - Sets the input data to send to the server. - - Arguments: - data - Pointer to the data. - size - Size of the data. - - Remarks: - The data should only consist of 4 bytes integer as they are - subject to byte swapping. -*/ -void snap_input(void *data, int size); - -/* - Function: snap_intratick - Returns the intra-tick mixing value. - - Returns: - Returns the mixing value between the previous snapshot - and the current snapshot. - - Remarks: - DOCTODO: Explain how to use it. -*/ -float snap_intratick(); - -/* - Group: Server Callbacks -*/ -/* - Function: mods_init - Called when the server is started. - - Remarks: - It's called after the map is loaded so all map items are available. -*/ -void mods_init(); - -/* - Function: mods_shutdown - Called when the server quits. - - Remarks: - Should be used to clean up all resources used. -*/ -void mods_shutdown(); - -/* - Function: mods_client_enter - Called when a client has joined the game. - - Arguments: - cid - Client ID. Is 0 - MAX_CLIENTS. - - Remarks: - It's called when the client is finished loading and should enter gameplay. -*/ -void mods_client_enter(int cid); - -/* - Function: mods_client_drop - Called when a client drops from the server. - - Arguments: - cid - Client ID. Is 0 - MAX_CLIENTS -*/ -void mods_client_drop(int cid); - -/* - Function: mods_client_input - Called when the server recives new input from a client. - - Arguments: - cid - Client ID. Is 0 - MAX_CLIENTS. - input - Pointer to the input data. - size - Size of the data. (NOT IMPLEMENTED YET) -*/ -void mods_client_input(int cid, void *input); - -/* - Function: mods_tick - Called with a regular interval to progress the gameplay. - - Remarks: - The SERVER_TICK_SPEED tells the number of ticks per second. -*/ -void mods_tick(); - -/* - Function: mods_presnap - Called before the server starts to construct snapshots for the clients. -*/ -void mods_presnap(); - -/* - Function: mods_snap - Called to create the snapshot for a client. - - Arguments: - cid - Client ID. Is 0 - MAX_CLIENTS. - - Remarks: - The game should make a series of calls to to construct - the snapshot for the client. -*/ -void mods_snap(int cid); - -/* - Function: mods_postsnap - Called after the server is done sending the snapshots. -*/ -void mods_postsnap(); - -/* - Group: Client Callbacks -*/ -/* - Function: modc_init - Called when the client starts. - - Remarks: - The game should load resources that are used during the entire - time of the game. No map is loaded. -*/ -void modc_init(); - -/* - Function: modc_newsnapshot - Called when the client progressed to a new snapshot. - - Remarks: - The client can check for items in the snapshot and perform one time - events like playing sounds, spawning client side effects etc. -*/ -void modc_newsnapshot(); - -/* - Function: modc_entergame - Called when the client has successfully connect to a server and - loaded a map. - - Remarks: - The client can check for items in the map and load them. -*/ -void modc_entergame(); - -/* - Function: modc_shutdown - Called when the client closes down. -*/ -void modc_shutdown(); - -/* - Function: modc_render - Called every frame to let the game render it self. -*/ -void modc_render(); - - - -/* - Group: Menu Callbacks -*/ -/* - Function: modmenu_init - Called when the menu starts. - - Remarks: - The menu should load resources that are used during the entire - time of the menu use. -*/ -void modmenu_init(); - -/* - Function: modmenu_shutdown - Called when the menu closes down. -*/ -void modmenu_shutdown(); - -/* - Function: modmenu_render - Called every frame to let the menu render it self. -*/ -int modmenu_render(void *server_address, char *name, int max_len); - -void snap_encode_string(const char *src, int *dst, int length, int max_length); -void snap_decode_string(const int *src, char *dst, int length); - -int server_getclientinfo(int client_id, client_info *info); -int server_tick(); -int server_tickspeed(); - -int inp_key_was_pressed(int key); -int inp_key_down(int key); -void inp_update(); -float client_frametime(); -float client_localtime(); - -#define MASTER_SERVER_ADDRESS "master.teewars.com" -#define MASTER_SERVER_PORT 8300 - - - -#endif diff --git a/src/lzw.cpp b/src/lzw.cpp deleted file mode 100644 index 80dd1c22..00000000 --- a/src/lzw.cpp +++ /dev/null @@ -1,223 +0,0 @@ -#include - -// LZW Compressor -struct SYM -{ - unsigned char *data; - int size; - int next; -}; - -struct SYMBOLS -{ - SYM syms[512]; - int jumptable[256]; - int currentsym; -}; - -static SYMBOLS symbols; - -// symbol info -inline int sym_size(int i) { return symbols.syms[i].size; } -inline unsigned char *sym_data(int i) { return symbols.syms[i].data; } - -static void sym_index(int sym) -{ - int table = symbols.syms[sym].data[0]; - symbols.syms[sym].next = symbols.jumptable[table]; - symbols.jumptable[table] = sym; -} - -static void sym_unindex(int sym) -{ - int table = symbols.syms[sym].data[0]; - int prev = -1; - int current = symbols.jumptable[table]; - - while(current != -1) - { - if(current == sym) - { - if(prev != -1) - symbols.syms[prev].next = symbols.syms[current].next; - else - symbols.jumptable[table] = symbols.syms[current].next; - break; - } - - prev = current; - current = symbols.syms[current].next; - } -} - -static int sym_add(unsigned char *sym, long len) -{ - int i = 256+symbols.currentsym; - symbols.syms[i].data = sym; - symbols.syms[i].size = len; - symbols.currentsym = (symbols.currentsym+1)%255; - return i; -} - -static int sym_add_and_index(unsigned char *sym, long len) -{ - if(symbols.syms[256+symbols.currentsym].size) - sym_unindex(256+symbols.currentsym); - int s = sym_add(sym, len); - sym_index( s); - return s; -} - -static void sym_init() -{ - static unsigned char table[256]; - for(int i = 0; i < 256; i++) - { - table[i] = i; - symbols.syms[i].data = &table[i]; - symbols.syms[i].size = 1; - symbols.jumptable[i] = -1; - } - - for(int i = 0; i < 512; i++) - symbols.syms[i].next = -1; - - /* - // insert some symbols to start with - static unsigned char zeros[8] = {0,0,0,0,0,0,0,0}; - //static unsigned char one1[4] = {0,0,0,1}; - //static unsigned char one2[4] = {1,0,0,0}; - sym_add_and_index(zeros, 2); - sym_add_and_index(zeros, 3); - sym_add_and_index(zeros, 4); - sym_add_and_index(zeros, 5); - sym_add_and_index(zeros, 6); - sym_add_and_index(zeros, 7); - sym_add_and_index(zeros, 8); - - //sym_add_and_index(one1, 4); - //sym_add_and_index(one2, 4);*/ - - symbols.currentsym = 0; -} - -static int sym_find(unsigned char *data, int size, int avoid) -{ - int best = data[0]; - int bestlen = 1; - int current = symbols.jumptable[data[0]]; - - while(current != -1) - { - if(current != avoid && symbols.syms[current].size <= size && memcmp(data, symbols.syms[current].data, symbols.syms[current].size) == 0) - { - if(bestlen < symbols.syms[current].size) - { - bestlen = symbols.syms[current].size; - best = current; - } - } - - current = symbols.syms[current].next; - } - - return best; -} - -// -// compress -// -long lzw_compress(const void *src_, int size, void *dst_) -{ - unsigned char *src = (unsigned char *)src_; - unsigned char *end = (unsigned char *)src_+size; - unsigned char *dst = (unsigned char *)dst_; - long left = (end-src); - int lastsym = -1; - - // init symboltable - sym_init(); - - bool done = false; - while(!done) - { - unsigned char *flagptr = dst; - unsigned char flagbits = 0; - int b = 0; - - dst++; // skip a byte where the flags are - - for(; b < 8; b++) - { - if(left <= 0) // check for EOF - { - // write EOF symbol - flagbits |= 1< symsize+1) // create new symbol - lastsym = sym_add_and_index(src, symsize+1); - - src += symsize; // advance src - left -= symsize; - } - - // write the flags - *flagptr = flagbits; - } - - return (long)(dst-(unsigned char*)dst_); -} - -// -// decompress -// -long lzw_decompress(const void *src_, void *dst_) -{ - unsigned char *src = (unsigned char *)src_; - unsigned char *dst = (unsigned char *)dst_; - unsigned char *prevdst = 0; - int prevsize = -1; - int item; - - sym_init(); - - while(1) - { - unsigned char flagbits = 0; - flagbits = *src++; // read flags - - int b = 0; - for(; b < 8; b++) - { - item = *src++; - if(flagbits&(1< -#include - -#include "datafile.h" - -static datafile *map; - -void *map_get_data(int index) -{ - return datafile_get_data(map, index); -} - -void *map_get_item(int index, int *type, int *id) -{ - return datafile_get_item(map, index, type, id); -} - -void map_get_type(int type, int *start, int *num) -{ - datafile_get_type(map, type, start, num); -} - -void *map_find_item(int type, int id) -{ - return datafile_find_item(map, type, id); -} - -int map_num_items() -{ - return datafile_num_items(map); -} - -void map_unload() -{ - datafile_unload(map); - map = 0x0; -} - -int map_is_loaded() -{ - return map != 0; -} - -int map_load(const char *mapname) -{ - map = datafile_load(mapname); - return map != 0; -} diff --git a/src/menu.cpp b/src/menu.cpp deleted file mode 100644 index b5794194..00000000 --- a/src/menu.cpp +++ /dev/null @@ -1,600 +0,0 @@ -#include -#include -#include -#include -#include -#include - -#include "interface.h" -#include "ui.h" -#include "versions.h" - -#include "game/mapres_image.h" -#include "game/mapres_tilemap.h" -#include "game/mapres.h" - -using namespace baselib; - -/******************************************************** - MENU -*********************************************************/ - -struct pretty_font -{ - char m_CharStartTable[256]; - char m_CharEndTable[256]; - int font_texture; -}; - -extern pretty_font *current_font; -void gfx_pretty_text(float x, float y, float size, const char *text); -float gfx_pretty_text_width(float size, const char *text); - -void draw_scrolling_background(int id, float w, float h, float t) -{ - float tx = w/256.0f; - float ty = h/256.0f; - - float start_x = fmod(t, 1.0f); - float start_y = 1.0f - fmod(t*0.8f, 1.0f); - - gfx_blend_normal(); - gfx_texture_set(id); - gfx_quads_begin(); - gfx_quads_setcolor(1,1,1,1); - gfx_quads_setsubset( - start_x, // startx - start_y, // starty - start_x+tx, // endx - start_y+ty); // endy - gfx_quads_drawTL(0.0f,0.0f,w,h); - gfx_quads_end(); -} - -int background_texture; -int not_empty_item_texture; -int empty_item_texture; -int active_item_texture; -int selected_item_texture; -int join_button_texture; -int join_button_hot_texture; -int join_button_active_texture; -int join_button_grey_texture; -int quit_button_texture; -int quit_button_hot_texture; -int quit_button_active_texture; -int up_button_texture; -int up_button_active_texture; -int down_button_texture; -int down_button_active_texture; -int teewars_banner_texture; -int scroll_indicator_texture; -int connect_localhost_texture; -int refresh_button_texture; -int refresh_button_hot_texture; -int refresh_button_active_texture; -int input_box_texture; - -int music_menu; -int music_menu_id = -1; - -struct button_textures -{ - int *normal_texture; - int *hot_texture; - int *active_texture; -}; - -button_textures connect_localhost_button = { &connect_localhost_texture, &connect_localhost_texture, &connect_localhost_texture }; -button_textures join_button = { &join_button_texture, &join_button_hot_texture, &join_button_active_texture }; -button_textures quit_button = { &quit_button_texture, &quit_button_hot_texture, &quit_button_active_texture }; -button_textures scroll_up_button = { &up_button_texture, &up_button_texture, &up_button_active_texture }; -button_textures scroll_down_button = { &down_button_texture, &down_button_texture, &down_button_active_texture }; -button_textures list_item_button = { ¬_empty_item_texture, &active_item_texture, &active_item_texture }; -button_textures selected_item_button = { &selected_item_texture, &selected_item_texture, &selected_item_texture }; -button_textures refresh_button = { &refresh_button_texture, &refresh_button_hot_texture, &refresh_button_active_texture }; - -void draw_menu_button(void *id, const char *text, int checked, float x, float y, float w, float h, void *extra) -{ - button_textures *tx = (button_textures *)extra; - - gfx_blend_normal(); - - if (ui_active_item() == id && ui_hot_item() == id) - gfx_texture_set(*tx->active_texture); - else if (ui_hot_item() == id) - gfx_texture_set(*tx->hot_texture); - else - gfx_texture_set(*tx->normal_texture); - - gfx_quads_begin(); - - gfx_quads_setcolor(1,1,1,1); - - gfx_quads_drawTL(x,y,w,h); - gfx_quads_end(); - - gfx_texture_set(current_font->font_texture); - gfx_pretty_text(x + 4, y - 3.5f, 18.f, text); -} - -void draw_image_button(void *id, const char *text, int checked, float x, float y, float w, float h, void *extra) -{ - ui_do_image(*(int *)id, x, y, w, h); -} - -struct server_info -{ - int version; - int players; - int max_players; - netaddr4 address; - char name[129]; - char map[65]; -}; - -struct server_list -{ - server_info infos[10]; - int active_count, info_count; - int scroll_index; - int selected_index; -}; -#include - -int ui_do_edit_box(void *id, float x, float y, float w, float h, char *str, int str_size) -{ - int inside = ui_mouse_inside(x, y, w, h); - int r = 0; - - if(inside) - { - ui_set_hot_item(id); - - if(ui_mouse_button(0)) - ui_set_active_item(id); - } - - if (ui_active_item() == id) - { - char c = keys::last_char(); - int k = keys::last_key(); - int len = strlen(str); - - if (c >= 32 && c < 128) - { - if (len < str_size - 1) - { - str[len] = c; - str[len+1] = 0; - } - } - - if (k == keys::backspace) - { - if (len > 0) - str[len-1] = 0; - } - r = 1; - } - - ui_do_label(x + 4, y - 3.5f, str); - - if (ui_active_item() == id) - { - float w = gfx_pretty_text_width(18.0f, str); - ui_do_label(x + 4 + w, y - 3.5f, "_"); - } - - return r; -} - -int do_scroll_bar(void *id, float x, float y, float height, int steps, int last_index) -{ - int r = last_index; - - if (ui_do_button(&up_button_texture, "", 0, x, y, 8, 8, draw_menu_button, &scroll_up_button)) - { - if (r > 0) - --r; - } - else if (ui_do_button(&down_button_texture, "", 0, x, y + height - 8, 8, 8, draw_menu_button, &scroll_down_button)) - { - if (r < steps) - ++r; - } - else if (steps > 0) // only if there's actually stuff to scroll through - { - int inside = ui_mouse_inside(x, y + 8, 8, height - 16); - if (inside && (!ui_active_item() || ui_active_item() == id)) - ui_set_hot_item(id); - - if(ui_active_item() == id) - { - if (ui_mouse_button(0)) - { - float pos = ui_mouse_y() - y - 8; - float perc = pos / (height - 16); - - r = (steps + 1) * perc; - if (r < 0) - r = 0; - else if (r > steps) - r = steps; - } - else - ui_set_active_item(0); - } - else if (ui_hot_item() == id && ui_mouse_button(0)) - ui_set_active_item(id); - else if (inside && (!ui_active_item() || ui_active_item() == id)) - ui_set_hot_item(id); - } - - ui_do_image(scroll_indicator_texture, x, y + 8 + r * ((height - 32) / steps), 8, 16); - - return r; -} - -int do_server_list(server_list *list, float x, float y, int visible_items) -{ - const float spacing = 1.5f; - const float item_height = 14; - const float item_width = 364; - const float real_width = item_width + 10; - const float real_height = item_height * visible_items + spacing * (visible_items - 1); - - int r = -1; - - for (int i = 0; i < visible_items; i++) - { - int item_index = i + list->scroll_index; - if (item_index >= list->active_count) - ui_do_image(empty_item_texture, x, y + i * item_height + i * spacing, item_width, item_height); - else - { - server_info *item = &list->infos[item_index]; - - bool clicked = false; - if (list->selected_index == item_index) - clicked = ui_do_button(item, item->name, 0, x, y + i * item_height + i * spacing, item_width, item_height, draw_menu_button, &selected_item_button); - else - clicked = ui_do_button(item, item->name, 0, x, y + i * item_height + i * spacing, item_width, item_height, draw_menu_button, &list_item_button); - - char temp[64]; // plenty of extra room so we don't get sad :o - sprintf(temp, "%i/%i", item->players, item->max_players); - - gfx_texture_set(current_font->font_texture); - gfx_pretty_text(x + 300, y + i * item_height + i * spacing - 3.5f, 18.f, temp); - gfx_pretty_text(x + 180, y + i * item_height + i * spacing - 3.5f, 18.f, item->map); - - if (clicked) - { - r = item_index; - list->selected_index = item_index; - } - } - } - - list->scroll_index = do_scroll_bar(&list->scroll_index, x + real_width - 8, y, real_height, list->active_count - visible_items, list->scroll_index); - - return r; -} - -#include - -char *read_int(char *buffer, int *value) -{ - *value = buffer[0] << 24; - *value |= buffer[1] << 16; - *value |= buffer[2] << 8; - *value |= buffer[3]; - - return buffer + 4; -} - -char *read_netaddr(char *buffer, netaddr4 *addr) -{ - addr->ip[0] = *buffer++; - addr->ip[1] = *buffer++; - addr->ip[2] = *buffer++; - addr->ip[3] = *buffer++; - - int port; - buffer = read_int(buffer, &port); - - addr->port = port; - - return buffer; -} - -void refresh_list(server_list *list) -{ - netaddr4 addr; - netaddr4 me(0, 0, 0, 0, 0); - - list->selected_index = -1; - - if (net_host_lookup(MASTER_SERVER_ADDRESS, MASTER_SERVER_PORT, &addr) == 0) - { - socket_tcp4 sock; - sock.open(&me); - - //sock.set_non_blocking(); - - // try and connect with a timeout of 1 second - if (sock.connect_non_blocking(&addr)) - { - char data[256]; - int total_received = 0; - int pointer = 0; - int received; - - int master_server_version = -1; - int server_count = -1; - - // read header - while (total_received < 12 && (received = sock.recv(data + total_received, 12 - total_received)) > 0) - total_received += received; - - // see if we have the header - if (total_received == 12) - { - int signature; - read_int(data, &signature); - - // check signature - if (signature == 'TWSL') - { - read_int(data + 4, &master_server_version); - read_int(data + 8, &server_count); - - // TODO: handle master server version O.o - - const int server_info_size = 212; - const int wanted_data_count = server_count * server_info_size; - - list->active_count = 0; - - for (int i = 0; i < server_count; i++) - { - total_received = 0; - - // read data for a server - while (sock.is_connected() && total_received < server_info_size && (received = sock.recv(data + total_received, server_info_size - total_received)) > 0) - total_received += received; - - // check if we got enough data - if (total_received == server_info_size) - { - char *d = data; - - server_info *info = &list->infos[i]; - - d = read_int(d, &info->version); - d = read_netaddr(d, &info->address); - - //dbg_msg("menu/got_serverinfo", "IP: %i.%i.%i.%i:%i", (int)info->address.ip[0], (int)info->address.ip[1], (int)info->address.ip[2], (int)info->address.ip[3], info->address.port); - - d = read_int(d, &info->players); - d = read_int(d, &info->max_players); - memcpy(info->name, d, 128); - d += 128; - memcpy(info->map, d, 64); - - // let's be safe. - info->name[128] = 0; - info->map[64] = 0; - - ++list->active_count; - } - else - break; - } - - if (list->scroll_index >= list->active_count) - list->scroll_index = list->active_count - 1; - - if (list->scroll_index < 0) - list->scroll_index = 0; - } - } - - sock.close(); - } - } -} - -static int menu_render(netaddr4 *server_address, char *str, int max_len) -{ - // background color - gfx_clear(89/255.f,122/255.f,0.0); - - // GRADIENT: top to bottom - // top color: 60, 80, 0 - // bottom color: 90, 120, 0 - - // world coordsys - float zoom = 3.0f; - gfx_mapscreen(0,0,400.0f*zoom,300.0f*zoom); - - // GUI coordsys - gfx_mapscreen(0,0,400.0f,300.0f); - - static server_list list; - static bool inited = false; - - if (!inited) - { - list.info_count = 256; - - list.scroll_index = 0; - list.selected_index = -1; - - inited = true; - - refresh_list(&list); - } - - static int64 start = time_get(); - - float t = double(time_get() - start) / double(time_freq()); - draw_scrolling_background(background_texture, 400, 300, t * 0.01); - - ui_do_image(teewars_banner_texture, 70, 10, 256, 64); - - do_server_list(&list, 10, 80, 8); - - /* - if (ui_do_button(&connect_localhost_button, "", 0, 15, 250, 64, 24, draw_menu_button, &connect_localhost_button)) - { - *server_address = netaddr4(127, 0, 0, 1, 8303); - return 1; - }*/ - - if (ui_do_button(&refresh_button, "", 0, 220, 210, 64, 24, draw_menu_button, &refresh_button)) - { - refresh_list(&list); - } - - if (list.selected_index == -1) - { - ui_do_image(join_button_grey_texture, 290, 210, 64, 24); - } - else if (ui_do_button(&join_button, "", 0, 290, 210, 64, 24, draw_menu_button, &join_button)) - { - *server_address = list.infos[list.selected_index].address; - - dbg_msg("menu/join_button", "IP: %i.%i.%i.%i:%i", (int)server_address->ip[0], (int)server_address->ip[1], (int)server_address->ip[2], (int)server_address->ip[3], server_address->port); - - return 1; - } - - const float name_x = 10, name_y = 215; - - ui_do_label(name_x + 4, name_y - 3.5f, "Name:"); - ui_do_image(input_box_texture, name_x + 50 - 5, name_y - 5, 150 + 10, 14 + 10); - ui_do_edit_box(str, name_x + 50, name_y, 150, 14, str, max_len); - - if (ui_do_button(&quit_button, "", 0, 290, 250, 69, 25, draw_menu_button, &quit_button)) - return -1; - - ui_do_label(10.0f, 300.0f-20.0f, "Version: " TEEWARS_VERSION); - - return 0; -} - -void modmenu_init() -{ - keys::enable_char_cache(); - keys::enable_key_cache(); - - current_font->font_texture = gfx_load_texture_tga("data/big_font.tga"); - background_texture = gfx_load_texture_tga("data/gui_bg.tga"); - not_empty_item_texture = gfx_load_texture_tga("data/gui/game_list_item_not_empty.tga"); - empty_item_texture = gfx_load_texture_tga("data/gui/game_list_item_empty.tga"); - active_item_texture = gfx_load_texture_tga("data/gui/game_list_item_active.tga"); - selected_item_texture = gfx_load_texture_tga("data/gui/game_list_item_selected.tga"); - - join_button_texture = gfx_load_texture_tga("data/gui/join_button.tga"); - join_button_hot_texture = gfx_load_texture_tga("data/gui/join_button_hot.tga"); - join_button_active_texture = gfx_load_texture_tga("data/gui/join_button_active.tga"); - join_button_grey_texture = gfx_load_texture_tga("data/gui/join_button_greyed.tga"); - - -// button_not_hilighted_texture = gfx_load_texture_tga("data/gui/game_list_join_button.tga"); -// button_hilighted_texture = gfx_load_texture_tga("data/gui/button_hilighted.tga"); -// button_active_texture = gfx_load_texture_tga("data/gui/button_active.tga"); - - quit_button_texture = gfx_load_texture_tga("data/gui/quit_button.tga"); - quit_button_hot_texture = gfx_load_texture_tga("data/gui/quit_button_hot.tga"); - quit_button_active_texture = gfx_load_texture_tga("data/gui/quit_button_active.tga"); - - up_button_texture = gfx_load_texture_tga("data/gui/scroll_arrow_up.tga"); - up_button_active_texture = gfx_load_texture_tga("data/gui/scroll_arrow_up_active.tga"); - - down_button_texture = gfx_load_texture_tga("data/gui/scroll_arrow_down.tga"); - down_button_active_texture = gfx_load_texture_tga("data/gui/scroll_arrow_down_active.tga"); - - teewars_banner_texture = gfx_load_texture_tga("data/gui_logo.tga"); - scroll_indicator_texture = gfx_load_texture_tga("data/gui/scroll_drag.tga"); - connect_localhost_texture = gfx_load_texture_tga("data/gui/game_list_connect_localhost.tga"); - - refresh_button_texture = gfx_load_texture_tga("data/gui/refresh_button.tga"); - refresh_button_hot_texture = gfx_load_texture_tga("data/gui/refresh_button_hot.tga"); - refresh_button_active_texture = gfx_load_texture_tga("data/gui/refresh_button_active.tga"); - - input_box_texture = gfx_load_texture_tga("data/gui/input_box.tga"); - - music_menu = snd_load_wav("data/audio/Music_Menu.wav"); -} - -void modmenu_shutdown() -{ -} - -int modmenu_render(void *ptr, char *str, int max_len) -{ - static int mouse_x = 0; - static int mouse_y = 0; - - if (music_menu_id == -1) - { - dbg_msg("menu", "no music is playing, so let's play some tunes!"); - music_menu_id = snd_play(music_menu, SND_LOOP); - } - - netaddr4 *server_address = (netaddr4 *)ptr; - - // handle mouse movement - float mx, my, mwx, mwy; - { - int rx, ry; - inp_mouse_relative(&rx, &ry); - mouse_x += rx; - mouse_y += ry; - if(mouse_x < 0) mouse_x = 0; - if(mouse_y < 0) mouse_y = 0; - if(mouse_x > gfx_screenwidth()) mouse_x = gfx_screenwidth(); - if(mouse_y > gfx_screenheight()) mouse_y = gfx_screenheight(); - - // update the ui - mx = (mouse_x/(float)gfx_screenwidth())*400.0f; - my = (mouse_y/(float)gfx_screenheight())*300.0f; - mwx = mx*3.0f; // adjust to zoom and offset - mwy = mx*3.0f; // adjust to zoom and offset - - int buttons = 0; - if(inp_mouse_button_pressed(0)) buttons |= 1; - if(inp_mouse_button_pressed(1)) buttons |= 2; - if(inp_mouse_button_pressed(2)) buttons |= 4; - - ui_update(mx,my,mx*3.0f,my*3.0f,buttons); - } - - int r = menu_render(server_address, str, max_len); - - // render butt ugly mouse cursor - gfx_texture_set(-1); - gfx_quads_begin(); - gfx_quads_setcolor(0,0,0,1); - gfx_quads_draw_freeform(mx,my,mx,my, - mx+7,my, - mx,my+7); - gfx_quads_setcolor(1,1,1,1); - gfx_quads_draw_freeform(mx+1,my+1,mx+1,my+1, - mx+5,my+1, - mx+1,my+5); - gfx_quads_end(); - - if (r) - { - snd_stop(music_menu_id); - music_menu_id = -1; - } - - keys::clear_char(); - keys::clear_key(); - - return r; -} diff --git a/src/packet.h b/src/packet.h deleted file mode 100644 index ebc5e41e..00000000 --- a/src/packet.h +++ /dev/null @@ -1,287 +0,0 @@ -#include -#include - -// TODO: this is not KISS -class packet -{ -protected: - enum - { - MAX_PACKET_SIZE = 1024, - }; - - // packet data - struct header - { - unsigned msg; - unsigned ack; - unsigned seq; - }; - - unsigned char packet_data[MAX_PACKET_SIZE]; - unsigned char *current; - - // these are used to prepend data in the packet - // used for debugging so we have checks that we - // pack and unpack the same way - enum - { - DEBUG_TYPE_INT=0x1, - DEBUG_TYPE_STR=0x2, - DEBUG_TYPE_RAW=0x3, - }; - - // writes an int to the packet - void write_int_raw(int i) - { - // TODO: check for overflow - *(int*)current = i; - current += sizeof(int); - } - - // reads an int from the packet - int read_int_raw() - { - // TODO: check for overflow - int i = *(int*)current; - current += sizeof(int); - return i; - } - - void debug_insert_mark(int type, int size) - { - write_int_raw((type<<16)|size); - } - - void debug_verify_mark(int type, int size) - { - if(read_int_raw() == ((type<<16) | size)) - dbg_assert(0, "error during packet disassembly"); - } - -public: - packet(unsigned msg=0) - { - current = packet_data; - current += sizeof(header); - ((header*)packet_data)->msg = msg; - } - - void set_header(unsigned ack, unsigned seq) - { - ((header*)packet_data)->ack = ack; - ((header*)packet_data)->seq = seq; - } - - // writes an int to the packet - void write_int(int i) - { - debug_insert_mark(DEBUG_TYPE_INT, 4); - write_int_raw(i); - } - - void write_raw(const char *raw, int size) - { - debug_insert_mark(DEBUG_TYPE_RAW, size); - while(size--) - *current++ = *raw++; - } - - // writes a string to the packet - void write_str(const char *str) - { - debug_insert_mark(DEBUG_TYPE_STR, 0); - int s = strlen(str)+1; - write_int_raw(s); - for(;*str; current++, str++) - *current = *str; - *current = 0; - current++; - } - - // reads an int from the packet - int read_int() - { - debug_verify_mark(DEBUG_TYPE_INT, 4); - return read_int_raw(); - } - - // reads a string from the packet - const char *read_str() - { - debug_verify_mark(DEBUG_TYPE_STR, 0); - const char *s = (const char *)current; - int size = read_int_raw(); - current += size; - return s; - } - - const char *read_raw(int size) - { - debug_verify_mark(DEBUG_TYPE_RAW, size); - const char *d = (const char *)current; - current += size; - return d; - } - - // TODO: impelement this - bool is_good() const { return true; } - - unsigned msg() const { return ((header*)packet_data)->msg; } - unsigned seq() const { return ((header*)packet_data)->seq; } - unsigned ack() const { return ((header*)packet_data)->ack; } - - // access functions to get the size and data - int size() const { return (int)(current-(unsigned char*)packet_data); } - int max_size() const { return MAX_PACKET_SIZE; } - void *data() { return packet_data; } -}; - -// -class connection -{ - baselib::socket_udp4 *socket; - baselib::netaddr4 addr; - unsigned seq; - unsigned ack; - unsigned last_ack; - - unsigned counter_sent_bytes; - unsigned counter_recv_bytes; - -public: - void counter_reset() - { - counter_sent_bytes = 0; - counter_recv_bytes = 0; - } - - void counter_get(unsigned *sent, unsigned *recved) - { - *sent = counter_sent_bytes; - *recved = counter_recv_bytes; - } - - void init(baselib::socket_udp4 *socket, const baselib::netaddr4 *addr) - { - this->addr = *addr; - this->socket = socket; - last_ack = 0; - ack = 0; - seq = 0; - counter_reset(); - } - - void send(packet *p) - { - if(p->msg()&(31<<1)) - { - // vital packet - seq++; - // TODO: save packet, we might need to retransmit - } - - p->set_header(ack, seq); - socket->send(&address(), p->data(), p->size()); - counter_sent_bytes += p->size(); - } - - packet *feed(packet *p) - { - counter_recv_bytes += p->size(); - - if(p->msg()&(31<<1)) - { - if(p->seq() == ack+1) - { - // packet in seqence, ack it - ack++; - //dbg_msg("network/connection", "packet in sequence. seq/ack=%x", ack); - return p; - } - else if(p->seq() > ack) - { - // TODO: request resend - // packet loss - dbg_msg("network/connection", "packet loss! seq=%x ack=%x+1", p->seq(), ack); - return p; - } - else - { - // we already got this packet - return 0x0; - } - } - - if(last_ack != p->ack()) - { - // TODO: remove acked packets - } - - return p; - } - - const baselib::netaddr4 &address() const { return addr; } - - void update() - { - } -}; - -//const char *NETWORK_VERSION = "development"; - -enum -{ - NETMSG_VITAL=0x80000000, - NETMSG_CONTEXT_CONNECT=0x00010000, - NETMSG_CONTEXT_GAME=0x00020000, - NETMSG_CONTEXT_GLOBAL=0x00040000, - - // connection phase - NETMSG_CLIENT_CONNECT=NETMSG_CONTEXT_CONNECT|1, - // str32 name - // str32 clan - // str32 password - // str32 skin - - // TODO: These should be implemented to make the server send the map to the client on connect - // NETMSG_CLIENT_FETCH, - // NETMSG_SERVER_MAPDATA, - - NETMSG_SERVER_ACCEPT=NETMSG_CONTEXT_CONNECT|2, - // str32 mapname - - - NETMSG_CLIENT_DONE=NETMSG_VITAL|NETMSG_CONTEXT_CONNECT|3, - // nothing - - // game phase - NETMSG_SERVER_SNAP = NETMSG_CONTEXT_GAME|1, // server will spam these - // int num_parts - // int part - // int size - // data * - - NETMSG_CLIENT_INPUT = NETMSG_CONTEXT_GAME|1, // client will spam these - // int input[MAX_INPUTS] - - NETMSG_SERVER_EVENT = NETMSG_CONTEXT_GAME|NETMSG_VITAL|2, - NETMSG_CLIENT_EVENT = NETMSG_CONTEXT_GAME|NETMSG_VITAL|2, - - NETMSG_CLIENT_CHECKALIVE = NETMSG_CONTEXT_GAME|NETMSG_VITAL|3, // check if client is alive - - NETMSG_CLIENT_ERROR=0x0fffffff, - // str128 reason - - NETMSG_SERVER_ERROR=0x0fffffff, - // str128 reason -}; - -enum -{ - MAX_NAME_LENGTH=32, - MAX_CLANNAME_LENGTH=32, - MAX_INPUT_SIZE=128, - MAX_SNAPSHOT_SIZE=64*1024, - MAX_SNAPSHOT_PACKSIZE=768 -}; diff --git a/src/server.cpp b/src/server.cpp deleted file mode 100644 index 1de5839b..00000000 --- a/src/server.cpp +++ /dev/null @@ -1,668 +0,0 @@ -#include -#include - -#include -#include - -#include "interface.h" - -//#include "socket.h" -#include "packet.h" -#include "snapshot.h" - -#include "lzw.h" - -#include "versions.h" - -namespace baselib {} -using namespace baselib; - -class snapshot_builder -{ -public: - static const int MAX_ITEMS = 512; - //static const int MAX_DATA_SIZE=1*1024; - - char data[MAX_SNAPSHOT_SIZE]; - int data_size; - - int offsets[MAX_ITEMS]; - int num_items; - - int top_size; - int top_items; - - int snapnum; - - snapshot_builder() - { - top_size = 0; - top_items = 0; - snapnum = 0; - } - - void start() - { - data_size = 0; - num_items = 0; - } - - int finish(void *snapdata) - { - snapnum++; - - // collect some data - /* - int change = 0; - if(data_size > top_size) - { - change++; - top_size = data_size; - } - - if(num_items > top_items) - { - change++; - top_items = num_items; - } - - if(change) - { - dbg_msg("snapshot", "new top, items=%d size=%d", top_items, top_size); - }*/ - - // flattern and make the snapshot - snapshot *snap = (snapshot *)snapdata; - snap->num_items = num_items; - int offset_size = sizeof(int)*num_items; - mem_copy(snap->offsets, offsets, offset_size); - mem_copy(snap->data_start(), data, data_size); - return sizeof(int) + offset_size + data_size; - } - - void *new_item(int type, int id, int size) - { - snapshot::item *obj = (snapshot::item *)(data+data_size); - obj->type_and_id = (type<<16)|id; - offsets[num_items] = data_size; - data_size += sizeof(int) + size; - num_items++; - dbg_assert(data_size < MAX_SNAPSHOT_SIZE, "too much data"); - dbg_assert(num_items < MAX_ITEMS, "too many items"); - - return &obj->data; - } -}; - -static snapshot_builder builder; - -void *snap_new_item(int type, int id, int size) -{ - dbg_assert(type >= 0 && type <=0xffff, "incorrect type"); - dbg_assert(id >= 0 && id <=0xffff, "incorrect id"); - return builder.new_item(type, id, size); -} - - -// -class client -{ -public: - enum - { - STATE_EMPTY = 0, - STATE_CONNECTING = 1, - STATE_INGAME = 2, - }; - - // connection state info - int state; - - // (ticks) if lastactivity > 5 seconds kick him - int64 lastactivity; - connection conn; - - char name[MAX_NAME_LENGTH]; - char clan[MAX_CLANNAME_LENGTH]; - /* - client() - { - state = STATE_EMPTY; - name[0] = 0; - clan[0] = 0; - } - - ~client() - { - dbg_assert(state == STATE_EMPTY, "client destoyed while in use"); - }*/ - - bool is_empty() const { return state == STATE_EMPTY; } - bool is_ingame() const { return state == STATE_INGAME; } - const netaddr4 &address() const { return conn.address(); } -}; - -static client clients[MAX_CLIENTS]; -static int current_tick = 0; -static int send_heartbeats = 1; - -int server_tick() -{ - return current_tick; -} - -int server_tickspeed() -{ - return 50; -} - -int server_init() -{ - for(int i = 0; i < MAX_CLIENTS; i++) - { - clients[i].state = client::STATE_EMPTY; - clients[i].name[0] = 0; - clients[i].clan[0] = 0; - clients[i].lastactivity = 0; - } - - current_tick = 0; - - return 0; -} - -int server_getclientinfo(int client_id, client_info *info) -{ - dbg_assert(client_id >= 0 && client_id < MAX_CLIENTS, "client_id is not valid"); - dbg_assert(info != 0, "info can not be null"); - - if(clients[client_id].is_ingame()) - { - info->name = clients[client_id].name; - info->latency = 0; - return 1; - } - return 0; -} - -// -class server -{ -public: - - socket_udp4 game_socket; - const char *map_name; - const char *server_name; - int64 lasttick; - int64 lastheartbeat; - netaddr4 master_server; - - int biggest_snapshot; - - bool run(const char *servername, const char *mapname) - { - biggest_snapshot = 0; - - net_init(); // For Windows compatibility. - map_name = mapname; - server_name = servername; - - // load map - if(!map_load(mapname)) - { - dbg_msg("server", "failed to load map. mapname='%s'"); - return false; - } - - // start server - if(!game_socket.open(8303)) - { - dbg_msg("network/server", "couldn't open socket"); - return false; - } - - for(int i = 0; i < MAX_CLIENTS; i++) - dbg_msg("network/server", "\t%d: %d", i, clients[i].state); - - if (net_host_lookup(MASTER_SERVER_ADDRESS, MASTER_SERVER_PORT, &master_server) != 0) - master_server = netaddr4(0, 0, 0, 0, 0); - - mods_init(); - - int64 time_per_tick = time_freq()/SERVER_TICK_SPEED; - int64 time_per_heartbeat = time_freq() * 30; - int64 starttime = time_get(); - //int64 lasttick = starttime; - lasttick = starttime; - lastheartbeat = 0; - - int64 reporttime = time_get(); - int64 reportinterval = time_freq()*3; - - int64 simulationtime = 0; - int64 snaptime = 0; - int64 networktime = 0; - - while(1) - { - int64 t = time_get(); - if(t-lasttick > time_per_tick) - { - { - int64 start = time_get(); - tick(); - simulationtime += time_get()-start; - } - - { - int64 start = time_get(); - snap(); - snaptime += time_get()-start; - } - - // Check for client timeouts - for (int i = 0; i < MAX_CLIENTS; i++) - { - if (clients[i].state != client::STATE_EMPTY) - { - // check last activity time - if (((lasttick - clients[i].lastactivity) / time_freq()) > SERVER_CLIENT_TIMEOUT) - client_timeout(i); - } - } - - lasttick += time_per_tick; - } - - if(send_heartbeats) - { - if (t > lastheartbeat+time_per_heartbeat) - { - if (master_server.port != 0) - { - int players = 0; - - for (int i = 0; i < MAX_CLIENTS; i++) - if (!clients[i].is_empty()) - players++; - - netaddr4 me(127, 0, 0, 0, 8303); - - send_heartbeat(0, &me, players, MAX_CLIENTS, server_name, mapname); - } - - lastheartbeat = t+time_per_heartbeat; - } - } - - { - int64 start = time_get(); - pump_network(); - networktime += time_get()-start; - } - - if(reporttime < time_get()) - { - int64 totaltime = simulationtime+snaptime+networktime; - dbg_msg("server/report", "sim=%.02fms snap=%.02fms net=%.02fms total=%.02fms load=%.02f%%", - simulationtime/(float)reportinterval*1000, - snaptime/(float)reportinterval*1000, - networktime/(float)reportinterval*1000, - totaltime/(float)reportinterval*1000, - (simulationtime+snaptime+networktime)/(float)reportinterval*100.0f); - - unsigned sent_total=0, recv_total=0; - for (int i = 0; i < MAX_CLIENTS; i++) - if (!clients[i].is_empty()) - { - unsigned s,r; - clients[i].conn.counter_get(&s,&r); - clients[i].conn.counter_reset(); - sent_total += s; - recv_total += r; - } - - - dbg_msg("server/report", "biggestsnap=%d send=%d recv=%d", - biggest_snapshot, sent_total/3, recv_total/3); - - simulationtime = 0; - snaptime = 0; - networktime = 0; - - reporttime += reportinterval; - } - - thread_sleep(1); - } - - mods_shutdown(); - map_unload(); - } - - void tick() - { - current_tick++; - mods_tick(); - } - - void snap() - { - mods_presnap(); - - for(int i = 0; i < MAX_CLIENTS; i++) - { - if(clients[i].is_ingame()) - { - char data[MAX_SNAPSHOT_SIZE]; - char compdata[MAX_SNAPSHOT_SIZE]; - builder.start(); - mods_snap(i); - - // finish snapshot - int snapshot_size = builder.finish(data); - - // compress it - int compsize = lzw_compress(data, snapshot_size, compdata); - snapshot_size = compsize; - - if(snapshot_size > biggest_snapshot) - biggest_snapshot = snapshot_size; - - const int max_size = MAX_SNAPSHOT_PACKSIZE; - int numpackets = (snapshot_size+max_size-1)/max_size; - for(int n = 0, left = snapshot_size; left; n++) - { - int chunk = left < max_size ? left : max_size; - left -= chunk; - - packet p(NETMSG_SERVER_SNAP); - p.write_int(numpackets); - p.write_int(n); - p.write_int(chunk); - p.write_raw(&compdata[n*max_size], chunk); - clients[i].conn.send(&p); - } - } - } - - mods_postsnap(); - } - - void send_accept(client *client, const char *map) - { - packet p(NETMSG_SERVER_ACCEPT); - p.write_str(map); - client->conn.send(&p); - } - - void drop(int cid, const char *reason) - { - if(clients[cid].state == client::STATE_EMPTY) - return; - - clients[cid].state = client::STATE_EMPTY; - mods_client_drop(cid); - dbg_msg("game", "player dropped. reason='%s' cid=%x name='%s'", reason, cid, clients[cid].name); - } - - int find_client(const netaddr4 *addr) - { - // fetch client - for(int i = 0; i < MAX_CLIENTS; i++) - { - if(!clients[i].is_empty() && clients[i].address() == *addr) - return i; - } - return -1; - } - - void client_process_packet(int cid, packet *p) - { - clients[cid].lastactivity = lasttick; - if(p->msg() == NETMSG_CLIENT_DONE) - { - dbg_msg("game", "player as entered the game. cid=%x", cid); - clients[cid].state = client::STATE_INGAME; - mods_client_enter(cid); - } - else if(p->msg() == NETMSG_CLIENT_INPUT) - { - int input[MAX_INPUT_SIZE]; - int size = p->read_int(); - for(int i = 0; i < size/4; i++) - input[i] = p->read_int(); - if(p->is_good()) - { - //dbg_msg("network/server", "applying input %d %d %d", input[0], input[1], input[2]); - mods_client_input(cid, input); - } - } - else if(p->msg() == NETMSG_CLIENT_ERROR) - { - const char *reason = p->read_str(); - if(p->is_good()) - dbg_msg("network/server", "client error. cid=%x reason='%s'", cid, reason); - else - dbg_msg("network/server", "client error. cid=%x", cid); - drop(cid, "client error"); - } - else - { - dbg_msg("network/server", "invalid message. cid=%x msg=%x", cid, p->msg()); - drop(cid, "invalid message"); - } - } - - void process_packet(packet *p, netaddr4 *from) - { - if(p->msg() == NETMSG_CLIENT_CONNECT) - { - // we got no state for this client yet - const char *version; - const char *name; - const char *clan; - const char *password; - const char *skin; - - version = p->read_str(); - name = p->read_str(); - clan = p->read_str(); - password = p->read_str(); - skin = p->read_str(); - - if(p->is_good()) - { - // check version - if(strncmp(version, TEEWARS_NETVERSION, 32) != 0) - { - // TODO: send error - return; - } - - // look for empty slot, linear search - int id = -1; - for(int i = 0; i < MAX_CLIENTS; i++) - if(clients[i].is_empty()) - { - id = i; - break; - } - - if(id != -1) - { - // slot found - // TODO: perform correct copy here - mem_copy(clients[id].name, name, MAX_NAME_LENGTH); - mem_copy(clients[id].clan, clan, MAX_CLANNAME_LENGTH); - clients[id].state = client::STATE_CONNECTING; - clients[id].conn.init(&game_socket, from); - - clients[id].lastactivity = lasttick; - clients[id].name[MAX_NAME_LENGTH-1] = 0; - clients[id].clan[MAX_CLANNAME_LENGTH-1] = 0; - - dbg_msg("network/server", "client connected. '%s' on slot %d", name, id); - - // TODO: return success - send_accept(&clients[id], map_name); - } - else - { - // no slot found - // TODO: send error - dbg_msg("network/server", "client connected but server is full"); - - for(int i = 0; i < MAX_CLIENTS; i++) - dbg_msg("network/server", "\t%d: %d", i, clients[i].state); - } - } - } - else - { - int cid = find_client(from); - if(cid >= 0) - { - if(clients[cid].conn.feed(p)) - { - // packet is ok - unsigned msg = p->msg(); - - // client found, check state - if(((msg>>16)&0xff)&clients[cid].state) - { - // state is ok - client_process_packet(cid, p); - } - else - { - // invalid state, disconnect the client - drop(cid, "invalid message at this state"); - } - } - else - { - drop(cid, "connection error"); - } - - } - else - dbg_msg("network/server", "packet from strange address."); - } - } - - void client_timeout(int clientId) - { - drop(clientId, "client timedout"); - } - - void pump_network() - { - while(1) - { - packet p; - netaddr4 from; - - int bytes = game_socket.recv(&from, p.data(), p.max_size()); - if(bytes <= 0) - break; - - process_packet(&p, &from); - } - // TODO: check for client timeouts - } - - char *write_int(char *buffer, int integer) - { - *buffer++ = integer >> 24; - *buffer++ = integer >> 16; - *buffer++ = integer >> 8; - *buffer++ = integer; - - return buffer; - } - - char *write_netaddr4(char *buffer, netaddr4 *address) - { - *buffer++ = address->ip[0]; - *buffer++ = address->ip[1]; - *buffer++ = address->ip[2]; - *buffer++ = address->ip[3]; - - return write_int(buffer, address->port); - } - - void send_heartbeat(int version, netaddr4 *address, int players, int max_players, const char *name, const char *map_name) - { - char buffer[216] = {0}; - char *d = buffer; - - d = write_int(d, 'TWHB'); - d = write_int(d, version); - d = write_netaddr4(d, address); - d = write_int(d,players); - d = write_int(d, max_players); - - int len = strlen(name); - if (len > 128) - len = 128; - - memcpy(d, name, len); - d += 128; - - len = strlen(map_name); - if (len > 64) - len = 64; - - memcpy(d, map_name, len); - d += 64; - - game_socket.send(&master_server, buffer, sizeof(buffer)); - } -}; - -int server_main(int argc, char **argv) -{ - dbg_msg("server", "starting..."); - - const char *mapname = "data/demo.map"; - const char *servername = 0; - // parse arguments - for(int i = 1; i < argc; i++) - { - if(argv[i][0] == '-' && argv[i][1] == 'm' && argv[i][2] == 0 && argc - i > 1) - { - // -m map - i++; - mapname = argv[i]; - } - else if(argv[i][0] == '-' && argv[i][1] == 'n' && argv[i][2] == 0 && argc - i > 1) - { - // -n server name - i++; - servername = argv[i]; - } - else if(argv[i][0] == '-' && argv[i][1] == 'p' && argv[i][2] == 0) - { - // -p (private server) - send_heartbeats = 0; - } - } - - if(!mapname) - { - dbg_msg("server", "no map given (-m MAPNAME)"); - return 0; - } - - if(!servername) - { - dbg_msg("server", "no server name given (-n \"server name\")"); - return 0; - } - - server_init(); - server s; - s.run(servername, mapname); - return 0; -} diff --git a/src/snapshot.h b/src/snapshot.h deleted file mode 100644 index 9d803486..00000000 --- a/src/snapshot.h +++ /dev/null @@ -1,19 +0,0 @@ - -struct snapshot -{ - int num_items; - int offsets[1]; - - struct item - { - int type_and_id; - char data[1]; - - int type() { return type_and_id>>16; } - int id() { return type_and_id&(0xffff); } - }; - - char *data_start() { return (char *)&offsets[num_items]; } - item *get_item(int index) { return (item *)(data_start() + offsets[index]); }; -}; - diff --git a/src/snd.cpp b/src/snd.cpp deleted file mode 100644 index 42997897..00000000 --- a/src/snd.cpp +++ /dev/null @@ -1,519 +0,0 @@ -#include -#include -#include - -#include "interface.h" - -using namespace baselib; - -static const int NUM_FRAMES_STOP = 512; -static const float NUM_FRAMES_STOP_INV = 1.0f/(float)NUM_FRAMES_STOP; -static const int NUM_FRAMES_LERP = 512; -static const float NUM_FRAMES_LERP_INV = 1.0f/(float)NUM_FRAMES_LERP; - -static const float GLOBAL_VOLUME_SCALE = 0.75f; - -static const int64 GLOBAL_SOUND_DELAY = 1000; - -// --- sound --- -class sound_data -{ -public: - short *data; - int num_samples; - int rate; - int channels; - int sustain_start; - int sustain_end; - int64 last_played; -}; - -inline short clamp(int i) -{ - if(i > 0x7fff) - return 0x7fff; - if(i < -0x7fff) - return -0x7fff; - return i; -} - -class mixer : public audio_stream -{ -public: - class channel - { - public: - channel() - { data = 0; lerp = -1; stop = -1; } - - sound_data *data; - int tick; - int loop; - float pan; - float vol; - float old_vol; - float new_vol; - int lerp; - int stop; - }; - - enum - { - MAX_CHANNELS=8, - }; - - channel channels[MAX_CHANNELS]; - - virtual void fill(void *output, unsigned long frames) - { - //dbg_msg("snd", "mixing!"); - - short *out = (short*)output; - bool clamp_flag = false; - - int active_channels = 0; - for(unsigned long i = 0; i < frames; i++) - { - int left = 0; - int right = 0; - - for(int c = 0; c < MAX_CHANNELS; c++) - { - if(channels[c].data) - { - if(channels[c].data->channels == 1) - { - left += (1.0f-(channels[c].pan+1.0f)*0.5f) * channels[c].vol * channels[c].data->data[channels[c].tick]; - right += (channels[c].pan+1.0f)*0.5f * channels[c].vol * channels[c].data->data[channels[c].tick]; - channels[c].tick++; - } - else - { - float pl = channels[c].pan<0.0f?-channels[c].pan:1.0f; - float pr = channels[c].pan>0.0f?1.0f-channels[c].pan:1.0f; - left += pl*channels[c].vol * channels[c].data->data[channels[c].tick]; - right += pr*channels[c].vol * channels[c].data->data[channels[c].tick + 1]; - channels[c].tick += 2; - } - - if(channels[c].loop) - { - if(channels[c].data->sustain_start >= 0 && channels[c].tick >= channels[c].data->sustain_end) - channels[c].tick = channels[c].data->sustain_start; - else if(channels[c].tick > channels[c].data->num_samples) - channels[c].tick = 0; - } - else if(channels[c].tick > channels[c].data->num_samples) - channels[c].data = 0; - - if(channels[c].stop == 0) - { - channels[c].stop = -1; - channels[c].data = 0; - } - else if(channels[c].stop > 0) - { - channels[c].vol = channels[c].old_vol * (float)channels[c].stop * NUM_FRAMES_STOP_INV; - channels[c].stop--; - } - if(channels[c].lerp > 0) - { - channels[c].vol = (1.0f - (float)channels[c].lerp * NUM_FRAMES_LERP_INV) * channels[c].new_vol + - (float)channels[c].lerp * NUM_FRAMES_LERP_INV * channels[c].old_vol; - channels[c].lerp--; - } - active_channels++; - } - } - - // TODO: remove these - - *out = clamp(left); // left - if(*out != left) clamp_flag = true; - out++; - *out = clamp(right); // right - if(*out != right) clamp_flag = true; - out++; - } - - if(clamp_flag) - dbg_msg("snd", "CLAMPED!"); - } - - int play(sound_data *sound, unsigned loop, float vol, float pan) - { - if(time_get() - sound->last_played < GLOBAL_SOUND_DELAY) - return -1; - - for(int c = 0; c < MAX_CHANNELS; c++) - { - if(channels[c].data == 0) - { - channels[c].data = sound; - channels[c].tick = 0; - channels[c].loop = loop; - channels[c].vol = vol * GLOBAL_VOLUME_SCALE; - channels[c].pan = pan; - sound->last_played = time_get(); - return c; - } - } - - return -1; - } - - void stop(int id) - { - dbg_assert(id >= 0 && id < MAX_CHANNELS, "id out of bounds"); - channels[id].old_vol = channels[id].vol; - channels[id].stop = NUM_FRAMES_STOP; - } - - void set_vol(int id, float vol) - { - dbg_assert(id >= 0 && id < MAX_CHANNELS, "id out of bounds"); - channels[id].new_vol = vol * GLOBAL_VOLUME_SCALE; - channels[id].old_vol = channels[id].vol; - channels[id].lerp = NUM_FRAMES_LERP; - } -}; - -static mixer mixer; -//static sound_data test_sound; - -extern "C" -{ -#include "wavpack/wavpack.h" -} - -/* -static file_stream *read_func_filestream; -static int32_t read_func(void *buff, int32_t bcount) -{ - return read_func_filestream->read(buff, bcount); -} -static uchar *format_samples(int bps, uchar *dst, int32_t *src, uint32_t samcnt) -{ - int32_t temp; - - switch (bps) { - - case 1: - while (samcnt--) - *dst++ = *src++ + 128; - - break; - - case 2: - while (samcnt--) { - *dst++ = (uchar)(temp = *src++); - *dst++ = (uchar)(temp >> 8); - } - - break; - - case 3: - while (samcnt--) { - *dst++ = (uchar)(temp = *src++); - *dst++ = (uchar)(temp >> 8); - *dst++ = (uchar)(temp >> 16); - } - - break; - - case 4: - while (samcnt--) { - *dst++ = (uchar)(temp = *src++); - *dst++ = (uchar)(temp >> 8); - *dst++ = (uchar)(temp >> 16); - *dst++ = (uchar)(temp >> 24); - } - - break; - } - - return dst; -}*/ - -/* -struct sound_holder -{ - sound_data sound; - int next; -}; - -static const int MAX_SOUNDS = 256; -static sound_holder sounds[MAX_SOUNDS]; -static int first_free_sound; - -bool snd_load_wv(const char *filename, sound_data *snd) -{ - // open file - file_stream file; - if(!file.open_r(filename)) - { - dbg_msg("sound/wv", "failed to open file. filename='%s'", filename); - return false; - } - read_func_filestream = &file; - - // get info - WavpackContext *wpc; - char error[128]; - wpc = WavpackOpenFileInput(read_func, error); - if(!wpc) - { - dbg_msg("sound/wv", "failed to open file. err=%s filename='%s'", error, filename); - return false; - } - - - snd->num_samples = WavpackGetNumSamples(wpc); - int bps = WavpackGetBytesPerSample(wpc); - int channels = WavpackGetReducedChannels(wpc); - snd->rate = WavpackGetSampleRate(wpc); - int bits = WavpackGetBitsPerSample(wpc); - - (void)bps; - (void)channels; - (void)bits; - - // decompress - int datasize = snd->num_samples*2; - snd->data = (short*)mem_alloc(datasize, 1); - int totalsamples = 0; - while(1) - { - int buffer[1024*4]; - int samples_unpacked = WavpackUnpackSamples(wpc, buffer, 1024*4); - totalsamples += samples_unpacked; - - if(samples_unpacked) - { - // convert - } - } - - if(snd->num_samples != totalsamples) - { - dbg_msg("sound/wv", "wrong amount of samples. filename='%s'", filename); - mem_free(snd->data); - return false;; - } - - return false; -}*/ - -struct sound_holder -{ - sound_data sound; - int next; -}; - -static const int MAX_SOUNDS = 1024; -static sound_holder sounds[MAX_SOUNDS]; -static int first_free_sound; - -bool snd_init() -{ - first_free_sound = 0; - for(int i = 0; i < MAX_SOUNDS; i++) - sounds[i].next = i+1; - sounds[MAX_SOUNDS-1].next = -1; - return mixer.create(); -} - -bool snd_shutdown() -{ - mixer.destroy(); - return true; -} - -static int snd_alloc_sound() -{ - if(first_free_sound < 0) - return -1; - int id = first_free_sound; - first_free_sound = sounds[id].next; - sounds[id].next = -1; - return id; -} - -int snd_load_wav(const char *filename) -{ - sound_data snd; - - // open file for reading - file_stream file; - if(!file.open_r(filename)) - { - dbg_msg("sound/wav", "failed to open file. filename='%s'", filename); - return -1; - } - - int id = -1; - int state = 0; - while(1) - { - // read chunk header - unsigned char head[8]; - if(file.read(head, sizeof(head)) != 8) - { - break; - } - - int chunk_size = head[4] | (head[5]<<8) | (head[6]<<16) | (head[7]<<24); - head[4] = 0; - - if(state == 0) - { - // read the riff and wave headers - if(head[0] != 'R' || head[1] != 'I' || head[2] != 'F' || head[3] != 'F') - { - dbg_msg("sound/wav", "not a RIFF file. filename='%s'", filename); - return -1; - } - - unsigned char type[4]; - file.read(type, 4); - - if(type[0] != 'W' || type[1] != 'A' || type[2] != 'V' || type[3] != 'E') - { - dbg_msg("sound/wav", "RIFF file is not a WAVE. filename='%s'", filename); - return -1; - } - - state++; - } - else if(state == 1) - { - // read the format chunk - if(head[0] == 'f' && head[1] == 'm' && head[2] == 't' && head[3] == ' ') - { - unsigned char fmt[16]; - if(file.read(fmt, sizeof(fmt)) != sizeof(fmt)) - { - dbg_msg("sound/wav", "failed to read format. filename='%s'", filename); - return -1; - } - - // decode format - int compression_code = fmt[0] | (fmt[1]<<8); - snd.channels = fmt[2] | (fmt[3]<<8); - snd.rate = fmt[4] | (fmt[5]<<8) | (fmt[6]<<16) | (fmt[7]<<24); - - if(compression_code != 1) - { - dbg_msg("sound/wav", "file is not uncompressed. filename='%s'", filename); - return -1; - } - - if(snd.channels > 2) - { - dbg_msg("sound/wav", "file is not mono or stereo. filename='%s'", filename); - return -1; - } - - if(snd.rate != 44100) - { - dbg_msg("sound/wav", "file is %d Hz, not 44100 Hz. filename='%s'", snd.rate, filename); - return -1; - } - - int bps = fmt[14] | (fmt[15]<<8); - if(bps != 16) - { - dbg_msg("sound/wav", "bps is %d, not 16, filname='%s'", bps, filename); - return -1; - } - - // skip extra bytes (not used for uncompressed) - //int extra_bytes = fmt[14] | (fmt[15]<<8); - //dbg_msg("sound/wav", "%d", extra_bytes); - //file.skip(extra_bytes); - - // next state - state++; - } - else - file.skip(chunk_size); - } - else if(state == 2) - { - // read the data - if(head[0] == 'd' && head[1] == 'a' && head[2] == 't' && head[3] == 'a') - { - snd.data = (short*)mem_alloc(chunk_size, 1); - file.read(snd.data, chunk_size); - snd.num_samples = chunk_size/(2); - snd.sustain_start = -1; - snd.sustain_end = -1; - snd.last_played = 0; - id = snd_alloc_sound(); - sounds[id].sound = snd; - state++; - } - else - file.skip(chunk_size); - } - else if(state == 3) - { - if(head[0] == 's' && head[1] == 'm' && head[2] == 'p' && head[3] == 'l') - { - int smpl[9]; - int loop[6]; - - file.read(smpl, sizeof(smpl)); - - if(smpl[7] > 0) - { - file.read(loop, sizeof(loop)); - sounds[id].sound.sustain_start = loop[2] * sounds[id].sound.channels; - sounds[id].sound.sustain_end = loop[3] * sounds[id].sound.channels; - } - - if(smpl[7] > 1) - file.skip((smpl[7]-1) * sizeof(loop)); - - file.skip(smpl[8]); - state++; - } - else - file.skip(chunk_size); - } - else - file.skip(chunk_size); - } - - if(id >= 0) - dbg_msg("sound/wav", "loaded %s", filename); - else - dbg_msg("sound/wav", "failed to load %s", filename); - - return id; -} - -int snd_play(int id, int loop, float vol, float pan) -{ - if(id < 0) - { - dbg_msg("snd", "bad sound id"); - return -1; - } - - dbg_assert(sounds[id].sound.data != 0, "null sound"); - dbg_assert(sounds[id].next == -1, "sound isn't allocated"); - return mixer.play(&sounds[id].sound, loop, vol, pan); -} - -void snd_stop(int id) -{ - if(id >= 0) - mixer.stop(id); -} - -void snd_set_vol(int id, float vol) -{ - if(id >= 0) - mixer.set_vol(id, vol); -} diff --git a/src/ui.cpp b/src/ui.cpp deleted file mode 100644 index 7ef19b72..00000000 --- a/src/ui.cpp +++ /dev/null @@ -1,115 +0,0 @@ -#include "interface.h" -#include "ui.h" - -/******************************************************** - UI -*********************************************************/ -//static unsigned mouse_buttons_last = 0; - -struct pretty_font -{ - char m_CharStartTable[256]; - char m_CharEndTable[256]; - int font_texture; -}; - -extern pretty_font *current_font; -void gfx_pretty_text(float x, float y, float size, const char *text); - - -static void *hot_item = 0; -static void *active_item = 0; -static void *becomming_hot_item = 0; -static float mouse_x, mouse_y; // in gui space -static float mouse_wx, mouse_wy; // in world space -static unsigned mouse_buttons = 0; - -float ui_mouse_x() { return mouse_x; } -float ui_mouse_y() { return mouse_y; } -float ui_mouse_world_x() { return mouse_wx; } -float ui_mouse_world_y() { return mouse_wy; } -int ui_mouse_button(int index) { return (mouse_buttons>>index)&1; } - -void ui_set_hot_item(void *id) { becomming_hot_item = id; } -void ui_set_active_item(void *id) { active_item = id; } -void *ui_hot_item() { return hot_item; } -void *ui_active_item() { return active_item; } - -int ui_update(float mx, float my, float mwx, float mwy, int buttons) -{ - //mouse_buttons_last = mouse_buttons; - mouse_x = mx; - mouse_y = my; - mouse_wx = mwx; - mouse_wy = mwy; - mouse_buttons = buttons; - hot_item = becomming_hot_item; - becomming_hot_item = 0; - return 0; -} - -/* -static int ui_mouse_button_released(int index) -{ - return ((mouse_buttons_last>>index)&1) && !(); -}*/ - -int ui_mouse_inside(float x, float y, float w, float h) -{ - if(mouse_x >= x && mouse_x <= x+w && mouse_y >= y && mouse_y <= y+h) - return 1; - return 0; -} - -void ui_do_image(int texture, float x, float y, float w, float h) -{ - gfx_blend_normal(); - gfx_texture_set(texture); - gfx_quads_begin(); - gfx_quads_setcolor(1,1,1,1); - gfx_quads_setsubset( - 0.0f, // startx - 0.0f, // starty - 1.0f, // endx - 1.0f); // endy - gfx_quads_drawTL(x,y,w,h); - gfx_quads_end(); -} - -void ui_do_label(float x, float y, char *text) -{ - gfx_blend_normal(); - gfx_texture_set(current_font->font_texture); - gfx_pretty_text(x, y, 18.f, text); -} - -int ui_do_button(void *id, const char *text, int checked, float x, float y, float w, float h, draw_button_callback draw_func, void *extra) -{ - // logic - int r = 0; - int inside = ui_mouse_inside(x,y,w,h); - - if(inside) - { - ui_set_hot_item(id); - - if(ui_mouse_button(0)) - ui_set_active_item(id); - } - - if(ui_active_item() == id && ui_hot_item() == id && !ui_mouse_button(0)) - { - ui_set_active_item(0); - r = 1; - } - - draw_func(id, text, checked, x, y, w, h, extra); - - return r; -} - -int ui_do_button(void *id, const char *text, int checked, float x, float y, float w, float h, draw_button_callback draw_func) -{ - return ui_do_button(id, text, checked, x, y, w, h, draw_func, 0x0); -} - diff --git a/src/ui.h b/src/ui.h deleted file mode 100644 index 1a420906..00000000 --- a/src/ui.h +++ /dev/null @@ -1,33 +0,0 @@ -#ifndef _UI_H -#define _UI_H -/* -extern void *hot_item; -extern void *active_item; -extern void *becomming_hot_item; -extern float mouse_x, mouse_y; // in gui space -extern float mouse_wx, mouse_wy; // in world space -extern unsigned mouse_buttons;*/ - -int ui_update(float mx, float my, float mwx, float mwy, int buttons); - -float ui_mouse_x(); -float ui_mouse_y(); -float ui_mouse_world_x(); -float ui_mouse_world_y(); -int ui_mouse_button(int index); - -void ui_set_hot_item(void *id); -void ui_set_active_item(void *id); -void *ui_hot_item(); -void *ui_active_item(); - -int ui_mouse_inside(float x, float y, float w, float h); - -typedef void (*draw_button_callback)(void *id, const char *text, int checked, float x, float y, float w, float h, void *extra); - -void ui_do_image(int texture, float x, float y, float w, float h); -void ui_do_label(float x, float y, char *text); -int ui_do_button(void *id, const char *text, int checked, float x, float y, float w, float h, draw_button_callback draw_func, void *extra); -int ui_do_button(void *id, const char *text, int checked, float x, float y, float w, float h, draw_button_callback draw_func); - -#endif diff --git a/src/versions.h b/src/versions.h deleted file mode 100644 index d70ee721..00000000 --- a/src/versions.h +++ /dev/null @@ -1,2 +0,0 @@ -#define TEEWARS_NETVERSION "dev v2" -#define TEEWARS_VERSION "0.2.1-dev" -- cgit 1.4.1