diff options
| -rw-r--r-- | data/game_main.png | bin | 20039 -> 23881 bytes | |||
| -rw-r--r-- | datasrc/teewars.ds | 6 | ||||
| -rw-r--r-- | docs/articles/tasks.txt | 32 | ||||
| -rw-r--r-- | src/engine/client/client.cpp | 242 | ||||
| -rw-r--r-- | src/engine/client/gfx.cpp | 2 | ||||
| -rw-r--r-- | src/engine/interface.h | 10 | ||||
| -rw-r--r-- | src/engine/msg.cpp | 19 | ||||
| -rw-r--r-- | src/engine/server/server.cpp | 90 | ||||
| -rw-r--r-- | src/game/client/game_client.cpp | 346 | ||||
| -rw-r--r-- | src/game/game.h | 19 | ||||
| -rw-r--r-- | src/game/server/game_server.cpp | 158 |
11 files changed, 649 insertions, 275 deletions
diff --git a/data/game_main.png b/data/game_main.png index 188068f3..a95fd69e 100644 --- a/data/game_main.png +++ b/data/game_main.png Binary files differdiff --git a/datasrc/teewars.ds b/datasrc/teewars.ds index 7e083d62..45df436c 100644 --- a/datasrc/teewars.ds +++ b/datasrc/teewars.ds @@ -252,9 +252,6 @@ sprites { part7 4 2 2 2 part8 6 2 2 2 part9 8 2 2 2 - - star1 0 0 2 2 - star2 0 2 2 2 } hud images.game 32 16 { @@ -262,6 +259,9 @@ sprites { health_empty 5 0 4 4 armor_full 0 5 4 4 armor_empty 5 5 4 4 + star1 0 10 3 3 + star2 3 10 3 3 + star3 6 10 3 3 } weapons images.weapons 32 32 { diff --git a/docs/articles/tasks.txt b/docs/articles/tasks.txt index 1a471d2b..bbf3969c 100644 --- a/docs/articles/tasks.txt +++ b/docs/articles/tasks.txt @@ -6,30 +6,38 @@ Group: 0.1.1 - Awesome Alpha Bug Fixed (NEXT) DONE * Bobbing powerups DONE * Get -c working under windows DONE * Bigger powerups -* Pickup sounds -* FAQ on the webpage DONE * Private server (no heartbeats) DONE * Can't pickup ammo, health or armor when not needed DONE * Fix so that the homepage is in the SVN Group: 0.2.x - -* Cleanup: SVN * Cleanup: client.cpp / server.cpp -* Cleanup: game_client.cpp / game_server.cpp * Cleanup: editor.cpp -* Masterserver: Master server should only hold ips. Clients should ask servers for more info + ping -* Masterserver: NAT/FW detection -* Game: Respawn time -* Game: Chat -* Game: Bigger and better scoreboard + +* Web: FAQ on the webpage * Game: Events for kills and such - DONE * Client: Only send input when needed -* Client: Should timeout from server +* Game: Pickup sounds +* Game: Add the sounds +* Game: Fix the hammer again +* Game: Tweak graphics + * Client: More robust and smoother handling of snapshots -* Client: Some sort of settings format (think KISS). + * Need to add some smoothed timer or something + +* Masterserver: Master server should only hold ips. Clients should ask servers for more info + ping +* Masterserver: NAT/FW detection * Server: LAN / Internet / Private servers. * Server: Answer to a getinfo message +DONE * Game: Chat +DONE * Game: Bigger and better scoreboard +DONE * Cleanup: SVN +DONE * Cleanup: game_client.cpp / game_server.cpp +DONE * Game: Respawn time +DONE * Client: Only send input when needed +DONE * Client: Should timeout from server +DONE * Client: Some sort of settings format (think KISS). + Group: 0.3.0 - Editor Edition * Fix the editor diff --git a/src/engine/client/client.cpp b/src/engine/client/client.cpp index 4fb869cd..04c25bba 100644 --- a/src/engine/client/client.cpp +++ b/src/engine/client/client.cpp @@ -194,7 +194,7 @@ public: { recived_snapshots = 0; - msg_pack_start(NETMSG_INFO, MSGFLAG_VITAL); + msg_pack_start_system(NETMSG_INFO, MSGFLAG_VITAL); msg_pack_string(config.player_name, 128); msg_pack_string(config.clan_name, 128); msg_pack_string(config.password, 128); @@ -205,7 +205,7 @@ public: void send_entergame() { - msg_pack_start(NETMSG_ENTERGAME, MSGFLAG_VITAL); + msg_pack_start_system(NETMSG_ENTERGAME, MSGFLAG_VITAL); msg_pack_end(); client_send_msg(); } @@ -226,7 +226,7 @@ public: void send_input() { - msg_pack_start(NETMSG_INPUT, 0); + msg_pack_start_system(NETMSG_INPUT, 0); msg_pack_int(input_data_size); for(int i = 0; i < input_data_size/4; i++) msg_pack_int(input_data[i]); @@ -462,135 +462,145 @@ public: void process_packet(NETPACKET *packet) { - int msg = msg_unpack_start(packet->data, packet->data_size); - if(msg == NETMSG_MAP) + int sys; + int msg = msg_unpack_start(packet->data, packet->data_size, &sys); + if(sys) { - const char *map = msg_unpack_string(); - dbg_msg("client/network", "connection accepted, map=%s", map); - set_state(STATE_LOADING); - - if(map_load(map)) - { - modc_entergame(); - send_entergame(); - 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(msg == NETMSG_SNAP || msg == NETMSG_SNAPSMALL || msg == NETMSG_SNAPEMPTY) - { - //dbg_msg("client/network", "got snapshot"); - int game_tick = msg_unpack_int(); - int delta_tick = game_tick-msg_unpack_int(); - int num_parts = 1; - int part = 0; - int part_size = 0; - - if(msg == NETMSG_SNAP) + // system message + if(msg == NETMSG_MAP) { - num_parts = msg_unpack_int(); - part = msg_unpack_int(); + const char *map = msg_unpack_string(); + dbg_msg("client/network", "connection accepted, map=%s", map); + set_state(STATE_LOADING); + + if(map_load(map)) + { + modc_entergame(); + send_entergame(); + dbg_msg("client/network", "loading done"); + // now we will wait for two snapshots + // to finish the connection + } + else + { + error("failure to load map"); + } } - - if(msg != NETMSG_SNAPEMPTY) - part_size = msg_unpack_int(); - - if(snapshot_part == part) + else if(msg == NETMSG_SNAP || msg == NETMSG_SNAPSMALL || msg == NETMSG_SNAPEMPTY) { - // TODO: clean this up abit - const char *d = (const char *)msg_unpack_raw(part_size); - mem_copy((char*)snapshots[SNAP_INCOMMING] + part*MAX_SNAPSHOT_PACKSIZE, d, part_size); - snapshot_part++; - - if(snapshot_part == num_parts) + //dbg_msg("client/network", "got snapshot"); + int game_tick = msg_unpack_int(); + int delta_tick = game_tick-msg_unpack_int(); + int num_parts = 1; + int part = 0; + int part_size = 0; + + if(msg == NETMSG_SNAP) + { + num_parts = msg_unpack_int(); + part = msg_unpack_int(); + } + + if(msg != NETMSG_SNAPEMPTY) + part_size = msg_unpack_int(); + + if(snapshot_part == part) { - snapshot *tmp = snapshots[SNAP_PREV]; - snapshots[SNAP_PREV] = snapshots[SNAP_CURRENT]; - snapshots[SNAP_CURRENT] = tmp; - current_tick = game_tick; - - // decompress snapshot - void *deltadata = snapshot_empty_delta(); - int deltasize = sizeof(int)*3; - - unsigned char tmpbuffer[MAX_SNAPSHOT_SIZE]; - unsigned char tmpbuffer2[MAX_SNAPSHOT_SIZE]; - if(part_size) + // TODO: clean this up abit + const char *d = (const char *)msg_unpack_raw(part_size); + mem_copy((char*)snapshots[SNAP_INCOMMING] + part*MAX_SNAPSHOT_PACKSIZE, d, part_size); + snapshot_part++; + + if(snapshot_part == num_parts) { - //int snapsize = lzw_decompress(snapshots[SNAP_INCOMMING], snapshots[SNAP_CURRENT]); - int compsize = zerobit_decompress(snapshots[SNAP_INCOMMING], part_size, tmpbuffer); - //int compsize = lzw_decompress(snapshots[SNAP_INCOMMING],tmpbuffer); - int intsize = intpack_decompress(tmpbuffer, compsize, tmpbuffer2); - deltadata = tmpbuffer2; - deltasize = intsize; - } + snapshot *tmp = snapshots[SNAP_PREV]; + snapshots[SNAP_PREV] = snapshots[SNAP_CURRENT]; + snapshots[SNAP_CURRENT] = tmp; + current_tick = game_tick; + + // decompress snapshot + void *deltadata = snapshot_empty_delta(); + int deltasize = sizeof(int)*3; + + unsigned char tmpbuffer[MAX_SNAPSHOT_SIZE]; + unsigned char tmpbuffer2[MAX_SNAPSHOT_SIZE]; + if(part_size) + { + //int snapsize = lzw_decompress(snapshots[SNAP_INCOMMING], snapshots[SNAP_CURRENT]); + int compsize = zerobit_decompress(snapshots[SNAP_INCOMMING], part_size, tmpbuffer); + //int compsize = lzw_decompress(snapshots[SNAP_INCOMMING],tmpbuffer); + int intsize = intpack_decompress(tmpbuffer, compsize, tmpbuffer2); + deltadata = tmpbuffer2; + deltasize = intsize; + } - // find snapshot that we should use as delta - static snapshot emptysnap; - emptysnap.data_size = 0; - emptysnap.num_items = 0; - - snapshot *deltashot = &emptysnap; - int deltashot_size; + // find snapshot that we should use as delta + static snapshot emptysnap; + emptysnap.data_size = 0; + emptysnap.num_items = 0; + + snapshot *deltashot = &emptysnap; + int deltashot_size; - if(delta_tick >= 0) - { - void *delta_data; - deltashot_size = snapshots_new.get(delta_tick, &delta_data); - if(deltashot_size >= 0) + if(delta_tick >= 0) { - deltashot = (snapshot *)delta_data; + void *delta_data; + deltashot_size = snapshots_new.get(delta_tick, &delta_data); + if(deltashot_size >= 0) + { + deltashot = (snapshot *)delta_data; + } + else + { + // TODO: handle this + dbg_msg("client", "error, couldn't find the delta snapshot"); + } } - else + + int snapsize = snapshot_unpack_delta(deltashot, (snapshot*)snapshots[SNAP_CURRENT], deltadata, deltasize); + //snapshot *shot = (snapshot *)snapshots[SNAP_CURRENT]; + + // purge old snapshots + snapshots_new.purge_until(delta_tick); + snapshots_new.purge_until(game_tick-50); // TODO: change this to server tickrate + + // add new + snapshots_new.add(game_tick, snapsize, 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) { - // TODO: handle this - dbg_msg("client", "error, couldn't find the delta snapshot"); + local_start_time = time_get(); + set_state(STATE_ONLINE); } + + if(recived_snapshots > 2) + modc_newsnapshot(); + + snapshot_part = 0; + + // ack snapshot + msg_pack_start_system(NETMSG_SNAPACK, 0); + msg_pack_int(game_tick); + msg_pack_end(); + client_send_msg(); } - - int snapsize = snapshot_unpack_delta(deltashot, (snapshot*)snapshots[SNAP_CURRENT], deltadata, deltasize); - //snapshot *shot = (snapshot *)snapshots[SNAP_CURRENT]; - - // purge old snapshots - snapshots_new.purge_until(delta_tick); - snapshots_new.purge_until(game_tick-50); // TODO: change this to server tickrate - - // add new - snapshots_new.add(game_tick, snapsize, 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(); - + } + else + { + dbg_msg("client", "snapshot reset!"); snapshot_part = 0; - - // ack snapshot - msg_pack_start(NETMSG_SNAPACK, 0); - msg_pack_int(game_tick); - msg_pack_end(); - client_send_msg(); } } - else - { - dbg_msg("client", "snapshot reset!"); - snapshot_part = 0; - } + } + else + { + // game message + modc_message(msg); } } diff --git a/src/engine/client/gfx.cpp b/src/engine/client/gfx.cpp index ba908c68..c30ad0f1 100644 --- a/src/engine/client/gfx.cpp +++ b/src/engine/client/gfx.cpp @@ -617,7 +617,7 @@ 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_texture_set(current_font->font_texture); gfx_quads_begin(); while (*text) diff --git a/src/engine/interface.h b/src/engine/interface.h index 27f73e17..a3bb2e15 100644 --- a/src/engine/interface.h +++ b/src/engine/interface.h @@ -705,6 +705,7 @@ enum MSGFLAG_VITAL=1, }; +void msg_pack_start_system(int msg, int flags); void msg_pack_start(int msg, int flags); void msg_pack_int(int i); void msg_pack_string(const char *p, int limit); @@ -722,20 +723,23 @@ struct msg_info const msg_info *msg_get_info(); // message unpacking -int msg_unpack_start(const void *data, int data_size); +int msg_unpack_start(const void *data, int data_size, int *system); int msg_unpack_int(); const char *msg_unpack_string(); const unsigned char *msg_unpack_raw(int size); // message sending -int server_send_msg(int client_id); +int server_send_msg(int client_id); // client_id == -1 == broadcast int client_send_msg(); int client_tick(); float client_intratick(); +void gfx_pretty_text(float x, float y, float size, const char *text); +float gfx_pretty_text_width(float size, const char *text); -int modc_message(); +void mods_message(int msg, int client_id); +void modc_message(int msg); #define MASTER_SERVER_ADDRESS "master.teewars.com" #define MASTER_SERVER_PORT 8300 diff --git a/src/engine/msg.cpp b/src/engine/msg.cpp index 024bab2c..fa2dab34 100644 --- a/src/engine/msg.cpp +++ b/src/engine/msg.cpp @@ -10,13 +10,22 @@ void msg_pack_int(int i) { packer.add_int(i); } void msg_pack_string(const char *p, int limit) { packer.add_string(p, limit); } void msg_pack_raw(const void *data, int size) { packer.add_raw((const unsigned char *)data, size); } +void msg_pack_start_system(int msg, int flags) +{ + packer.reset(); + pack_info.msg = (msg<<1)|1; + pack_info.flags = flags; + + msg_pack_int(pack_info.msg); +} + void msg_pack_start(int msg, int flags) { packer.reset(); - pack_info.msg = msg; + pack_info.msg = msg<<1; pack_info.flags = flags; - msg_pack_int(msg); + msg_pack_int(pack_info.msg); } void msg_pack_end() @@ -32,10 +41,12 @@ const msg_info *msg_get_info() // message unpacking static data_unpacker unpacker; -int msg_unpack_start(const void *data, int data_size) +int msg_unpack_start(const void *data, int data_size, int *system) { unpacker.reset((const unsigned char *)data, data_size); - return msg_unpack_int(); + int msg = msg_unpack_int(); + *system = msg&1; + return msg>>1; } int msg_unpack_int() { return unpacker.get_int(); } diff --git a/src/engine/server/server.cpp b/src/engine/server/server.cpp index 5632689e..6fe84ade 100644 --- a/src/engine/server/server.cpp +++ b/src/engine/server/server.cpp @@ -107,8 +107,19 @@ int server_send_msg(int client_id) if(info->flags&MSGFLAG_VITAL) packet.flags = PACKETFLAG_VITAL; - - net.send(&packet); + + if(client_id == -1) + { + // broadcast + for(int i = 0; i < MAX_CLIENTS; i++) + if(clients[i].is_ingame()) + { + packet.client_id = i; + net.send(&packet); + } + } + else + net.send(&packet); return 0; } @@ -337,9 +348,9 @@ public: left -= chunk; if(numpackets == 1) - msg_pack_start(NETMSG_SNAPSMALL, 0); + msg_pack_start_system(NETMSG_SNAPSMALL, 0); else - msg_pack_start(NETMSG_SNAP, 0); + msg_pack_start_system(NETMSG_SNAP, 0); msg_pack_int(current_tick); msg_pack_int(current_tick-delta_tick); // compressed with msg_pack_int(chunk); @@ -352,7 +363,7 @@ public: } else { - msg_pack_start(NETMSG_SNAPEMPTY, 0); + msg_pack_start_system(NETMSG_SNAPEMPTY, 0); msg_pack_int(current_tick); msg_pack_int(current_tick-delta_tick); // compressed with msg_pack_end(); @@ -366,7 +377,7 @@ public: void send_map(int cid) { - msg_pack_start(NETMSG_MAP, MSGFLAG_VITAL); + msg_pack_start_system(NETMSG_MAP, MSGFLAG_VITAL); msg_pack_string(map_name, 0); msg_pack_end(); server_send_msg(cid); @@ -385,40 +396,49 @@ public: void process_client_packet(NETPACKET *packet) { int cid = packet->client_id; - int msg = msg_unpack_start(packet->data, packet->data_size); - if(msg == NETMSG_INFO) - { - strncpy(clients[cid].name, msg_unpack_string(), MAX_NAME_LENGTH); - strncpy(clients[cid].clan, msg_unpack_string(), MAX_CLANNAME_LENGTH); - const char *password = msg_unpack_string(); - const char *skin = msg_unpack_string(); - (void)password; // ignore these variables - (void)skin; - send_map(cid); - } - else if(msg == NETMSG_ENTERGAME) - { - dbg_msg("game", "player as entered the game. cid=%x", cid); - clients[cid].state = client::STATE_INGAME; - mods_client_enter(cid); - } - else if(msg == NETMSG_INPUT) - { - int input[MAX_INPUT_SIZE]; - int size = msg_unpack_int(); - for(int i = 0; i < size/4; i++) - input[i] = msg_unpack_int(); - mods_client_input(cid, input); - } - else if(msg == NETMSG_SNAPACK) + int sys; + int msg = msg_unpack_start(packet->data, packet->data_size, &sys); + if(sys) { - clients[cid].last_acked_snapshot = msg_unpack_int(); + // system message + if(msg == NETMSG_INFO) + { + strncpy(clients[cid].name, msg_unpack_string(), MAX_NAME_LENGTH); + strncpy(clients[cid].clan, msg_unpack_string(), MAX_CLANNAME_LENGTH); + const char *password = msg_unpack_string(); + const char *skin = msg_unpack_string(); + (void)password; // ignore these variables + (void)skin; + send_map(cid); + } + else if(msg == NETMSG_ENTERGAME) + { + dbg_msg("game", "player as entered the game. cid=%x", cid); + clients[cid].state = client::STATE_INGAME; + mods_client_enter(cid); + } + else if(msg == NETMSG_INPUT) + { + int input[MAX_INPUT_SIZE]; + int size = msg_unpack_int(); + for(int i = 0; i < size/4; i++) + input[i] = msg_unpack_int(); + mods_client_input(cid, input); + } + else if(msg == NETMSG_SNAPACK) + { + clients[cid].last_acked_snapshot = msg_unpack_int(); + } + else + { + dbg_msg("server", "strange message cid=%d msg=%d data_size=%d", cid, msg, packet->data_size); + } } else { - dbg_msg("server", "strange message cid=%d msg=%d data_size=%d", cid, msg, packet->data_size); + // game message + mods_message(msg, cid); } - } void process_packet(NETPACKET *packet) diff --git a/src/game/client/game_client.cpp b/src/game/client/game_client.cpp index 2f05aa77..361d938b 100644 --- a/src/game/client/game_client.cpp +++ b/src/game/client/game_client.cpp @@ -1,6 +1,8 @@ #include <baselib/math.h> +//#include <baselib/keys.h> #include <stdlib.h> #include <stdio.h> +#include <string.h> #include <engine/config.h> #include "../game.h" #include "mapres_image.h" @@ -17,6 +19,11 @@ static vec2 mouse_pos; static vec2 local_player_pos; static obj_player *local_player; +struct client_data +{ + char name[64]; +} client_datas[MAX_CLIENTS]; + inline float frandom() { return rand()/(float)(RAND_MAX); } void snd_play_random(int setid, float vol, float pan) @@ -144,28 +151,27 @@ void move_point(vec2 *inout_pos, vec2 *inout_vel, float elasticity) } } -class health_texts +class damage_indicators { public: int64 lastupdate; struct item { vec2 pos; - vec2 vel; - int amount; - int istar; + vec2 dir; float life; float startangle; }; enum { - MAX_ITEMS=16, + MAX_ITEMS=64, }; - health_texts() + damage_indicators() { lastupdate = 0; + num_items = 0; } item items[MAX_ITEMS]; @@ -188,54 +194,35 @@ public: *i = items[num_items]; } - void create(vec2 pos, int amount) + void create(vec2 pos, vec2 dir) { - amount = max(1,amount); - for (int j = 0; j < amount; j++) + item *i = create_i(); + if (i) { - //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()%2; - i->vel = vec2(((float)rand()/(float)RAND_MAX) * 50.0f,-150.0f); - i->startangle = (( (float)rand()/(float)RAND_MAX) - 1.0f) * 2.0f * pi; - } + i->pos = pos; + i->life = 0.75f; + i->dir = dir; + 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(data->images[IMAGE_PARTICLES].id); + gfx_texture_set(data->images[IMAGE_GAME].id); 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; + vec2 pos = mix(items[i].pos+items[i].dir*75.0f, items[i].pos, clamp((items[i].life-0.60f)/0.15f, 0.0f, 1.0f)); + + items[i].life -= client_frametime(); 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_setcolor(1.0f,1.0f,1.0f, items[i].life/0.1f); gfx_quads_setrotation(items[i].startangle + items[i].life * 2.0f); - float size = 64.0f; - const int stars[] = {SPRITE_STAR1, SPRITE_STAR2}; - select_sprite(stars[items[i].istar]); - gfx_quads_draw(items[i].pos.x-size/2, items[i].pos.y-size/2, size, size); + select_sprite(SPRITE_STAR1); + draw_sprite(pos.x, pos.y, 48.0f); i++; } } @@ -244,7 +231,7 @@ public: }; -static health_texts healthmods; +static damage_indicators damageind; class particle_system { @@ -350,6 +337,35 @@ public: static particle_system temp_system; + +static bool chat_active = false; +static char chat_input[512]; +static unsigned chat_input_len; +static const int chat_max_lines = 10; + +struct chatline +{ + int tick; + char text[512+64]; +}; + +chatline chat_lines[chat_max_lines]; +static int chat_current_line = 0; + +void chat_reset() +{ + for(int i = 0; i < chat_max_lines; i++) + chat_lines[i].tick = -1000000; + chat_current_line = 0; +} + +void chat_add_line(int client_id, const char *line) +{ + chat_current_line = (chat_current_line+1)%chat_max_lines; + chat_lines[chat_current_line].tick = client_tick(); + sprintf(chat_lines[chat_current_line].text, "%s: %s", client_datas[client_id].name, line); // TODO: abit nasty +} + void modc_init() { // load the data container @@ -370,6 +386,10 @@ void modc_entergame() col_init(32); img_init(); tilemap_init(); + chat_reset(); + + for(int i = 0; i < MAX_CLIENTS; i++) + client_datas[i].name[0] = 0; } void modc_shutdown() @@ -384,10 +404,10 @@ void modc_newsnapshot() snap_item item; void *data = snap_get_item(SNAP_CURRENT, i, &item); - if(item.type == EVENT_HEALTHMOD) + if(item.type == EVENT_DAMAGEINDICATION) { - ev_healthmod *ev = (ev_healthmod *)data; - healthmods.create(vec2(ev->x, ev->y), ev->amount); + ev_damageind *ev = (ev_damageind *)data; + damageind.create(vec2(ev->x, ev->y), get_direction(ev->angle)); } else if(item.type == EVENT_EXPLOSION) { @@ -567,6 +587,8 @@ struct animstate keyframe attach; }; + + static void anim_eval(animation *anim, float time, animstate *state) { anim_seq_eval(&anim->body, time, &state->body); @@ -597,8 +619,64 @@ static void anim_eval_add(animstate *state, animation *anim, float time, float a anim_add(state, &add, amount); } +static void render_tee(animstate *anim, int skin, vec2 dir, vec2 pos) +{ + vec2 direction = dir; + //float angle = info->angle; + vec2 position = pos; + + gfx_texture_set(data->images[IMAGE_CHAR_DEFAULT].id); + gfx_quads_begin(); + + // draw foots + for(int p = 0; p < 2; p++) + { + // first pass we draw the outline + // second pass we draw the filling + + int outline = p==0 ? 1 : 0; + int shift = charids[skin%16]; + + for(int f = 0; f < 2; f++) + { + float basesize = 10.0f; + if(f == 1) + { + // draw body + select_sprite(outline?SPRITE_TEE_BODY_OUTLINE:SPRITE_TEE_BODY, 0, 0, shift*4); + gfx_quads_draw(position.x+anim->body.x, position.y+anim->body.y, 4*basesize, 4*basesize); + + // draw eyes + if(p == 1) + { + // normal + select_sprite(SPRITE_TEE_EYE_NORMAL, 0, 0, shift*4); + gfx_quads_draw(position.x-4+direction.x*4, position.y-8+direction.y*3, basesize, basesize); + gfx_quads_draw(position.x+4+direction.x*4, position.y-8+direction.y*3, basesize, basesize); + } + } + + // draw feet + select_sprite(outline?SPRITE_TEE_FOOT_OUTLINE:SPRITE_TEE_FOOT, 0, 0, shift*4); + + keyframe *foot = f ? &anim->front_foot : &anim->back_foot; + + float w = basesize*2.5f; + float h = basesize*1.425f; + + gfx_quads_setrotation(foot->angle); + gfx_quads_draw(position.x+foot->x, position.y+foot->y, w, h); + } + } + + gfx_quads_end(); +} + static void render_player(obj_player *prev, obj_player *player) { + if(player->health < 0) // dont render dead players + return; + 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), client_intratick()); @@ -692,6 +770,9 @@ static void render_player(obj_player *prev, obj_player *player) gfx_quads_end(); } + render_tee(&state, player->clientid, direction, position); + + /* gfx_texture_set(data->images[IMAGE_CHAR_DEFAULT].id); gfx_quads_begin(); @@ -741,10 +822,60 @@ static void render_player(obj_player *prev, obj_player *player) } gfx_quads_end(); + */ } + + void modc_render() { + if(inp_key_down(input::enter)) + { + if(chat_active) + { + // send message + msg_pack_start(MSG_SAY, MSGFLAG_VITAL); + msg_pack_string(chat_input, 512); + msg_pack_end(); + client_send_msg(); + } + else + { + mem_zero(chat_input, sizeof(chat_input)); + chat_input_len = 0; + } + chat_active = !chat_active; + } + + if(chat_active) + { + int c = input::last_char(); // TODO: bypasses the engine interface + int k = input::last_key(); // TODO: bypasses the engine interface + + if (c >= 32 && c < 255) + { + if (chat_input_len < sizeof(chat_input) - 1) + { + chat_input[chat_input_len] = c; + chat_input[chat_input_len+1] = 0; + chat_input_len++; + } + } + + if(k == input::backspace) + { + if(chat_input_len > 0) + { + chat_input[chat_input_len-1] = 0; + chat_input_len--; + } + } + + } + + input::clear_char(); // TODO: bypasses the engine interface + input::clear_key(); // TODO: bypasses the engine interface + // fetch new input { int x, y; @@ -763,22 +894,26 @@ void modc_render() 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.left = inp_key_pressed(config.key_move_left); - input.right = inp_key_pressed(config.key_move_right); - input.jump = inp_key_pressed(config.key_jump); - input.fire = inp_key_pressed(config.key_fire); - input.hook = inp_key_pressed(config.key_hook); - - input.blink = inp_key_pressed('S'); - - // Weapon switching input.activeweapon = -1; - input.activeweapon = inp_key_pressed('1') ? 0 : input.activeweapon; - input.activeweapon = inp_key_pressed('2') ? 1 : input.activeweapon; - input.activeweapon = inp_key_pressed('3') ? 2 : input.activeweapon; - input.activeweapon = inp_key_pressed('4') ? 3 : input.activeweapon; + + if(!chat_active) + { + input.left = inp_key_pressed(config.key_move_left); + input.right = inp_key_pressed(config.key_move_right); + input.jump = inp_key_pressed(config.key_jump); + input.fire = inp_key_pressed(config.key_fire); + input.hook = inp_key_pressed(config.key_hook); + + input.blink = inp_key_pressed('S'); + + // Weapon switching + input.activeweapon = inp_key_pressed('1') ? 0 : input.activeweapon; + input.activeweapon = inp_key_pressed('2') ? 1 : input.activeweapon; + input.activeweapon = inp_key_pressed('3') ? 2 : input.activeweapon; + input.activeweapon = inp_key_pressed('4') ? 3 : input.activeweapon; + } snap_input(&input, sizeof(input)); } @@ -811,7 +946,7 @@ void modc_render() } // pseudo format - float zoom = inp_key_pressed('T') ? 1.0 : 3.0f; + float zoom = 3.0f; float width = 400*zoom; float height = 300*zoom; @@ -907,8 +1042,8 @@ void modc_render() tilemap_render(32.0f, 1); - // render health mods - healthmods.render(); + // render damage indications + damageind.render(); if(local_player) { @@ -958,14 +1093,73 @@ void modc_render() // render gui stuff gfx_mapscreen(0,0,400,300); + { + float x = 10.0f; + float y = 300.0f-50.0f; + float starty = -1; + if(chat_active) + { + + gfx_texture_set(-1); // TODO: remove when the font looks better + gfx_quads_begin(); + gfx_quads_setcolor(0,0,0,0.4f); + gfx_quads_drawTL(x-2, y+1, 300, 8); + gfx_quads_end(); + + // render chat input + char buf[sizeof(chat_input)+16]; + sprintf(buf, "Chat: %s_", chat_input); + gfx_pretty_text(x, y, 10, buf); + starty = y; + } + + y -= 10; + + int i; + for(i = 0; i < chat_max_lines; i++) + { + int r = ((chat_current_line-i)+chat_max_lines)%chat_max_lines; + if(client_tick() > chat_lines[r].tick+50*15) + break; + + gfx_texture_set(-1); // TODO: remove when the font looks better + gfx_quads_begin(); + gfx_quads_setcolor(0,0,0,0.4f); + gfx_quads_drawTL(x-2, y+1, gfx_pretty_text_width(10, chat_lines[r].text)+3, 8); + gfx_quads_end(); + + gfx_pretty_text(x, y, 10, chat_lines[r].text); + y -= 8; + } + } + // render score board if(inp_key_pressed(baselib::input::tab)) { + gfx_mapscreen(0, 0, width, height); + + float x = 50.0f; + float y = 150.0f; + + gfx_blend_normal(); + gfx_texture_set(-1); - gfx_quads_text(10, 50, 8, "Score Board"); + gfx_quads_begin(); + gfx_quads_setcolor(0,0,0,0.5f); + gfx_quads_drawTL(x-10.f, y-10.f, 400.0f, 600.0f); + gfx_quads_end(); + + //gfx_texture_set(current_font->font_texture); + gfx_pretty_text(x, y, 64, "Score Board"); + y += 100.0f; + + //gfx_texture_set(-1); + //gfx_quads_text(10, 50, 8, "Score Board"); + animstate state; + anim_eval(&data->animations[ANIM_BASE], 0, &state); + anim_eval_add(&state, &data->animations[ANIM_IDLE], 0, 1.0f); int num = snap_num_items(SNAP_CURRENT); - int row = 1; for(int i = 0; i < num; i++) { snap_item item; @@ -977,13 +1171,31 @@ void modc_render() if(player) { char buf[128]; - char name[32] = "tjo"; - //snap_decode_string(player->name, name, 32); - sprintf(buf, "%4d %s", player->score, name); - gfx_quads_text(10, 50 + 10 * row, 8, buf); - row++; + sprintf(buf, "%4d", player->score); + gfx_pretty_text(x+60-gfx_pretty_text_width(48,buf), y, 48, buf); + gfx_pretty_text(x+128, y, 48, client_datas[player->clientid].name); + + render_tee(&state, player->clientid, vec2(1,0), vec2(x+90, y+24)); + y += 58.0f; } } } } } + +void modc_message(int msg) +{ + if(msg == MSG_CHAT) + { + int cid = msg_unpack_int(); + const char *message = msg_unpack_string(); + dbg_msg("message", "chat cid=%d msg='%s'", cid, message); + chat_add_line(cid, message); + } + else if(msg == MSG_SETNAME) + { + int cid = msg_unpack_int(); + const char *name = msg_unpack_string(); + strncpy(client_datas[cid].name, name, 64); + } +} diff --git a/src/game/game.h b/src/game/game.h index 9b21a4ed..f9f41d13 100644 --- a/src/game/game.h +++ b/src/game/game.h @@ -30,13 +30,21 @@ enum OBJTYPE_PROJECTILE, OBJTYPE_POWERUP, EVENT_EXPLOSION, - EVENT_HEALTHMOD, + EVENT_DAMAGEINDICATION, EVENT_SOUND, EVENT_SMOKE, }; enum { + MSG_NULL=0, + MSG_SAY, + MSG_CHAT, + MSG_SETNAME, +}; + +enum +{ EMOTE_NORMAL=0, EMOTE_BLINK, EMOTE_WINK, @@ -67,11 +75,10 @@ struct ev_sound int sound; // if (0x80000000 flag is set -> looping) if (0x40000000 is set -> stop looping }; -struct ev_healthmod +struct ev_damageind { - int x, y; // could perhaps be an client id - int vx, vy; // should be an angle instead - int amount; + int x, y; + int angle; }; struct obj_projectile @@ -91,7 +98,6 @@ struct obj_powerup struct obj_player { //int name[8]; - int local; int clientid; @@ -106,7 +112,6 @@ struct obj_player // current active weapon int weapon; // current active modifier - //int modifier; // num attack ticks left of current attack int attacktick; diff --git a/src/game/server/game_server.cpp b/src/game/server/game_server.cpp index f86b56af..4b06470e 100644 --- a/src/game/server/game_server.cpp +++ b/src/game/server/game_server.cpp @@ -5,8 +5,8 @@ using namespace baselib; -// --------- -const bool debug_bots = false; +// --------- DEBUG STUFF --------- +const bool debug_bots = true; // --------- PHYSICS TWEAK! -------- const float ground_control_speed = 7.0f; @@ -23,7 +23,7 @@ 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_damageind(vec2 p, vec2 dir, 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); @@ -175,6 +175,10 @@ private: friend class player; entity *prev_entity; entity *next_entity; + + entity *prev_type_entity; + entity *next_type_entity; + int index; static int current_id; protected: @@ -189,19 +193,28 @@ public: enum { FLAG_DESTROY=0x00000001, + FLAG_ALIVE=0x00000002, }; - + entity(int objtype) { this->objtype = objtype; pos = vec2(0,0); - flags = 0; + flags = FLAG_ALIVE; proximity_radius = 0; current_id++; id = current_id; + + next_entity = 0; + prev_entity = 0; + prev_type_entity = 0; + next_type_entity = 0; } + void set_flag(unsigned flag) { flags |= flag; } + void clear_flag(unsigned flag) { flags &= ~flag; } + virtual ~entity() { } @@ -221,10 +234,19 @@ int entity::current_id = 1; class game_world { public: + enum + { + NUM_ENT_TYPES=10, + }; + entity *first_entity; + entity *first_entity_types[NUM_ENT_TYPES]; + game_world() { first_entity = 0x0; + for(int i = 0; i < NUM_ENT_TYPES; i++) + first_entity_types[i] = 0; } int find_entities(vec2 pos, float radius, entity **ents, int max) @@ -232,6 +254,9 @@ public: int num = 0; for(entity *ent = first_entity; ent; ent = ent->next_entity) { + if(!(ent->flags&entity::FLAG_ALIVE)) + continue; + if(distance(ent->pos, pos) < radius+ent->proximity_radius) { ents[num] = ent; @@ -247,14 +272,13 @@ public: 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 t = 0; t < maxtypes; t++) { - for (int i = 0; i < maxtypes; i++) + for(entity *ent = first_entity_types[types[t]]; ent; ent = ent->next_type_entity) { - if (ent->objtype != types[i]) + if(!(ent->flags&entity::FLAG_ALIVE)) continue; - - // TODO: this seams like it could be done several times unnessesary + if(distance(ent->pos, pos) < radius+ent->proximity_radius) { ents[num] = ent; @@ -276,11 +300,18 @@ public: ent->next_entity = first_entity; ent->prev_entity = 0x0; first_entity = ent; + + // into typelist aswell + if(first_entity_types[ent->objtype]) + first_entity_types[ent->objtype]->prev_type_entity = ent; + ent->next_type_entity = first_entity_types[ent->objtype]; + ent->prev_type_entity = 0x0; + first_entity_types[ent->objtype] = ent; } void destroy_entity(entity *ent) { - ent->flags |= entity::FLAG_DESTROY; + ent->set_flag(entity::FLAG_DESTROY); } void remove_entity(entity *ent) @@ -292,6 +323,13 @@ public: first_entity = ent->next_entity; if(ent->next_entity) ent->next_entity->prev_entity = ent->prev_entity; + + if(ent->prev_type_entity) + ent->prev_type_entity->next_type_entity = ent->next_type_entity; + else + first_entity_types[ent->objtype] = ent->next_type_entity; + if(ent->next_type_entity) + ent->next_type_entity->prev_type_entity = ent->prev_type_entity; } // @@ -394,7 +432,7 @@ public: if (flags & PROJECTILE_FLAGS_EXPLODE) create_explosion(oldpos, owner); else if (targetplayer) - targetplayer->take_damage(normalize(vel) * force, damage, owner); + targetplayer->take_damage(normalize(vel) * max(0.001f, force), damage, owner); world.destroy_entity(this); } @@ -442,7 +480,7 @@ public: // int client_id; - char name[32]; + char name[64]; // input player_input previnput; @@ -455,6 +493,9 @@ public: int armor; int score; + + bool dead; + int die_tick; // hooking stuff enum @@ -496,6 +537,8 @@ public: direction = vec2(0.0f, 1.0f); client_id = -1; score = 0; + dead = true; + die_tick = 0; } virtual void destroy() { } @@ -505,6 +548,8 @@ public: health = PLAYER_MAXHEALTH; armor = 0; jumped = 0; + dead = false; + set_flag(entity::FLAG_ALIVE); mem_zero(&input, sizeof(input)); vel = vec2(0.0f, 0.0f); @@ -520,6 +565,7 @@ public: } else pos = vec2(100.0f, -60.0f); + defered_pos = pos; // init weapons mem_zero(&weapons, sizeof(weapons)); @@ -638,6 +684,14 @@ public: { // TODO: rework the input to be more robust // TODO: remove this tick count, it feels weird + if(dead) + { + if(server_tick()-die_tick >= server_tickspeed()*3) // auto respawn after 3 sec + respawn(); + if(input.fire && server_tick()-die_tick >= server_tickspeed()/2) // auto respawn after 0.5 sec + respawn(); + return; + } // fetch some info bool grounded = is_grounded(); @@ -796,7 +850,9 @@ public: release_hooks(); // TODO: insert timer here - respawn(); + dead = true; + die_tick = server_tick(); + clear_flag(entity::FLAG_ALIVE); } virtual bool take_damage(vec2 force, int dmg, int from) @@ -819,7 +875,7 @@ public: armor -= dmg; // create healthmod indicator - create_healthmod(pos, dmg); + create_damageind(pos, normalize(force), dmg); damage_taken_tick = server_tick()+50; @@ -876,6 +932,9 @@ public: player->armor = armor; } + if(dead) + player->health = -1; + if(length(vel) > 15.0f) player->emote = EMOTE_HAPPY; @@ -981,20 +1040,26 @@ void powerup::snap(int snapping_client) // POWERUP END /////////////////////// -static const int NUM_BOTS = 1; -static player players[MAX_CLIENTS+NUM_BOTS]; +static player players[MAX_CLIENTS]; player *get_player(int index) { return &players[index]; } -void create_healthmod(vec2 p, int amount) +void create_damageind(vec2 p, vec2 dir, 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; + float a = get_angle(dir); + float s = a-pi/3; + float e = a+pi/3; + for(int i = 0; i < amount; i++) + { + float f = mix(s, e, float(i+1)/float(amount+2)); + ev_damageind *ev = (ev_damageind *)events.create(EVENT_DAMAGEINDICATION, sizeof(ev_damageind)); + ev->x = (int)p.x; + ev->y = (int)p.y; + ev->angle = (int)(f*256.0f); + } } void create_explosion(vec2 p, int owner, bool bnodamage) @@ -1052,13 +1117,13 @@ player* intersect_player(vec2 pos0, vec2 pos1, vec2& new_pos, entity* notthis) 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); + const int types[] = {OBJTYPE_PLAYER}; + int num = world.find_entities(center, radius, ents, 64, types, 1); for (int i = 0; i < num; i++) { // Check if entity is a player - if (ents[i] != notthis && ents[i]->objtype == OBJTYPE_PLAYER) + if (ents[i] != notthis) { - // temp, set hook pos to our position new_pos = ents[i]->pos; return (player*)ents[i]; } @@ -1082,8 +1147,11 @@ void mods_tick() count++; if(count == 10) { - for(int i = 0; i < NUM_BOTS; i++) - mods_client_enter(MAX_CLIENTS+i); + for(int i = 0; i < 1; i++) + { + mods_client_enter(MAX_CLIENTS-i-1); + strcpy(players[MAX_CLIENTS-i-1].name, "(bot)"); + } count = -1; } } @@ -1108,6 +1176,30 @@ void mods_client_enter(int client_id) players[client_id].client_id = client_id; players[client_id].respawn(); world.insert_entity(&players[client_id]); + + client_info info; // fetch login name + if(server_getclientinfo(client_id, &info)) + strcpy(players[client_id].name, info.name); + else + strcpy(players[client_id].name, "(bot)"); + + msg_pack_start(MSG_SETNAME, MSGFLAG_VITAL); + msg_pack_int(client_id); + msg_pack_string(players[client_id].name, 64); + msg_pack_end(); + server_send_msg(-1); + + for(int i = 0; i < MAX_CLIENTS; i++) + { + if(players[client_id].client_id != -1) + { + msg_pack_start(MSG_SETNAME, MSGFLAG_VITAL); + msg_pack_int(i); + msg_pack_string(players[i].name, 64); + msg_pack_end(); + server_send_msg(client_id); + } + } } void mods_client_drop(int client_id) @@ -1116,6 +1208,18 @@ void mods_client_drop(int client_id) world.remove_entity(&players[client_id]); } +void mods_message(int msg, int client_id) +{ + if(msg == MSG_SAY) + { + msg_pack_start(MSG_CHAT, MSGFLAG_VITAL); + msg_pack_int(client_id); + msg_pack_string(msg_unpack_string(), 512); + msg_pack_end(); + server_send_msg(-1); + } +} + void mods_init() { col_init(32); |