alias cl_hook_gamestart_cts
alias cl_hook_gamestart_ka
alias cl_hook_gamestart_ft
+alias cl_hook_gamestart_infect
alias cl_hook_gamestart_inv
alias cl_hook_gamestart_duel
alias cl_hook_gamestart_mayhem
alias sv_hook_gamestart_cts
alias sv_hook_gamestart_ka
alias sv_hook_gamestart_ft
+alias sv_hook_gamestart_infect
alias sv_hook_gamestart_inv
alias sv_hook_gamestart_duel
alias sv_hook_gamestart_mayhem
alias sv_vote_gametype_hook_dm
alias sv_vote_gametype_hook_dom
alias sv_vote_gametype_hook_ft
+alias sv_vote_gametype_hook_infection
alias sv_vote_gametype_hook_inv
alias sv_vote_gametype_hook_ka
alias sv_vote_gametype_hook_kh
set g_race_teams 0 "when 2, 3, or 4, the race is played as a team game (the team members can add up their laps)"
set g_race_cptimes_onlyself 0 "only show player's own checkpoint times"
+// ===========
+// infection
+// ===========
+
+set g_infection 0 "Infection: survivors must avoid getting infected"
+set g_infection_round_timelimit 900 "maximum time for the infected to infect all survivors"
+set g_infection_warmup "how long the players will have time to run around the map before the round starts"
+set g_infection_infected_count "the minimum amount of players to infect"
+set g_infection_round_enddelay "seconds of delay for score evaluation after round could end"
+
// ==========
// invasion
// ==========
seta notification_INFO_SUPERWEAPON_PICKUP "1" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)"
seta notification_INFO_SURVIVAL_HUNTER_WIN "1" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)"
seta notification_INFO_SURVIVAL_SURVIVOR_WIN "1" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)"
+seta notification_INFO_INFECTION_INFECTED_WIN "1" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)"
+seta notification_INFO_INFECTION_SURVIVOR_WIN "1" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)"
seta notification_INFO_TEAMCHANGE_LARGERTEAM "1" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)"
seta notification_INFO_TEAMCHANGE_NOTALLOWED "1" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)"
seta notification_INFO_VERSION_BETA "1" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)"
#include <common/gamemodes/gamemode/domination/_mod.inc>
#include <common/gamemodes/gamemode/duel/_mod.inc>
#include <common/gamemodes/gamemode/freezetag/_mod.inc>
+#include <common/gamemodes/gamemode/infection/_mod.inc>
#include <common/gamemodes/gamemode/invasion/_mod.inc>
#include <common/gamemodes/gamemode/keepaway/_mod.inc>
#include <common/gamemodes/gamemode/keyhunt/_mod.inc>
#include <common/gamemodes/gamemode/domination/_mod.qh>
#include <common/gamemodes/gamemode/duel/_mod.qh>
#include <common/gamemodes/gamemode/freezetag/_mod.qh>
+#include <common/gamemodes/gamemode/infection/_mod.qh>
#include <common/gamemodes/gamemode/invasion/_mod.qh>
#include <common/gamemodes/gamemode/keepaway/_mod.qh>
#include <common/gamemodes/gamemode/keyhunt/_mod.qh>
--- /dev/null
+// generated file; do not modify
+#include <common/gamemodes/gamemode/infection/infection.qc>
+#ifdef SVQC
+ #include <common/gamemodes/gamemode/infection/sv_infection.qc>
+#endif
--- /dev/null
+// generated file; do not modify
+#include <common/gamemodes/gamemode/infection/infection.qh>
+#ifdef SVQC
+ #include <common/gamemodes/gamemode/infection/sv_infection.qh>
+#endif
--- /dev/null
+#include "infection.qh"
--- /dev/null
+#pragma once
+
+#include <common/gamemodes/gamemode/deathmatch/deathmatch.qh>
+#include <common/gamemodes/gamemode/tdm/tdm.qh>
+#include <common/mapinfo.qh>
+
+CLASS(Infection, Gametype)
+ INIT(Infection)
+ {
+ this.gametype_init(this, _("Infection"),"inf","g_infection",GAMETYPE_FLAG_TEAMPLAY,"","timelimit=30",_("Survivors must avoid getting infected"));
+ }
+ METHOD(Infection, m_isAlwaysSupported, bool(Gametype this, int spawnpoints, float diameter))
+ {
+ return true;
+ }
+ METHOD(Infection, m_isForcedSupported, bool(Gametype this))
+ {
+ if(!(MapInfo_Map_supportedGametypes & this.gametype_flags) && (MapInfo_Map_supportedGametypes & MAPINFO_TYPE_DEATHMATCH.gametype_flags))
+ {
+ return true;
+ }
+ if(!(MapInfo_Map_supportedGametypes & this.gametype_flags) && (MapInfo_Map_supportedGametypes & MAPINFO_TYPE_TEAM_DEATHMATCH.gametype_flags))
+ {
+ return true;
+ }
+ return false;
+ }
+ENDCLASS(Infection)
+REGISTER_GAMETYPE(INFECTION, NEW(Infection));
+#define g_infection IS_GAMETYPE(TEAM_DEATHMATCH)
--- /dev/null
+#include "sv_infection.qh"
+
+const int NUM_TEAM_SURVIVOR = 2;
+const int NUM_TEAM_INFECTED = 1;
+
+float autocvar_g_infection_round_timelimit = 900;
+float autocvar_g_infection_warmup = 10;
+float autocvar_g_infection_infected_count = 1;
+float autocvar_g_infection_round_enddelay = 1;
+
+spawnfunc(inf_team)
+{
+ if(!g_infection || !this.cnt) { delete(this); return; }
+
+ this.team = this.cnt + 1;
+}
+
+// code from here on is just to support maps that don't have team entities
+void Infection_SpawnTeam (string teamname, int teamcolor)
+{
+ entity this = new_pure(inf_team);
+ this.netname = teamname;
+ this.cnt = teamcolor - 1;
+ this.team = teamcolor;
+ this.spawnfunc_checked = true;
+}
+
+void Infection_InitTeams(entity this)
+{
+ // if no teams are found, spawn defaults
+ if(find(NULL, classname, "Infection_team") == NULL)
+ {
+ LOG_TRACE("No \"infection_team\" entities found on this map, creating them anyway.");
+
+ int numteams = 2;
+
+ int teams = BITS(numteams);
+ if(teams & BIT(0))
+ Infection_SpawnTeam("Infected", NUM_TEAM_2);
+ if(teams & BIT(1))
+ Infection_SpawnTeam("Survivor", NUM_TEAM_1);
+ }
+}
+
+bool Infection_CheckPlayers()
+{
+ allowed_to_spawn = true;
+ int playercount = 0;
+ FOREACH_CLIENT(IS_PLAYER(it) && !IS_DEAD(it),
+ {
+ ++playercount;
+ });
+ if (playercount < 2) {
+ Kill_Notification(NOTIF_ALL, NULL, MSG_CENTER, CPID_MISSING_PLAYERS);
+ return false;
+ }
+ return true;
+}
+
+bool Infection_CheckWinner()
+{
+ if(round_handler_GetEndTime() > 0 && round_handler_GetEndTime() - time <= 0
+ && autocvar_g_infection_round_enddelay == -1)
+ {
+ // if the match times out, survivors win
+ Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, CENTER_INFECTION_SURVIVOR_WIN);
+ Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_INFECTION_SURVIVOR_WIN);
+
+ allowed_to_spawn = false;
+ game_stopped = true;
+ round_handler_Init(5, autocvar_g_infection_warmup, autocvar_g_infection_round_timelimit);
+ return 1;
+ }
+
+ int survivor_count = 0, infected_count = 0;
+ FOREACH_CLIENT(IS_PLAYER(it) && !IS_DEAD(it),
+ {
+ if(Entity_GetTeamIndex(it) == NUM_TEAM_SURVIVOR)
+ ++survivor_count;
+ else if(Entity_GetTeamIndex(it) == NUM_TEAM_INFECTED)
+ ++infected_count;
+ });
+ // Not enough infected? Let's infect some players.
+ int playercount = survivor_count + infected_count;
+ int must_infect = bound(1, ((autocvar_g_infection_infected_count >= 1) ? autocvar_g_infection_infected_count : floor(playercount * autocvar_g_infection_infected_count)), playercount - 1); // 20%, but ensure at least 1 and less than total
+ FOREACH_CLIENT_RANDOM(IS_PLAYER(it) && !IS_DEAD(it),
+ {
+ if(infected_count >= must_infect)
+ break;
+ ++infected_count;
+ Player_SetTeamIndex(it, NUM_TEAM_INFECTED);
+ });
+
+ if (survivor_count > 0) {
+ round_handler_ResetEndDelayTime();
+ return 0;
+ }
+
+ // delay round ending a bit
+ if (autocvar_g_infection_round_enddelay
+ && round_handler_GetEndTime() > 0
+ && round_handler_GetEndTime() - time > 0) // don't delay past timelimit
+ {
+ if (round_handler_GetEndDelayTime() == -1)
+ {
+ round_handler_SetEndDelayTime(min(time + autocvar_g_infection_round_enddelay, round_handler_GetEndTime()));
+ return 0;
+ }
+ else if (round_handler_GetEndDelayTime() >= time)
+ {
+ return 0;
+ }
+ }
+
+ if(infected_count > 0) // infected win
+ {
+ Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, CENTER_INFECTION_INFECTED_WIN);
+ Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_INFECTION_INFECTED_WIN);
+ } else {
+ Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, CENTER_ROUND_TIED);
+ Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_ROUND_TIED);
+ }
+
+ allowed_to_spawn = false;
+ game_stopped = true;
+ round_handler_Init(5, autocvar_g_infection_warmup, autocvar_g_infection_round_timelimit);
+ return 1;
+}
+
+void Infection_RoundStart()
+{
+ allowed_to_spawn = boolean(warmup_stage);
+ FOREACH_CLIENT(IS_PLAYER(it), {
+ it.player_blocked = false;
+ Player_SetTeamIndex(it, NUM_TEAM_SURVIVOR);
+ });
+}
+
+void Infection_Initialize()
+{
+ GameRules_teams(true);
+ GameRules_spawning_teams(2);
+ InitializeEntity(NULL, Infection_InitTeams, INITPRIO_GAMETYPE);
+
+ allowed_to_spawn = true;
+ round_handler_Spawn(Infection_CheckPlayers, Infection_CheckWinner, Infection_RoundStart);
+ round_handler_Init(5, autocvar_g_infection_warmup, autocvar_g_infection_round_timelimit);
+}
+
+MUTATOR_HOOKFUNCTION(inf, TeamBalance_CheckAllowedTeams)
+{
+ M_ARGV(0, float) = BIT(0);
+ return true;
+}
+
+MUTATOR_HOOKFUNCTION(inf, PlayerDies)
+{
+ entity attacker = M_ARGV(1, entity);
+ entity target = M_ARGV(2, entity);
+
+ if (!IS_PLAYER(attacker)) {
+ Player_SetTeamIndex(target, NUM_TEAM_INFECTED);
+ return true;
+ }
+
+ int attacker_team = Entity_GetTeamIndex(attacker);
+
+ if (DIFF_TEAM(attacker, target) && attacker_team != NUM_TEAM_SURVIVOR)
+ Player_SetTeamIndex(target, attacker_team);
+
+ return true;
+}
--- /dev/null
+#pragma once
+
+#include <common/mutators/base.qh>
+void Infection_Initialize();
+
+REGISTER_MUTATOR(inf, false)
+{
+ MUTATOR_STATIC();
+ MUTATOR_ONADD
+ {
+ Infection_Initialize();
+ }
+ return false;
+}
MSG_INFO_NOTIF(SURVIVAL_HUNTER_WIN, N_CONSOLE, 0, 0, "", "", "", _("^K1Hunters^BG win the round"), "")
MSG_INFO_NOTIF(SURVIVAL_SURVIVOR_WIN, N_CONSOLE, 0, 0, "", "", "", _("^F1Survivors^BG win the round"), "")
+ MSG_INFO_NOTIF(INFECTION_INFECTED_WIN, N_CONSOLE, 0, 0, "", "", "", _("^K1Infected^BG wins the round"), "")
+ MSG_INFO_NOTIF(INFECTION_SURVIVOR_WIN, N_CONSOLE, 0, 0, "", "", "", _("^F1Survivors^BG win the round"), "")
+
MSG_INFO_NOTIF(TEAMCHANGE_LARGERTEAM, N_CONSOLE, 0, 0, "", "", "", _("^BGYou cannot change to a larger team"), "")
MSG_INFO_NOTIF(TEAMCHANGE_NOTALLOWED, N_CONSOLE, 0, 0, "", "", "", _("^BGYou are not allowed to change teams"), "")
MSG_CENTER_NOTIF(SURVIVAL_SURVIVOR, N_ENABLE, 0, 0, "", CPID_SURVIVAL, "5 0", BOLD(_("^BGYou are a ^F1survivor^BG! Identify and eliminate the hunter(s)!")), "")
MSG_CENTER_NOTIF(SURVIVAL_SURVIVOR_WIN, N_ENABLE, 0, 0, "", CPID_ROUND, "0 0", _("^F1Survivors^BG win the round"), "")
+ MSG_CENTER_NOTIF(INFECTION_INFECTED, N_ENABLE, 0, 0, "", CPID_SURVIVAL, "5 0", BOLD(_("^BGYou are ^K1infected^BG! Eliminate the survivor(s)!")), "")
+ MSG_CENTER_NOTIF(INFECTION_INFECTED_WIN, N_ENABLE, 0, 0, "", CPID_ROUND, "0 0", _("^K1Infected^BG wins the round"), "")
+ MSG_CENTER_NOTIF(INFECTION_SURVIVOR, N_ENABLE, 0, 0, "", CPID_SURVIVAL, "5 0", BOLD(_("^BGYou are a ^F1survivor^BG! Don't get infected!")), "")
+ MSG_CENTER_NOTIF(INFECTION_SURVIVOR_WIN, N_ENABLE, 0, 0, "", CPID_ROUND, "0 0", _("^F1Survivors^BG win the round"), "")
+
MULTITEAM_CENTER(TEAMCHANGE, N_ENABLE, 0, 1, "", CPID_TEAMCHANGE, "1 f1", _("^K1Changing to ^TC^TT^K1 in ^COUNT"), "", NAME)
MSG_CENTER_NOTIF(TEAMCHANGE_AUTO, N_ENABLE, 0, 1, "", CPID_TEAMCHANGE, "1 f1", _("^K1Changing team in ^COUNT"), "")
MSG_CENTER_NOTIF(TEAMCHANGE_SPECTATE, N_ENABLE, 0, 1, "", CPID_TEAMCHANGE, "1 f1", _("^K1Spectating in ^COUNT"), "")
GAMETYPE(MAPINFO_TYPE_ONSLAUGHT) \
GAMETYPE(MAPINFO_TYPE_ASSAULT) \
GAMETYPE(MAPINFO_TYPE_SURVIVAL) \
+ GAMETYPE(MAPINFO_TYPE_INFECTION) \
/* GAMETYPE(MAPINFO_TYPE_DUEL) */ \
/**/
BADCVAR("g_tmayhem");
BADCVAR("g_tmayhem_teams");
BADCVAR("g_vip");
+ BADCVAR("g_infection");
BADCVAR("leadlimit");
BADCVAR("nextmap");
BADCVAR("teamplay");