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 | |
| 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')
| -rw-r--r-- | src/game/server/gs_common.hpp | 411 | ||||
| -rw-r--r-- | src/game/server/gs_ent_pickup.cpp | 140 | ||||
| -rw-r--r-- | src/game/server/gs_ent_player.cpp | 1128 | ||||
| -rw-r--r-- | src/game/server/gs_game.cpp | 83 | ||||
| -rw-r--r-- | src/game/server/gs_game_ctf.cpp | 77 | ||||
| -rw-r--r-- | src/game/server/gs_game_ctf.hpp | 6 | ||||
| -rw-r--r-- | src/game/server/gs_game_tdm.cpp | 6 | ||||
| -rw-r--r-- | src/game/server/gs_game_tdm.hpp | 2 | ||||
| -rw-r--r-- | src/game/server/gs_server.cpp | 1641 |
9 files changed, 1871 insertions, 1623 deletions
diff --git a/src/game/server/gs_common.hpp b/src/game/server/gs_common.hpp index b6c809fa..ff939116 100644 --- a/src/game/server/gs_common.hpp +++ b/src/game/server/gs_common.hpp @@ -2,16 +2,15 @@ #include "../g_game.hpp" #include "../generated/gs_data.hpp" - extern TUNING_PARAMS tuning; -void create_sound_global(int sound, int target=-1); - inline int cmask_all() { return -1; } inline int cmask_one(int cid) { return 1<<cid; } inline int cmask_all_except_one(int cid) { return 0x7fffffff^cmask_one(cid); } inline bool cmask_is_set(int mask, int cid) { return (mask&cmask_one(cid)) != 0; } + + // class EVENT_HANDLER { @@ -33,57 +32,94 @@ public: void snap(int snapping_client); }; -extern EVENT_HANDLER events; - -// a basic entity +/* + Class: Entity + Basic entity class. +*/ class ENTITY { private: friend class GAMEWORLD; // thy these? - friend class PLAYER; ENTITY *prev_entity; ENTITY *next_entity; ENTITY *prev_type_entity; ENTITY *next_type_entity; protected: + bool marked_for_destroy; int id; -public: - float proximity_radius; - unsigned flags; int objtype; - vec2 pos; - - enum - { - FLAG_DESTROY=0x00000001, - FLAG_PHYSICS=0x00000002, - }; +public: ENTITY(int objtype); virtual ~ENTITY(); - virtual void reset() {} - virtual void post_reset() {} - - void set_flag(unsigned flag) { flags |= flag; } - void clear_flag(unsigned flag) { flags &= ~flag; } + ENTITY *typenext() { return next_type_entity; } + ENTITY *typeprev() { return prev_type_entity; } + /* + Function: destroy + Destorys the entity. + */ virtual void destroy() { delete this; } + + /* + Function: reset + Called when the game resets the map. Puts the entity + back to it's starting state or perhaps destroys it. + */ + virtual void reset() {} + + /* + Function: tick + Called progress the entity to the next tick. Updates + and moves the entity to it's new state and position. + */ virtual void tick() {} + + /* + Function: tick + Called after all entities tick() function has been called. + */ virtual void tick_defered() {} + + /* + Function: snap + Called when a new snapshot is being generated for a specific + client. + Arguments: + snapping_client - ID of the client which snapshot is + being generated. Could be -1 to create a complete + snapshot of everything in the game for demo + recording. + */ virtual void snap(int snapping_client) {} - - virtual bool take_damage(vec2 force, int dmg, int from, int weapon) { return true; } + + /* + Variable: proximity_radius + Contains the physical size of the entity. + */ + float proximity_radius; + + /* + Variable: pos + Contains the current posititon of the entity. + */ + vec2 pos; }; +/* + Class: Game World + Tracks all entities in the game. Propagates tick and + snap calls to all entities. +*/ class GAMEWORLD { void reset(); void remove_entities(); -public: + enum { NUM_ENT_TYPES=10, // TODO: are more exact value perhaps? :) @@ -92,30 +128,108 @@ public: // TODO: two lists seams kinda not good, shouldn't be needed ENTITY *first_entity; ENTITY *first_entity_types[NUM_ENT_TYPES]; - bool paused; + +public: bool reset_requested; - + bool paused; WORLD_CORE core; GAMEWORLD(); ~GAMEWORLD(); - int find_entities(vec2 pos, float radius, ENTITY **ents, int max); - int find_entities(vec2 pos, float radius, ENTITY **ents, int max, const int* types, int maxtypes); - - void insert_entity(ENTITY *ent); - void destroy_entity(ENTITY *ent); - void remove_entity(ENTITY *ent); - // + ENTITY *find_first() { return first_entity; } + ENTITY *find_first(int type); + + /* + Function: find_entities + Finds entities close to a position and returns them in a list. + Arguments: + pos - Position. + radius - How close the entities have to be. + ents - Pointer to a list that should be filled with the pointers + to the entities. + max - Number of entities that fits into the ents array. + type - Type of the entities to find. -1 for all types. + Returns: + Number of entities found and added to the ents array. + */ + int find_entities(vec2 pos, float radius, ENTITY **ents, int max, int type = -1); + + /* + Function: interserct_character + Finds the closest character that intersects the line. + Arguments: + pos0 - Start position + pos2 - End position + radius - How for from the line the character is allowed to be. + new_pos - Intersection position + notthis - Entity to ignore intersecting with + Returns: + Returns a pointer to the closest hit or NULL of there is no intersection. + */ + class CHARACTER *intersect_character(vec2 pos0, vec2 pos1, float radius, vec2 &new_pos, class ENTITY *notthis = 0); + + /* + Function: closest_character + Finds the closest character to a specific point. + Arguments: + pos - The center position. + radius - How far off the character is allowed to be + notthis - Entity to ignore + Returns: + Returns a pointer to the closest character or NULL if no character is close enough. + */ + class CHARACTER *closest_character(vec2 pos, float radius, ENTITY *notthis); + + /* + Function: insert_entity + Adds an entity to the world. + Arguments: + entity - Entity to add + */ + void insert_entity(ENTITY *entity); + + /* + Function: remove_entity + Removes an entity from the world. + Arguments: + entity - Entity to remove + */ + void remove_entity(ENTITY *entity); + + /* + Function: destroy_entity + Destroys an entity in the world. + Arguments: + entity - Entity to destroy + */ + void destroy_entity(ENTITY *entity); + + /* + Function: snap + Calls snap on all the entities in the world to create + the snapshot. + Arguments: + snapping_client - ID of the client which snapshot + is being created. + */ void snap(int snapping_client); + + /* + Function: tick + Calls tick on all the entities in the world to progress + the world to the next tick. + + */ void tick(); }; -extern GAMEWORLD *world; - -// game object -// TODO: should change name of this one -class GAMECONTROLLER : public ENTITY +/* + Class: Game Controller + Controls the main game logic. Keeping track of team and player score, + winning conditions and specific game logic. +*/ +class GAMECONTROLLER { protected: void cyclemap(); @@ -145,25 +259,57 @@ public: void endround(); bool is_friendly_fire(int cid1, int cid2); + + /* - virtual bool on_entity(int index, vec2 pos); - - virtual void post_reset(); + */ virtual void tick(); - virtual void on_player_spawn(class PLAYER *p) {} - virtual int on_player_death(class PLAYER *victim, class PLAYER *killer, int weapon); + virtual void snap(int snapping_client); + + /* + Function: on_entity + Called when the map is loaded to process an entity + in the map. + Arguments: + index - Entity index. + pos - Where the entity is located in the world. + Returns: + bool? + */ + virtual bool on_entity(int index, vec2 pos); + /* + Function: on_character_spawn + Called when a character spawns into the game world. + Arguments: + chr - The character that was spawned. + */ + virtual void on_character_spawn(class CHARACTER *chr) {} + + /* + Function: on_character_death + Called when a character in the world dies. + Arguments: + victim - The character that died. + killer - The player that killed it. + weapon - What weapon that killed it. Can be -1 for undefined + weapon when switching team or player suicides. + */ + virtual int on_character_death(class CHARACTER *victim, class PLAYER *killer, int weapon); + virtual void on_player_info_change(class PLAYER *p); + + /* - virtual void snap(int snapping_client); + */ + virtual const char *get_team_name(int team); virtual int get_auto_team(int notthisid); virtual bool can_join_team(int team, int notthisid); int clampteam(int team); -}; - -extern GAMECONTROLLER *gamecontroller; + virtual void post_reset(); +}; // TODO: move to seperate file class PICKUP : public ENTITY @@ -221,25 +367,28 @@ class LASER : public ENTITY float energy; int bounces; int eval_tick; - PLAYER *owner; + CHARACTER *owner; - bool hit_player(vec2 from, vec2 to); + bool hit_character(vec2 from, vec2 to); void do_bounce(); public: - LASER(vec2 pos, vec2 direction, float start_energy, PLAYER *owner); + LASER(vec2 pos, vec2 direction, float start_energy, CHARACTER *owner); virtual void reset(); virtual void tick(); virtual void snap(int snapping_client); }; -// player entity -class PLAYER : public ENTITY + +class CHARACTER : public ENTITY { public: - static const int phys_size = 28; + // player controlling this character + class PLAYER *player; + + bool alive; // weapon info ENTITY *hitobjects[10]; @@ -264,14 +413,13 @@ public: int emote_type; int emote_stop; - int last_action; // last tick that the player took any action ie some input - - // - int client_id; + // TODO: clean this up char skin_name[64]; int use_custom_color; int color_body; int color_feet; + + int last_action; // last tick that the player took any action ie some input // these are non-heldback inputs NETOBJ_PLAYER_INPUT latest_previnput; @@ -298,40 +446,19 @@ public: } ninja; // - int score; + //int score; int team; int player_state; // if the client is chatting, accessing a menu or so - - bool spawning; - bool dead; - int die_tick; - vec2 die_pos; - - // latency calculations - int latency_accum; - int latency_accum_min; - int latency_accum_max; - int latency_avg; - int latency_min; - int latency_max; // the player core for the physics - PLAYER_CORE core; - - // - int64 last_chat; + CHARACTER_CORE core; // - PLAYER(); - void init(); + CHARACTER(); + virtual void reset(); virtual void destroy(); - void try_respawn(); - void respawn(); - - void set_team(int team); - bool is_grounded(); void set_weapon(int w); @@ -341,17 +468,123 @@ public: int handle_weapons(); int handle_ninja(); - - void on_direct_input(NETOBJ_PLAYER_INPUT *input); + + void on_predicted_input(NETOBJ_PLAYER_INPUT *new_input); + void on_direct_input(NETOBJ_PLAYER_INPUT *new_input); void fire_weapon(); + void die(int killer, int weapon); + + bool take_damage(vec2 force, int dmg, int from, int weapon); + + + bool spawn(PLAYER *player, vec2 pos, int team); + //bool init_tryspawn(int team); + bool remove(); + + static const int phys_size = 28; + virtual void tick(); virtual void tick_defered(); + virtual void snap(int snaping_client); - void die(int killer, int weapon); + bool increase_health(int amount); + bool increase_armor(int amount); +}; + +// player object +class PLAYER +{ +public: + PLAYER(); + + // TODO: clean this up + char skin_name[64]; + int use_custom_color; + int color_body; + int color_feet; - virtual bool take_damage(vec2 force, int dmg, int from, int weapon); - virtual void snap(int snaping_client); + // + bool spawning; + int client_id; + int team; + int score; + + // + int64 last_chat; + + // network latency calculations + struct + { + int accum; + int accum_min; + int accum_max; + int avg; + int min; + int max; + } latency; + + CHARACTER character; + + // this is used for snapping so we know how we can clip the view for the player + vec2 view_pos; + + void init(int client_id); + + CHARACTER *get_character(); + + void kill_character(); + + void try_respawn(); + void respawn(); + void set_team(int team); + + void tick(); + void snap(int snaping_client); + + void on_direct_input(NETOBJ_PLAYER_INPUT *new_input); + void on_predicted_input(NETOBJ_PLAYER_INPUT *new_input); + void on_disconnect(); +}; + +class GAMECONTEXT +{ +public: + GAMECONTEXT(); + void clear(); + + EVENT_HANDLER events; + PLAYER players[MAX_CLIENTS]; + + GAMECONTROLLER *controller; + GAMEWORLD world; + + void tick(); + void snap(int client_id); + + // helper functions + 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); + void create_sound_global(int sound, int target=-1); + + // network + void send_chat(int cid, int team, const char *text); + void send_emoticon(int cid, int emoticon); + void send_weapon_pickup(int cid, int weapon); + void send_broadcast(const char *text, int cid); + void send_info(int who, int to_who); }; -extern PLAYER *players; +extern GAMECONTEXT game; + +enum +{ + CHAT_ALL=-2, + CHAT_SPEC=-1, + CHAT_RED=0, + CHAT_BLUE=1 +}; diff --git a/src/game/server/gs_ent_pickup.cpp b/src/game/server/gs_ent_pickup.cpp new file mode 100644 index 00000000..304596d0 --- /dev/null +++ b/src/game/server/gs_ent_pickup.cpp @@ -0,0 +1,140 @@ +#include <engine/e_server_interface.h> +#include "gs_common.hpp" + +////////////////////////////////////////////////// +// 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? + game.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 PICKUP::tick() +{ + // wait for respawn + if(spawntick > 0) + { + if(server_tick() > spawntick) + { + // respawn + spawntick = -1; + + if(type == POWERUP_WEAPON) + game.create_sound(pos, SOUND_WEAPON_SPAWN); + } + else + return; + } + // Check if a player intersected us + CHARACTER *chr = game.world.closest_character(pos, 20.0f, 0); + if(chr) + { + // player picked us up, is someone was hooking us, let them go + int respawntime = -1; + switch (type) + { + case POWERUP_HEALTH: + if(chr->increase_health(1)) + { + game.create_sound(pos, SOUND_PICKUP_HEALTH); + respawntime = data->pickups[type].respawntime; + } + break; + case POWERUP_ARMOR: + if(chr->increase_armor(1)) + { + game.create_sound(pos, SOUND_PICKUP_ARMOR); + respawntime = data->pickups[type].respawntime; + } + break; + + case POWERUP_WEAPON: + if(subtype >= 0 && subtype < NUM_WEAPONS) + { + if(chr->weapons[subtype].ammo < data->weapons.id[subtype].maxammo || !chr->weapons[subtype].got) + { + chr->weapons[subtype].got = true; + chr->weapons[subtype].ammo = min(data->weapons.id[subtype].maxammo, chr->weapons[subtype].ammo + 10); + respawntime = data->pickups[type].respawntime; + + // TODO: data compiler should take care of stuff like this + if(subtype == WEAPON_GRENADE) + game.create_sound(pos, SOUND_PICKUP_GRENADE); + else if(subtype == WEAPON_SHOTGUN) + game.create_sound(pos, SOUND_PICKUP_SHOTGUN); + else if(subtype == WEAPON_RIFLE) + game.create_sound(pos, SOUND_PICKUP_SHOTGUN); + + if(chr->player) + game.send_weapon_pickup(chr->player->client_id, subtype); + } + } + break; + case POWERUP_NINJA: + { + // activate ninja on target player + chr->ninja.activationtick = server_tick(); + chr->weapons[WEAPON_NINJA].got = true; + chr->last_weapon = chr->active_weapon; + chr->active_weapon = WEAPON_NINJA; + respawntime = data->pickups[type].respawntime; + game.create_sound(pos, SOUND_PICKUP_NINJA); + + // loop through all players, setting their emotes + ENTITY *ents[64]; + int num = game.world.find_entities(vec2(0, 0), 1000000, ents, 64, NETOBJTYPE_CHARACTER); + for (int i = 0; i < num; i++) + { + CHARACTER *c = (CHARACTER *)ents[i]; + if (c != chr) + { + c->emote_type = EMOTE_SURPRISE; + c->emote_stop = server_tick() + server_tickspeed(); + } + } + + chr->emote_type = EMOTE_ANGRY; + chr->emote_stop = server_tick() + 1200 * server_tickspeed() / 1000; + + break; + } + default: + break; + }; + + if(respawntime >= 0) + { + dbg_msg("game", "pickup player='%d:%s' item=%d/%d", + chr->player->client_id, server_clientname(chr->player->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; +} diff --git a/src/game/server/gs_ent_player.cpp b/src/game/server/gs_ent_player.cpp new file mode 100644 index 00000000..b476a972 --- /dev/null +++ b/src/game/server/gs_ent_player.cpp @@ -0,0 +1,1128 @@ +#include <new> +#include <engine/e_server_interface.h> +#include <engine/e_config.h> +#include "gs_common.hpp" + + +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; +} + +// player +CHARACTER::CHARACTER() +: ENTITY(NETOBJTYPE_CHARACTER) +{} + +void CHARACTER::reset() +{ + +} + +bool CHARACTER::spawn(PLAYER *player, vec2 pos, int team) +{ + player_state = PLAYERSTATE_UNKNOWN; + emote_stop = -1; + last_action = -1; + active_weapon = WEAPON_GUN; + last_weapon = WEAPON_HAMMER; + queued_weapon = -1; + + //clear(); + this->player = player; + this->pos = pos; + this->team = team; + + core.reset(); + core.world = &game.world.core; + core.pos = pos; + game.world.core.characters[player->client_id] = &core; + + game.world.insert_entity(this); + alive = true; + return true; +} + +void CHARACTER::destroy() +{ + game.world.core.characters[player->client_id] = 0; + alive = false; +} + +void CHARACTER::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; + + game.create_sound(pos, SOUND_WEAPON_SWITCH); +} + +bool CHARACTER::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 CHARACTER::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; + + game.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 + { + CHARACTER *ents[64]; + vec2 dir = pos - oldpos; + float radius = phys_size * 2.0f; //length(dir * 0.5f); + vec2 center = oldpos + dir * 0.5f; + int num = game.world.find_entities(center, radius, (ENTITY**)ents, 64, NETOBJTYPE_CHARACTER); + + 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... + game.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, player->client_id,WEAPON_NINJA); + } + } + return 0; + } + + return 0; +} + + +void CHARACTER::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 CHARACTER::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 CHARACTER::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) + { + game.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; + game.create_sound(pos, SOUND_HAMMER_FIRE); + + CHARACTER *ents[64]; + int num = game.world.find_entities(pos+direction*phys_size*0.75f, phys_size*0.5f, (ENTITY**)ents, 64, NETOBJTYPE_CHARACTER); + + for (int i = 0; i < num; i++) + { + CHARACTER *target = 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) + game.create_sound(pos, SOUND_HAMMER_HIT); + ents[i]->take_damage(vec2(0,-1.0f), data->weapons.hammer.base->damage, player->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, + player->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(player->client_id); + + game.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, + player->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(player->client_id); + + game.create_sound(pos, SOUND_SHOTGUN_FIRE); + } break; + + case WEAPON_GRENADE: + { + PROJECTILE *proj = new PROJECTILE(WEAPON_GRENADE, + player->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(player->client_id); + + game.create_sound(pos, SOUND_GRENADE_FIRE); + } break; + + case WEAPON_RIFLE: + { + new LASER(pos, direction, tuning.laser_reach, this); + game.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 CHARACTER::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 CHARACTER::on_predicted_input(NETOBJ_PLAYER_INPUT *new_input) +{ + // check for changes + if(mem_comp(&input, new_input, sizeof(NETOBJ_PLAYER_INPUT)) != 0) + last_action = server_tick(); + + // copy new input + mem_copy(&input, new_input, sizeof(input)); + num_inputs++; + + // or are not allowed to aim in the center + if(input.target_x == 0 && input.target_y == 0) + input.target_y = -1; +} + +void CHARACTER::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) + { + handle_weaponswitch(); + fire_weapon(); + } +} + +void CHARACTER::tick() +{ + //input = latest_input; + + // 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; + }*/ + + //game.world.core.players[player->client_id] = &core; + + // enable / disable physics + /* + if(team == -1 || dead) + { + game.world.core.players[client_id] = 0; + //game.world.remove_entity(this); + } + else + { + game.world.core.players[client_id] = &core; + //game.world._entity(this); + } + + // 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 CHARACTER::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(player->client_id); + + if(events&COREEVENT_GROUND_JUMP) game.create_sound(pos, SOUND_PLAYER_JUMP, mask); + if(events&COREEVENT_AIR_JUMP) + { + game.create_sound(pos, SOUND_PLAYER_AIRJUMP, mask); + NETEVENT_COMMON *c = (NETEVENT_COMMON *)game.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) game.create_sound(pos, SOUND_HOOK_ATTACH_PLAYER, cmask_all()); + if(events&COREEVENT_HOOK_ATTACH_GROUND) game.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; + } +} + +bool CHARACTER::increase_health(int amount) +{ + if(health >= 10) + return false; + health = clamp(health+amount, 0, 10); + return true; +} + +bool CHARACTER::increase_armor(int amount) +{ + if(armor >= 10) + return false; + armor = clamp(armor+amount, 0, 10); + return true; +} + +void CHARACTER::die(int killer, int weapon) +{ + /*if (dead || team == -1) + return;*/ + int mode_special = game.controller->on_character_death(this, &game.players[killer], weapon); + + dbg_msg("game", "kill killer='%d:%s' victim='%d:%s' weapon=%d special=%d", + killer, server_clientname(killer), + player->client_id, server_clientname(player->client_id), weapon, mode_special); + + // send the kill message + NETMSG_SV_KILLMSG msg; + msg.killer = killer; + msg.victim = player->client_id; + msg.weapon = weapon; + msg.mode_special = mode_special; + msg.pack(MSGFLAG_VITAL); + server_send_msg(-1); + + // a nice sound + game.create_sound(pos, SOUND_PLAYER_DIE); + + // set dead state + // TODO: do stuff here + /* + die_pos = pos; + dead = true; + die_tick = server_tick(); + */ + alive = false; + game.world.remove_entity(this); + game.create_death(pos, player->client_id); +} + +bool CHARACTER::take_damage(vec2 force, int dmg, int from, int weapon) +{ + core.vel += force; + + if(game.controller->is_friendly_fire(player->client_id, from) && !config.sv_teamdamage) + return false; + + // player only inflicts half damage on self + if(from == player->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 + game.create_damageind(pos, damage_taken*0.25f, dmg); + } + else + { + damage_taken = 0; + game.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 != player->client_id) + game.create_sound(game.players[from].view_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 != player->client_id) + { + CHARACTER *chr = &game.players[from].character; + chr->emote_type = EMOTE_HAPPY; + chr->emote_stop = server_tick() + server_tickspeed(); + } + + return false; + } + + if (dmg > 2) + game.create_sound(pos, SOUND_PLAYER_PAIN_LONG); + else + game.create_sound(pos, SOUND_PLAYER_PAIN_SHORT); + + emote_type = EMOTE_PAIN; + emote_stop = server_tick() + 500 * server_tickspeed() / 1000; + + // spawn blood? + return true; +} + +void CHARACTER::snap(int snaping_client) +{ + if(distance(game.players[snaping_client].view_pos, pos) > 1000.0f) + return; + + NETOBJ_CHARACTER *character = (NETOBJ_CHARACTER *)snap_new_item(NETOBJTYPE_CHARACTER, player->client_id, sizeof(NETOBJ_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 != player->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(player->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::PLAYER() +{ +} + +void PLAYER::init(int client_id) +{ + // clear everything + mem_zero(this, sizeof(*this)); + new(this) PLAYER(); + this->client_id = client_id; +} + +void PLAYER::tick() +{ + server_setclientscore(client_id, score); + + // 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; + } + } + + if(spawning && !get_character()) + try_respawn(); + + if(get_character()) + view_pos = get_character()->pos; +} + +void PLAYER::snap(int snaping_client) +{ + 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; +} + +void PLAYER::on_disconnect() +{ + kill_character(); + + //game.controller->on_player_death(&game.players[client_id], 0, -1); + + char buf[512]; + str_format(buf, sizeof(buf), "%s has left the game", server_clientname(client_id)); + game.send_chat(-1, CHAT_ALL, buf); + + dbg_msg("game", "leave player='%d:%s'", client_id, server_clientname(client_id)); + + // clear this whole structure + init(-1); + + /*game.world.remove_entity(&game.players[client_id]); + game.world.core.players[client_id] = 0x0; + game.players[client_id].client_id = -1; */ +} + +void PLAYER::on_predicted_input(NETOBJ_PLAYER_INPUT *new_input) +{ + CHARACTER *chr = get_character(); + if(chr) + chr->on_predicted_input(new_input); +} + +void PLAYER::on_direct_input(NETOBJ_PLAYER_INPUT *new_input) +{ + CHARACTER *chr = get_character(); + if(chr) + chr->on_direct_input(new_input); + + if(!chr && team >= 0 && (new_input->fire&1)) + { + spawning = true; + dbg_msg("", "I wanna spawn"); + } +} + +CHARACTER *PLAYER::get_character() +{ + if(character.alive) + return &character; + return 0; +} + +void PLAYER::kill_character() +{ + CHARACTER *chr = get_character(); + if(chr) + chr->die(-1, -1); +} + +void PLAYER::respawn() +{ + spawning = true; +} + +void PLAYER::set_team(int new_team) +{ + // clamp the team + new_team = game.controller->clampteam(new_team); + if(team == new_team) + return; + + char buf[512]; + str_format(buf, sizeof(buf), "%s joined the %s", server_clientname(client_id), game.controller->get_team_name(new_team)); + game.send_chat(-1, CHAT_ALL, buf); + + kill_character(); + team = new_team; + score = 0; + dbg_msg("game", "team_join player='%d:%s' team=%d", client_id, server_clientname(client_id), team); + + game.controller->on_player_info_change(&game.players[client_id]); + + // send all info to this client + for(int i = 0; i < MAX_CLIENTS; i++) + { + if(game.players[i].client_id != -1) + game.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; + CHARACTER *c = (CHARACTER *)game.world.find_first(NETOBJTYPE_CHARACTER); + for(; c; c = (CHARACTER *)c->typenext()) + { + // team mates are not as dangerous as enemies + float scoremod = 1.0f; + if(eval->friendly_team != -1 && c->team == eval->friendly_team) + scoremod = 0.5f; + + float d = distance(pos, 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(game.controller->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(game.controller->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 num_ents = game.world.find_entities(spawnpos, 64, ents, 2, NETOBJTYPE_CHARACTER); + + if(num_ents == 0) + { + spawning = false; + character.spawn(this, spawnpos, team); + } + + /* + 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; + player_state = PLAYERSTATE_PLAYING; + + game.world.insert_entity(this); + + 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; + + active_weapon = WEAPON_GUN; + last_weapon = WEAPON_HAMMER; + queued_weapon = 0; + + reload_timer = 0; + + // Create sound and spawn effects + game.create_sound(pos, SOUND_PLAYER_SPAWN); + game.create_playerspawn(pos); + + game.controller->on_player_spawn(player); + */ +} diff --git a/src/game/server/gs_game.cpp b/src/game/server/gs_game.cpp index 55ffc288..685d61ad 100644 --- a/src/game/server/gs_game.cpp +++ b/src/game/server/gs_game.cpp @@ -6,7 +6,6 @@ #include "gs_common.hpp" GAMECONTROLLER::GAMECONTROLLER() -: ENTITY(NETOBJTYPE_GAME) { // select gametype if(strcmp(config.sv_gametype, "ctf") == 0) @@ -91,14 +90,32 @@ void GAMECONTROLLER::endround() if(warmup) // game can't end when we are running warmup return; - world->paused = true; + game.world.paused = true; game_over_tick = server_tick(); sudden_death = 0; } void GAMECONTROLLER::resetgame() { - world->reset_requested = true; + game.world.reset_requested = true; +} + +const char *GAMECONTROLLER::get_team_name(int team) +{ + if(is_teamplay) + { + if(team == 0) + return "red team"; + else if(team == 1) + return "blue team"; + } + else + { + if(team == 0) + return "game"; + } + + return "spectators"; } static bool is_separator(char c) { return c == ';' || c == ' ' || c == ',' || c == '\t'; } @@ -110,7 +127,7 @@ void GAMECONTROLLER::startround() round_start_tick = server_tick(); sudden_death = 0; game_over_tick = -1; - world->paused = false; + game.world.paused = false; teamscore[0] = 0; teamscore[1] = 0; round_count++; @@ -178,8 +195,8 @@ void GAMECONTROLLER::post_reset() { for(int i = 0; i < MAX_CLIENTS; i++) { - if(players[i].client_id != -1) - players[i].respawn(); + if(game.players[i].client_id != -1) + game.players[i].respawn(); } } @@ -198,13 +215,13 @@ void GAMECONTROLLER::on_player_info_change(class PLAYER *p) } -int GAMECONTROLLER::on_player_death(class PLAYER *victim, class PLAYER *killer, int weapon) +int GAMECONTROLLER::on_character_death(class CHARACTER *victim, class PLAYER *killer, int weapon) { // do scoreing if(!killer) return 0; - if(killer == victim) - victim->score--; // suicide + if(killer == victim->player) + victim->player->score--; // suicide else { if(is_teamplay && victim->team == killer->team) @@ -227,7 +244,7 @@ bool GAMECONTROLLER::is_friendly_fire(int cid1, int cid2) if(is_teamplay) { - if(players[cid1].team == players[cid2].team) + if(game.players[cid1].team == game.players[cid2].team) return true; } @@ -271,8 +288,8 @@ void GAMECONTROLLER::tick() { for(int i = 0; i < MAX_CLIENTS; i++) { - if(players[i].client_id != -1) - prog = max(prog, (players[i].score*100)/config.sv_scorelimit); + if(game.players[i].client_id != -1) + prog = max(prog, (game.players[i].score*100)/config.sv_scorelimit); } } } @@ -285,20 +302,20 @@ void GAMECONTROLLER::tick() void GAMECONTROLLER::snap(int snapping_client) { - NETOBJ_GAME *game = (NETOBJ_GAME *)snap_new_item(NETOBJTYPE_GAME, 0, sizeof(NETOBJ_GAME)); - game->paused = world->paused; - game->game_over = game_over_tick==-1?0:1; - game->sudden_death = sudden_death; + NETOBJ_GAME *gameobj = (NETOBJ_GAME *)snap_new_item(NETOBJTYPE_GAME, 0, sizeof(NETOBJ_GAME)); + gameobj->paused = game.world.paused; + gameobj->game_over = game_over_tick==-1?0:1; + gameobj->sudden_death = sudden_death; - game->score_limit = config.sv_scorelimit; - game->time_limit = config.sv_timelimit; - game->round_start_tick = round_start_tick; - game->gametype = gametype; + gameobj->score_limit = config.sv_scorelimit; + gameobj->time_limit = config.sv_timelimit; + gameobj->round_start_tick = round_start_tick; + gameobj->gametype = gametype; - game->warmup = warmup; + gameobj->warmup = warmup; - game->teamscore_red = teamscore[0]; - game->teamscore_blue = teamscore[1]; + gameobj->teamscore_red = teamscore[0]; + gameobj->teamscore_blue = teamscore[1]; } int GAMECONTROLLER::get_auto_team(int notthisid) @@ -306,10 +323,10 @@ int GAMECONTROLLER::get_auto_team(int notthisid) int numplayers[2] = {0,0}; for(int i = 0; i < MAX_CLIENTS; i++) { - if(players[i].client_id != -1 && players[i].client_id != notthisid) + if(game.players[i].client_id != -1 && game.players[i].client_id != notthisid) { - if(players[i].team == 0 || players[i].team == 1) - numplayers[players[i].team]++; + if(game.players[i].team == 0 || game.players[i].team == 1) + numplayers[game.players[i].team]++; } } @@ -328,10 +345,10 @@ bool GAMECONTROLLER::can_join_team(int team, int notthisid) int numplayers[2] = {0,0}; for(int i = 0; i < MAX_CLIENTS; i++) { - if(players[i].client_id != -1 && players[i].client_id != notthisid) + if(game.players[i].client_id != -1 && game.players[i].client_id != notthisid) { - if(players[i].team >= 0 || players[i].team == 1) - numplayers[players[i].team]++; + if(game.players[i].team >= 0 || game.players[i].team == 1) + numplayers[game.players[i].team]++; } } @@ -347,14 +364,14 @@ void GAMECONTROLLER::do_player_score_wincheck() int topscore_count = 0; for(int i = 0; i < MAX_CLIENTS; i++) { - if(players[i].client_id != -1) + if(game.players[i].client_id != -1) { - if(players[i].score > topscore) + if(game.players[i].score > topscore) { - topscore = players[i].score; + topscore = game.players[i].score; topscore_count = 1; } - else if(players[i].score == topscore) + else if(game.players[i].score == topscore) topscore_count++; } } diff --git a/src/game/server/gs_game_ctf.cpp b/src/game/server/gs_game_ctf.cpp index 031be42a..27da121a 100644 --- a/src/game/server/gs_game_ctf.cpp +++ b/src/game/server/gs_game_ctf.cpp @@ -29,26 +29,22 @@ bool GAMECONTROLLER_CTF::on_entity(int index, vec2 pos) return true; } -void GAMECONTROLLER_CTF::on_player_spawn(class PLAYER *p) +int GAMECONTROLLER_CTF::on_character_death(class CHARACTER *victim, class PLAYER *killer, int weaponid) { -} - -int GAMECONTROLLER_CTF::on_player_death(class PLAYER *victim, class PLAYER *killer, int weaponid) -{ - GAMECONTROLLER::on_player_death(victim, killer, weaponid); + GAMECONTROLLER::on_character_death(victim, killer, weaponid); int had_flag = 0; // drop flags for(int fi = 0; fi < 2; fi++) { FLAG *f = flags[fi]; - if(f && f->carrying_player == killer) + if(f && f->carrying_character == &killer->character) had_flag |= 2; - if(f && f->carrying_player == victim) + if(f && f->carrying_character == victim) { - create_sound_global(SOUND_CTF_DROP); + game.create_sound_global(SOUND_CTF_DROP); f->drop_tick = server_tick(); - f->carrying_player = 0; + f->carrying_character = 0; f->vel = vec2(0,0); if(killer && killer->team != victim->team) @@ -75,10 +71,10 @@ void GAMECONTROLLER_CTF::tick() continue; // - if(f->carrying_player) + if(f->carrying_character) { // update flag position - f->pos = f->carrying_player->pos; + f->pos = f->carrying_character->pos; if(flags[fi^1] && flags[fi^1]->at_stand) { @@ -86,35 +82,38 @@ void GAMECONTROLLER_CTF::tick() { // CAPTURE! \o/ teamscore[fi^1] += 100; - f->carrying_player->score += 5; + f->carrying_character->player->score += 5; - dbg_msg("game", "flag_capture player='%d:%s'", f->carrying_player->client_id, server_clientname(f->carrying_player->client_id)); + dbg_msg("game", "flag_capture player='%d:%s'", + f->carrying_character->player->client_id, + server_clientname(f->carrying_character->player->client_id)); for(int i = 0; i < 2; i++) flags[i]->reset(); - create_sound_global(SOUND_CTF_CAPTURE); + game.create_sound_global(SOUND_CTF_CAPTURE); } } } else { - PLAYER *close_players[MAX_CLIENTS]; - int types[] = {NETOBJTYPE_PLAYER_CHARACTER}; - int num = world->find_entities(f->pos, 32.0f, (ENTITY**)close_players, MAX_CLIENTS, types, 1); + CHARACTER *close_characters[MAX_CLIENTS]; + int num = game.world.find_entities(f->pos, 32.0f, (ENTITY**)close_characters, MAX_CLIENTS, NETOBJTYPE_CHARACTER); for(int i = 0; i < num; i++) { - if(close_players[i]->team == f->team) + if(close_characters[i]->team == f->team) { // return the flag if(!f->at_stand) { - PLAYER *p = close_players[i]; - p->score += 1; + CHARACTER *chr = close_characters[i]; + chr->player->score += 1; - dbg_msg("game", "flag_return player='%d:%s'", p->client_id, server_clientname(p->client_id)); + dbg_msg("game", "flag_return player='%d:%s'", + chr->player->client_id, + server_clientname(chr->player->client_id)); - create_sound_global(SOUND_CTF_RETURN); + game.create_sound_global(SOUND_CTF_RETURN); f->reset(); } } @@ -124,35 +123,37 @@ void GAMECONTROLLER_CTF::tick() if(f->at_stand) teamscore[fi^1]++; f->at_stand = 0; - f->carrying_player = close_players[i]; - f->carrying_player->score += 1; + f->carrying_character = close_characters[i]; + f->carrying_character->player->score += 1; - dbg_msg("game", "flag_grab player='%d:%s'", f->carrying_player->client_id, server_clientname(f->carrying_player->client_id)); + dbg_msg("game", "flag_grab player='%d:%s'", + f->carrying_character->player->client_id, + server_clientname(f->carrying_character->player->client_id)); for(int c = 0; c < MAX_CLIENTS; c++) { - if(players[c].client_id == -1) + if(game.players[c].client_id == -1) continue; - if(players[c].team == fi) - create_sound_global(SOUND_CTF_GRAB_EN, players[c].client_id); + if(game.players[c].team == fi) + game.create_sound_global(SOUND_CTF_GRAB_EN, game.players[c].client_id); else - create_sound_global(SOUND_CTF_GRAB_PL, players[c].client_id); + game.create_sound_global(SOUND_CTF_GRAB_PL, game.players[c].client_id); } break; } } - if(!f->carrying_player && !f->at_stand) + if(!f->carrying_character && !f->at_stand) { if(server_tick() > f->drop_tick + server_tickspeed()*30) { - create_sound_global(SOUND_CTF_RETURN); + game.create_sound_global(SOUND_CTF_RETURN); f->reset(); } else { - f->vel.y += world->core.tuning.gravity; + f->vel.y += game.world.core.tuning.gravity; move_box(&f->pos, &f->vel, vec2(f->phys_size, f->phys_size), 0.5f); } } @@ -166,17 +167,17 @@ FLAG::FLAG(int _team) { team = _team; proximity_radius = phys_size; - carrying_player = 0x0; + carrying_character = 0x0; reset(); // TODO: should this be done here? - world->insert_entity(this); + game.world.insert_entity(this); } void FLAG::reset() { - carrying_player = 0; + carrying_character = 0; at_stand = 1; pos = stand_pos; vel = vec2(0,0); @@ -192,6 +193,6 @@ void FLAG::snap(int snapping_client) if(at_stand) flag->carried_by = -2; - else if(carrying_player) - flag->carried_by = carrying_player->client_id; + else if(carrying_character) + flag->carried_by = carrying_character->player->client_id; } diff --git a/src/game/server/gs_game_ctf.hpp b/src/game/server/gs_game_ctf.hpp index 6d512815..bf6282e7 100644 --- a/src/game/server/gs_game_ctf.hpp +++ b/src/game/server/gs_game_ctf.hpp @@ -10,9 +10,7 @@ public: virtual void tick(); virtual bool on_entity(int index, vec2 pos); - - virtual void on_player_spawn(class PLAYER *p); - virtual int on_player_death(class PLAYER *victim, class PLAYER *killer, int weapon); + virtual int on_character_death(class CHARACTER *victim, class PLAYER *killer, int weapon); }; // TODO: move to seperate file @@ -20,7 +18,7 @@ class FLAG : public ENTITY { public: static const int phys_size = 14; - PLAYER *carrying_player; + CHARACTER *carrying_character; vec2 vel; vec2 stand_pos; diff --git a/src/game/server/gs_game_tdm.cpp b/src/game/server/gs_game_tdm.cpp index c591b447..d865a65a 100644 --- a/src/game/server/gs_game_tdm.cpp +++ b/src/game/server/gs_game_tdm.cpp @@ -8,14 +8,14 @@ GAMECONTROLLER_TDM::GAMECONTROLLER_TDM() is_teamplay = true; } -int GAMECONTROLLER_TDM::on_player_death(class PLAYER *victim, class PLAYER *killer, int weapon) +int GAMECONTROLLER_TDM::on_character_death(class CHARACTER *victim, class PLAYER *killer, int weapon) { - GAMECONTROLLER::on_player_death(victim, killer, weapon); + GAMECONTROLLER::on_character_death(victim, killer, weapon); if(weapon >= 0) { // do team scoring - if(killer == victim) + if(killer == victim->player) teamscore[killer->team&1]--; // klant arschel else teamscore[killer->team&1]++; // good shit diff --git a/src/game/server/gs_game_tdm.hpp b/src/game/server/gs_game_tdm.hpp index 516b581c..bb35260c 100644 --- a/src/game/server/gs_game_tdm.hpp +++ b/src/game/server/gs_game_tdm.hpp @@ -5,6 +5,6 @@ class GAMECONTROLLER_TDM : public GAMECONTROLLER public: GAMECONTROLLER_TDM(); - int on_player_death(class PLAYER *victim, class PLAYER *killer, int weapon); + int on_character_death(class CHARACTER *victim, class PLAYER *killer, int weapon); virtual void tick(); }; 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; } |