diff --git a/src/game/server/gs_ent_player.cpp b/src/game/server/entities/character.cpp similarity index 99% rename from src/game/server/gs_ent_player.cpp rename to src/game/server/entities/character.cpp index b476a9725..160e080a9 100644 --- a/src/game/server/gs_ent_player.cpp +++ b/src/game/server/entities/character.cpp @@ -1,8 +1,11 @@ #include #include #include -#include "gs_common.hpp" +#include +#include "character.hpp" +#include "laser.hpp" +#include "projectile.hpp" struct INPUT_COUNT { diff --git a/src/game/server/entities/character.hpp b/src/game/server/entities/character.hpp new file mode 100644 index 000000000..6ceb987c5 --- /dev/null +++ b/src/game/server/entities/character.hpp @@ -0,0 +1,122 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ + +#ifndef GAME_SERVER_ENTITY_CHARACTER_H +#define GAME_SERVER_ENTITY_CHARACTER_H + +#include +#include +#include + +#include + +class CHARACTER : public ENTITY +{ +public: + // player controlling this character + class PLAYER *player; + + bool alive; + + // weapon info + ENTITY *hitobjects[10]; + int numobjectshit; + struct WEAPONSTAT + { + int ammoregenstart; + int ammo; + int ammocost; + bool got; + } weapons[NUM_WEAPONS]; + + int active_weapon; + int last_weapon; + int queued_weapon; + + int reload_timer; + int attack_tick; + + int damage_taken; + + int emote_type; + int emote_stop; + + // 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; + NETOBJ_PLAYER_INPUT latest_input; + + // input + NETOBJ_PLAYER_INPUT previnput; + NETOBJ_PLAYER_INPUT input; + int num_inputs; + int jumped; + + int damage_taken_tick; + + int health; + int armor; + + // ninja + struct + { + vec2 activationdir; + int activationtick; + int currentcooldown; + int currentmovetime; + } ninja; + + // + //int score; + int team; + int player_state; // if the client is chatting, accessing a menu or so + + // the player core for the physics + CHARACTER_CORE core; + + // + CHARACTER(); + + virtual void reset(); + virtual void destroy(); + + bool is_grounded(); + + void set_weapon(int w); + + void handle_weaponswitch(); + void do_weaponswitch(); + + int handle_weapons(); + int handle_ninja(); + + 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); + + bool increase_health(int amount); + bool increase_armor(int amount); +}; + +#endif diff --git a/src/game/server/entities/laser.cpp b/src/game/server/entities/laser.cpp new file mode 100644 index 000000000..cedf78507 --- /dev/null +++ b/src/game/server/entities/laser.cpp @@ -0,0 +1,112 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ +#include +#include +#include +#include "laser.hpp" + +////////////////////////////////////////////////// +// laser +////////////////////////////////////////////////// +LASER::LASER(vec2 pos, vec2 direction, float start_energy, CHARACTER *owner) +: ENTITY(NETOBJTYPE_LASER) +{ + this->pos = pos; + this->owner = owner; + energy = start_energy; + dir = direction; + bounces = 0; + do_bounce(); + + game.world.insert_entity(this); +} + + +bool LASER::hit_character(vec2 from, vec2 to) +{ + vec2 at; + 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->player->client_id, WEAPON_RIFLE); + return true; +} + +void LASER::do_bounce() +{ + eval_tick = server_tick(); + + if(energy < 0) + { + //dbg_msg("laser", "%d removed", server_tick()); + game.world.destroy_entity(this); + return; + } + + vec2 to = pos + dir*energy; + + if(col_intersect_line(pos, to, &to)) + { + if(!hit_character(pos, to)) + { + // intersected + from = pos; + pos = to - dir*2; + vec2 temp_pos = pos; + vec2 temp_dir = dir*4.0f; + + move_point(&temp_pos, &temp_dir, 1.0f, 0); + pos = temp_pos; + dir = normalize(temp_dir); + + energy -= distance(from, pos) + tuning.laser_bounce_cost; + bounces++; + + if(bounces > tuning.laser_bounce_num) + energy = -1; + + game.create_sound(pos, SOUND_RIFLE_BOUNCE); + } + } + else + { + if(!hit_character(pos, to)) + { + from = pos; + pos = to; + energy = -1; + } + } + + //dbg_msg("laser", "%d done %f %f %f %f", server_tick(), from.x, from.y, pos.x, pos.y); +} + +void LASER::reset() +{ + game.world.destroy_entity(this); +} + +void LASER::tick() +{ + if(server_tick() > eval_tick+(server_tickspeed()*tuning.laser_bounce_delay)/1000.0f) + { + do_bounce(); + } + +} + +void LASER::snap(int snapping_client) +{ + 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)); + obj->x = (int)pos.x; + obj->y = (int)pos.y; + obj->from_x = (int)from.x; + obj->from_y = (int)from.y; + obj->start_tick = eval_tick; +} diff --git a/src/game/server/entities/laser.hpp b/src/game/server/entities/laser.hpp new file mode 100644 index 000000000..4842b3f87 --- /dev/null +++ b/src/game/server/entities/laser.hpp @@ -0,0 +1,31 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ + +#ifndef GAME_SERVER_ENTITY_LASER_H +#define GAME_SERVER_ENTITY_LASER_H + +#include + +class CHARACTER; + +class LASER : public ENTITY +{ + vec2 from; + vec2 dir; + float energy; + int bounces; + int eval_tick; + CHARACTER *owner; + + bool hit_character(vec2 from, vec2 to); + void do_bounce(); + +public: + + LASER(vec2 pos, vec2 direction, float start_energy, CHARACTER *owner); + + virtual void reset(); + virtual void tick(); + virtual void snap(int snapping_client); +}; + +#endif diff --git a/src/game/server/gs_ent_pickup.cpp b/src/game/server/entities/pickup.cpp similarity index 97% rename from src/game/server/gs_ent_pickup.cpp rename to src/game/server/entities/pickup.cpp index 304596d0a..a4a3a2c91 100644 --- a/src/game/server/gs_ent_pickup.cpp +++ b/src/game/server/entities/pickup.cpp @@ -1,8 +1,10 @@ #include -#include "gs_common.hpp" +#include +#include +#include "pickup.hpp" ////////////////////////////////////////////////// -// powerup +// pickup ////////////////////////////////////////////////// PICKUP::PICKUP(int _type, int _subtype) : ENTITY(NETOBJTYPE_PICKUP) diff --git a/src/game/server/entities/pickup.hpp b/src/game/server/entities/pickup.hpp new file mode 100644 index 000000000..cd480d92a --- /dev/null +++ b/src/game/server/entities/pickup.hpp @@ -0,0 +1,24 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ + +#ifndef GAME_SERVER_ENTITY_PICKUP_H +#define GAME_SERVER_ENTITY_PICKUP_H + +#include + +// TODO: move to seperate file +class PICKUP : public ENTITY +{ +public: + static const int phys_size = 14; + + int type; + int subtype; // weapon type for instance? + int spawntick; + PICKUP(int _type, int _subtype = 0); + + virtual void reset(); + virtual void tick(); + virtual void snap(int snapping_client); +}; + +#endif diff --git a/src/game/server/entities/projectile.cpp b/src/game/server/entities/projectile.cpp new file mode 100644 index 000000000..d258e69ef --- /dev/null +++ b/src/game/server/entities/projectile.cpp @@ -0,0 +1,108 @@ +#include +#include +#include +#include "projectile.hpp" + + +////////////////////////////////////////////////// +// projectile +////////////////////////////////////////////////// +PROJECTILE::PROJECTILE(int type, int owner, vec2 pos, vec2 dir, int span, ENTITY* powner, + int damage, int flags, float force, int sound_impact, int weapon) +: ENTITY(NETOBJTYPE_PROJECTILE) +{ + this->type = type; + this->pos = pos; + this->direction = dir; + this->lifespan = span; + this->owner = owner; + this->powner = powner; + this->flags = flags; + this->force = force; + this->damage = damage; + this->sound_impact = sound_impact; + this->weapon = weapon; + this->bounce = 0; + this->start_tick = server_tick(); + game.world.insert_entity(this); +} + +void PROJECTILE::reset() +{ + game.world.destroy_entity(this); +} + +vec2 PROJECTILE::get_pos(float time) +{ + float curvature = 0; + float speed = 0; + if(type == WEAPON_GRENADE) + { + curvature = tuning.grenade_curvature; + speed = tuning.grenade_speed; + } + else if(type == WEAPON_SHOTGUN) + { + curvature = tuning.shotgun_curvature; + speed = tuning.shotgun_speed; + } + else if(type == WEAPON_GUN) + { + curvature = tuning.gun_curvature; + speed = tuning.gun_speed; + } + + return calc_pos(pos, direction, curvature, speed, time); +} + + +void PROJECTILE::tick() +{ + + float pt = (server_tick()-start_tick-1)/(float)server_tickspeed(); + float ct = (server_tick()-start_tick)/(float)server_tickspeed(); + vec2 prevpos = get_pos(pt); + vec2 curpos = get_pos(ct); + + lifespan--; + + int collide = col_intersect_line(prevpos, curpos, &curpos); + //int collide = col_check_point((int)curpos.x, (int)curpos.y); + + CHARACTER *targetchr = game.world.intersect_character(prevpos, curpos, 6.0f, curpos, powner); + if(targetchr || collide || lifespan < 0) + { + if(lifespan >= 0 || weapon == WEAPON_GRENADE) + game.create_sound(curpos, sound_impact); + + if(flags & PROJECTILE_FLAGS_EXPLODE) + game.create_explosion(curpos, owner, weapon, false); + else if(targetchr) + { + targetchr->take_damage(direction * max(0.001f, force), damage, owner, weapon); + } + + game.world.destroy_entity(this); + } +} + +void PROJECTILE::fill_info(NETOBJ_PROJECTILE *proj) +{ + proj->x = (int)pos.x; + proj->y = (int)pos.y; + proj->vx = (int)(direction.x*100.0f); + proj->vy = (int)(direction.y*100.0f); + proj->start_tick = start_tick; + proj->type = type; +} + +void PROJECTILE::snap(int snapping_client) +{ + float ct = (server_tick()-start_tick)/(float)server_tickspeed(); + + 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)); + fill_info(proj); +} diff --git a/src/game/server/entities/projectile.hpp b/src/game/server/entities/projectile.hpp new file mode 100644 index 000000000..c1370af18 --- /dev/null +++ b/src/game/server/entities/projectile.hpp @@ -0,0 +1,38 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ + +#ifndef GAME_SERVER_ENTITY_PROJECTILE_H +#define GAME_SERVER_ENTITY_PROJECTILE_H + +class PROJECTILE : public ENTITY +{ +public: + enum + { + PROJECTILE_FLAGS_EXPLODE = 1 << 0, + }; + + vec2 direction; + ENTITY *powner; // this is nasty, could be removed when client quits + int lifespan; + int owner; + int type; + int flags; + int damage; + int sound_impact; + int weapon; + int bounce; + float force; + int start_tick; + + PROJECTILE(int type, int owner, vec2 pos, vec2 vel, int span, ENTITY* powner, + int damage, int flags, float force, int sound_impact, int weapon); + + vec2 get_pos(float time); + void fill_info(NETOBJ_PROJECTILE *proj); + + virtual void reset(); + virtual void tick(); + virtual void snap(int snapping_client); +}; + +#endif diff --git a/src/game/server/entity.hpp b/src/game/server/entity.hpp new file mode 100644 index 000000000..d3ae3a1cd --- /dev/null +++ b/src/game/server/entity.hpp @@ -0,0 +1,83 @@ +#ifndef GAME_SERVER_ENTITY_H +#define GAME_SERVER_ENTITY_H + +#include + +/* + Class: Entity + Basic entity class. +*/ +class ENTITY +{ +private: + friend class GAMEWORLD; // thy these? + ENTITY *prev_entity; + ENTITY *next_entity; + + ENTITY *prev_type_entity; + ENTITY *next_type_entity; +protected: + bool marked_for_destroy; + int id; + int objtype; +public: + + ENTITY(int objtype); + virtual ~ENTITY(); + + 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_defered + 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) {} + + /* + Variable: proximity_radius + Contains the physical size of the entity. + */ + float proximity_radius; + + /* + Variable: pos + Contains the current posititon of the entity. + */ + vec2 pos; +}; + +#endif diff --git a/src/game/server/eventhandler.hpp b/src/game/server/eventhandler.hpp new file mode 100644 index 000000000..4d5131545 --- /dev/null +++ b/src/game/server/eventhandler.hpp @@ -0,0 +1,25 @@ +#ifndef GAME_SERVER_EVENTHANDLER_H +#define GAME_SERVER_EVENTHANDLER_H + +// +class EVENTHANDLER +{ + static const int MAX_EVENTS = 128; + static const int MAX_DATASIZE = 128*64; + + int types[MAX_EVENTS]; // TODO: remove some of these arrays + int offsets[MAX_EVENTS]; + int sizes[MAX_EVENTS]; + int client_masks[MAX_EVENTS]; + char data[MAX_DATASIZE]; + + int current_offset; + int num_events; +public: + EVENTHANDLER(); + void *create(int type, int size, int mask = -1); + void clear(); + void snap(int snapping_client); +}; + +#endif diff --git a/src/game/server/gamecontext.hpp b/src/game/server/gamecontext.hpp new file mode 100644 index 000000000..c4035b3f6 --- /dev/null +++ b/src/game/server/gamecontext.hpp @@ -0,0 +1,38 @@ + +#include "eventhandler.hpp" +#include "gamecontroller.hpp" +#include "gameworld.hpp" + +class GAMECONTEXT +{ +public: + GAMECONTEXT(); + void clear(); + + EVENTHANDLER 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 GAMECONTEXT game; diff --git a/src/game/server/gs_game.cpp b/src/game/server/gamecontroller.cpp similarity index 99% rename from src/game/server/gs_game.cpp rename to src/game/server/gamecontroller.cpp index 685d61ad3..aeab559c5 100644 --- a/src/game/server/gs_game.cpp +++ b/src/game/server/gamecontroller.cpp @@ -5,6 +5,8 @@ #include #include "gs_common.hpp" +#include "entities/pickup.hpp" + GAMECONTROLLER::GAMECONTROLLER() { // select gametype diff --git a/src/game/server/gamecontroller.hpp b/src/game/server/gamecontroller.hpp new file mode 100644 index 000000000..a3cfe9ba7 --- /dev/null +++ b/src/game/server/gamecontroller.hpp @@ -0,0 +1,97 @@ +#ifndef GAME_SERVER_GAMECONTROLLER_H +#define GAME_SERVER_GAMECONTROLLER_H + +#include + +/* + 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(); + void resetgame(); + + int round_start_tick; + int game_over_tick; + int sudden_death; + + int teamscore[2]; + + int warmup; + int round_count; + + bool is_teamplay; + +public: + int gametype; + GAMECONTROLLER(); + + void do_team_score_wincheck(); + void do_player_score_wincheck(); + + void do_warmup(int seconds); + + void startround(); + void endround(); + + bool is_friendly_fire(int cid1, int cid2); + + /* + + */ + virtual void tick(); + + 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 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); + + virtual void post_reset(); +}; + +#endif diff --git a/src/game/server/gs_game_ctf.cpp b/src/game/server/gamemodes/ctf.cpp similarity index 96% rename from src/game/server/gs_game_ctf.cpp rename to src/game/server/gamemodes/ctf.cpp index 27da121aa..4733ae827 100644 --- a/src/game/server/gs_game_ctf.cpp +++ b/src/game/server/gamemodes/ctf.cpp @@ -1,8 +1,10 @@ /* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ #include #include -#include "gs_common.hpp" -#include "gs_game_ctf.hpp" +#include +#include +#include +#include "ctf.hpp" GAMECONTROLLER_CTF::GAMECONTROLLER_CTF() { diff --git a/src/game/server/gs_game_ctf.hpp b/src/game/server/gamemodes/ctf.hpp similarity index 89% rename from src/game/server/gs_game_ctf.hpp rename to src/game/server/gamemodes/ctf.hpp index bf6282e7b..636f9f38b 100644 --- a/src/game/server/gs_game_ctf.hpp +++ b/src/game/server/gamemodes/ctf.hpp @@ -1,6 +1,8 @@ /* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -// game object +#include +#include + class GAMECONTROLLER_CTF : public GAMECONTROLLER { public: diff --git a/src/game/server/gs_game_dm.cpp b/src/game/server/gamemodes/dm.cpp similarity index 66% rename from src/game/server/gs_game_dm.cpp rename to src/game/server/gamemodes/dm.cpp index 264063bf3..b38d18f64 100644 --- a/src/game/server/gs_game_dm.cpp +++ b/src/game/server/gamemodes/dm.cpp @@ -1,7 +1,5 @@ /* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#include -#include "gs_common.hpp" -#include "gs_game_dm.hpp" +#include "dm.hpp" void GAMECONTROLLER_DM::tick() { diff --git a/src/game/server/gs_game_dm.hpp b/src/game/server/gamemodes/dm.hpp similarity index 77% rename from src/game/server/gs_game_dm.hpp rename to src/game/server/gamemodes/dm.hpp index 99ceaec16..f57fe06d5 100644 --- a/src/game/server/gs_game_dm.hpp +++ b/src/game/server/gamemodes/dm.hpp @@ -1,5 +1,7 @@ /* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -// game object + +#include + class GAMECONTROLLER_DM : public GAMECONTROLLER { public: diff --git a/src/game/server/gs_game_tdm.cpp b/src/game/server/gamemodes/tdm.cpp similarity index 80% rename from src/game/server/gs_game_tdm.cpp rename to src/game/server/gamemodes/tdm.cpp index d865a65ab..26441c9f7 100644 --- a/src/game/server/gs_game_tdm.cpp +++ b/src/game/server/gamemodes/tdm.cpp @@ -1,7 +1,8 @@ /* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#include -#include "gs_common.hpp" -#include "gs_game_tdm.hpp" +#include +#include +#include +#include "tdm.hpp" GAMECONTROLLER_TDM::GAMECONTROLLER_TDM() { diff --git a/src/game/server/gs_game_tdm.hpp b/src/game/server/gamemodes/tdm.hpp similarity index 85% rename from src/game/server/gs_game_tdm.hpp rename to src/game/server/gamemodes/tdm.hpp index bb35260ca..51c47ca5e 100644 --- a/src/game/server/gs_game_tdm.hpp +++ b/src/game/server/gamemodes/tdm.hpp @@ -1,5 +1,7 @@ /* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -// game object + +#include + class GAMECONTROLLER_TDM : public GAMECONTROLLER { public: diff --git a/src/game/server/gameworld.hpp b/src/game/server/gameworld.hpp new file mode 100644 index 000000000..76b39d23f --- /dev/null +++ b/src/game/server/gameworld.hpp @@ -0,0 +1,126 @@ + +class CHARACTER; + +/* + Class: Game World + Tracks all entities in the game. Propagates tick and + snap calls to all entities. +*/ +class GAMEWORLD +{ + void reset(); + void remove_entities(); + + enum + { + NUM_ENT_TYPES=10, // TODO: are more exact value perhaps? :) + }; + + // TODO: two lists seams kinda not good, shouldn't be needed + ENTITY *first_entity; + ENTITY *first_entity_types[NUM_ENT_TYPES]; + +public: + bool reset_requested; + bool paused; + WORLD_CORE core; + + GAMEWORLD(); + ~GAMEWORLD(); + + 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(); +}; diff --git a/src/game/server/gs_common.hpp b/src/game/server/gs_common.hpp index ce03d7283..37bb2638b 100644 --- a/src/game/server/gs_common.hpp +++ b/src/game/server/gs_common.hpp @@ -32,590 +32,15 @@ inline bool cmask_is_set(int mask, int cid) { return (mask&cmask_one(cid)) != 0; */ +#include "eventhandler.hpp" -// -class EVENT_HANDLER -{ - static const int MAX_EVENTS = 128; - static const int MAX_DATASIZE = 128*64; - - int types[MAX_EVENTS]; // TODO: remove some of these arrays - int offsets[MAX_EVENTS]; - int sizes[MAX_EVENTS]; - int client_masks[MAX_EVENTS]; - char data[MAX_DATASIZE]; - - int current_offset; - int num_events; -public: - EVENT_HANDLER(); - void *create(int type, int size, int mask = -1); - void clear(); - void snap(int snapping_client); -}; - -/* - Class: Entity - Basic entity class. -*/ -class ENTITY -{ -private: - friend class GAMEWORLD; // thy these? - ENTITY *prev_entity; - ENTITY *next_entity; - - ENTITY *prev_type_entity; - ENTITY *next_type_entity; -protected: - bool marked_for_destroy; - int id; - int objtype; -public: - - ENTITY(int objtype); - virtual ~ENTITY(); - - 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_defered - 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) {} - - /* - Variable: proximity_radius - Contains the physical size of the entity. - */ - float proximity_radius; - - /* - Variable: pos - Contains the current posititon of the entity. - */ - vec2 pos; -}; +#include "entity.hpp" -/* - Class: Game World - Tracks all entities in the game. Propagates tick and - snap calls to all entities. -*/ -class GAMEWORLD -{ - void reset(); - void remove_entities(); - - enum - { - NUM_ENT_TYPES=10, // TODO: are more exact value perhaps? :) - }; - - // TODO: two lists seams kinda not good, shouldn't be needed - ENTITY *first_entity; - ENTITY *first_entity_types[NUM_ENT_TYPES]; - -public: - bool reset_requested; - bool paused; - WORLD_CORE core; - - GAMEWORLD(); - ~GAMEWORLD(); - - 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(); -}; - -/* - 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(); - void resetgame(); - - int round_start_tick; - int game_over_tick; - int sudden_death; - - int teamscore[2]; - - int warmup; - int round_count; - - bool is_teamplay; - -public: - int gametype; - GAMECONTROLLER(); - - void do_team_score_wincheck(); - void do_player_score_wincheck(); - - void do_warmup(int seconds); - - void startround(); - void endround(); - - bool is_friendly_fire(int cid1, int cid2); - - /* - - */ - virtual void tick(); - - 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 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); - - virtual void post_reset(); -}; - -// TODO: move to seperate file -class PICKUP : public ENTITY -{ -public: - static const int phys_size = 14; - - int type; - int subtype; // weapon type for instance? - int spawntick; - PICKUP(int _type, int _subtype = 0); - - virtual void reset(); - virtual void tick(); - virtual void snap(int snapping_client); -}; - -// projectile entity -class PROJECTILE : public ENTITY -{ -public: - enum - { - PROJECTILE_FLAGS_EXPLODE = 1 << 0, - }; - - vec2 direction; - ENTITY *powner; // this is nasty, could be removed when client quits - int lifespan; - int owner; - int type; - int flags; - int damage; - int sound_impact; - int weapon; - int bounce; - float force; - int start_tick; - - PROJECTILE(int type, int owner, vec2 pos, vec2 vel, int span, ENTITY* powner, - int damage, int flags, float force, int sound_impact, int weapon); - - vec2 get_pos(float time); - void fill_info(NETOBJ_PROJECTILE *proj); - - virtual void reset(); - virtual void tick(); - virtual void snap(int snapping_client); -}; - -class LASER : public ENTITY -{ - vec2 from; - vec2 dir; - float energy; - int bounces; - int eval_tick; - CHARACTER *owner; - - bool hit_character(vec2 from, vec2 to); - void do_bounce(); - -public: - - LASER(vec2 pos, vec2 direction, float start_energy, CHARACTER *owner); - - virtual void reset(); - virtual void tick(); - virtual void snap(int snapping_client); -}; - - -class CHARACTER : public ENTITY -{ -public: - // player controlling this character - class PLAYER *player; - - bool alive; - - // weapon info - ENTITY *hitobjects[10]; - int numobjectshit; - struct WEAPONSTAT - { - int ammoregenstart; - int ammo; - int ammocost; - bool got; - } weapons[NUM_WEAPONS]; - - int active_weapon; - int last_weapon; - int queued_weapon; - - int reload_timer; - int attack_tick; - - int damage_taken; - - int emote_type; - int emote_stop; - - // 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; - NETOBJ_PLAYER_INPUT latest_input; - - // input - NETOBJ_PLAYER_INPUT previnput; - NETOBJ_PLAYER_INPUT input; - int num_inputs; - int jumped; - - int damage_taken_tick; - - int health; - int armor; - - // ninja - struct - { - vec2 activationdir; - int activationtick; - int currentcooldown; - int currentmovetime; - } ninja; - - // - //int score; - int team; - int player_state; // if the client is chatting, accessing a menu or so - - // the player core for the physics - CHARACTER_CORE core; - - // - CHARACTER(); - - virtual void reset(); - virtual void destroy(); - - bool is_grounded(); - - void set_weapon(int w); - - void handle_weaponswitch(); - void do_weaponswitch(); - - int handle_weapons(); - int handle_ninja(); - - 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); - - 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; - - // - 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 GAMECONTEXT game; +#include "gamecontroller.hpp" +#include "entities/character.hpp" +#include "player.hpp" +#include "gamecontext.hpp" enum { diff --git a/src/game/server/gs_server.cpp b/src/game/server/gs_server.cpp index 5af6758b5..1d981ea5c 100644 --- a/src/game/server/gs_server.cpp +++ b/src/game/server/gs_server.cpp @@ -11,9 +11,10 @@ #include #include #include "gs_common.hpp" -#include "gs_game_ctf.hpp" -#include "gs_game_tdm.hpp" -#include "gs_game_dm.hpp" + +#include "gamemodes/dm.hpp" +#include "gamemodes/tdm.hpp" +#include "gamemodes/ctf.hpp" TUNING_PARAMS tuning; GAMECONTEXT game; @@ -106,12 +107,12 @@ void send_tuning_params(int cid) ////////////////////////////////////////////////// // Event handler ////////////////////////////////////////////////// -EVENT_HANDLER::EVENT_HANDLER() +EVENTHANDLER::EVENTHANDLER() { clear(); } -void *EVENT_HANDLER::create(int type, int size, int mask) +void *EVENTHANDLER::create(int type, int size, int mask) { if(num_events == MAX_EVENTS) return 0; @@ -128,13 +129,13 @@ void *EVENT_HANDLER::create(int type, int size, int mask) return p; } -void EVENT_HANDLER::clear() +void EVENTHANDLER::clear() { num_events = 0; current_offset = 0; } -void EVENT_HANDLER::snap(int snapping_client) +void EVENTHANDLER::snap(int snapping_client) { for(int i = 0; i < num_events; i++) { @@ -327,217 +328,6 @@ void GAMEWORLD::tick() remove_entities(); } -////////////////////////////////////////////////// -// projectile -////////////////////////////////////////////////// -PROJECTILE::PROJECTILE(int type, int owner, vec2 pos, vec2 dir, int span, ENTITY* powner, - int damage, int flags, float force, int sound_impact, int weapon) -: ENTITY(NETOBJTYPE_PROJECTILE) -{ - this->type = type; - this->pos = pos; - this->direction = dir; - this->lifespan = span; - this->owner = owner; - this->powner = powner; - this->flags = flags; - this->force = force; - this->damage = damage; - this->sound_impact = sound_impact; - this->weapon = weapon; - this->bounce = 0; - this->start_tick = server_tick(); - game.world.insert_entity(this); -} - -void PROJECTILE::reset() -{ - game.world.destroy_entity(this); -} - -vec2 PROJECTILE::get_pos(float time) -{ - float curvature = 0; - float speed = 0; - if(type == WEAPON_GRENADE) - { - curvature = tuning.grenade_curvature; - speed = tuning.grenade_speed; - } - else if(type == WEAPON_SHOTGUN) - { - curvature = tuning.shotgun_curvature; - speed = tuning.shotgun_speed; - } - else if(type == WEAPON_GUN) - { - curvature = tuning.gun_curvature; - speed = tuning.gun_speed; - } - - return calc_pos(pos, direction, curvature, speed, time); -} - - -void PROJECTILE::tick() -{ - - float pt = (server_tick()-start_tick-1)/(float)server_tickspeed(); - float ct = (server_tick()-start_tick)/(float)server_tickspeed(); - vec2 prevpos = get_pos(pt); - vec2 curpos = get_pos(ct); - - lifespan--; - - int collide = col_intersect_line(prevpos, curpos, &curpos); - //int collide = col_check_point((int)curpos.x, (int)curpos.y); - - CHARACTER *targetchr = game.world.intersect_character(prevpos, curpos, 6.0f, curpos, powner); - if(targetchr || collide || lifespan < 0) - { - if(lifespan >= 0 || weapon == WEAPON_GRENADE) - game.create_sound(curpos, sound_impact); - - if(flags & PROJECTILE_FLAGS_EXPLODE) - game.create_explosion(curpos, owner, weapon, false); - else if(targetchr) - { - targetchr->take_damage(direction * max(0.001f, force), damage, owner, weapon); - } - - game.world.destroy_entity(this); - } -} - -void PROJECTILE::fill_info(NETOBJ_PROJECTILE *proj) -{ - proj->x = (int)pos.x; - proj->y = (int)pos.y; - proj->vx = (int)(direction.x*100.0f); - proj->vy = (int)(direction.y*100.0f); - proj->start_tick = start_tick; - proj->type = type; -} - -void PROJECTILE::snap(int snapping_client) -{ - float ct = (server_tick()-start_tick)/(float)server_tickspeed(); - - 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)); - fill_info(proj); -} - - -////////////////////////////////////////////////// -// laser -////////////////////////////////////////////////// -LASER::LASER(vec2 pos, vec2 direction, float start_energy, CHARACTER *owner) -: ENTITY(NETOBJTYPE_LASER) -{ - this->pos = pos; - this->owner = owner; - energy = start_energy; - dir = direction; - bounces = 0; - do_bounce(); - - game.world.insert_entity(this); -} - - -bool LASER::hit_character(vec2 from, vec2 to) -{ - vec2 at; - 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->player->client_id, WEAPON_RIFLE); - return true; -} - -void LASER::do_bounce() -{ - eval_tick = server_tick(); - - if(energy < 0) - { - //dbg_msg("laser", "%d removed", server_tick()); - game.world.destroy_entity(this); - return; - } - - vec2 to = pos + dir*energy; - - if(col_intersect_line(pos, to, &to)) - { - if(!hit_character(pos, to)) - { - // intersected - from = pos; - pos = to - dir*2; - vec2 temp_pos = pos; - vec2 temp_dir = dir*4.0f; - - move_point(&temp_pos, &temp_dir, 1.0f, 0); - pos = temp_pos; - dir = normalize(temp_dir); - - energy -= distance(from, pos) + tuning.laser_bounce_cost; - bounces++; - - if(bounces > tuning.laser_bounce_num) - energy = -1; - - game.create_sound(pos, SOUND_RIFLE_BOUNCE); - } - } - else - { - if(!hit_character(pos, to)) - { - from = pos; - pos = to; - energy = -1; - } - } - - //dbg_msg("laser", "%d done %f %f %f %f", server_tick(), from.x, from.y, pos.x, pos.y); -} - -void LASER::reset() -{ - game.world.destroy_entity(this); -} - -void LASER::tick() -{ - if(server_tick() > eval_tick+(server_tickspeed()*tuning.laser_bounce_delay)/1000.0f) - { - do_bounce(); - } - -} - -void LASER::snap(int snapping_client) -{ - 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)); - obj->x = (int)pos.x; - obj->y = (int)pos.y; - obj->from_x = (int)from.x; - obj->from_y = (int)from.y; - obj->start_tick = eval_tick; -} - GAMECONTEXT::GAMECONTEXT() { clear(); diff --git a/src/game/server/player.hpp b/src/game/server/player.hpp new file mode 100644 index 000000000..c1866f853 --- /dev/null +++ b/src/game/server/player.hpp @@ -0,0 +1,55 @@ + +// player object +class PLAYER +{ +public: + PLAYER(); + + // TODO: clean this up + char skin_name[64]; + int use_custom_color; + int color_body; + int color_feet; + + // + 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(); +};