diff --git a/src/server/gamerules.h b/src/server/gamerules.h index 41f2388..e2c8f45 100644 --- a/src/server/gamerules.h +++ b/src/server/gamerules.h @@ -28,5 +28,7 @@ class TFCGameRules:CGameRules virtual void(NSClientPlayer) PlayerRespawn; virtual void(NSClientPlayer) PlayerDeath; + virtual void(NSClientPlayer) DropGoalItem; + virtual void(void) LevelNewParms; }; diff --git a/src/server/gamerules.qc b/src/server/gamerules.qc index b65c7cc..70a4e58 100644 --- a/src/server/gamerules.qc +++ b/src/server/gamerules.qc @@ -22,6 +22,35 @@ TFCGameRules::IsTeamPlay(void) return TRUE; } +void +TFCGameRules::DropGoalItem(NSClientPlayer pp) +{ + /* skip normal players */ + if (!(pp.g_items & ITEM_GOALITEM)) + return; + + + item_tfgoal target; + + for (entity e = world; (e = find(e, ::classname, "item_tfgoal"));) { + target = (item_tfgoal)e; + + /* item is still pick-upable */ + if (target.solid != SOLID_NOT) { + print("the item is not picked up. \n"); + continue; + } + + /* that's us, yup */ + if (target.m_eActivator == pp) { + target.DropReturnable(pp); + return; + } + } + + print("^1WARNING: ^7Player marked as having impossible goal-item\n"); +} + /* we check what fields have changed over the course of the frame and network * only the ones that have actually changed */ void @@ -66,6 +95,7 @@ TFCGameRules::PlayerDeath(NSClientPlayer pp) { player pl = (player)pp; + DropGoalItem(pp); pl.SetSolid(SOLID_NOT); pl.SetMovetype(MOVETYPE_NONE); pl.think = PlayerRespawn; diff --git a/src/server/item_tfgoal.qc b/src/server/item_tfgoal.qc index a57d5d8..85d80e0 100644 --- a/src/server/item_tfgoal.qc +++ b/src/server/item_tfgoal.qc @@ -27,6 +27,7 @@ A gameplay pickup. It generally gets picked up and held. "goal_no" : Identifer for this pickup, should be unique per map "team_no" : Which team can use this item (0 means all) "owned_by" : Which team owns this item (aka who can return it) +"pausetime" : How long the item will stay on ground for when dropped "b_b" : Message to show to all when picked up "message" : Message to show to the activator when picked up "b_t" : Message to show to activator's team when picked up @@ -34,6 +35,9 @@ A gameplay pickup. It generally gets picked up and held. "b_o" : Message to show to owner team when picked up "non_owners_team_broadcast" : Message to show to everyone else? +"noise3" : Message to the owner team when the item is returned +"noise4" : Message to the other team when the item is returned + "speak" : VOX announcement to everyone when picked up "AP_speak" : VOX announcement to activator when picked up "team_speak" : VOX announcement to activator's team when picked up @@ -48,6 +52,12 @@ Duplicate keys: */ +typedef enum +{ + GISTATUS_HOME, + GISTATUS_DROPPED +} goalitem_status_e; + class item_tfgoal:NSRenderableEntity { float m_dItemID; @@ -58,6 +68,8 @@ class item_tfgoal:NSRenderableEntity string m_strSound; player m_eActivator; + goalitem_status_e m_status; + /* visual fluff */ string m_msgAll; /* global */ string m_msgActivator; /* AP */ @@ -73,12 +85,50 @@ class item_tfgoal:NSRenderableEntity string m_voxOwnerTeam; /* owner team */ string m_voxNonOwnerTeams; /* non-owner team */ + string m_returnTeam; + string m_returnOwner; + + float m_flPausetime; + void(void) item_tfgoal; virtual void(entity) Touch; virtual void(void) Respawn; virtual void(string, string) SpawnKey; + virtual void(NSClientPlayer) DropReturnable; + virtual void(void) TeamOwnerReturns; + virtual void(void) Spawned; }; +void +item_tfgoal::DropReturnable(NSClientPlayer pp) +{ + player pl = (player)pp; + + /* make it available again, put it exactly where we died */ + Respawn(); + SetOrigin(pl.origin); + + /* untag it from the player */ + pl.g_items &= ~ITEM_GOALITEM; + + /* return after N secs */ + think = TeamOwnerReturns; + nextthink = time + m_flPausetime; +} + +void +item_tfgoal::TeamOwnerReturns(void) +{ + Respawn(); + + for (entity e = world; (e = find(e, ::classname, "player")); ) { + if (e.team == m_iTeamUses) + env_message_single(e, m_returnTeam); + else if (e.team == m_iTeamOwner) + env_message_single(e, m_returnOwner); + } +} + void item_tfgoal::Touch(entity eToucher) { @@ -88,6 +138,15 @@ item_tfgoal::Touch(entity eToucher) player pl = (player)eToucher; + /* if it's dropped, just let the other team return it... + otherwise let the other teams pick it up as normal */ + if (m_status == GISTATUS_DROPPED) { + if (m_iTeamOwner == pl.team) { + TeamOwnerReturns(); + return; + } + } + /* team filter */ if (m_iTeamUses) if (m_iTeamUses != pl.team) @@ -147,10 +206,9 @@ item_tfgoal::Touch(entity eToucher) void item_tfgoal::Respawn(void) { - solid = SOLID_TRIGGER; - movetype = MOVETYPE_NONE; SetModel(GetSpawnModel()); - setsize(this, VEC_HULL_MIN, VEC_HULL_MAX); + SetSize(VEC_HULL_MIN, VEC_HULL_MAX); + SetSolid(SOLID_TRIGGER); SetOrigin(GetSpawnOrigin()); m_eActivator = __NULL__; } @@ -215,20 +273,32 @@ item_tfgoal::SpawnKey(string strKey, string strValue) case "non_owners_team_speak": m_voxNonOwnerTeams = strValue; /* non-owner team */ break; + case "noise4": + m_returnTeam = strValue; + break; + case "noise3": + m_returnOwner = strValue; + break; + case "pausetime": + m_flPausetime = stof(strValue); + break; default: super::SpawnKey(strKey, strValue); break; } } +void +item_tfgoal::Spawned(void) +{ + super::Spawned(); + precache_sound(m_strSound); +} + void item_tfgoal::item_tfgoal(void) { - for (int i = 1; i < (tokenize(__fullspawndata) - 1); i += 2) { - SpawnKey(argv(i), argv(i+1)); - } - precache_sound(m_strSound); - - super::NSRenderableEntity(); - item_tfgoal::Respawn(); + m_status = GISTATUS_HOME; + m_returnOwner = m_returnTeam = __NULL__; + m_flPausetime = 0; }