From 6eb688299155b424dd40969b366922f8ceff0a1b Mon Sep 17 00:00:00 2001 From: TimePath Date: Mon, 15 Dec 2014 20:57:03 +1100 Subject: [PATCH] digging out gamemode infection by fruitieX (cherry picked from commit 4644e4cdda0033c7db72a7ec5c65cdd3f4c8d654) Conflicts: gamemodes.cfg qcsrc/client/hud.qc qcsrc/common/mapinfo.qh qcsrc/server/autocvars.qh qcsrc/server/bot/aim.qc qcsrc/server/cl_player.qc qcsrc/server/g_damage.qc qcsrc/server/mutators/mutators.qh qcsrc/server/progs.src --- gamemodes.cfg | 8 + qcsrc/client/hud.qc | 32 +- qcsrc/common/mapinfo.qc | 18 ++ qcsrc/common/mapinfo.qh | 3 + qcsrc/menu/xonotic/util.qc | 1 + qcsrc/server/autocvars.qh | 1 + qcsrc/server/bot/aim.qc | 4 +- qcsrc/server/bot/bot.qc | 2 + qcsrc/server/cl_client.qc | 4 +- qcsrc/server/cl_player.qc | 11 + qcsrc/server/defs.qh | 4 + qcsrc/server/g_damage.qc | 3 +- qcsrc/server/mutators/gamemode_infection.qc | 337 ++++++++++++++++++++ qcsrc/server/mutators/mutators.qh | 1 + qcsrc/server/mutators/mutators_include.qc | 1 + qcsrc/server/teamplay.qc | 5 + 16 files changed, 422 insertions(+), 13 deletions(-) create mode 100644 qcsrc/server/mutators/gamemode_infection.qc diff --git a/gamemodes.cfg b/gamemodes.cfg index b6cbe92cfc..5c97c7cd05 100644 --- a/gamemodes.cfg +++ b/gamemodes.cfg @@ -499,6 +499,7 @@ set g_race_qualifying_timelimit 0 set g_race_qualifying_timelimit_override -1 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)" + // ========== // invasion // ========== @@ -511,3 +512,10 @@ set g_invasion_spawn_delay 0.25 set g_invasion_spawnpoint_spawn_delay 0.5 set g_invasion_teams 0 "number of teams in invasion (note: use mapinfo to set this)" set g_invasion_team_spawns 1 "use team spawns in teamplay invasion mode" + + +// =========== +// infection +// =========== +set g_infection 0 "Infection: Infect everyone with your color to win!" +set g_infection_delay_round 5 "delay during which new colors can still be formed, after this a new player will join an existing team (and as such cannot win anymore)" diff --git a/qcsrc/client/hud.qc b/qcsrc/client/hud.qc index 7359468e09..78b21647ab 100644 --- a/qcsrc/client/hud.qc +++ b/qcsrc/client/hud.qc @@ -3063,6 +3063,17 @@ float kaball_statuschange_time; // time when the status changed // we don't need to reset for keepaway since it immediately // autocorrects prevstatus as to if the player has the ball or not +void HUD_Mod_Infection(vector pos, vector mySize) +{ + mod_active = 1; // Infection should never hide the mod icons panel + + float f = stof(getplayerkeyvalue(player_localentnum - 1, "colors")); + + vector rgb = colormapPaletteColor(floor(f / 16), 0); + + drawpic_aspect_skin(pos, "player", mySize, rgb, panel_fg_alpha, DRAWFLAG_NORMAL); +} + void HUD_Mod_Keepaway(vector pos, vector mySize) { mod_active = 1; // keepaway should always show the mod HUD @@ -3381,15 +3392,16 @@ void HUD_ModIcons_SetFunc() { switch(gametype) { - case MAPINFO_TYPE_KEYHUNT: HUD_ModIcons_GameType = HUD_Mod_KH; break; - case MAPINFO_TYPE_CTF: HUD_ModIcons_GameType = HUD_Mod_CTF; break; - case MAPINFO_TYPE_NEXBALL: HUD_ModIcons_GameType = HUD_Mod_NexBall; break; + case MAPINFO_TYPE_KEYHUNT: HUD_ModIcons_GameType = HUD_Mod_KH; break; + case MAPINFO_TYPE_CTF: HUD_ModIcons_GameType = HUD_Mod_CTF; break; + case MAPINFO_TYPE_NEXBALL: HUD_ModIcons_GameType = HUD_Mod_NexBall; break; case MAPINFO_TYPE_CTS: - case MAPINFO_TYPE_RACE: HUD_ModIcons_GameType = HUD_Mod_Race; break; + case MAPINFO_TYPE_RACE: HUD_ModIcons_GameType = HUD_Mod_Race; break; case MAPINFO_TYPE_CA: - case MAPINFO_TYPE_FREEZETAG: HUD_ModIcons_GameType = HUD_Mod_CA; break; - case MAPINFO_TYPE_DOMINATION: HUD_ModIcons_GameType = HUD_Mod_Dom; break; - case MAPINFO_TYPE_KEEPAWAY: HUD_ModIcons_GameType = HUD_Mod_Keepaway; break; + case MAPINFO_TYPE_FREEZETAG: HUD_ModIcons_GameType = HUD_Mod_CA; break; + case MAPINFO_TYPE_DOMINATION: HUD_ModIcons_GameType = HUD_Mod_Dom; break; + case MAPINFO_TYPE_KEEPAWAY: HUD_ModIcons_GameType = HUD_Mod_Keepaway; break; + case MAPINFO_TYPE_INFECTION: HUD_ModIcons_GameType = HUD_Mod_Infection; break; } } @@ -3429,9 +3441,9 @@ void HUD_ModIcons(void) } if(autocvar__hud_configure) - HUD_Mod_CTF(panel_pos, panel_size); - else - HUD_ModIcons_GameType(panel_pos, panel_size); + HUD_Mod_CTF(panel_pos, panel_size); + else + HUD_ModIcons_GameType(panel_pos, panel_size); draw_endBoldFont(); } diff --git a/qcsrc/common/mapinfo.qc b/qcsrc/common/mapinfo.qc index 2230d5c790..30ce6cd788 100644 --- a/qcsrc/common/mapinfo.qc +++ b/qcsrc/common/mapinfo.qc @@ -368,6 +368,7 @@ float _MapInfo_Generate(string pFilename) // 0: failure, 1: ok ent, 2: ok bsp MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_DEATHMATCH; // DM always works MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_LMS; // LMS always works MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_KEEPAWAY; // Keepaway always works + MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_INFECTION; // INFECTION always works if(spawnpoints >= 8 && diameter > 4096) { MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_TEAM_DEATHMATCH; @@ -426,6 +427,7 @@ string _MapInfo_GetDefault(float t) case MAPINFO_TYPE_NEXBALL: return "5 20 0"; case MAPINFO_TYPE_CTS: return "20 0 0"; case MAPINFO_TYPE_FREEZETAG: return "10 20 0"; + case MAPINFO_TYPE_INFECTION: return "5 20 0"; // NOTE: DO NOT ADD ANY MORE GAME TYPES HERE // THIS IS JUST LEGACY SUPPORT FOR NEXUIZ MAPS // ONLY ADD NEW STUFF TO _MapInfo_GetDefaultEx @@ -500,6 +502,22 @@ void _MapInfo_Map_ApplyGametype(string s, float pWantedType, float pThisType, fl cvar_set("fraglimit", sa); s = cdr(s); } + + if(pWantedType == MAPINFO_TYPE_INFECTION) + { + sa = car(s); + if(sa != "") + cvar_set("fraglimit", sa); + s = cdr(s); + } + + if(pWantedType == MAPINFO_TYPE_INFECTION) + { + sa = car(s); + if(sa != "") + cvar_set("fraglimit", sa); + s = cdr(s); + } /* keepaway wuz here if(pWantedType == MAPINFO_TYPE_KEEPAWAY) diff --git a/qcsrc/common/mapinfo.qh b/qcsrc/common/mapinfo.qh index bbcc267b2b..bd51473a45 100644 --- a/qcsrc/common/mapinfo.qh +++ b/qcsrc/common/mapinfo.qh @@ -82,6 +82,9 @@ REGISTER_GAMETYPE(_("Keepaway"),ka,g_keepaway,KEEPAWAY,TRUE,"timelimit=20 pointl REGISTER_GAMETYPE(_("Invasion"),inv,g_invasion,INVASION,FALSE,"pointlimit=50 teams=0",_("Survive against waves of monsters")); #define g_invasion IS_GAMETYPE(INVASION) +REGISTER_GAMETYPE(_("Infection"),inf,g_infection,INFECTION,FALSE,"timelimit=20",_("Survive against the infection")) +#define g_infection IS_GAMETYPE(INFECTION) + const float MAPINFO_FEATURE_WEAPONS = 1; // not defined for instagib-only maps const float MAPINFO_FEATURE_VEHICLES = 2; const float MAPINFO_FEATURE_TURRETS = 4; diff --git a/qcsrc/menu/xonotic/util.qc b/qcsrc/menu/xonotic/util.qc index cf4139fdee..cea9098f83 100644 --- a/qcsrc/menu/xonotic/util.qc +++ b/qcsrc/menu/xonotic/util.qc @@ -652,6 +652,7 @@ float updateCompression() GAMETYPE(MAPINFO_TYPE_DEATHMATCH) \ GAMETYPE(MAPINFO_TYPE_DOMINATION) \ GAMETYPE(MAPINFO_TYPE_FREEZETAG) \ + GAMETYPE(MAPINFO_TYPE_INFECTION) \ GAMETYPE(MAPINFO_TYPE_KEEPAWAY) \ GAMETYPE(MAPINFO_TYPE_KEYHUNT) \ GAMETYPE(MAPINFO_TYPE_LMS) \ diff --git a/qcsrc/server/autocvars.qh b/qcsrc/server/autocvars.qh index 4b9468552d..0d74b49c91 100644 --- a/qcsrc/server/autocvars.qh +++ b/qcsrc/server/autocvars.qh @@ -837,6 +837,7 @@ float autocvar_g_campcheck_damage; float autocvar_g_campcheck_distance; float autocvar_g_campcheck_interval; float autocvar_g_jump_grunt; +float autocvar_g_infection_delay_round = 5; float autocvar_g_spawn_near_teammate_distance; float autocvar_g_spawn_near_teammate_ignore_spawnpoint; float autocvar_g_spawn_near_teammate_ignore_spawnpoint_delay; diff --git a/qcsrc/server/bot/aim.qc b/qcsrc/server/bot/aim.qc index 0ae3312471..bb8451afdd 100644 --- a/qcsrc/server/bot/aim.qc +++ b/qcsrc/server/bot/aim.qc @@ -1,4 +1,3 @@ - entity ka_ball; // traces multiple trajectories to find one that will impact the target // 'end' vector is the place it aims for, @@ -111,6 +110,9 @@ float bot_shouldattack(entity e) return FALSE; } + if(g_infection && e.infectioncolor == self.infectioncolor) + return FALSE; + if(e.frozen) return FALSE; diff --git a/qcsrc/server/bot/bot.qc b/qcsrc/server/bot/bot.qc index 40b769ddd3..ea15130664 100644 --- a/qcsrc/server/bot/bot.qc +++ b/qcsrc/server/bot/bot.qc @@ -408,6 +408,8 @@ void bot_clientconnect() else JoinBestTeam(self, FALSE, TRUE); + self.infection_playernum = -1; + havocbot_setupbot(); } diff --git a/qcsrc/server/cl_client.qc b/qcsrc/server/cl_client.qc index b1dccc7b5c..e1c76fb39f 100644 --- a/qcsrc/server/cl_client.qc +++ b/qcsrc/server/cl_client.qc @@ -345,7 +345,7 @@ void FixPlayermodel() UpdatePlayerSounds(); // update skin sounds } - if(!teamplay) + if(!teamplay || !g_infection) //&& ? if(strlen(autocvar_sv_defaultplayercolors)) if(self.clientcolors != stof(autocvar_sv_defaultplayercolors)) setcolor(self, stof(autocvar_sv_defaultplayercolors)); @@ -1188,6 +1188,8 @@ void ClientConnect (void) if(autocvar_sv_teamnagger && !(autocvar_bot_vs_human && (c3==-1 && c4==-1)) && !g_ca && !g_cts && !g_race) // teamnagger is currently bad for ca, race & cts send_CSQC_teamnagger(); + + self.infection_playernum = -1; CheatInitClient(); diff --git a/qcsrc/server/cl_player.qc b/qcsrc/server/cl_player.qc index 17569147fb..3e6817c2c9 100644 --- a/qcsrc/server/cl_player.qc +++ b/qcsrc/server/cl_player.qc @@ -299,6 +299,7 @@ void calculate_player_respawn_time() } void ClientKill_Now_TeamChange(); +void infection_CheckWinner(); void PlayerDamage (entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force) { @@ -536,6 +537,16 @@ void PlayerDamage (entity inflictor, entity attacker, float damage, float deatht // print an obituary message Obituary (attacker, inflictor, self, deathtype); + + if(g_infection) + { + if(deathtype == DEATH_HURTTRIGGER || deathtype == DEATH_KILL) + { + PutClientInServer(); // respawn with a random color + infection_CheckWinner(); + return; + } + } // increment frag counter for used weapon type float w; diff --git a/qcsrc/server/defs.qh b/qcsrc/server/defs.qh index ab4dee3185..e951c04fab 100644 --- a/qcsrc/server/defs.qh +++ b/qcsrc/server/defs.qh @@ -565,6 +565,10 @@ string deathmessage; float serverflags; +.float infection_playernum; +.float infectioncolor; +.float infectioncolor_original; + .float team_forced; // can be a team number to force a team, or 0 for default action, or -1 for forced spectator .float player_blocked; diff --git a/qcsrc/server/g_damage.qc b/qcsrc/server/g_damage.qc index 28a1c8e8ca..ef81b236bb 100644 --- a/qcsrc/server/g_damage.qc +++ b/qcsrc/server/g_damage.qc @@ -849,7 +849,8 @@ void Damage (entity targ, entity inflictor, entity attacker, float damage, float } if (targ == attacker) - damage = damage * autocvar_g_balance_selfdamagepercent; // Partial damage if the attacker hits himself + damage *= //g_infection ? 0 : + autocvar_g_balance_selfdamagepercent; // Partial damage if the attacker hits himself // count the damage if(attacker) diff --git a/qcsrc/server/mutators/gamemode_infection.qc b/qcsrc/server/mutators/gamemode_infection.qc new file mode 100644 index 0000000000..ea2502819a --- /dev/null +++ b/qcsrc/server/mutators/gamemode_infection.qc @@ -0,0 +1,337 @@ +#define INFECTIONCOLOR_MAX 16 + +entity infection_players[INFECTIONCOLOR_MAX]; +float infection_players_count; +float next_round; + +void infection_Initialize() +{ + float i; + for(i = 0; i < INFECTIONCOLOR_MAX; ++i) + { + infection_players[i] = world; + } + next_round = time + 5; +} + +void infection_CheckWinner() +{ + // Check if only one color remains + float i, previnfectioncolor, we_have_a_winner; + entity e; + we_have_a_winner = TRUE; // TRUE until below loop proves us wrong + entity winner = world; //? + previnfectioncolor = FALSE; + + float temp_infection_players_count; + temp_infection_players_count = infection_players_count; + + for(i = 0; i < temp_infection_players_count; ++i) + { + e = infection_players[i]; + if(e == world) // empty slot, skip to next + { + if(temp_infection_players_count < INFECTIONCOLOR_MAX - 1) // just in case + ++temp_infection_players_count; // keep on searching for one additional since this was empty + continue; + } + + if(i > 0) + { + if(previnfectioncolor != e.infectioncolor) // all infection colors are the same if we have a winner, in this case we still have more than one color alive + { + we_have_a_winner = FALSE; + break; + } + } + + previnfectioncolor = e.infectioncolor; + } + + if(we_have_a_winner) + { + temp_infection_players_count = infection_players_count; + for(i = 0; i < temp_infection_players_count; ++i) + { + e = infection_players[i]; + if(e == world) // empty slot, skip to next + { + if(temp_infection_players_count < INFECTIONCOLOR_MAX - 1) // just in case + ++temp_infection_players_count; + continue; + } + + if(e.infectioncolor == e.infectioncolor_original) // here's our winner :) + { + winner = e; + break; // break, we found the winner + } + } + + if(winner.netname != "" && infection_players_count > 1) + { + UpdateFrags(winner, +1); + + FOR_EACH_PLAYER(e) + { + centerprint(e, strcat(winner.netname, "^1 wins the round since their color survived.\n")); + } + bprint(winner.netname, "^1 wins the round since their color survived.\n"); + } + + next_round = time + 5; + } +} + +string color_owner_green, color_owner_red; +float color_owner_self; +// find whose color your attacker is carrying, set color_owner_self to 1 if it's his own, otherwise set color_owner_* to the other owner +#define infection_GetFragAttackers_ColorOwner()\ +{\ + float j;\ + entity get_frag_attacker_temp;\ + temp_infection_players_count = infection_players_count;\ + color_owner_self = 0;\ + for(j = 0; j < temp_infection_players_count; ++j)\ + {\ + get_frag_attacker_temp = infection_players[j];\ + if(get_frag_attacker_temp == world)\ + {\ + if(temp_infection_players_count < INFECTIONCOLOR_MAX - 1)\ + ++temp_infection_players_count;\ + continue;\ + }\ + if(get_frag_attacker_temp.infectioncolor_original == frag_attacker.infectioncolor_original)\ + {\ + color_owner_self = 1;\ + break;\ + }\ + else if(get_frag_attacker_temp.infectioncolor_original == frag_attacker.infectioncolor)\ + {\ + color_owner_green = strcat(get_frag_attacker_temp.netname, "^2's");\ + color_owner_red = strcat(get_frag_attacker_temp.netname, "^1's");\ + break;\ + }\ + }\ +} ENDS_WITH_CURLY_BRACE + +MUTATOR_HOOKFUNCTION(infection_PlayerDies) +{ + float i; + entity e; + + float temp_infection_players_count; + temp_infection_players_count = infection_players_count; + + if(frag_target.infectioncolor == frag_target.infectioncolor_original) // if this is the first time we die... (our infectioncolor remained unchanged) + { + for(i = 0; i < temp_infection_players_count; ++i) // check other players... + { + e = infection_players[i]; + if(e == world) // empty slot, skip to next + { + if(temp_infection_players_count < INFECTIONCOLOR_MAX - 1) // just in case + ++temp_infection_players_count; + continue; + } + + if(e.infectioncolor == frag_target.infectioncolor_original) // and see if they have our original infection color + { + infection_GetFragAttackers_ColorOwner(); + centerprint(e, strcat("^1Your master ^7", frag_target.netname, "^1 was infected by ^7", frag_attacker.netname, " ^1with ^7", color_owner_red, " ^1color.\n")); + e.infectioncolor = frag_attacker.infectioncolor; // if so, remove it, our infection color has now "died out" from this round and we can not win anymore. The attacker will "summon" all of our previously fragged targets, and also us. + setcolor(e, 16 * e.infectioncolor + e.infectioncolor); + } + } + } + else + { + frag_target.infectioncolor = frag_attacker.infectioncolor; + setcolor(frag_target, 16 * frag_target.infectioncolor + frag_target.infectioncolor); + } + + infection_GetFragAttackers_ColorOwner(); + + if(color_owner_self) + color_owner_green = "^2your own"; + centerprint(frag_attacker, strcat("^2You infected ^7", frag_target.netname, " ^2with ^7", color_owner_green, " ^2color.\n")); + + if(color_owner_self) + color_owner_red = "^1their own"; + centerprint(frag_target, strcat("^1You were infected by ^7", frag_attacker.netname, " ^1with ^7", color_owner_red, " ^1color.\n")); + bprint("^7", frag_target.netname, "^1 was infected by ^7", frag_attacker.netname, " ^1with ^7", color_owner_red, " ^1color.\n"); + + frag_target.health = cvar("g_balance_health_start"); // "respawn" the player :P + + infection_CheckWinner(); + + return 1; +} + +MUTATOR_HOOKFUNCTION(infection_RemovePlayer) +{ + if(self.infection_playernum == -1) + return 0; // nothing to remove + + float i, j; + /*for (i = self.infection_playernum; i < infection_players_count; ++i) + { + infection_players[i] = infection_players[i+1]; + infection_players[i].infection_playernum = infection_players[i].infection_playernum - 1; + } + */ + + infection_players[self.infection_playernum] = world; + infection_players_count = infection_players_count - 1; + + // if other players have our color, randomize their color + entity e; + float temp_infection_players_count; + temp_infection_players_count = infection_players_count; + + if(!next_round) // ... but ONLY if next_round isn't set. We don't care about the colors if the round has already ended + for(i = 0; i < temp_infection_players_count; ++i) // check other players... + { + e = infection_players[i]; + if(e == world) // empty slot, skip to next + { + if(temp_infection_players_count < INFECTIONCOLOR_MAX - 1) // just in case + ++temp_infection_players_count; + continue; + } + + if(e.infectioncolor == self.infectioncolor_original) // and see if they have our original infection color + { + entity random_player = world; + + for(j = 0; j < 100; ++j) // try 100 times to find a color that isn't the same as our color. If this fails we are either damn unlucky, or there are really only players left of our color + { + random_player = infection_players[floor(random() * (INFECTIONCOLOR_MAX - 1))]; + + if(random_player == world) // hit empty slot, try again + continue; + + if(random_player.infectioncolor != self.infectioncolor_original) // break if we found another color + { + break; + } + } + e.infectioncolor = random_player.infectioncolor; + setcolor(e, 16 * e.infectioncolor + e.infectioncolor); + } + } + + self.infection_playernum = -1; + + if(infection_players_count > 1 && time > autocvar_g_infection_delay_round) + infection_CheckWinner(); + + return 1; +} + +MUTATOR_HOOKFUNCTION(infection_PlayerSpawn) +{ + float i; + if(infection_players_count == 1 && time > autocvar_g_infection_delay_round) + next_round = time; // start a new round immediately + + if(infection_players_count + 1 >= INFECTIONCOLOR_MAX) + { + bprint("^1Sorry, only ", ftos(INFECTIONCOLOR_MAX), " players can play infection at once."); + return 0; + } + + if(self.infection_playernum == -1) // only add if we don't have a infection_playernum + { + // add player to the players array, give him a playernum + for(i = 0; i <= infection_players_count; ++i) // <= because we might not find any empty slots until then, and in that case we need a new slot + { + if(infection_players[i] == world) // empty slot, give player this slot + { + self.infection_playernum = i; + break; + } + } + infection_players[i] = self; + infection_players_count = infection_players_count + 1; + } + + if(time > autocvar_g_infection_delay_round) // spawn too late, give player a random color + { + entity random_player = world; + + for(i = 0; i < 100; ++i) // try 100 times to find a color that isn't the same as our color. If this fails we are either damn unlucky, or there are really only players left of our color + { + random_player = infection_players[floor(random() * (INFECTIONCOLOR_MAX - 1))]; + + if(random_player == world) // hit empty slot, try again + continue; + + if(random_player.infectioncolor != self.infectioncolor_original) // break if we found another color + { + break; + } + } + + self.infectioncolor = random_player.infectioncolor; + self.infectioncolor_original = -1; // can't win if player didn't spawn during the round delay + } + else // we are still in the delay period before the round starts + { + self.infectioncolor = self.infection_playernum; + self.infectioncolor_original = self.infectioncolor; + } + setcolor(self, 16 * self.infectioncolor + self.infectioncolor); + + return 1; +} + +MUTATOR_HOOKFUNCTION(infection_GiveFragsForKill) +{ + frag_score = 0; // no frags counted in infection, maybe later (TODO) + return 1; +} + +MUTATOR_HOOKFUNCTION(infection_PlayerPreThink) +{ + setcolor(self, 16 * self.infectioncolor + self.infectioncolor); // prevent cheating by changing player colors + return 1; +} + +MUTATOR_HOOKFUNCTION(infection_PlayerDamage_Calculate) +{ + if ((frag_attacker.infectioncolor == frag_target.infectioncolor && frag_attacker != frag_target) || (frag_deathtype == DEATH_FALL) || (frag_deathtype == DEATH_DROWN) || (frag_deathtype == DEATH_SLIME) || (frag_deathtype == DEATH_LAVA)) + { + frag_damage = 0; + frag_force = '0 0 0'; + } + return 1; +} + +MUTATOR_DEFINITION(gamemode_infection) +{ + MUTATOR_HOOK(MakePlayerObserver, infection_RemovePlayer, CBC_ORDER_ANY); + MUTATOR_HOOK(ClientDisconnect, infection_RemovePlayer, CBC_ORDER_ANY); + MUTATOR_HOOK(PlayerDies, infection_PlayerDies, CBC_ORDER_ANY); + MUTATOR_HOOK(PlayerSpawn, infection_PlayerSpawn, CBC_ORDER_ANY); + MUTATOR_HOOK(GiveFragsForKill, infection_GiveFragsForKill, CBC_ORDER_FIRST); + MUTATOR_HOOK(PlayerPreThink, infection_PlayerPreThink, CBC_ORDER_FIRST); + MUTATOR_HOOK(PlayerDamage_Calculate, infection_PlayerDamage_Calculate, 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."); + //g_infection = 1; + infection_Initialize(); + } + + MUTATOR_ONREMOVE + { + //g_infection = 0; + error("This is a game type and it cannot be removed at runtime."); + return -1; + } + + return 0; +} diff --git a/qcsrc/server/mutators/mutators.qh b/qcsrc/server/mutators/mutators.qh index 955ba1a526..af85da6d97 100644 --- a/qcsrc/server/mutators/mutators.qh +++ b/qcsrc/server/mutators/mutators.qh @@ -12,6 +12,7 @@ MUTATOR_DECLARATION(gamemode_invasion); MUTATOR_DECLARATION(gamemode_cts); MUTATOR_DECLARATION(gamemode_race); MUTATOR_DECLARATION(gamemode_tdm); +MUTATOR_DECLARATION(gamemode_infection); MUTATOR_DECLARATION(mutator_dodging); MUTATOR_DECLARATION(mutator_invincibleprojectiles); diff --git a/qcsrc/server/mutators/mutators_include.qc b/qcsrc/server/mutators/mutators_include.qc index 220bd05957..b6f3f8da6a 100644 --- a/qcsrc/server/mutators/mutators_include.qc +++ b/qcsrc/server/mutators/mutators_include.qc @@ -4,6 +4,7 @@ #include "gamemode_ctf.qc" #include "gamemode_domination.qc" #include "gamemode_freezetag.qc" +#include "gamemode_infection.qc" #include "gamemode_keyhunt.qc" #include "gamemode_keepaway.qc" #include "gamemode_nexball.qc" diff --git a/qcsrc/server/teamplay.qc b/qcsrc/server/teamplay.qc index bd5d3607c1..2b697bf6a7 100644 --- a/qcsrc/server/teamplay.qc +++ b/qcsrc/server/teamplay.qc @@ -202,6 +202,11 @@ void InitGameplayMode() { MUTATOR_ADD(gamemode_keepaway); } + + if(g_infection) + { + MUTATOR_ADD(gamemode_infection); + } if(g_invasion) { -- 2.39.2