set g_arena 0 "Arena: many one-on-one rounds are played to find the winner"
set g_arena_maxspawned 2 "maximum number of players to spawn at once (the rest is spectating, waiting for their turn)"
set g_arena_roundbased 1 "if disabled, the next player will spawn as soon as someone dies"
+set g_arena_round_timelimit 180
set g_arena_warmup 5 "time, newly spawned players have to prepare themselves in round based matches"
-float maxspawned;
-float numspawned;
-float arena_roundbased;
-.float spawned;
-.entity spawnqueue_next;
-.entity spawnqueue_prev;
-.float spawnqueue_in;
-entity spawnqueue_first;
-entity spawnqueue_last;
-entity champion;
-float warmup;
-
-void PutObserverInServer();
void PutClientInServer();
-float next_round;
-
/**
* Resets the state of all clients, items, flags, runes, keys, weapons, waypoints, ... of the map.
- * Sets the 'warmup' global variable.
*/
void reset_map(float dorespawn)
{
if(time <= game_starttime && round_handler_IsActive())
round_handler_Reset(game_starttime + 1);
- if(g_arena)
- {
- warmup = max(time, game_starttime);
- if(autocvar_g_arena_warmup > 0)
- warmup += autocvar_g_arena_warmup;
- }
- else if(g_race || g_cts)
+ if(g_race || g_cts)
race_ReadyRestart();
else MUTATOR_CALLHOOK(reset_map_global);
FOR_EACH_CLIENT(self) {
if(self.flags & FL_CLIENT) // reset all players
{
- if(g_arena)
- {
- if(self.spawned)
- PutClientInServer();
- else
- PutObserverInServer();
- }
- else
{
/*
only reset players if a restart countdown is active
if(g_keyhunt)
kh_Controller_SetThink_NoMsg(autocvar_g_balance_keyhunt_delay_round+(game_starttime - time), kh_StartRound);
- if(g_arena)
- if(champion && champion.classname == "player" && player_count > 1)
- UpdateFrags(champion, +1);
-
self = oldself;
}
-void Spawnqueue_Insert(entity e)
-{
- if(e.spawnqueue_in)
- return;
- dprint(strcat("Into queue: ", e.netname, "\n"));
- e.spawnqueue_in = TRUE;
- e.spawnqueue_prev = spawnqueue_last;
- e.spawnqueue_next = world;
- if(spawnqueue_last)
- spawnqueue_last.spawnqueue_next = e;
- spawnqueue_last = e;
- if(!spawnqueue_first)
- spawnqueue_first = e;
-}
-
-void Spawnqueue_Remove(entity e)
-{
- if(!e.spawnqueue_in)
- return;
- dprint(strcat("Out of queue: ", e.netname, "\n"));
- e.spawnqueue_in = FALSE;
- if(e == spawnqueue_first)
- spawnqueue_first = e.spawnqueue_next;
- if(e == spawnqueue_last)
- spawnqueue_last = e.spawnqueue_prev;
- if(e.spawnqueue_prev)
- e.spawnqueue_prev.spawnqueue_next = e.spawnqueue_next;
- if(e.spawnqueue_next)
- e.spawnqueue_next.spawnqueue_prev = e.spawnqueue_prev;
- e.spawnqueue_next = world;
- e.spawnqueue_prev = world;
-}
-
-void Spawnqueue_Unmark(entity e)
-{
- if(!e.spawned)
- return;
- e.spawned = FALSE;
- numspawned = numspawned - 1;
-}
-
-void Spawnqueue_Mark(entity e)
-{
- if(e.spawned)
- return;
- e.spawned = TRUE;
- numspawned = numspawned + 1;
-}
-
-/**
- * If roundbased arena game mode is active, it centerprints the texts for the
- * player when player is waiting for the countdown to finish.
- * Blocks the players movement while countdown is active.
- * Unblocks the player once the countdown is over.
- *
- * Called in StartFrame()
- */
-float roundStartTime_prev; // prevent networkspam
-void Arena_Warmup()
-{
- float f;
- entity e;
-
- if(gameover)
- {
- if(champion)
- {
- FOR_EACH_REALCLIENT(e)
- centerprint(e, strcat("The Champion is ", champion.netname));
- champion = world;
- }
- return;
- }
- if(time < game_starttime)
- return;
-
- f = ceil(warmup - time);
-
- if(time < warmup && !inWarmupStage)
- {
- if(champion)
- {
- FOR_EACH_REALCLIENT(e)
- centerprint(e, strcat("The Champion is ", champion.netname));
- }
-
- if(f != roundStartTime_prev) {
- roundStartTime_prev = f;
-
- if(f == 5)
- Announce("prepareforbattle");
- else if(f == 3)
- Announce("3");
- else if(f == 2)
- Announce("2");
- else if(f == 1)
- Announce("1");
-
- FOR_EACH_REALCLIENT(e)
- Send_CSQC_Centerprint_Generic(e, CPID_ROUND_STARTING, "Round will start in %d", 1, f);
- }
-
- FOR_EACH_CLIENT(e)
- {
- if(e.spawned && e.classname == "player")
- e.player_blocked = 1;
- }
- }
- else if(f > -1 && f != roundStartTime_prev && !inWarmupStage)
- {
- roundStartTime_prev = f;
-
- Announce("begin");
- FOR_EACH_REALCLIENT(e)
- Send_CSQC_Centerprint_Generic(e, CPID_ROUND_STARTING, "^1Begin!", 1, 0);
-
- FOR_EACH_CLIENT(e)
- {
- if(e.player_blocked)
- e.player_blocked = 0;
- }
- }
-
- // clear champion to avoid centerprinting again the champion msg
- if (champion)
- champion = world;
-}
-
-/**
- * This function finds out whether an arena round is over 1 player is left.
- * It determines the last player who's still alive and saves it's entity reference
- * in the global variable 'champion'. Then the new enemy/enemies are put into the server.
- *
- * Gets called in StartFrame()
- */
-void Spawnqueue_Check()
-{
- if(time < warmup + 1 || inWarmupStage || intermission_running)
- return;
-
- //extend next_round if it isn't set yet and only 1 player is spawned
- if(!next_round)
- if(numspawned < 2)
- next_round = time + 3;
-
- if(!arena_roundbased || (next_round && next_round < time && player_count > 1))
- {
- next_round = 0;
-
- if(arena_roundbased)
- {
- champion = find(world, classname, "player");
- while(champion && champion.deadflag)
- champion = find(champion, classname, "player");
- reset_map(TRUE);
- }
-
- while(numspawned < maxspawned && spawnqueue_first)
- {
- self = spawnqueue_first;
-
- bprint ("^4", self.netname, "^4 is the next challenger\n");
-
- Spawnqueue_Remove(self);
- Spawnqueue_Mark(self);
-
- self.classname = "player";
- PutClientInServer();
- }
- }
-}
-
-void Arena_Main()
-{
- if(!g_arena)
- return;
- if(arena_roundbased)
- Arena_Warmup();
- Spawnqueue_Check();
-}
-
float autocvar_g_arena_point_leadlimit;
float autocvar_g_arena_point_limit;
float autocvar_g_arena_roundbased;
+float autocvar_g_arena_round_timelimit;
float autocvar_g_arena_warmup;
float autocvar_g_assault;
float autocvar_g_balance_armor_blockpercent;
WriteEntity(MSG_ONE, self);
}
- if(g_arena)
- {
- if(self.version_mismatch)
- {
- Spawnqueue_Unmark(self);
- Spawnqueue_Remove(self);
- }
- else
- {
- Spawnqueue_Insert(self);
- }
- }
- else if(g_lms)
+ if(g_lms)
{
// Only if the player cannot play at all
if(PlayerScore_Add(self, SP_LMS_RANK, 0) == 666)
if(PlayerScore_Add(self, SP_LMS_RANK, 0) > 0)
self.classname = "observer";
}
- else if(g_arena && !self.spawned)
- self.classname = "observer";
MUTATOR_CALLHOOK(PutClientInServer);
self.lastteleporttime = time; // prevent insane speeds due to changing origin
self.hud = HUD_NORMAL;
- if(g_arena)
- {
- Spawnqueue_Remove(self);
- Spawnqueue_Mark(self);
- }
-
self.event_damage = PlayerDamage;
self.bot_attack = TRUE;
void ClientKill (void)
{
- if (gameover)
- return;
+ if(gameover) return;
+ if(g_ca && player_count == 1) return;
+ if(self.player_blocked) return;
+ if(self.freezetag_frozen) return;
- if((g_arena || g_ca) && ((champion && champion.classname == "player" && player_count > 1) || player_count == 1)) // don't allow a kill in this case either
- {
- // do nothing
- }
- else if(self.freezetag_frozen)
- {
- // do nothing
- }
- else
- ClientKill_TeamChange(0);
+ ClientKill_TeamChange(0);
}
void CTS_ClientKill (entity e) // silent version of ClientKill, used when player finishes a CTS run. Useful to prevent cheating by running back to the start line and starting out with more speed
bot_relinkplayerlist();
- if(g_arena)
- {
- Spawnqueue_Unmark(self);
- Spawnqueue_Remove(self);
- }
-
accuracy_free(self);
ClientData_Detach();
PlayerScore_Detach(self);
self.stat_allow_oldnexbeam = autocvar_g_allow_oldnexbeam;
self.stat_leadlimit = autocvar_leadlimit;
- if(g_arena)
- self.stat_respawn_time = 0;
- else
- self.stat_respawn_time = self.respawn_time;
+ self.stat_respawn_time = self.respawn_time;
if(frametime)
{
float valid_damage_for_weaponstats;
float excess;
- if((g_arena && numspawned < 2) && !inWarmupStage)
- return;
-
dh = max(self.health, 0);
da = max(self.armorvalue, 0);
if(defer_ClientKill_Now_TeamChange)
ClientKill_Now_TeamChange(); // can turn player into spectator
- if(g_arena)
- Spawnqueue_Unmark(self);
-
// player could have been miraculously resuscitated ;)
// e.g. players in freezetag get frozen, they don't really die
if(self.health >= 1 || self.classname != "player")
if (frametime)
self.weapon_frametime = frametime;
- if(((g_arena && arena_roundbased) && time < warmup) || ((time < game_starttime) && !autocvar_sv_ready_restart_after_countdown))
+ if(time < game_starttime && !autocvar_sv_ready_restart_after_countdown)
return;
if(round_handler_IsActive() && !round_handler_IsRoundStarted())
return;
+ if(self.player_blocked)
+ return;
+
if(self.freezetag_frozen == 1)
return;
PlayerScore_Add(targ, SP_DEATHS, 1);
- if(g_arena && arena_roundbased)
- return;
-
if(targ != attacker) // not for suicides
if(g_weaponarena_random)
{
float s;
vector vs;
- if((arena_roundbased && time < warmup) || (time < game_starttime))
- return;
-
- if(self.freezetag_frozen)
- return;
-
- if(self.vehicle)
- return;
+ if(time < game_starttime) return;
+ if(self.player_blocked) return;
+ if(self.freezetag_frozen) return;
+ if(self.vehicle) return;
makevectors(self.v_angle);
--- /dev/null
+void Spawnqueue_Insert(entity e)
+{
+ if(e.spawnqueue_in)
+ return;
+ dprint(strcat("Into queue: ", e.netname, "\n"));
+ e.spawnqueue_in = TRUE;
+ e.spawnqueue_prev = spawnqueue_last;
+ e.spawnqueue_next = world;
+ if(spawnqueue_last)
+ spawnqueue_last.spawnqueue_next = e;
+ spawnqueue_last = e;
+ if(!spawnqueue_first)
+ spawnqueue_first = e;
+}
+
+void Spawnqueue_Remove(entity e)
+{
+ if(!e.spawnqueue_in)
+ return;
+ dprint(strcat("Out of queue: ", e.netname, "\n"));
+ e.spawnqueue_in = FALSE;
+ if(e == spawnqueue_first)
+ spawnqueue_first = e.spawnqueue_next;
+ if(e == spawnqueue_last)
+ spawnqueue_last = e.spawnqueue_prev;
+ if(e.spawnqueue_prev)
+ e.spawnqueue_prev.spawnqueue_next = e.spawnqueue_next;
+ if(e.spawnqueue_next)
+ e.spawnqueue_next.spawnqueue_prev = e.spawnqueue_prev;
+ e.spawnqueue_next = world;
+ e.spawnqueue_prev = world;
+}
+
+void Spawnqueue_Unmark(entity e)
+{
+ if(!e.spawned)
+ return;
+ e.spawned = FALSE;
+ numspawned = numspawned - 1;
+}
+
+void Spawnqueue_Mark(entity e)
+{
+ if(e.spawned)
+ return;
+ e.spawned = TRUE;
+ numspawned = numspawned + 1;
+}
+
+float Arena_CheckWinner()
+{
+ entity e;
+
+ // FIXME do this only once (on round start)
+ FOR_EACH_PLAYER(e)
+ e.player_blocked = 0;
+
+ if(round_handler_GetTimeLeft() <= 0)
+ {
+ FOR_EACH_REALCLIENT(e)
+ centerprint(e, "Round over, there's no winner");
+ bprint("Round over, there's no winner\n");
+ return 1;
+ }
+
+ if(numspawned > 1)
+ return 0;
+
+ entity champion;
+ champion = world;
+ FOR_EACH_CLIENT(e)
+ {
+ if(e.spawned && e.classname == "player")
+ champion = e;
+ }
+
+ if(champion)
+ {
+ FOR_EACH_REALCLIENT(e)
+ centerprint(e, strcat("The Champion is ", champion.netname));
+ bprint("The Champion is ", champion.netname, "\n");
+ UpdateFrags(champion, +1);
+ }
+ else
+ {
+ FOR_EACH_REALCLIENT(e)
+ centerprint(e, "Round tied");
+ bprint("Round tied\n");
+ }
+ return 1;
+}
+void Arena_AddChallengers()
+{
+ entity e;
+ e = self;
+ while(numspawned < maxspawned && spawnqueue_first)
+ {
+ self = spawnqueue_first;
+
+ bprint ("^4", self.netname, "^4 is the next challenger\n");
+
+ Spawnqueue_Remove(self);
+ Spawnqueue_Mark(self);
+
+ self.classname = "player";
+ PutClientInServer();
+ }
+ self = e;
+}
+
+float Arena_CheckPlayers()
+{
+ Arena_AddChallengers();
+
+ if(numspawned >= 2)
+ return 1;
+
+ return 0;
+}
+
+MUTATOR_HOOKFUNCTION(arena_ClientDisconnect)
+{
+ Spawnqueue_Unmark(self);
+ Spawnqueue_Remove(self);
+ return 1;
+}
+
+MUTATOR_HOOKFUNCTION(arena_reset_map_players)
+{
+ FOR_EACH_CLIENT(self)
+ {
+ if(self.spawned)
+ {
+ PutClientInServer();
+ self.player_blocked = 1;
+ }
+ else
+ PutObserverInServer();
+ }
+ return 1;
+}
+
+MUTATOR_HOOKFUNCTION(arena_MakePlayerObserver)
+{
+ self.frags = FRAGS_PLAYER;
+ if(self.version_mismatch)
+ {
+ Spawnqueue_Unmark(self);
+ Spawnqueue_Remove(self);
+ }
+ else
+ Spawnqueue_Insert(self);
+ return 1;
+}
+
+MUTATOR_HOOKFUNCTION(arena_PutClientInServer)
+{
+ if(!self.spawned)
+ self.classname = "observer";
+ return 1;
+}
+
+MUTATOR_HOOKFUNCTION(arena_PlayerSpawn)
+{
+ Spawnqueue_Remove(self);
+ Spawnqueue_Mark(self);
+ if(arena_roundbased)
+ self.player_blocked = 1;
+ return 1;
+}
+
+MUTATOR_HOOKFUNCTION(arena_PlayerPreThink)
+{
+ self.stat_respawn_time = 0;
+
+ // put dead players in the spawn queue
+ if(arena_roundbased)
+ if(self.deadflag && time - self.death_time >= 1.5)
+ PutClientInServer();
+
+ return 1;
+}
+
+MUTATOR_HOOKFUNCTION(arena_ForbidPlayerScore_Clear)
+{
+ return 1;
+}
+
+MUTATOR_HOOKFUNCTION(arena_GiveFragsForKill)
+{
+ if(arena_roundbased)
+ frag_score = 0;
+ return 1;
+}
+
+MUTATOR_HOOKFUNCTION(arena_PlayerDies)
+{
+ Spawnqueue_Unmark(self);
+ return 1;
+}
+
+MUTATOR_HOOKFUNCTION(arena_SV_StartFrame)
+{
+ if(arena_roundbased) return 1;
+ if(time <= game_starttime) return 1;
+ if(gameover) return 1;
+
+ Arena_AddChallengers();
+ return 1;
+}
+
+void arena_Initialize()
+{
+ maxspawned = max(2, autocvar_g_arena_maxspawned);
+ arena_roundbased = autocvar_g_arena_roundbased;
+ if(arena_roundbased)
+ round_handler_Spawn(Arena_CheckPlayers, Arena_CheckWinner, 5, autocvar_g_arena_warmup, autocvar_g_arena_round_timelimit);
+}
+
+MUTATOR_DEFINITION(gamemode_arena)
+{
+ MUTATOR_HOOK(ClientDisconnect, arena_ClientDisconnect, CBC_ORDER_ANY);
+ MUTATOR_HOOK(reset_map_players, arena_reset_map_players, CBC_ORDER_ANY);
+ MUTATOR_HOOK(MakePlayerObserver, arena_MakePlayerObserver, CBC_ORDER_ANY);
+ MUTATOR_HOOK(PutClientInServer, arena_PutClientInServer, CBC_ORDER_ANY);
+ MUTATOR_HOOK(PlayerSpawn, arena_PlayerSpawn, CBC_ORDER_ANY);
+ MUTATOR_HOOK(PlayerPreThink, arena_PlayerPreThink, CBC_ORDER_ANY);
+ MUTATOR_HOOK(ForbidPlayerScore_Clear, arena_ForbidPlayerScore_Clear, CBC_ORDER_ANY);
+ MUTATOR_HOOK(GiveFragsForKill, arena_GiveFragsForKill, CBC_ORDER_ANY);
+ MUTATOR_HOOK(PlayerDies, arena_PlayerDies, CBC_ORDER_ANY);
+ MUTATOR_HOOK(SV_StartFrame, arena_SV_StartFrame, CBC_ORDER_ANY);
+
+ MUTATOR_ONADD
+ {
+ if(time > 1) // game loads at time 1
+ error("This is a game type and it cannot be added at runtime.");
+ arena_Initialize();
+ }
+
+ MUTATOR_ONREMOVE
+ {
+ error("This is a game type and it cannot be removed at runtime.");
+ }
+
+ return 0;
+}
--- /dev/null
+float maxspawned;
+float numspawned;
+float arena_roundbased;
+float arena_roundbased_state;
+.float spawned;
+.entity spawnqueue_next;
+.entity spawnqueue_prev;
+.float spawnqueue_in;
+entity spawnqueue_first;
+entity spawnqueue_last;
+
+void Spawnqueue_Insert(entity e);
+void Spawnqueue_Remove(entity e);
+void Spawnqueue_Unmark(entity e);
+void Spawnqueue_Mark(entity e);
+MUTATOR_DECLARATION(gamemode_arena);
MUTATOR_DECLARATION(gamemode_ca);
MUTATOR_DECLARATION(gamemode_keyhunt);
MUTATOR_DECLARATION(gamemode_freezetag);
mutators/base.qh
mutators/mutators.qh
+mutators/gamemode_arena.qh
mutators/gamemode_ca.qh
mutators/gamemode_ctf.qh
mutators/gamemode_keyhunt.qh // TODO fix this
../common/explosion_equation.qc
mutators/base.qc
+mutators/gamemode_arena.qc
mutators/gamemode_ca.qc
mutators/gamemode_ctf.qc
mutators/gamemode_freezetag.qc
if(MUTATOR_CALLHOOK(ForbidPlayerScore_Clear)) return;
if(g_lms) return;
- if(g_arena) return;
if(g_cts) return; // in CTS, you don't lose score by observing
if(g_race && g_race_qualifying) return; // in qualifying, you don't lose score by observing
float game_delay;
float game_delay_last;
-void Arena_Main();
void RuneMatchGivePoints();
float RedirectionThink();
entity SelectSpawnPoint (float anypoint);
return;
}
- Arena_Main();
-
CreatureFrame ();
CheckRules_World ();
{
fraglimit_override = autocvar_g_arena_point_limit;
leadlimit_override = autocvar_g_arena_point_leadlimit;
- maxspawned = autocvar_g_arena_maxspawned;
- if(maxspawned < 2)
- maxspawned = 2;
- arena_roundbased = autocvar_g_arena_roundbased;
+ MUTATOR_ADD(gamemode_arena);
}
if(g_ca)