3 float autocvar_g_infection_round_timelimit;
4 float autocvar_g_infection_warmup;
5 int autocvar_g_infection_teams;
6 bool autocvar_g_infection_conversions;
8 int infection_players_count;
11 .int infectioncolor_original;
13 const int INFECTIONTEAM_NONE = -1;
14 const int INFECTIONTEAM_UNDEFINED = -2;
16 // safe team comparisons
17 #define INF_SAMETEAM(a, b) (a.infectioncolor == b.infectioncolor)
18 #define INF_DIFFTEAM(a, b) (a.infectioncolor != b.infectioncolor)
20 void infection_SetColor(entity e, int _color)
22 e.infectioncolor = _color;
23 setcolor(e, (_color << 4) | _color);
26 string color_owner_green, color_owner_red;
27 // find whose color a player is carrying, true if it's his own, otherwise set color_owner_* to the other owner
28 void infection_GetColorOwner(entity me)
30 if (me.infectioncolor == me.infectioncolor_original)
32 color_owner_green = "^2your own";
33 color_owner_red = "^1their own";
36 FOREACH_CLIENT(IS_PLAYER(it) && me.infectioncolor == it.infectioncolor_original, {
37 color_owner_green = sprintf("%s^2's", it.netname);
38 color_owner_red = sprintf("%s^1's", it.netname);
43 void infection_Assign(bool late)
48 int infection_coloridx = 0;
49 FOREACH_CLIENT(IS_PLAYER(it), {
50 if (infection_coloridx < autocvar_g_infection_teams) // Limit alphas
51 it.infectioncolor_original = infection_coloridx;
52 infection_SetColor(it, infection_coloridx++ % bound(0, autocvar_g_infection_teams, 15));
57 // Spawn too late, give player a random color
59 int skip = rint(random() * (infection_players_count - 1)); // Ignore self
61 FOREACH_CLIENT(IS_PLAYER(it), {
62 if (it == this || IS_OBSERVER(it)) continue;
68 LOG_DEBUGF("[INFECTION] copying %s's color", e.netname);
69 color = e.infectioncolor;
70 this.infectioncolor_original = INFECTIONTEAM_NONE; // Can't win if player didn't spawn during the round delay
71 infection_SetColor(this, color);
75 bool infection_CheckTeams()
77 return infection_players_count > 1;
80 bool infection_CheckWinner()
82 if (infection_players_count <= 1) return false; // There can be no winner
84 if (0 < round_handler_GetEndTime() && round_handler_GetEndTime() <= time) // Round over
86 Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_ROUND_OVER);
87 Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_ROUND_OVER);
88 round_handler_Init(5, autocvar_g_infection_warmup, autocvar_g_infection_round_timelimit);
92 // Check if only one color remains
93 int previnfectioncolor = -1;
94 bool we_have_a_winner = true; // until loop below proves us wrong
95 FOREACH_CLIENT(IS_PLAYER(it), {
96 // All infection colors are the same if we have a winner
97 if (previnfectioncolor != -1 && previnfectioncolor != it.infectioncolor)
99 // In this case we still have more than one color alive
100 we_have_a_winner = false;
103 previnfectioncolor = it.infectioncolor;
106 if (!we_have_a_winner) return false;
109 FOREACH_CLIENT(IS_PLAYER(it) && it.infectioncolor == it.infectioncolor_original, {
110 UpdateFrags(it, 10); // Bonus points
111 Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_ROUND_PLAYER_WIN, it.netname);
112 Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_ROUND_PLAYER_WIN, it.netname);
115 round_handler_Init(5, autocvar_g_infection_warmup, autocvar_g_infection_round_timelimit);
119 void infection_RoundStart()
121 infection_Assign(false);
122 infection_CheckWinner();
125 MUTATOR_HOOKFUNCTION(inf, PlayerDies)
127 infection_CheckWinner();
131 bool inf_RemovePlayer();
132 MUTATOR_HOOKFUNCTION(inf, MakePlayerObserver)
134 return inf_RemovePlayer();
136 MUTATOR_HOOKFUNCTION(inf, ClientDisconnect)
138 return inf_RemovePlayer();
140 bool inf_RemovePlayer()
143 if (!IS_PLAYER(this)) return true; // Wasn't playing
145 infection_players_count--;
147 this.infectioncolor_original = INFECTIONTEAM_UNDEFINED;
149 // Grant alpha status to next of kin
150 FOREACH_CLIENT(IS_PLAYER(it) && it.infectioncolor == this.infectioncolor_original, {
151 it.infectioncolor_original = this.infectioncolor;
152 centerprint(it, "^2You are now an alpha.\n");
156 infection_CheckWinner();
160 MUTATOR_HOOKFUNCTION(inf, PlayerSpawn)
163 if (this.infectioncolor_original != INFECTIONTEAM_UNDEFINED) return true; // Wasn't observing
164 infection_players_count++;
166 infection_Assign(true);
171 MUTATOR_HOOKFUNCTION(inf, GiveFragsForKill, CBC_ORDER_FIRST)
174 infection_GetColorOwner(frag_attacker);
175 // If this is the first time we die... (our infectioncolor remained unchanged)
176 if (autocvar_g_infection_conversions && frag_target.infectioncolor == frag_target.infectioncolor_original)
178 // check other players and see if they have our original infection color
179 FOREACH_CLIENT(IS_PLAYER(it) && INF_SAMETEAM(it, frag_target), {
180 // If so, remove it, our infection color has now "died out" from this round and we can not win anymore.
181 // The attacker will "summon" all of our previously fragged targets, and also us.
182 centerprint(it, sprintf("^1Your alpha ^7%s^1 was infected by ^7%s^1 with ^7%s^1 color.\n",
183 frag_target.netname, frag_attacker.netname, color_owner_red));
184 infection_SetColor(it, frag_attacker.infectioncolor);
190 infection_SetColor(frag_target, frag_attacker.infectioncolor);
194 string target = frag_target.netname, attacker = frag_attacker.netname;
196 centerprint(frag_attacker, sprintf("^2You infected ^7%s^2 with ^7%s^2 color.\n", target, color_owner_green));
197 centerprint(frag_target, sprintf("^1You were infected by ^7%s^1 with ^7%s^1 color.\n", attacker, color_owner_red));
199 bprint(sprintf("^7%s^1 was infected by ^7%s^1 with ^7%s^1 color.\n", frag_target.netname, attacker,
205 MUTATOR_HOOKFUNCTION(inf, PlayerPreThink, CBC_ORDER_FIRST)
210 // Prevent cheating by changing player colors
211 infection_SetColor(this, round_handler_IsRoundStarted()
212 ? this.infectioncolor
218 MUTATOR_HOOKFUNCTION(inf, PlayerDamage_Calculate)
220 if (IS_PLAYER(frag_attacker) // Allow environment damage
221 && frag_attacker != frag_target // Allow self damage
222 && INF_SAMETEAM(frag_attacker, frag_target) // Block friendly fire
226 frag_force = '0 0 0';
231 MUTATOR_HOOKFUNCTION(inf, BotShouldAttack)
234 return INF_SAMETEAM(checkentity, this);
237 MUTATOR_HOOKFUNCTION(inf, ClientConnect)
240 this.infectioncolor_original = INFECTIONTEAM_UNDEFINED;
241 stuffcmd(this, "settemp cl_forceplayercolors 0\n");
246 REGISTER_MUTATOR(inf, false)
250 if (time > 1) // game loads at time 1
251 error("This is a game type and it cannot be added at runtime.");
252 infection_players_count = 0;
253 round_handler_Spawn(infection_CheckTeams, infection_CheckWinner, infection_RoundStart);
254 round_handler_Init(5, autocvar_g_infection_warmup, autocvar_g_infection_round_timelimit);
257 MUTATOR_ONROLLBACK_OR_REMOVE
259 // we actually cannot roll back inf_Initialize here
260 // BUT: we don't need to! If this gets called, adding always
266 print("This is a game type and it cannot be removed at runtime.");