diff options
| author | Magnus Auvinen <magnus.auvinen@gmail.com> | 2008-07-06 11:21:21 +0000 |
|---|---|---|
| committer | Magnus Auvinen <magnus.auvinen@gmail.com> | 2008-07-06 11:21:21 +0000 |
| commit | 9d632dd826c8a312095de0f56df66b2580d336cb (patch) | |
| tree | 3fdde543c94323d6c698d278a58bf18e3c385776 /src/game/server/gs_server.cpp | |
| parent | 3705064b109580103a3d13f44693503da9927281 (diff) | |
| download | zcatch-9d632dd826c8a312095de0f56df66b2580d336cb.tar.gz zcatch-9d632dd826c8a312095de0f56df66b2580d336cb.zip | |
major update. server clean up and much added documentation
Diffstat (limited to 'src/game/server/gs_server.cpp')
| -rw-r--r-- | src/game/server/gs_server.cpp | 1641 |
1 files changed, 186 insertions, 1455 deletions
diff --git a/src/game/server/gs_server.cpp b/src/game/server/gs_server.cpp index 4e7b4179..5f5662d0 100644 --- a/src/game/server/gs_server.cpp +++ b/src/game/server/gs_server.cpp @@ -14,32 +14,12 @@ #include "gs_game_dm.hpp" TUNING_PARAMS tuning; +GAMECONTEXT game; -void create_damageind(vec2 p, float angle_mod, int amount); -void create_explosion(vec2 p, int owner, int weapon, bool bnodamage); -void create_smoke(vec2 p); -void create_playerspawn(vec2 p); -void create_death(vec2 p, int who); -void create_sound(vec2 pos, int sound, int mask=-1); - -PLAYER *get_player(int index); -class PLAYER *intersect_player(vec2 pos0, vec2 pos1, float radius, vec2 &new_pos, class ENTITY *notthis = 0); -class PLAYER *closest_player(vec2 pos, float radius, ENTITY *notthis); - -GAMEWORLD *world; - -enum -{ - CHAT_ALL=-2, - CHAT_SPEC=-1, - CHAT_RED=0, - CHAT_BLUE=1 -}; - -static void send_chat(int cid, int team, const char *text) +void GAMECONTEXT::send_chat(int chatter_cid, int team, const char *text) { - if(cid >= 0 && cid < MAX_CLIENTS) - dbg_msg("chat", "%d:%d:%s: %s", cid, team, server_clientname(cid), text); + if(chatter_cid >= 0 && chatter_cid < MAX_CLIENTS) + dbg_msg("chat", "%d:%d:%s: %s", chatter_cid, team, server_clientname(chatter_cid), text); else dbg_msg("chat", "*** %s", text); @@ -47,7 +27,7 @@ static void send_chat(int cid, int team, const char *text) { NETMSG_SV_CHAT msg; msg.team = 0; - msg.cid = cid; + msg.cid = chatter_cid; msg.message = text; msg.pack(MSGFLAG_VITAL); server_send_msg(-1); @@ -56,20 +36,20 @@ static void send_chat(int cid, int team, const char *text) { NETMSG_SV_CHAT msg; msg.team = 1; - msg.cid = cid; + msg.cid = chatter_cid; msg.message = text; msg.pack(MSGFLAG_VITAL); for(int i = 0; i < MAX_CLIENTS; i++) { - if(players[i].client_id != -1 && players[i].team == team) + if(game.players[i].client_id != -1 && game.players[i].team == team) server_send_msg(i); } } } -void send_info(int who, int to_who) +void GAMECONTEXT::send_info(int who, int to_who) { NETMSG_SV_SETINFO msg; msg.cid = who; @@ -77,13 +57,13 @@ void send_info(int who, int to_who) msg.skin = players[who].skin_name; msg.use_custom_color = players[who].use_custom_color; msg.color_body = players[who].color_body; - msg.color_feet =players[who].color_feet; + msg.color_feet = players[who].color_feet; msg.pack(MSGFLAG_VITAL); server_send_msg(to_who); } -void send_emoticon(int cid, int emoticon) +void GAMECONTEXT::send_emoticon(int cid, int emoticon) { NETMSG_SV_EMOTICON msg; msg.cid = cid; @@ -92,7 +72,7 @@ void send_emoticon(int cid, int emoticon) server_send_msg(-1); } -void send_weapon_pickup(int cid, int weapon) +void GAMECONTEXT::send_weapon_pickup(int cid, int weapon) { NETMSG_SV_WEAPONPICKUP msg; msg.weapon = weapon; @@ -101,7 +81,7 @@ void send_weapon_pickup(int cid, int weapon) } -void send_broadcast(const char *text, int cid) +void GAMECONTEXT::send_broadcast(const char *text, int cid) { NETMSG_SV_BROADCAST msg; msg.message = text; @@ -159,7 +139,7 @@ void EVENT_HANDLER::snap(int snapping_client) if(cmask_is_set(client_masks[i], snapping_client)) { NETEVENT_COMMON *ev = (NETEVENT_COMMON *)&data[offsets[i]]; - if(distance(players[snapping_client].pos, vec2(ev->x, ev->y)) < 1500.0f) + if(distance(game.players[snapping_client].view_pos, vec2(ev->x, ev->y)) < 1500.0f) { void *d = snap_new_item(types[i], i, sizes[i]); mem_copy(d, &data[offsets[i]], sizes[i]); @@ -168,8 +148,6 @@ void EVENT_HANDLER::snap(int snapping_client) } } -EVENT_HANDLER events; - ////////////////////////////////////////////////// // Entity ////////////////////////////////////////////////// @@ -177,9 +155,9 @@ ENTITY::ENTITY(int objtype) { this->objtype = objtype; pos = vec2(0,0); - flags = FLAG_PHYSICS; proximity_radius = 0; + marked_for_destroy = false; id = snap_new_id(); next_entity = 0; @@ -190,7 +168,7 @@ ENTITY::ENTITY(int objtype) ENTITY::~ENTITY() { - world->remove_entity(this); + game.world.remove_entity(this); snap_free_id(id); } @@ -213,14 +191,18 @@ GAMEWORLD::~GAMEWORLD() delete first_entity; } -int GAMEWORLD::find_entities(vec2 pos, float radius, ENTITY **ents, int max) +ENTITY *GAMEWORLD::find_first(int type) +{ + return first_entity_types[type]; +} + + +int GAMEWORLD::find_entities(vec2 pos, float radius, ENTITY **ents, int max, int type) { int num = 0; - for(ENTITY *ent = first_entity; ent; ent = ent->next_entity) + for(ENTITY *ent = (type<0) ? first_entity : first_entity_types[type]; + ent; ent = (type<0) ? ent->next_entity : ent->next_type_entity) { - if(!(ent->flags&ENTITY::FLAG_PHYSICS)) - continue; - if(distance(ent->pos, pos) < radius+ent->proximity_radius) { ents[num] = ent; @@ -233,29 +215,6 @@ int GAMEWORLD::find_entities(vec2 pos, float radius, ENTITY **ents, int max) return num; } -int GAMEWORLD::find_entities(vec2 pos, float radius, ENTITY **ents, int max, const int* types, int maxtypes) -{ - int num = 0; - for(int t = 0; t < maxtypes; t++) - { - for(ENTITY *ent = first_entity_types[types[t]]; ent; ent = ent->next_type_entity) - { - if(!(ent->flags&ENTITY::FLAG_PHYSICS)) - continue; - - if(distance(ent->pos, pos) < radius+ent->proximity_radius) - { - ents[num] = ent; - num++; - if(num == max) - break; - } - } - } - - return num; -} - void GAMEWORLD::insert_entity(ENTITY *ent) { ENTITY *cur = first_entity; @@ -282,7 +241,7 @@ void GAMEWORLD::insert_entity(ENTITY *ent) void GAMEWORLD::destroy_entity(ENTITY *ent) { - ent->set_flag(ENTITY::FLAG_DESTROY); + ent->marked_for_destroy = true; } void GAMEWORLD::remove_entity(ENTITY *ent) @@ -326,8 +285,7 @@ void GAMEWORLD::reset() ent->reset(); remove_entities(); - for(ENTITY *ent = first_entity; ent; ent = ent->next_entity) - ent->post_reset(); + game.controller->post_reset(); remove_entities(); reset_requested = false; @@ -340,7 +298,7 @@ void GAMEWORLD::remove_entities() while(ent) { ENTITY *next = ent->next_entity; - if(ent->flags&ENTITY::FLAG_DESTROY) + if(ent->marked_for_destroy) { remove_entity(ent); ent->destroy(); @@ -356,86 +314,17 @@ void GAMEWORLD::tick() if(!paused) { - /* - static PERFORMACE_INFO scopes[OBJTYPE_FLAG+1] = - { - {"null", 0}, - {"game", 0}, - {"player_info", 0}, - {"player_character", 0}, - {"projectile", 0}, - {"powerup", 0}, - {"flag", 0} - }; - - static PERFORMACE_INFO scopes_def[OBJTYPE_FLAG+1] = - { - {"null", 0}, - {"game", 0}, - {"player_info", 0}, - {"player_character", 0}, - {"projectile", 0}, - {"powerup", 0}, - {"flag", 0} - }; - - static PERFORMACE_INFO tick_scope = {"tick", 0}; - perf_start(&tick_scope);*/ - // update all objects for(ENTITY *ent = first_entity; ent; ent = ent->next_entity) - { - /*if(ent->objtype >= 0 && ent->objtype < OBJTYPE_FLAG) - perf_start(&scopes[ent->objtype]);*/ ent->tick(); - /*if(ent->objtype >= 0 && ent->objtype < OBJTYPE_FLAG) - perf_end();*/ - } - /* - perf_end(); - - static PERFORMACE_INFO deftick_scope = {"tick_defered", 0}; - perf_start(&deftick_scope);*/ for(ENTITY *ent = first_entity; ent; ent = ent->next_entity) - { - /*if(ent->objtype >= 0 && ent->objtype < OBJTYPE_FLAG) - perf_start(&scopes_def[ent->objtype]);*/ ent->tick_defered(); - /*if(ent->objtype >= 0 && ent->objtype < OBJTYPE_FLAG) - perf_end();*/ - } - /*perf_end();*/ } remove_entities(); } -struct INPUT_COUNT -{ - int presses; - int releases; -}; - -static INPUT_COUNT count_input(int prev, int cur) -{ - INPUT_COUNT c = {0,0}; - prev &= INPUT_STATE_MASK; - cur &= INPUT_STATE_MASK; - int i = prev; - while(i != cur) - { - i = (i+1)&INPUT_STATE_MASK; - if(i&1) - c.presses++; - else - c.releases++; - } - - return c; -} - - ////////////////////////////////////////////////// // projectile ////////////////////////////////////////////////// @@ -456,12 +345,12 @@ PROJECTILE::PROJECTILE(int type, int owner, vec2 pos, vec2 dir, int span, ENTITY this->weapon = weapon; this->bounce = 0; this->start_tick = server_tick(); - world->insert_entity(this); + game.world.insert_entity(this); } void PROJECTILE::reset() { - world->destroy_entity(this); + game.world.destroy_entity(this); } vec2 PROJECTILE::get_pos(float time) @@ -501,20 +390,20 @@ void PROJECTILE::tick() int collide = col_intersect_line(prevpos, curpos, &curpos); //int collide = col_check_point((int)curpos.x, (int)curpos.y); - ENTITY *targetplayer = (ENTITY*)intersect_player(prevpos, curpos, 6.0f, curpos, powner); - if(targetplayer || collide || lifespan < 0) + CHARACTER *targetchr = game.world.intersect_character(prevpos, curpos, 6.0f, curpos, powner); + if(targetchr || collide || lifespan < 0) { - if (lifespan >= 0 || weapon == WEAPON_GRENADE) - create_sound(curpos, sound_impact); + if(lifespan >= 0 || weapon == WEAPON_GRENADE) + game.create_sound(curpos, sound_impact); - if (flags & PROJECTILE_FLAGS_EXPLODE) - create_explosion(curpos, owner, weapon, false); - else if (targetplayer) + if(flags & PROJECTILE_FLAGS_EXPLODE) + game.create_explosion(curpos, owner, weapon, false); + else if(targetchr) { - targetplayer->take_damage(direction * max(0.001f, force), damage, owner, weapon); + targetchr->take_damage(direction * max(0.001f, force), damage, owner, weapon); } - world->destroy_entity(this); + game.world.destroy_entity(this); } } @@ -532,7 +421,7 @@ void PROJECTILE::snap(int snapping_client) { float ct = (server_tick()-start_tick)/(float)server_tickspeed(); - if(distance(players[snapping_client].pos, get_pos(ct)) > 1000.0f) + if(distance(game.players[snapping_client].view_pos, get_pos(ct)) > 1000.0f) return; NETOBJ_PROJECTILE *proj = (NETOBJ_PROJECTILE *)snap_new_item(NETOBJTYPE_PROJECTILE, id, sizeof(NETOBJ_PROJECTILE)); @@ -543,7 +432,7 @@ void PROJECTILE::snap(int snapping_client) ////////////////////////////////////////////////// // laser ////////////////////////////////////////////////// -LASER::LASER(vec2 pos, vec2 direction, float start_energy, PLAYER *owner) +LASER::LASER(vec2 pos, vec2 direction, float start_energy, CHARACTER *owner) : ENTITY(NETOBJTYPE_LASER) { this->pos = pos; @@ -553,21 +442,21 @@ LASER::LASER(vec2 pos, vec2 direction, float start_energy, PLAYER *owner) bounces = 0; do_bounce(); - world->insert_entity(this); + game.world.insert_entity(this); } -bool LASER::hit_player(vec2 from, vec2 to) +bool LASER::hit_character(vec2 from, vec2 to) { vec2 at; - PLAYER *hit = intersect_player(pos, to, 0.0f, at, owner); + CHARACTER *hit = game.world.intersect_character(pos, to, 0.0f, at, owner); if(!hit) return false; this->from = from; pos = at; energy = -1; - hit->take_damage(vec2(0,0), tuning.laser_damage, owner->client_id, WEAPON_RIFLE); + hit->take_damage(vec2(0,0), tuning.laser_damage, owner->player->client_id, WEAPON_RIFLE); return true; } @@ -578,7 +467,7 @@ void LASER::do_bounce() if(energy < 0) { //dbg_msg("laser", "%d removed", server_tick()); - world->destroy_entity(this); + game.world.destroy_entity(this); return; } @@ -586,7 +475,7 @@ void LASER::do_bounce() if(col_intersect_line(pos, to, &to)) { - if(!hit_player(pos, to)) + if(!hit_character(pos, to)) { // intersected from = pos; @@ -604,12 +493,12 @@ void LASER::do_bounce() if(bounces > tuning.laser_bounce_num) energy = -1; - create_sound(pos, SOUND_RIFLE_BOUNCE); + game.create_sound(pos, SOUND_RIFLE_BOUNCE); } } else { - if(!hit_player(pos, to)) + if(!hit_character(pos, to)) { from = pos; pos = to; @@ -622,7 +511,7 @@ void LASER::do_bounce() void LASER::reset() { - world->destroy_entity(this); + game.world.destroy_entity(this); } void LASER::tick() @@ -636,7 +525,7 @@ void LASER::tick() void LASER::snap(int snapping_client) { - if(distance(players[snapping_client].pos, pos) > 1000.0f) + if(distance(game.players[snapping_client].view_pos, pos) > 1000.0f) return; NETOBJ_LASER *obj = (NETOBJ_LASER *)snap_new_item(NETOBJTYPE_LASER, id, sizeof(NETOBJ_LASER)); @@ -647,1193 +536,20 @@ void LASER::snap(int snapping_client) obj->start_tick = eval_tick; } - -////////////////////////////////////////////////// -// player -////////////////////////////////////////////////// -// TODO: move to separate file -PLAYER::PLAYER() -: ENTITY(NETOBJTYPE_PLAYER_CHARACTER) -{ - init(); -} - -void PLAYER::init() -{ - proximity_radius = phys_size; - client_id = -1; - team = -1; // -1 == spectator - - latency_accum = 0; - latency_accum_min = 0; - latency_accum_max = 0; - latency_avg = 0; - latency_min = 0; - latency_max = 0; - - reset(); -} - -void PLAYER::reset() -{ - pos = vec2(0.0f, 0.0f); - core.reset(); - - emote_type = 0; - emote_stop = -1; - - //direction = vec2(0.0f, 1.0f); - score = 0; - dead = true; - clear_flag(ENTITY::FLAG_PHYSICS); - spawning = false; - die_tick = 0; - die_pos = vec2(0,0); - damage_taken = 0; - last_chat = 0; - player_state = PLAYERSTATE_UNKNOWN; - - mem_zero(&input, sizeof(input)); - mem_zero(&previnput, sizeof(previnput)); - num_inputs = 0; - - last_action = -1; - - emote_stop = 0; - damage_taken_tick = 0; - attack_tick = 0; - - mem_zero(&ninja, sizeof(ninja)); - - active_weapon = WEAPON_GUN; - last_weapon = WEAPON_HAMMER; - queued_weapon = -1; -} - -void PLAYER::destroy() { } - -void PLAYER::set_weapon(int w) -{ - if(w == active_weapon) - return; - - last_weapon = active_weapon; - queued_weapon = -1; - active_weapon = w; - if(active_weapon < 0 || active_weapon >= NUM_WEAPONS) - active_weapon = 0; - - create_sound(pos, SOUND_WEAPON_SWITCH); -} - -void PLAYER::respawn() -{ - spawning = true; -} - -const char *get_team_name(int team) +GAMECONTEXT::GAMECONTEXT() { - if(gamecontroller->gametype == GAMETYPE_DM) - { - if(team == 0) - return "game"; - } - else - { - if(team == 0) - return "red team"; - else if(team == 1) - return "blue team"; - } - - return "spectators"; + clear(); } -void PLAYER::set_team(int new_team) +void GAMECONTEXT::clear() { - // clamp the team - new_team = gamecontroller->clampteam(new_team); - if(team == new_team) - return; - - char buf[512]; - str_format(buf, sizeof(buf), "%s joined the %s", server_clientname(client_id), get_team_name(new_team)); - send_chat(-1, CHAT_ALL, buf); - - die(client_id, -1); - team = new_team; - score = 0; - dbg_msg("game", "team_join player='%d:%s' team=%d", client_id, server_clientname(client_id), team); - - gamecontroller->on_player_info_change(&players[client_id]); - - // send all info to this client + // reset all players for(int i = 0; i < MAX_CLIENTS; i++) - { - if(players[i].client_id != -1) - send_info(i, -1); - } -} - -vec2 spawn_points[3][64]; -int num_spawn_points[3] = {0}; - -struct SPAWNEVAL -{ - SPAWNEVAL() - { - got = false; - friendly_team = -1; - die_pos = vec2(0,0); - pos = vec2(100,100); - } - - vec2 pos; - bool got; - int friendly_team; - float score; - vec2 die_pos; -}; - -static float evaluate_spawn(SPAWNEVAL *eval, vec2 pos) -{ - float score = 0.0f; - - for(int c = 0; c < MAX_CLIENTS; c++) - { - if(players[c].client_id == -1) - continue; - - // don't count dead people - if(!(players[c].flags&ENTITY::FLAG_PHYSICS)) - continue; - - // team mates are not as dangerous as enemies - float scoremod = 1.0f; - if(eval->friendly_team != -1 && players[c].team == eval->friendly_team) - scoremod = 0.5f; - - float d = distance(pos, players[c].pos); - if(d == 0) - score += 1000000000.0f; - else - score += 1.0f/d; - } - - // weight in the die posititon - float d = distance(pos, eval->die_pos); - if(d == 0) - score += 1000000000.0f; - else - score += 1.0f/d; - - return score; -} - -static void evaluate_spawn_type(SPAWNEVAL *eval, int t) -{ - // get spawn point - /* - int start, num; - map_get_type(t, &start, &num); - if(!num) - return; - */ - for(int i = 0; i < num_spawn_points[t]; i++) - { - //num_spawn_points[t] - //mapres_spawnpoint *sp = (mapres_spawnpoint*)map_get_item(start + i, NULL, NULL); - vec2 p = spawn_points[t][i];// vec2((float)sp->x, (float)sp->y); - float s = evaluate_spawn(eval, p); - if(!eval->got || eval->score > s) - { - eval->got = true; - eval->score = s; - eval->pos = p; - } - } -} - -void PLAYER::try_respawn() -{ - vec2 spawnpos = vec2(100.0f, -60.0f); - - // get spawn point - SPAWNEVAL eval; - eval.die_pos = die_pos; - - eval.pos = vec2(100, 100); - - if(gamecontroller->gametype == GAMETYPE_CTF) - { - eval.friendly_team = team; - - // try first try own team spawn, then normal spawn and then enemy - evaluate_spawn_type(&eval, 1+(team&1)); - if(!eval.got) - { - evaluate_spawn_type(&eval, 0); - if(!eval.got) - evaluate_spawn_type(&eval, 1+((team+1)&1)); - } - } - else - { - if(gamecontroller->gametype == GAMETYPE_TDM) - eval.friendly_team = team; - - evaluate_spawn_type(&eval, 0); - evaluate_spawn_type(&eval, 1); - evaluate_spawn_type(&eval, 2); - } - - spawnpos = eval.pos; - - // check if the position is occupado - ENTITY *ents[2] = {0}; - int types[] = {NETOBJTYPE_PLAYER_CHARACTER}; - int num_ents = world->find_entities(spawnpos, 64, ents, 2, types, 1); - for(int i = 0; i < num_ents; i++) - { - if(ents[i] != this) - return; - } - - spawning = false; - pos = spawnpos; - - core.pos = pos; - core.vel = vec2(0,0); - core.hooked_player = -1; - - health = 10; - armor = 0; - jumped = 0; - - mem_zero(&ninja, sizeof(ninja)); - - dead = false; - set_flag(ENTITY::FLAG_PHYSICS); - player_state = PLAYERSTATE_PLAYING; - - core.hook_state = HOOK_IDLE; - - mem_zero(&input, sizeof(input)); - - // init weapons - mem_zero(&weapons, sizeof(weapons)); - weapons[WEAPON_HAMMER].got = true; - weapons[WEAPON_HAMMER].ammo = -1; - weapons[WEAPON_GUN].got = true; - weapons[WEAPON_GUN].ammo = 10; - - /*weapons[WEAPON_RIFLE].got = true; - weapons[WEAPON_RIFLE].ammo = -1;*/ - - active_weapon = WEAPON_GUN; - last_weapon = WEAPON_HAMMER; - queued_weapon = 0; - - reload_timer = 0; - - // Create sound and spawn effects - create_sound(pos, SOUND_PLAYER_SPAWN); - create_playerspawn(pos); - - gamecontroller->on_player_spawn(this); -} - -bool PLAYER::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; -} - - -int PLAYER::handle_ninja() -{ - vec2 direction = normalize(vec2(latest_input.target_x, latest_input.target_y)); - - if ((server_tick() - ninja.activationtick) > (data->weapons.ninja.duration * server_tickspeed() / 1000)) - { - // time's up, return - weapons[WEAPON_NINJA].got = false; - active_weapon = last_weapon; - if(active_weapon == WEAPON_NINJA) - active_weapon = WEAPON_GUN; - set_weapon(active_weapon); - return 0; - } - - // force ninja weapon - set_weapon(WEAPON_NINJA); - - // Check if it should activate - if (count_input(latest_previnput.fire, latest_input.fire).presses && (server_tick() > ninja.currentcooldown)) - { - // ok then, activate ninja - attack_tick = server_tick(); - ninja.activationdir = direction; - ninja.currentmovetime = data->weapons.ninja.movetime * server_tickspeed() / 1000; - ninja.currentcooldown = data->weapons.ninja.base->firedelay * server_tickspeed() / 1000 + server_tick(); - - // reset hit objects - numobjectshit = 0; - - create_sound(pos, SOUND_NINJA_FIRE); - - // release all hooks when ninja is activated - //release_hooked(); - //release_hooks(); - } - - ninja.currentmovetime--; - - if (ninja.currentmovetime == 0) - { - // reset player velocity - core.vel *= 0.2f; - //return MODIFIER_RETURNFLAGS_OVERRIDEWEAPON; - } - - if (ninja.currentmovetime > 0) - { - // Set player velocity - core.vel = ninja.activationdir * data->weapons.ninja.velocity; - vec2 oldpos = pos; - move_box(&core.pos, &core.vel, vec2(phys_size, phys_size), 0.0f); - // reset velocity so the client doesn't predict stuff - core.vel = vec2(0.0f,0.0f); - if ((ninja.currentmovetime % 2) == 0) - { - //create_smoke(pos); - } - - // check if we hit anything along the way - { - int type = NETOBJTYPE_PLAYER_CHARACTER; - ENTITY *ents[64]; - vec2 dir = pos - oldpos; - float radius = phys_size * 2.0f; //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] == this) - 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, pos) > (phys_size * 2.0f)) - continue; - - // hit a player, give him damage and stuffs... - create_sound(ents[i]->pos, SOUND_NINJA_HIT); - // set his velocity to fast upward (for now) - if(numobjectshit < 10) - hitobjects[numobjectshit++] = ents[i]; - ents[i]->take_damage(vec2(0,10.0f), data->weapons.ninja.base->damage, client_id,WEAPON_NINJA); - } - } - return 0; - } - - return 0; -} - - -void PLAYER::do_weaponswitch() -{ - if(reload_timer != 0) // make sure we have reloaded - return; - - if(queued_weapon == -1) // check for a queued weapon - return; - - if(weapons[WEAPON_NINJA].got) // if we have ninja, no weapon selection is possible - return; - - // switch weapon - set_weapon(queued_weapon); -} - -void PLAYER::handle_weaponswitch() -{ - int wanted_weapon = active_weapon; - if(queued_weapon != -1) - wanted_weapon = queued_weapon; - - // select weapon - int next = count_input(latest_previnput.next_weapon, latest_input.next_weapon).presses; - int prev = count_input(latest_previnput.prev_weapon, latest_input.prev_weapon).presses; - - if(next < 128) // make sure we only try sane stuff - { - while(next) // next weapon selection - { - wanted_weapon = (wanted_weapon+1)%NUM_WEAPONS; - if(weapons[wanted_weapon].got) - next--; - } - } - - if(prev < 128) // make sure we only try sane stuff - { - while(prev) // prev weapon selection - { - wanted_weapon = (wanted_weapon-1)<0?NUM_WEAPONS-1:wanted_weapon-1; - if(weapons[wanted_weapon].got) - prev--; - } - } - - // direct weapon selection - if(latest_input.wanted_weapon) - wanted_weapon = input.wanted_weapon-1; - - // check for insane values - if(wanted_weapon >= 0 && wanted_weapon < NUM_WEAPONS && wanted_weapon != active_weapon && weapons[wanted_weapon].got) - queued_weapon = wanted_weapon; - - do_weaponswitch(); -} - -void PLAYER::fire_weapon() -{ - if(reload_timer != 0 || active_weapon == WEAPON_NINJA) - return; - - do_weaponswitch(); - - vec2 direction = normalize(vec2(latest_input.target_x, latest_input.target_y)); - - bool fullauto = false; - if(active_weapon == WEAPON_GRENADE || active_weapon == WEAPON_SHOTGUN || active_weapon == WEAPON_RIFLE) - fullauto = true; - - - // check if we gonna fire - bool will_fire = false; - if(count_input(latest_previnput.fire, latest_input.fire).presses) will_fire = true; - if(fullauto && (latest_input.fire&1) && weapons[active_weapon].ammo) will_fire = true; - if(!will_fire) - return; - - // check for ammo - if(!weapons[active_weapon].ammo) - { - create_sound(pos, SOUND_WEAPON_NOAMMO); - return; - } - - vec2 projectile_startpos = pos+direction*phys_size*0.75f; - - switch(active_weapon) - { - case WEAPON_HAMMER: - { - // reset objects hit - numobjectshit = 0; - create_sound(pos, SOUND_HAMMER_FIRE); - - int type = NETOBJTYPE_PLAYER_CHARACTER; - ENTITY *ents[64]; - int num = world->find_entities(pos+direction*phys_size*0.75f, phys_size*0.5f, ents, 64, &type, 1); - - for (int i = 0; i < num; i++) - { - PLAYER *target = (PLAYER*)ents[i]; - if (target == this) - continue; - - // hit a player, give him damage and stuffs... - vec2 fdir = normalize(ents[i]->pos - pos); - - // set his velocity to fast upward (for now) - create_sound(pos, SOUND_HAMMER_HIT); - ents[i]->take_damage(vec2(0,-1.0f), data->weapons.hammer.base->damage, client_id, active_weapon); - vec2 dir; - if (length(target->pos - pos) > 0.0f) - dir = normalize(target->pos - pos); - else - dir = vec2(0,-1); - - target->core.vel += normalize(dir + vec2(0,-1.1f)) * 10.0f; - } - - } break; - - case WEAPON_GUN: - { - PROJECTILE *proj = new PROJECTILE(WEAPON_GUN, - client_id, - projectile_startpos, - direction, - (int)(server_tickspeed()*tuning.gun_lifetime), - this, - 1, 0, 0, -1, WEAPON_GUN); - - // pack the projectile and send it to the client directly - NETOBJ_PROJECTILE p; - proj->fill_info(&p); - - msg_pack_start(NETMSGTYPE_SV_EXTRAPROJECTILE, 0); - msg_pack_int(1); - for(unsigned i = 0; i < sizeof(NETOBJ_PROJECTILE)/sizeof(int); i++) - msg_pack_int(((int *)&p)[i]); - msg_pack_end(); - server_send_msg(client_id); - - create_sound(pos, SOUND_GUN_FIRE); - } break; - - case WEAPON_SHOTGUN: - { - int shotspread = 2; - - msg_pack_start(NETMSGTYPE_SV_EXTRAPROJECTILE, 0); - msg_pack_int(shotspread*2+1); - - for(int i = -shotspread; i <= shotspread; i++) - { - float spreading[] = {-0.185f, -0.070f, 0, 0.070f, 0.185f}; - float a = get_angle(direction); - a += spreading[i+2]; - float v = 1-(abs(i)/(float)shotspread); - float speed = mix((float)tuning.shotgun_speeddiff, 1.0f, v); - PROJECTILE *proj = new PROJECTILE(WEAPON_SHOTGUN, - client_id, - projectile_startpos, - vec2(cosf(a), sinf(a))*speed, - (int)(server_tickspeed()*tuning.shotgun_lifetime), - this, - 1, 0, 0, -1, WEAPON_SHOTGUN); - - // pack the projectile and send it to the client directly - NETOBJ_PROJECTILE p; - proj->fill_info(&p); - - for(unsigned i = 0; i < sizeof(NETOBJ_PROJECTILE)/sizeof(int); i++) - msg_pack_int(((int *)&p)[i]); - } - - msg_pack_end(); - server_send_msg(client_id); - - create_sound(pos, SOUND_SHOTGUN_FIRE); - } break; - - case WEAPON_GRENADE: - { - PROJECTILE *proj = new PROJECTILE(WEAPON_GRENADE, - client_id, - projectile_startpos, - direction, - (int)(server_tickspeed()*tuning.grenade_lifetime), - this, - 1, PROJECTILE::PROJECTILE_FLAGS_EXPLODE, 0, SOUND_GRENADE_EXPLODE, WEAPON_GRENADE); - - // pack the projectile and send it to the client directly - NETOBJ_PROJECTILE p; - proj->fill_info(&p); - - msg_pack_start(NETMSGTYPE_SV_EXTRAPROJECTILE, 0); - msg_pack_int(1); - for(unsigned i = 0; i < sizeof(NETOBJ_PROJECTILE)/sizeof(int); i++) - msg_pack_int(((int *)&p)[i]); - msg_pack_end(); - server_send_msg(client_id); - - create_sound(pos, SOUND_GRENADE_FIRE); - } break; - - case WEAPON_RIFLE: - { - new LASER(pos, direction, tuning.laser_reach, this); - create_sound(pos, SOUND_RIFLE_FIRE); - } break; - - } - - if(weapons[active_weapon].ammo > 0) // -1 == unlimited - weapons[active_weapon].ammo--; - attack_tick = server_tick(); - reload_timer = data->weapons.id[active_weapon].firedelay * server_tickspeed() / 1000; -} - -int PLAYER::handle_weapons() -{ - vec2 direction = normalize(vec2(latest_input.target_x, latest_input.target_y)); - - if(config.dbg_stress) - { - for(int i = 0; i < NUM_WEAPONS; i++) - { - weapons[i].got = true; - weapons[i].ammo = 10; - } - - if(reload_timer) // twice as fast reload - reload_timer--; - } - - // check reload timer - if(reload_timer) - { - reload_timer--; - return 0; - } - - if (active_weapon == WEAPON_NINJA) - { - // don't update other weapons while ninja is active - return handle_ninja(); - } - - // fire weapon, if wanted - fire_weapon(); - - // ammo regen - int ammoregentime = data->weapons.id[active_weapon].ammoregentime; - if(ammoregentime) - { - // If equipped and not active, regen ammo? - if (reload_timer <= 0) - { - if (weapons[active_weapon].ammoregenstart < 0) - weapons[active_weapon].ammoregenstart = server_tick(); - - if ((server_tick() - weapons[active_weapon].ammoregenstart) >= ammoregentime * server_tickspeed() / 1000) - { - // Add some ammo - weapons[active_weapon].ammo = min(weapons[active_weapon].ammo + 1, 10); - weapons[active_weapon].ammoregenstart = -1; - } - } - else - { - weapons[active_weapon].ammoregenstart = -1; - } - } - - return 0; -} - -void PLAYER::on_direct_input(NETOBJ_PLAYER_INPUT *new_input) -{ - mem_copy(&latest_previnput, &latest_input, sizeof(latest_input)); - mem_copy(&latest_input, new_input, sizeof(latest_input)); - if(num_inputs > 2 && team != -1 && !dead) - { - handle_weaponswitch(); - fire_weapon(); - } -} - -void PLAYER::tick() -{ - server_setclientscore(client_id, score); - - // grab latest input - /* - { - int size = 0; - int *input = server_latestinput(client_id, &size); - if(input) - { - mem_copy(&latest_previnput, &latest_input, sizeof(latest_input)); - mem_copy(&latest_input, input, sizeof(latest_input)); - } - }*/ - - // check if we have enough input - // this is to prevent initial weird clicks - if(num_inputs < 2) - { - latest_previnput = latest_input; - previnput = input; - } - - // do latency stuff - { - CLIENT_INFO info; - if(server_getclientinfo(client_id, &info)) - { - latency_accum += info.latency; - latency_accum_max = max(latency_accum_max, info.latency); - latency_accum_min = min(latency_accum_min, info.latency); - } - - if(server_tick()%server_tickspeed() == 0) - { - latency_avg = latency_accum/server_tickspeed(); - latency_max = latency_accum_max; - latency_min = latency_accum_min; - latency_accum = 0; - latency_accum_min = 1000; - latency_accum_max = 0; - } - } - - // enable / disable physics - if(team == -1 || dead) - { - world->core.players[client_id] = 0; - clear_flag(FLAG_PHYSICS); - } - else - { - world->core.players[client_id] = &core; - set_flag(FLAG_PHYSICS); - } - - // spectator - if(team == -1) - return; - - if(spawning) - try_respawn(); - - // TODO: rework the input to be more robust - if(dead) - { - if(server_tick()-die_tick >= server_tickspeed()/2 && count_input(latest_previnput.fire, latest_input.fire).presses) - die_tick = -1; - if(server_tick()-die_tick >= server_tickspeed()*5) // auto respawn after 3 sec - respawn(); - //if((input.fire&1) && server_tick()-die_tick >= server_tickspeed()/2) // auto respawn after 0.5 sec - //respawn(); - return; - } - - //player_core core; - //core.pos = pos; - //core.jumped = jumped; - core.input = input; - core.tick(); - - // handle weapons - handle_weapons(); - - player_state = input.player_state; - - // Previnput - previnput = input; - return; -} - -void PLAYER::tick_defered() -{ - if(!dead) - { - vec2 start_pos = core.pos; - vec2 start_vel = core.vel; - bool stuck_before = test_box(core.pos, vec2(28.0f, 28.0f)); - - core.move(); - bool stuck_after_move = test_box(core.pos, vec2(28.0f, 28.0f)); - core.quantize(); - bool stuck_after_quant = test_box(core.pos, vec2(28.0f, 28.0f)); - pos = core.pos; - - if(!stuck_before && (stuck_after_move || stuck_after_quant)) - { - dbg_msg("player", "STUCK!!! %d %d %d %f %f %f %f %x %x %x %x", - stuck_before, - stuck_after_move, - stuck_after_quant, - start_pos.x, start_pos.y, - start_vel.x, start_vel.y, - *((unsigned *)&start_pos.x), *((unsigned *)&start_pos.y), - *((unsigned *)&start_vel.x), *((unsigned *)&start_vel.y)); - } - - int events = core.triggered_events; - int mask = cmask_all_except_one(client_id); - - if(events&COREEVENT_GROUND_JUMP) create_sound(pos, SOUND_PLAYER_JUMP, mask); - if(events&COREEVENT_AIR_JUMP) - { - create_sound(pos, SOUND_PLAYER_AIRJUMP, mask); - NETEVENT_COMMON *c = (NETEVENT_COMMON *)::events.create(NETEVENTTYPE_AIRJUMP, sizeof(NETEVENT_COMMON), mask); - if(c) - { - c->x = (int)pos.x; - c->y = (int)pos.y; - } - } - - //if(events&COREEVENT_HOOK_LAUNCH) snd_play_random(CHN_WORLD, SOUND_HOOK_LOOP, 1.0f, pos); - if(events&COREEVENT_HOOK_ATTACH_PLAYER) create_sound(pos, SOUND_HOOK_ATTACH_PLAYER, cmask_all()); - if(events&COREEVENT_HOOK_ATTACH_GROUND) create_sound(pos, SOUND_HOOK_ATTACH_GROUND, mask); - //if(events&COREEVENT_HOOK_RETRACT) snd_play_random(CHN_WORLD, SOUND_PLAYER_JUMP, 1.0f, pos); - - } - - if(team == -1) - { - pos.x = input.target_x; - pos.y = input.target_y; - } -} - -void PLAYER::die(int killer, int weapon) -{ - if (dead || team == -1) - return; - - int mode_special = gamecontroller->on_player_death(this, get_player(killer), weapon); - - dbg_msg("game", "kill killer='%d:%s' victim='%d:%s' weapon=%d special=%d", - killer, server_clientname(killer), - client_id, server_clientname(client_id), weapon, mode_special); - - // send the kill message - NETMSG_SV_KILLMSG msg; - msg.killer = killer; - msg.victim = client_id; - msg.weapon = weapon; - msg.mode_special = mode_special; - msg.pack(MSGFLAG_VITAL); - server_send_msg(-1); - - // a nice sound - create_sound(pos, SOUND_PLAYER_DIE); - - // set dead state - die_pos = pos; - dead = true; - die_tick = server_tick(); - clear_flag(FLAG_PHYSICS); - create_death(pos, client_id); -} - -bool PLAYER::take_damage(vec2 force, int dmg, int from, int weapon) -{ - core.vel += force; - - if(gamecontroller->is_friendly_fire(client_id, from) && !config.sv_teamdamage) - return false; - - // player only inflicts half damage on self - if(from == client_id) - dmg = max(1, dmg/2); - - // CTF and TDM (TODO: check for FF) - //if (gameobj->gametype != GAMETYPE_DM && from >= 0 && players[from].team == team) - //return false; - - damage_taken++; - - // create healthmod indicator - if(server_tick() < damage_taken_tick+25) - { - // make sure that the damage indicators doesn't group together - create_damageind(pos, damage_taken*0.25f, dmg); - } - else - { - damage_taken = 0; - create_damageind(pos, 0, dmg); - } - - if(dmg) - { - if(armor) - { - if(dmg > 1) - { - health--; - dmg--; - } - - if(dmg > armor) - { - dmg -= armor; - armor = 0; - } - else - { - armor -= dmg; - dmg = 0; - } - } - - health -= dmg; - } - - damage_taken_tick = server_tick(); - - // do damage hit sound - if(from >= 0 && from != client_id) - create_sound(get_player(from)->pos, SOUND_HIT, cmask_one(from)); - - // check for death - if(health <= 0) - { - die(from, weapon); - - // set attacker's face to happy (taunt!) - if (from >= 0 && from != client_id) - { - PLAYER *p = get_player(from); - - p->emote_type = EMOTE_HAPPY; - p->emote_stop = server_tick() + server_tickspeed(); - } - - return false; - } - - if (dmg > 2) - create_sound(pos, SOUND_PLAYER_PAIN_LONG); - else - create_sound(pos, SOUND_PLAYER_PAIN_SHORT); - - emote_type = EMOTE_PAIN; - emote_stop = server_tick() + 500 * server_tickspeed() / 1000; - - // spawn blood? - return true; -} - -void PLAYER::snap(int snaping_client) -{ - if(1) - { - NETOBJ_PLAYER_INFO *info = (NETOBJ_PLAYER_INFO *)snap_new_item(NETOBJTYPE_PLAYER_INFO, client_id, sizeof(NETOBJ_PLAYER_INFO)); - - info->latency = latency_min; - info->latency_flux = latency_max-latency_min; - info->local = 0; - info->cid = client_id; - info->score = score; - info->team = team; - - if(client_id == snaping_client) - info->local = 1; - } - - if(!dead && health > 0 && team >= 0 && distance(players[snaping_client].pos, pos) < 1000.0f) - { - NETOBJ_PLAYER_CHARACTER *character = (NETOBJ_PLAYER_CHARACTER *)snap_new_item(NETOBJTYPE_PLAYER_CHARACTER, client_id, sizeof(NETOBJ_PLAYER_CHARACTER)); - - core.write(character); - - // this is to make sure that players that are just standing still - // isn't sent. this is because the physics keep bouncing between - // 0-128 when just standing. - // TODO: fix the physics so this isn't needed - if(snaping_client != client_id && abs(character->vy) < 256.0f) - character->vy = 0; - - if (emote_stop < server_tick()) - { - emote_type = EMOTE_NORMAL; - emote_stop = -1; - } - - character->emote = emote_type; - - character->ammocount = 0; - character->health = 0; - character->armor = 0; - - character->weapon = active_weapon; - character->attacktick = attack_tick; - - character->wanted_direction = input.direction; - /* - if(input.left && !input.right) - character->wanted_direction = -1; - else if(!input.left && input.right) - character->wanted_direction = 1;*/ - - - if(client_id == snaping_client) - { - character->health = health; - character->armor = armor; - if(weapons[active_weapon].ammo > 0) - character->ammocount = weapons[active_weapon].ammo; - } - - if (character->emote == EMOTE_NORMAL) - { - if(250 - ((server_tick() - last_action)%(250)) < 5) - character->emote = EMOTE_BLINK; - } - - character->player_state = player_state; - } -} - -PLAYER *players; - -////////////////////////////////////////////////// -// powerup -////////////////////////////////////////////////// -PICKUP::PICKUP(int _type, int _subtype) -: ENTITY(NETOBJTYPE_PICKUP) -{ - type = _type; - subtype = _subtype; - proximity_radius = phys_size; - - reset(); - - // TODO: should this be done here? - world->insert_entity(this); -} - -void PICKUP::reset() -{ - if (data->pickups[type].spawndelay > 0) - spawntick = server_tick() + server_tickspeed() * data->pickups[type].spawndelay; - else - spawntick = -1; -} - - -void send_weapon_pickup(int cid, int weapon); - -void PICKUP::tick() -{ - // wait for respawn - if(spawntick > 0) - { - if(server_tick() > spawntick) - { - // respawn - spawntick = -1; - - if(type == POWERUP_WEAPON) - create_sound(pos, SOUND_WEAPON_SPAWN); - } - else - return; - } - // Check if a player intersected us - PLAYER* pplayer = closest_player(pos, 20.0f, 0); - if (pplayer) - { - // player picked us up, is someone was hooking us, let them go - int respawntime = -1; - switch (type) - { - case POWERUP_HEALTH: - if(pplayer->health < 10) - { - create_sound(pos, SOUND_PICKUP_HEALTH); - pplayer->health = min(10, pplayer->health + 1); - respawntime = data->pickups[type].respawntime; - } - break; - case POWERUP_ARMOR: - if(pplayer->armor < 10) - { - create_sound(pos, SOUND_PICKUP_ARMOR); - pplayer->armor = min(10, pplayer->armor + 1); - respawntime = data->pickups[type].respawntime; - } - break; - - case POWERUP_WEAPON: - if(subtype >= 0 && subtype < NUM_WEAPONS) - { - if(pplayer->weapons[subtype].ammo < data->weapons.id[subtype].maxammo || !pplayer->weapons[subtype].got) - { - pplayer->weapons[subtype].got = true; - pplayer->weapons[subtype].ammo = min(data->weapons.id[subtype].maxammo, pplayer->weapons[subtype].ammo + 10); - respawntime = data->pickups[type].respawntime; - - // TODO: data compiler should take care of stuff like this - if(subtype == WEAPON_GRENADE) - create_sound(pos, SOUND_PICKUP_GRENADE); - else if(subtype == WEAPON_SHOTGUN) - create_sound(pos, SOUND_PICKUP_SHOTGUN); - else if(subtype == WEAPON_RIFLE) - create_sound(pos, SOUND_PICKUP_SHOTGUN); - - send_weapon_pickup(pplayer->client_id, subtype); - } - } - break; - case POWERUP_NINJA: - { - // activate ninja on target player - pplayer->ninja.activationtick = server_tick(); - pplayer->weapons[WEAPON_NINJA].got = true; - pplayer->last_weapon = pplayer->active_weapon; - pplayer->active_weapon = WEAPON_NINJA; - respawntime = data->pickups[type].respawntime; - create_sound(pos, SOUND_PICKUP_NINJA); - - // loop through all players, setting their emotes - ENTITY *ents[64]; - const int types[] = {NETOBJTYPE_PLAYER_CHARACTER}; - int num = world->find_entities(vec2(0, 0), 1000000, ents, 64, types, 1); - for (int i = 0; i < num; i++) - { - PLAYER *p = (PLAYER *)ents[i]; - if (p != pplayer) - { - p->emote_type = EMOTE_SURPRISE; - p->emote_stop = server_tick() + server_tickspeed(); - } - } - - pplayer->emote_type = EMOTE_ANGRY; - pplayer->emote_stop = server_tick() + 1200 * server_tickspeed() / 1000; - - break; - } - default: - break; - }; - - if(respawntime >= 0) - { - dbg_msg("game", "pickup player='%d:%s' item=%d/%d", - pplayer->client_id, server_clientname(pplayer->client_id), type, subtype); - spawntick = server_tick() + server_tickspeed() * respawntime; - } - } -} - -void PICKUP::snap(int snapping_client) -{ - if(spawntick != -1) - return; - - NETOBJ_PICKUP *up = (NETOBJ_PICKUP *)snap_new_item(NETOBJTYPE_PICKUP, id, sizeof(NETOBJ_PICKUP)); - up->x = (int)pos.x; - up->y = (int)pos.y; - up->type = type; // TODO: two diffrent types? what gives? - up->subtype = subtype; + players[i].init(-1); } -// POWERUP END /////////////////////// -PLAYER *get_player(int index) -{ - return &players[index]; -} - -void create_damageind(vec2 p, float angle, int amount) +void GAMECONTEXT::create_damageind(vec2 p, float angle, int amount) { float a = 3 * 3.14159f / 2 + angle; //float a = get_angle(dir); @@ -1852,7 +568,7 @@ void create_damageind(vec2 p, float angle, int amount) } } -void create_explosion(vec2 p, int owner, int weapon, bool bnodamage) +void GAMECONTEXT::create_explosion(vec2 p, int owner, int weapon, bool bnodamage) { // create the event NETEVENT_EXPLOSION *ev = (NETEVENT_EXPLOSION *)events.create(NETEVENTTYPE_EXPLOSION, sizeof(NETEVENT_EXPLOSION)); @@ -1865,11 +581,10 @@ void create_explosion(vec2 p, int owner, int weapon, bool bnodamage) if (!bnodamage) { // deal damage - ENTITY *ents[64]; + CHARACTER *ents[64]; float radius = 128.0f; float innerradius = 42.0f; - - int num = world->find_entities(p, radius, ents, 64); + int num = game.world.find_entities(p, radius, (ENTITY**)ents, 64, NETOBJTYPE_CHARACTER); for(int i = 0; i < num; i++) { vec2 diff = ents[i]->pos - p; @@ -1897,7 +612,7 @@ void create_smoke(vec2 p) } }*/ -void create_playerspawn(vec2 p) +void GAMECONTEXT::create_playerspawn(vec2 p) { // create the event NETEVENT_SPAWN *ev = (NETEVENT_SPAWN *)events.create(NETEVENTTYPE_SPAWN, sizeof(NETEVENT_SPAWN)); @@ -1908,7 +623,7 @@ void create_playerspawn(vec2 p) } } -void create_death(vec2 p, int cid) +void GAMECONTEXT::create_death(vec2 p, int cid) { // create the event NETEVENT_DEATH *ev = (NETEVENT_DEATH *)events.create(NETEVENTTYPE_DEATH, sizeof(NETEVENT_DEATH)); @@ -1920,7 +635,7 @@ void create_death(vec2 p, int cid) } } -void create_sound(vec2 pos, int sound, int mask) +void GAMECONTEXT::create_sound(vec2 pos, int sound, int mask) { if (sound < 0) return; @@ -1935,7 +650,7 @@ void create_sound(vec2 pos, int sound, int mask) } } -void create_sound_global(int sound, int target) +void GAMECONTEXT::create_sound_global(int sound, int target) { if (sound < 0) return; @@ -1947,30 +662,28 @@ void create_sound_global(int sound, int target) } // TODO: should be more general -PLAYER *intersect_player(vec2 pos0, vec2 pos1, float radius, vec2& new_pos, ENTITY *notthis) +CHARACTER *GAMEWORLD::intersect_character(vec2 pos0, vec2 pos1, float radius, vec2& new_pos, ENTITY *notthis) { // Find other players float closest_len = distance(pos0, pos1) * 100.0f; vec2 line_dir = normalize(pos1-pos0); - PLAYER *closest = 0; - - for(int i = 0; i < MAX_CLIENTS; i++) - { - if(players[i].client_id < 0 || (ENTITY *)&players[i] == notthis) + CHARACTER *closest = 0; + + CHARACTER *p = (CHARACTER *)game.world.find_first(NETOBJTYPE_CHARACTER); + for(; p; p = (CHARACTER *)p->typenext()) + { + if(p == notthis) continue; - if(!(players[i].flags&ENTITY::FLAG_PHYSICS)) - continue; - - vec2 intersect_pos = closest_point_on_line(pos0, pos1, players[i].pos); - float len = distance(players[i].pos, intersect_pos); - if(len < PLAYER::phys_size+radius) + vec2 intersect_pos = closest_point_on_line(pos0, pos1, p->pos); + float len = distance(p->pos, intersect_pos); + if(len < CHARACTER::phys_size+radius) { if(len < closest_len) { new_pos = intersect_pos; closest_len = len; - closest = &players[i]; + closest = p; } } } @@ -1979,27 +692,25 @@ PLAYER *intersect_player(vec2 pos0, vec2 pos1, float radius, vec2& new_pos, ENTI } -PLAYER *closest_player(vec2 pos, float radius, ENTITY *notthis) +CHARACTER *GAMEWORLD::closest_character(vec2 pos, float radius, ENTITY *notthis) { // Find other players float closest_range = radius*2; - PLAYER *closest = 0; + CHARACTER *closest = 0; - for(int i = 0; i < MAX_CLIENTS; i++) - { - if(players[i].client_id < 0 || (ENTITY *)&players[i] == notthis) + CHARACTER *p = (CHARACTER *)game.world.find_first(NETOBJTYPE_CHARACTER); + for(; p; p = (CHARACTER *)p->typenext()) + { + if(p == notthis) continue; - if(!(players[i].flags&ENTITY::FLAG_PHYSICS)) - continue; - - float len = distance(pos, players[i].pos); - if(len < PLAYER::phys_size+radius) + float len = distance(pos, p->pos); + if(len < CHARACTER::phys_size+radius) { if(len < closest_range) { closest_range = len; - closest = &players[i]; + closest = p; } } } @@ -2007,26 +718,40 @@ PLAYER *closest_player(vec2 pos, float radius, ENTITY *notthis) return closest; } -// Server hooks -void mods_tick() + +void GAMECONTEXT::tick() { - world->core.tuning = tuning; - world->tick(); + world.core.tuning = tuning; + world.tick(); - if(world->paused) // make sure that the game object always updates - gamecontroller->tick(); + if(world.paused) // make sure that the game object always updates + controller->tick(); + + for(int i = 0; i < MAX_CLIENTS; i++) + { + if(players[i].client_id != -1) + players[i].tick(); + } } -void mods_snap(int client_id) +void GAMECONTEXT::snap(int client_id) { - world->snap(client_id); + world.snap(client_id); events.snap(client_id); + controller->snap(client_id); + + for(int i = 0; i < MAX_CLIENTS; i++) + { + if(players[i].client_id != -1) + players[i].snap(client_id); + } } +// Server hooks void mods_client_direct_input(int client_id, void *input) { - if(!world->paused) - players[client_id].on_direct_input((NETOBJ_PLAYER_INPUT *)input); + if(!game.world.paused) + game.players[client_id].on_direct_input((NETOBJ_PLAYER_INPUT *)input); /* if(i->fire) @@ -2039,45 +764,60 @@ void mods_client_direct_input(int client_id, void *input) void mods_client_predicted_input(int client_id, void *input) { - if(!world->paused) + if(!game.world.paused) + game.players[client_id].on_predicted_input((NETOBJ_PLAYER_INPUT *)input); + + /* { - if (memcmp(&players[client_id].input, input, sizeof(NETOBJ_PLAYER_INPUT)) != 0) - players[client_id].last_action = server_tick(); + + on_predicted_input() + if (memcmp(&game.players[client_id].input, input, sizeof(NETOBJ_PLAYER_INPUT)) != 0) + game.players[client_id].last_action = server_tick(); - //players[client_id].previnput = players[client_id].input; - players[client_id].input = *(NETOBJ_PLAYER_INPUT*)input; - players[client_id].num_inputs++; + //game.players[client_id].previnput = game.players[client_id].input; + game.players[client_id].input = *(NETOBJ_PLAYER_INPUT*)input; + game.players[client_id].num_inputs++; - if(players[client_id].input.target_x == 0 && players[client_id].input.target_y == 0) - players[client_id].input.target_y = -1; - } + if(game.players[client_id].input.target_x == 0 && game.players[client_id].input.target_y == 0) + game.players[client_id].input.target_y = -1; + }*/ } +// Server hooks +void mods_tick() +{ + game.tick(); +} + +void mods_snap(int client_id) +{ + game.snap(client_id); +} void mods_client_enter(int client_id) { - world->insert_entity(&players[client_id]); - players[client_id].respawn(); + //game.world.insert_entity(&game.players[client_id]); + game.players[client_id].respawn(); dbg_msg("game", "join player='%d:%s'", client_id, server_clientname(client_id)); char buf[512]; - str_format(buf, sizeof(buf), "%s entered and joined the %s", server_clientname(client_id), get_team_name(players[client_id].team)); - send_chat(-1, CHAT_ALL, buf); + str_format(buf, sizeof(buf), "%s entered and joined the %s", server_clientname(client_id), game.controller->get_team_name(game.players[client_id].team)); + game.send_chat(-1, CHAT_ALL, buf); - dbg_msg("game", "team_join player='%d:%s' team=%d", client_id, server_clientname(client_id), players[client_id].team); + dbg_msg("game", "team_join player='%d:%s' team=%d", client_id, server_clientname(client_id), game.players[client_id].team); } void mods_connected(int client_id) { - players[client_id].init(); - players[client_id].client_id = client_id; + game.players[client_id].init(client_id); + //game.players[client_id].client_id = client_id; // Check which team the player should be on if(config.sv_tournament_mode) - players[client_id].team = -1; + game.players[client_id].team = -1; else - players[client_id].team = gamecontroller->get_auto_team(client_id); + game.players[client_id].team = game.controller->get_auto_team(client_id); // send motd NETMSG_SV_MOTD msg; @@ -2088,16 +828,8 @@ void mods_connected(int client_id) void mods_client_drop(int client_id) { - char buf[512]; - str_format(buf, sizeof(buf), "%s has left the game", server_clientname(client_id)); - send_chat(-1, CHAT_ALL, buf); - - dbg_msg("game", "leave player='%d:%s'", client_id, server_clientname(client_id)); + game.players[client_id].on_disconnect(); - gamecontroller->on_player_death(&players[client_id], 0, -1); - world->remove_entity(&players[client_id]); - world->core.players[client_id] = 0x0; - players[client_id].client_id = -1; } void mods_message(int msgtype, int client_id) @@ -2114,18 +846,18 @@ void mods_message(int msgtype, int client_id) NETMSG_CL_SAY *msg = (NETMSG_CL_SAY *)rawmsg; int team = msg->team; if(team) - team = players[client_id].team; + team = game.players[client_id].team; else team = CHAT_ALL; - if(config.sv_spamprotection && players[client_id].last_chat+time_freq() > time_get()) + if(config.sv_spamprotection && game.players[client_id].last_chat+time_freq() > time_get()) { // consider this as spam } else { - players[client_id].last_chat = time_get(); - send_chat(client_id, team, msg->message); + game.players[client_id].last_chat = time_get(); + game.send_chat(client_id, team, msg->message); } } else if (msgtype == NETMSGTYPE_CL_SETTEAM) @@ -2133,21 +865,21 @@ void mods_message(int msgtype, int client_id) NETMSG_CL_SETTEAM *msg = (NETMSG_CL_SETTEAM *)rawmsg; // Switch team on given client and kill/respawn him - if(gamecontroller->can_join_team(msg->team, client_id)) - players[client_id].set_team(msg->team); + if(game.controller->can_join_team(msg->team, client_id)) + game.players[client_id].set_team(msg->team); else { char buf[128]; str_format(buf, sizeof(buf), "Only %d active players are allowed", config.sv_max_clients-config.sv_spectator_slots); - send_broadcast(buf, client_id); + game.send_broadcast(buf, client_id); } } else if (msgtype == NETMSGTYPE_CL_CHANGEINFO || msgtype == NETMSGTYPE_CL_STARTINFO) { NETMSG_CL_CHANGEINFO *msg = (NETMSG_CL_CHANGEINFO *)rawmsg; - players[client_id].use_custom_color = msg->use_custom_color; - players[client_id].color_body = msg->color_body; - players[client_id].color_feet = msg->color_feet; + game.players[client_id].use_custom_color = msg->use_custom_color; + game.players[client_id].color_body = msg->color_body; + game.players[client_id].color_feet = msg->color_feet; // check for invalid chars /* @@ -2168,13 +900,13 @@ void mods_message(int msgtype, int client_id) { char chattext[256]; str_format(chattext, sizeof(chattext), "%s changed name to %s", oldname, server_clientname(client_id)); - send_chat(-1, CHAT_ALL, chattext); + game.send_chat(-1, CHAT_ALL, chattext); } // set skin - str_copy(players[client_id].skin_name, msg->skin, sizeof(players[client_id].skin_name)); + str_copy(game.players[client_id].skin_name, msg->skin, sizeof(game.players[client_id].skin_name)); - gamecontroller->on_player_info_change(&players[client_id]); + game.controller->on_player_info_change(&game.players[client_id]); if(msgtype == NETMSGTYPE_CL_STARTINFO) { @@ -2183,8 +915,8 @@ void mods_message(int msgtype, int client_id) // send all info to this client for(int i = 0; i < MAX_CLIENTS; i++) { - if(players[i].client_id != -1) - send_info(i, client_id); + if(game.players[i].client_id != -1) + game.send_info(i, client_id); } // send tuning parameters to client @@ -2196,23 +928,20 @@ void mods_message(int msgtype, int client_id) server_send_msg(client_id); } - send_info(client_id, -1); + game.send_info(client_id, -1); } else if (msgtype == NETMSGTYPE_CL_EMOTICON) { NETMSG_CL_EMOTICON *msg = (NETMSG_CL_EMOTICON *)rawmsg; - send_emoticon(client_id, msg->emoticon); + game.send_emoticon(client_id, msg->emoticon); } else if (msgtype == NETMSGTYPE_CL_KILL) { - PLAYER *pplayer = get_player(client_id); - pplayer->die(client_id, -1); + //PLAYER *pplayer = get_player(client_id); + game.players[client_id].kill_character(); //(client_id, -1); } } -extern unsigned char internal_data[]; - - static void con_tune_param(void *result, void *user_data) { const char *param_name = console_arg_string(result, 0); @@ -2249,19 +978,19 @@ static void con_tune_dump(void *result, void *user_data) static void con_restart(void *result, void *user_data) { if(console_arg_num(result)) - gamecontroller->do_warmup(console_arg_int(result, 0)); + game.controller->do_warmup(console_arg_int(result, 0)); else - gamecontroller->startround(); + game.controller->startround(); } static void con_broadcast(void *result, void *user_data) { - send_broadcast(console_arg_string(result, 0), -1); + game.send_broadcast(console_arg_string(result, 0), -1); } static void con_say(void *result, void *user_data) { - send_chat(-1, CHAT_ALL, console_arg_string(result, 0)); + game.send_chat(-1, CHAT_ALL, console_arg_string(result, 0)); } static void con_set_team(void *result, void *user_data) @@ -2271,10 +1000,10 @@ static void con_set_team(void *result, void *user_data) dbg_msg("", "%d %d", client_id, team); - if(players[client_id].client_id != client_id) + if(game.players[client_id].client_id != client_id) return; - players[client_id].set_team(team); + game.players[client_id].set_team(team); } void mods_console_init() @@ -2293,32 +1022,38 @@ void mods_init() { //if(!data) /* only load once */ //data = load_data_from_memory(internal_data); + + for(int i = 0; i < NUM_NETOBJTYPES; i++) + snap_set_staticsize(i, netobj_get_size(i)); layers_init(); col_init(); - world = new GAMEWORLD; - players = new PLAYER[MAX_CLIENTS]; + // reset everything here + //world = new GAMEWORLD; + //players = new PLAYER[MAX_CLIENTS]; // select gametype if(strcmp(config.sv_gametype, "ctf") == 0) - gamecontroller = new GAMECONTROLLER_CTF; + game.controller = new GAMECONTROLLER_CTF; else if(strcmp(config.sv_gametype, "tdm") == 0) - gamecontroller = new GAMECONTROLLER_TDM; + game.controller = new GAMECONTROLLER_TDM; else - gamecontroller = new GAMECONTROLLER_DM; + game.controller = new GAMECONTROLLER_DM; // setup core world - for(int i = 0; i < MAX_CLIENTS; i++) - players[i].core.world = &world->core; + //for(int i = 0; i < MAX_CLIENTS; i++) + // game.players[i].core.world = &game.world.core; // create all entities from the game layer MAPITEM_LAYER_TILEMAP *tmap = layers_game_layer(); TILE *tiles = (TILE *)map_get_data(tmap->data); + /* num_spawn_points[0] = 0; num_spawn_points[1] = 0; num_spawn_points[2] = 0; + */ for(int y = 0; y < tmap->height; y++) { @@ -2326,11 +1061,11 @@ void mods_init() { int index = tiles[y*tmap->width+x].index - ENTITY_OFFSET; vec2 pos(x*32.0f+16.0f, y*32.0f+16.0f); - gamecontroller->on_entity(index, pos); + game.controller->on_entity(index, pos); } } - world->insert_entity(gamecontroller); + //game.world.insert_entity(game.controller); if(config.dbg_dummies) { @@ -2338,26 +1073,22 @@ void mods_init() { mods_connected(MAX_CLIENTS-i-1); mods_client_enter(MAX_CLIENTS-i-1); - if(gamecontroller->gametype != GAMETYPE_DM) - players[MAX_CLIENTS-i-1].team = i&1; + if(game.controller->gametype != GAMETYPE_DM) + game.players[MAX_CLIENTS-i-1].team = i&1; } } } void mods_shutdown() { - delete [] players; - delete gamecontroller; - delete world; - gamecontroller = 0; - players = 0; - world = 0; + delete game.controller; + game.controller = 0; } void mods_presnap() {} void mods_postsnap() { - events.clear(); + game.events.clear(); } extern "C" const char *mods_net_version() { return GAME_NETVERSION; } |