From ac971ab6de8ded42ea2e339efe1f14788ba16166 Mon Sep 17 00:00:00 2001 From: Rudolf Polzer Date: Sat, 6 Mar 2010 17:29:48 +0100 Subject: [PATCH] start of a generic mutator system --- qcsrc/server/cl_client.qc | 4 +- qcsrc/server/cl_player.qc | 7 +- qcsrc/server/clientcommands.qc | 2 +- qcsrc/server/g_damage.qc | 9 +- qcsrc/server/keyhunt.qh | 34 ------ qcsrc/server/mutators/base.qc | 108 ++++++++++++++++++ qcsrc/server/mutators/base.qh | 43 +++++++ .../gamemode_keyhunt.qc} | 65 ++++++++++- qcsrc/server/progs.src | 10 +- 9 files changed, 233 insertions(+), 49 deletions(-) delete mode 100644 qcsrc/server/keyhunt.qh create mode 100644 qcsrc/server/mutators/base.qc create mode 100644 qcsrc/server/mutators/base.qh rename qcsrc/server/{keyhunt.qc => mutators/gamemode_keyhunt.qc} (95%) diff --git a/qcsrc/server/cl_client.qc b/qcsrc/server/cl_client.qc index 2f424b8a8..2cf959cda 100644 --- a/qcsrc/server/cl_client.qc +++ b/qcsrc/server/cl_client.qc @@ -582,7 +582,7 @@ void PutObserverInServer (void) } DropAllRunes(self); - kh_Key_DropAll(self, TRUE); + MUTATOR_CALLHOOK(MakePlayerObserver); Portal_ClearAll(self); @@ -1578,7 +1578,7 @@ void ClientDisconnect (void) SoundEntity_Detach(self); DropAllRunes(self); - kh_Key_DropAll(self, TRUE); + MUTATOR_CALLHOOK(ClientDisconnect); Portal_ClearAll(self); diff --git a/qcsrc/server/cl_player.qc b/qcsrc/server/cl_player.qc index 92335faee..1911f4027 100644 --- a/qcsrc/server/cl_player.qc +++ b/qcsrc/server/cl_player.qc @@ -633,12 +633,7 @@ void PlayerDamage (entity inflictor, entity attacker, float damage, float deatht Obituary (attacker, inflictor, self, deathtype); race_PreDie(); DropAllRunes(self); - if(self == attacker) - kh_Key_DropAll(self, TRUE); - else if(attacker.classname == "player" || attacker.classname == "gib") - kh_Key_DropAll(self, FALSE); - else - kh_Key_DropAll(self, TRUE); + frag_attacker = attacker; MUTATOR_CALLHOOK(PlayerDies); if(self.flagcarried) { if(attacker.classname != "player" && attacker.classname != "gib") diff --git a/qcsrc/server/clientcommands.qc b/qcsrc/server/clientcommands.qc index 8fc949fa4..d272d694a 100644 --- a/qcsrc/server/clientcommands.qc +++ b/qcsrc/server/clientcommands.qc @@ -225,7 +225,7 @@ void SV_ParseClientCommand(string s) { DropFlag(self.flagcarried, world, world); if(self.ballcarried) DropBall(self.ballcarried, self.origin, self.velocity); - kh_Key_DropAll(self, TRUE); + MUTATOR_CALLHOOK(MakePlayerSpectator); WaypointSprite_PlayerDead(); self.classname = "observer"; if(g_ca) diff --git a/qcsrc/server/g_damage.qc b/qcsrc/server/g_damage.qc index d84546cf2..c1fcd6e3d 100644 --- a/qcsrc/server/g_damage.qc +++ b/qcsrc/server/g_damage.qc @@ -146,7 +146,14 @@ void GiveFrags (entity attacker, entity targ, float f) } // FIXME fix the mess this is (we have REAL points now!) - if(g_runematch) + frag_attacker = attacker; + frag_target = targ; + frag_score = f; + if(MUTATOR_CALLHOOK(GiveFragsForKill)) + { + f = frag_score; + } + else if(g_runematch) { f = RunematchHandleFrags(attacker, targ, f); } diff --git a/qcsrc/server/keyhunt.qh b/qcsrc/server/keyhunt.qh deleted file mode 100644 index 99f756664..000000000 --- a/qcsrc/server/keyhunt.qh +++ /dev/null @@ -1,34 +0,0 @@ -.float kh_state; -float kh_teams; -float kh_tracking_enabled; -.entity kh_next, kh_prev; - -void kh_Scores_Event(entity player, entity key, string what, float frags_player, float frags_owner); -void kh_Key_Attach(entity key); -void kh_Key_Detach(entity key); -void kh_Key_AssignTo(entity key, entity player); -void kh_Key_Spawn(entity initial_owner, float angle, float idx); -void kh_Key_Remove(entity key); -void kh_Key_Collect(entity key, entity player); -void kh_Key_DropAll(entity player, float suicide); -void kh_Key_Touch(); -void kh_Key_Think(); -void kh_WinnerTeam(float teem); -void kh_LoserTeam(float teem, entity lostkey); -void kh_FinishRound(); -void kh_StartRound(); -void kh_EnableTrackingDevice(); -void kh_init(); -void kh_finalize(); -float kh_KeyCarrier_waypointsprite_visible_for_player(entity e); -float kh_Key_waypointsprite_visible_for_player(entity e); -float kh_HandleFrags(entity attacker, entity targ, float f); -float kh_Key_AllOwnedByWhichTeam(); - -void kh_update_state(); - -#define STR_ITEM_KH_KEY "item_kh_key" -typedef void(void) kh_Think_t; -var kh_Think_t kh_Controller_Thinkfunc; -void kh_Controller_SetThink(float t, string msg, kh_Think_t func) -void kh_Key_Remove(entity key) diff --git a/qcsrc/server/mutators/base.qc b/qcsrc/server/mutators/base.qc new file mode 100644 index 000000000..0b1bea45e --- /dev/null +++ b/qcsrc/server/mutators/base.qc @@ -0,0 +1,108 @@ +.float() cbc_func; +.entity cbc_next; +.float cbc_order; + +entity CallbackChain_New(string name) +{ + entity e; + e = spawn(); + e.classname = "callbackchain"; + e.netname = name; + return e; +} + +float CallbackChain_Add(entity cb, float() func, float order) +{ + entity e; + if(order & CBC_ORDER_FIRST) + { + if(order & CBC_ORDER_LAST) + if(cb.cbc_order & CBC_ORDER_ANY) + return 0; + if(cb.cbc_order & CBC_ORDER_FIRST) + return 0; + } + else if(order & CBC_ORDER_LAST) + { + if(cb.cbc_order & CBC_ORDER_LAST) + return 0; + } + entity thiscb; + thiscb = spawn(); + thiscb.classname = "callback"; + thiscb.cbc_func = func; + thiscb.cbc_order = order; + if(order & CBC_ORDER_FIRST) + { + thiscb.cbc_next = cb.cbc_next; + cb.cbc_next = thiscb; + } + else if(order & CBC_ORDER_LAST) + { + for(e = cb; e.cbc_next; e = e.cbc_next); + e.cbc_next = thiscb; + } + else + { + // by default we execute last, but before a possible CBC_ORDER_LAST callback + for(e = cb; e.cbc_next && !(e.cbc_next.cbc_order & CBC_ORDER_LAST); e = e.cbc_next); // we must make sure that we insert BEFORE an CBC_ORDER_LAST mutator! + thiscb.cbc_next = e.cbc_next; + e.cbc_next = thiscb; + } + cb.cbc_order |= (order | CBC_ORDER_ANY); + return 1; +} + +float CallbackChain_Remove(entity cb, float() func) +{ + float order; + entity e; + float n; + n = 0; + for(e = cb; e.cbc_next; e = e.cbc_next) + { + while(e.cbc_next.cbc_func == func) + { + // remove e.cbc_next from the chain + entity e2; + e2 = e.cbc_next.cbc_next; + remove(e.cbc_next); + e.cbc_next = e2; + ++n; + } + // e.cbc_next is now something we want to keep + order |= (e.cbc_next.cbc_order & CBC_ORDER_ANY); + } + cb.cbc_order = order; + return n; +} + +float CallbackChain_Call(entity cb) +{ + float r; + entity e; + r = 0; + for(e = cb; e.cbc_next; e = e.cbc_next) + r |= e.cbc_next.cbc_func(); + return r; // callbacks return an error status, so 0 is default return value +} + +float Mutator_Add(float(float) func) +{ + if(func(MUTATOR_ADDING) == 0) + { + // good + return 1; + } + backtrace("WARNING: when adding mutator: adding failed\n"); + Mutator_Remove(func); + return 0; +} +void Mutator_Remove(float(float) func) +{ + if(func(MUTATOR_REMOVING) != 0) + { + // baaaaad + error("Mutator_Remove: removing mutator failed"); + } +} diff --git a/qcsrc/server/mutators/base.qh b/qcsrc/server/mutators/base.qh new file mode 100644 index 000000000..64ea0fa82 --- /dev/null +++ b/qcsrc/server/mutators/base.qh @@ -0,0 +1,43 @@ +#define CBC_ORDER_EXCLUSIVE 3 +#define CBC_ORDER_FIRST 1 +#define CBC_ORDER_LAST 2 +#define CBC_ORDER_ANY 4 + +entity CallbackChain_New(string name); +float CallbackChain_Add(entity cb, float() func, float order) +float CallbackChain_Remove(entity cb, float() func); +// a callback function is like this: +// float mycallback(entity me) +// { +// do something +// return r; +// } +float CallbackChain_Call(entity cb); + +#define MUTATOR_REMOVING 0 +#define MUTATOR_ADDING 1 +float Mutator_Add(float(float) func); +void Mutator_Remove(float(float) func); // calls error() on fail + +#define MUTATOR_ADD(name) Mutator_Add(MUTATOR_##name) +#define MUTATOR_REMOVE(name) Mutator_Remove(MUTATOR_##name) +#define MUTATOR_DEFINITION(name) float MUTATOR_##name(float mode) +#define MUTATOR_HOOKFUNCTION(name) float HOOKFUNCTION_##name() +#define MUTATOR_HOOK(cb,func,order) do { if(mode == MUTATOR_ADDING) { if(!HOOK_##cb) HOOK_##cb = CallbackChain_New(#cb); if(!CallbackChain_Add(HOOK_##cb,HOOKFUNCTION_##func,order)) { print("HOOK FAILED: ", #func, "\n"); return 1; } } else if(mode == MUTATOR_REMOVING) { if(HOOK_##cb) CallbackChain_Remove(HOOK_##cb,HOOKFUNCTION_##func); } } while(0) +#define MUTATOR_ONADD if(mode == MUTATOR_ADDING) +#define MUTATOR_ONREMOVE if(mode == MUTATOR_REMOVING) + +#define MUTATOR_HOOKABLE(cb) entity HOOK_##cb +#define MUTATOR_CALLHOOK(cb) CallbackChain_Call(HOOK_##cb) + + + + + +// register all possible hooks here +MUTATOR_HOOKABLE(MakePlayerObserver); +MUTATOR_HOOKABLE(MakePlayerSpectator); +MUTATOR_HOOKABLE(ClientDisconnect); +MUTATOR_HOOKABLE(PlayerDies); entity other; entity frag_attacker; +MUTATOR_HOOKABLE(GiveFragsForKill); entity frag_attacker, frag_target; float frag_score; +MUTATOR_HOOKABLE(MatchEnd); diff --git a/qcsrc/server/keyhunt.qc b/qcsrc/server/mutators/gamemode_keyhunt.qc similarity index 95% rename from qcsrc/server/keyhunt.qc rename to qcsrc/server/mutators/gamemode_keyhunt.qc index eb3e2399f..2daea2069 100644 --- a/qcsrc/server/keyhunt.qc +++ b/qcsrc/server/mutators/gamemode_keyhunt.qc @@ -929,7 +929,7 @@ float kh_HandleFrags(entity attacker, entity targ, float f) // adds to the play return f; } -void kh_init() // sets up th KH environment +void kh_Initialize() // sets up th KH environment { precache_sound(kh_sound_capture); precache_sound(kh_sound_destroy); @@ -1012,3 +1012,66 @@ void kh_update_state() } //print(ftos((nextent(world)).kh_state), "\n"); } + + + + +// register this as a mutator + +MUTATOR_HOOKFUNCTION(kh_Key_DropAll) +{ + kh_Key_DropAll(self, TRUE); + return 0; +} + +MUTATOR_HOOKFUNCTION(kh_PlayerDies) +{ + if(self == other) + kh_Key_DropAll(self, TRUE); + else if(other.classname == "player" || other.classname == "gib") + kh_Key_DropAll(self, FALSE); + else + kh_Key_DropAll(self, TRUE); + return 0; +} + +MUTATOR_HOOKFUNCTION(kh_GiveFragsForKill) +{ + frag_score = kh_HandleFrags(frag_attacker, frag_target, frag_score); + return 0; +} + +MUTATOR_HOOKFUNCTION(kh_finalize) +{ + kh_finalize(); + return 0; +} + +MUTATOR_DEFINITION(gamemode_keyhunt) +{ + MUTATOR_HOOK(MakePlayerObserver, kh_Key_DropAll, CBC_ORDER_ANY); + MUTATOR_HOOK(MakePlayerSpectator, kh_Key_DropAll, CBC_ORDER_ANY); + MUTATOR_HOOK(ClientDisconnect, kh_Key_DropAll, CBC_ORDER_ANY); + MUTATOR_HOOK(PlayerDies, kh_PlayerDies, CBC_ORDER_ANY); + MUTATOR_HOOK(GiveFragsForKill, kh_GiveFragsForKill, CBC_ORDER_EXCLUSIVE); + MUTATOR_HOOK(MatchEnd, kh_finalize, CBC_ORDER_ANY); + + MUTATOR_ONADD + { + g_keyhunt = 1; + kh_Initialize(); + } + + MUTATOR_ONREMOVE + { + g_keyhunt = 0; + error("This is a game type and it cannot be removed at runtime."); + } + + return 0; +} + +void kh_init() +{ + MUTATOR_ADD(gamemode_keyhunt); +} diff --git a/qcsrc/server/progs.src b/qcsrc/server/progs.src index 3c3ba5bb5..b74d3b914 100644 --- a/qcsrc/server/progs.src +++ b/qcsrc/server/progs.src @@ -20,6 +20,9 @@ constants.qh defs.qh // Should rename this, it has fields and globals +mutators/base.qh +mutators/gamemode_keyhunt.qh // TODO fix this + //// tZork Turrets //// tturrets/include/turrets_early.qh @@ -44,8 +47,6 @@ ipban.qh race.qh -keyhunt.qh - antilag.qh vote.qh @@ -127,8 +128,6 @@ campaign.qc ../common/gamecommand.qc gamecommand.qc -keyhunt.qc - assault.qc ipban.qc @@ -170,6 +169,9 @@ playerdemo.qc anticheat.qc cheats.qc +mutators/base.qc +mutators/gamemode_keyhunt.qc + ../warpzonelib/anglestransform.qc ../warpzonelib/mathlib.qc ../warpzonelib/common.qc -- 2.39.2