From: terencehill Date: Thu, 6 Dec 2012 15:51:47 +0000 (+0100) Subject: Refactor round handling in freezetag because I couldn't find any way to fix some... X-Git-Tag: xonotic-v0.7.0~61^2~89 X-Git-Url: https://git.rm.cloudns.org/?a=commitdiff_plain;h=a145a2268ac5f18a675e550144543011a66c1727;p=xonotic%2Fxonotic-data.pk3dir.git Refactor round handling in freezetag because I couldn't find any way to fix some issues keeping the previous messy code (even if already a lot simplified in my previous work). Freezetag code now uses methods of the new interface round_handler instead of the code previously shared with arena and CA. What's changed: Countdown doesn't start anymore after warmup stage or if g_start_delay is 0 at the beginning of the game when there aren't enough players Countdown to round start is now stopped if there aren't enough players Weapons can't be used anymore when the round ends (it avoids to add accuracy stats) --- diff --git a/qcsrc/server/arena.qc b/qcsrc/server/arena.qc index a4968af80..e2d73cc44 100644 --- a/qcsrc/server/arena.qc +++ b/qcsrc/server/arena.qc @@ -394,12 +394,6 @@ void Spawnqueue_Check() next_round = 0; reset_map(TRUE); } - } else if(g_freezetag) { - if((next_round && next_round < time)) - { - next_round = 0; - reset_map(TRUE); - } } else { // arena //extend next_round if it isn't set yet and only 1 player is spawned if(!next_round) @@ -436,7 +430,7 @@ void Spawnqueue_Check() void Arena_Main() { - if(!(g_ca || g_freezetag || g_arena)) + if(!(g_ca || g_arena)) return; if(g_ca) diff --git a/qcsrc/server/cl_weapons.qc b/qcsrc/server/cl_weapons.qc index 236c0923f..0b12297c7 100644 --- a/qcsrc/server/cl_weapons.qc +++ b/qcsrc/server/cl_weapons.qc @@ -374,7 +374,10 @@ void W_WeaponFrame() if (frametime) self.weapon_frametime = frametime; - if(((arena_roundbased || g_ca || g_freezetag) && time < warmup) || ((time < game_starttime) && !autocvar_sv_ready_restart_after_countdown)) + if(((arena_roundbased || g_ca) && time < warmup) || ((time < game_starttime) && !autocvar_sv_ready_restart_after_countdown)) + return; + + if(round_handler_IsActive() && !round_handler_IsRoundStarted()) return; if(self.freezetag_frozen == 1) diff --git a/qcsrc/server/mutators/gamemode_freezetag.qc b/qcsrc/server/mutators/gamemode_freezetag.qc index ca453c5c0..e9ff6153f 100644 --- a/qcsrc/server/mutators/gamemode_freezetag.qc +++ b/qcsrc/server/mutators/gamemode_freezetag.qc @@ -1,9 +1,12 @@ +float freezetag_TeamsCanPlay(); +float freezetag_CheckWinner(); void freezetag_Initialize() { precache_model("models/ice/ice.md3"); - warmup = max(time, game_starttime) + autocvar_g_freezetag_warmup; ScoreRules_freezetag(); + round_handler_Spawn(freezetag_TeamsCanPlay, freezetag_CheckWinner, 5, autocvar_g_freezetag_warmup); + addstat(STAT_REDALIVE, AS_INT, redalive_stat); addstat(STAT_BLUEALIVE, AS_INT, bluealive_stat); addstat(STAT_YELLOWALIVE, AS_INT, yellowalive_stat); @@ -13,21 +16,56 @@ void freezetag_Initialize() addstat(STAT_REVIVE_PROGRESS, AS_FLOAT, freezetag_revive_progress); } -void freezetag_CheckWinner() +void freezetag_count_alive_players() { - if(time <= game_starttime || inWarmupStage) - return; - - if(next_round || (time > warmup - autocvar_g_freezetag_warmup && time < warmup)) - return; // already waiting for next round to start + entity e; + total_players = redalive = bluealive = yellowalive = pinkalive = 0; + FOR_EACH_PLAYER(e) { + if(e.team == COLOR_TEAM1 && e.health >= 1) + { + ++total_players; + if (!e.freezetag_frozen) ++redalive; + } + else if(e.team == COLOR_TEAM2 && e.health >= 1) + { + ++total_players; + if (!e.freezetag_frozen) ++bluealive; + } + else if(e.team == COLOR_TEAM3 && e.health >= 1) + { + ++total_players; + if (!e.freezetag_frozen) ++yellowalive; + } + else if(e.team == COLOR_TEAM4 && e.health >= 1) + { + ++total_players; + if (!e.freezetag_frozen) ++pinkalive; + } + } + FOR_EACH_REALCLIENT(e) { + e.redalive_stat = redalive; + e.bluealive_stat = bluealive; + e.yellowalive_stat = yellowalive; + e.pinkalive_stat = pinkalive; + } +} +float freezetag_TeamsCanPlay() +{ if((redalive >= 1 && bluealive >= 1) || (redalive >= 1 && yellowalive >= 1) || (redalive >= 1 && pinkalive >= 1) || (bluealive >= 1 && yellowalive >= 1) || (bluealive >= 1 && pinkalive >= 1) || (yellowalive >= 1 && pinkalive >= 1)) - return; // we still have active players on two or more teams, nobody won yet + return 1; // we still have active players on two or more teams, nobody won yet + return 0; +} + +float freezetag_CheckWinner() +{ + if(freezetag_TeamsCanPlay()) + return 0; entity e, winner; string teamname; @@ -59,7 +97,7 @@ void freezetag_CheckWinner() TeamScore_AddToTeam(winner.team, ST_SCORE, +1); } - next_round = time + 5; + return 1; } // this is needed to allow the player to turn his view around (fixangle can't @@ -71,40 +109,6 @@ void freezetag_Ice_Think() self.nextthink = time; } -void freezetag_count_alive_players() -{ - entity e; - total_players = redalive = bluealive = yellowalive = pinkalive = 0; - FOR_EACH_PLAYER(e) { - if(e.team == COLOR_TEAM1 && e.health >= 1) - { - ++total_players; - if (!e.freezetag_frozen) ++redalive; - } - else if(e.team == COLOR_TEAM2 && e.health >= 1) - { - ++total_players; - if (!e.freezetag_frozen) ++bluealive; - } - else if(e.team == COLOR_TEAM3 && e.health >= 1) - { - ++total_players; - if (!e.freezetag_frozen) ++yellowalive; - } - else if(e.team == COLOR_TEAM4 && e.health >= 1) - { - ++total_players; - if (!e.freezetag_frozen) ++pinkalive; - } - } - FOR_EACH_REALCLIENT(e) { - e.redalive_stat = redalive; - e.bluealive_stat = bluealive; - e.yellowalive_stat = yellowalive; - e.pinkalive_stat = pinkalive; - } -} - void freezetag_Freeze(entity attacker) { if(self.freezetag_frozen) @@ -288,30 +292,25 @@ MUTATOR_HOOKFUNCTION(freezetag_RemovePlayer) { self.health = 0; // neccessary to update correctly alive stats freezetag_Unfreeze(world); - freezetag_count_alive_players(); - - if(total_players > 1) // only check for winners if we had more than two players (one of them left, don't let the other player win just because of that) - freezetag_CheckWinner(); - return 1; } MUTATOR_HOOKFUNCTION(freezetag_PlayerDies) { - // let the player die in these cases - if(time <= game_starttime) - return 1; - if(next_round || (time > warmup - autocvar_g_freezetag_warmup && time < warmup)) - return 1; + if(round_handler_IsActive()) + if(round_handler_CountdownRunning()) + { + if(self.freezetag_frozen) + freezetag_Unfreeze(world); + freezetag_count_alive_players(); + return 1; // let the player die so that he can respawn whenever he wants + } if(frag_deathtype == DEATH_HURTTRIGGER) { if(!self.freezetag_frozen) - { freezetag_Freeze(world); - freezetag_CheckWinner(); - } PutClientInServer(); // respawn the player self.health = 1; self.armorvalue = 0; @@ -340,29 +339,21 @@ MUTATOR_HOOKFUNCTION(freezetag_PlayerDies) frag_target.health = 1; // "respawn" the player :P - freezetag_CheckWinner(); - return 1; } MUTATOR_HOOKFUNCTION(freezetag_PlayerSpawn) { + if(self.freezetag_frozen_timeout == -1) // if PlayerSpawn is called by reset_map_players + return 1; // do nothing, round is starting right now + freezetag_count_alive_players(); if(self.freezetag_frozen) // stay frozen if respawning after death (DEATH_HURTTRIGGER) return 1; - if(time <= game_starttime || inWarmupStage || total_players == 1) - return 1; - - if(total_players == 2) // only one player active on server, start a new match immediately - if(!next_round && warmup && (time < warmup - autocvar_g_freezetag_warmup || time > warmup)) // not awaiting next round - { - next_round = time; - return 1; - } - - if(warmup && time > warmup) // spawn too late, freeze player + if(round_handler_IsActive()) + if(round_handler_IsRoundStarted()) { centerprint(self, "^1Round already started, you spawn as frozen."); freezetag_Freeze(world); @@ -371,23 +362,17 @@ MUTATOR_HOOKFUNCTION(freezetag_PlayerSpawn) return 1; } -MUTATOR_HOOKFUNCTION(freezetag_reset_map_global) -{ - redalive = bluealive = yellowalive = pinkalive = 0; - warmup = max(time, game_starttime); - if(autocvar_g_freezetag_warmup > 0) - warmup += autocvar_g_freezetag_warmup; - return 1; -} - MUTATOR_HOOKFUNCTION(freezetag_reset_map_players) { FOR_EACH_PLAYER(self) { if (self.freezetag_frozen) freezetag_Unfreeze(world); + self.freezetag_frozen_timeout = -1; PutClientInServer(); + self.freezetag_frozen_timeout = 0; } + freezetag_count_alive_players(); return 1; } @@ -402,9 +387,6 @@ MUTATOR_HOOKFUNCTION(freezetag_PlayerPreThink) float n; vector revive_extra_size; - if(gameover) - return 1; - if(self.freezetag_frozen) { // keep health = 1 @@ -418,8 +400,10 @@ MUTATOR_HOOKFUNCTION(freezetag_PlayerPreThink) return 1; } } - if(next_round || (time > warmup - autocvar_g_freezetag_warmup && time < warmup)) - return 1; // already waiting for next round to start + + if(round_handler_IsActive()) + if(!round_handler_IsRoundStarted()) + return 1; revive_extra_size = '1 1 1' * autocvar_g_freezetag_revive_extra_size; @@ -551,7 +535,6 @@ MUTATOR_DEFINITION(gamemode_freezetag) MUTATOR_HOOK(ClientDisconnect, freezetag_RemovePlayer, CBC_ORDER_ANY); MUTATOR_HOOK(PlayerDies, freezetag_PlayerDies, CBC_ORDER_ANY); MUTATOR_HOOK(PlayerSpawn, freezetag_PlayerSpawn, CBC_ORDER_ANY); - MUTATOR_HOOK(reset_map_global, freezetag_reset_map_global, CBC_ORDER_ANY); MUTATOR_HOOK(reset_map_players, freezetag_reset_map_players, CBC_ORDER_ANY); MUTATOR_HOOK(GiveFragsForKill, freezetag_GiveFragsForKill, CBC_ORDER_FIRST); MUTATOR_HOOK(PlayerPreThink, freezetag_PlayerPreThink, CBC_ORDER_FIRST); diff --git a/qcsrc/server/progs.src b/qcsrc/server/progs.src index 028519372..23e872df2 100644 --- a/qcsrc/server/progs.src +++ b/qcsrc/server/progs.src @@ -78,6 +78,8 @@ antilag.qh playerdemo.qh +round_handler.qh + // singleplayer stuff item_key.qh secret.qh @@ -204,6 +206,8 @@ anticheat.qc cheats.qc playerstats.qc +round_handler.qc + ../common/explosion_equation.qc mutators/base.qc diff --git a/qcsrc/server/round_handler.qc b/qcsrc/server/round_handler.qc new file mode 100644 index 000000000..882538d1e --- /dev/null +++ b/qcsrc/server/round_handler.qc @@ -0,0 +1,127 @@ +void round_handler_Think() +{ + entity e; + float f; + + if(inWarmupStage || time < game_starttime) + { + self.nextthink = time + 1; + return; + } + + if(gameover) + { + round_handler_Stop(); + round_handler_Remove(); + return; + } + + if(self.wait) + { + reset_map(TRUE); + self.wait = FALSE; + self.cnt = self.count + 1; // init countdown + } + + if(self.cnt > 0) // countdown running + { + if(self.canRoundStart()) + { + f = self.cnt - 1; + if(f == 5) Announce("prepareforbattle"); + else if(f == 3) Announce("3"); + else if(f == 2) Announce("2"); + else if(f == 1) Announce("1"); + else if(f == 0) + { + Announce("begin"); + FOR_EACH_REALCLIENT(e) + Send_CSQC_Centerprint_Generic(e, CPID_ROUND_STARTING, "^1Begin!", 1, 0); + self.cnt = 0; + self.nextthink = time; + return; + } + + FOR_EACH_REALCLIENT(e) + Send_CSQC_Centerprint_Generic(e, CPID_ROUND_STARTING, "Round will start in %d", 1, f); + self.cnt = self.cnt - 1; + } + else + { + round_handler_Stop(); + } + self.nextthink = time + 1; // canRoundStart every second + } + else + { + if(self.canRoundEnd()) + { + // schedule a new round + self.wait = TRUE; + self.nextthink = time + self.delay; + } + else + { + self.nextthink = time; // canRoundEnd every frame + } + } +} + +void round_handler_Spawn(float() canRoundStart_func, float() canRoundEnd_func, float the_delay, float the_count) +{ + if(round_handler) + { + backtrace("Can't spawn round_handler again!"); + return; + } + round_handler = spawn(); + round_handler.classname = "round_handler"; + + round_handler.think = round_handler_Think; + round_handler.canRoundStart = canRoundStart_func; + round_handler.canRoundEnd = canRoundEnd_func; + round_handler.delay = (the_delay > 0) ? the_delay : 0; + round_handler.count = fabs(floor(the_count)); + round_handler.wait = FALSE; + round_handler.cnt = round_handler.count + 1; + round_handler.nextthink = time; +} + +float round_handler_IsActive() +{ + return (round_handler && !inWarmupStage && time > game_starttime); +} + +float round_handler_AwaitingNextRound() +{ + return (round_handler.wait); +} + +float round_handler_CountdownRunning() +{ + return (!round_handler.wait && round_handler.cnt); +} + +float round_handler_IsRoundStarted() +{ + return (!round_handler.wait && !round_handler.cnt); +} + +void round_handler_Stop() +{ + entity e; + if(round_handler.count) + if(round_handler.cnt < round_handler.count + 1) + { + FOR_EACH_REALCLIENT(e) + Send_CSQC_Centerprint_Generic_Expire(e, CPID_ROUND_STARTING); + round_handler.cnt = round_handler.count + 1; + } + round_handler.nextthink = 0; +} + +void round_handler_Remove() +{ + remove(round_handler); +} + diff --git a/qcsrc/server/round_handler.qh b/qcsrc/server/round_handler.qh new file mode 100644 index 000000000..7388b1ad4 --- /dev/null +++ b/qcsrc/server/round_handler.qh @@ -0,0 +1,17 @@ +entity round_handler; +.float delay; // stores delay from round end to countdown start +.float count; // stores initial number of the countdown +.float wait; // it's set to TRUE when round ends, to FALSE when countdown starts +.float cnt; // its initial value is .count + 1, then decreased while counting down + // reaches 0 when the round starts +.float() canRoundStart; +.float() canRoundEnd; + +void round_handler_Spawn(float() canRoundStart_func, float() canRoundEnd_func, float the_delay, float the_count); +float round_handler_IsActive(); +float round_handler_AwaitingNextRound(); +float round_handler_CountdownRunning(); +float round_handler_IsRoundStarted(); +void round_handler_Stop(); +void round_handler_Remove(); +