]> git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/commitdiff
Merge branch 'master' into TimePath/gametypes/infection
authorTimePath <andrew.hardaker1995@gmail.com>
Sat, 12 Mar 2016 05:34:48 +0000 (16:34 +1100)
committerTimePath <andrew.hardaker1995@gmail.com>
Sat, 12 Mar 2016 05:59:47 +0000 (16:59 +1100)
1  2 
gamemodes.cfg
qcsrc/client/hud/panel/modicons.qc
qcsrc/common/mapinfo.qc
qcsrc/common/mapinfo.qh
qcsrc/menu/xonotic/util.qc
qcsrc/server/mutators/mutator/gamemode_infection.qc

diff --combined gamemodes.cfg
index 2949b1ad98ae6a6c518c0b93a62863cbc086d28a,a4b33b7e21fbbfbb647c9d343cac067a324cc5dd..6bf264fd42321b6bc6d7eb1c3a33fe6eae6eb026
@@@ -37,7 -37,6 +37,7 @@@ alias cl_hook_gamestart_n
  alias cl_hook_gamestart_cts
  alias cl_hook_gamestart_ka
  alias cl_hook_gamestart_ft
 +alias cl_hook_gamestart_inf
  alias cl_hook_gamestart_inv
  alias cl_hook_gameend "rpn /cl_matchcount dup load 1 + =" // increase match count every time a game ends
  alias cl_hook_activeweapon
@@@ -59,7 -58,6 +59,7 @@@ alias sv_hook_gamestart_n
  alias sv_hook_gamestart_cts
  alias sv_hook_gamestart_ka
  alias sv_hook_gamestart_ft
 +alias sv_hook_gamestart_inf
  alias sv_hook_gamestart_inv
  alias sv_hook_gamerestart
  alias sv_hook_gameend
@@@ -80,7 -78,6 +80,7 @@@ alias sv_vote_gametype_hook_ct
  alias sv_vote_gametype_hook_dm
  alias sv_vote_gametype_hook_dom
  alias sv_vote_gametype_hook_ft
 +alias sv_vote_gametype_hook_inf
  alias sv_vote_gametype_hook_inv
  alias sv_vote_gametype_hook_ka
  alias sv_vote_gametype_hook_kh
@@@ -223,13 -220,6 +223,13 @@@ set g_ft_respawn_delay_large_count 
  set g_ft_respawn_delay_max 0
  set g_ft_respawn_waves 0
  set g_ft_weapon_stay 0
 +set g_inf_respawn_delay_small 0
 +set g_inf_respawn_delay_small_count 0
 +set g_inf_respawn_delay_large 0
 +set g_inf_respawn_delay_large_count 0
 +set g_inf_respawn_delay_max 0
 +set g_inf_respawn_waves 0
 +set g_inf_weapon_stay 0
  set g_inv_respawn_delay_small 0
  set g_inv_respawn_delay_small_count 0
  set g_inv_respawn_delay_large 0
@@@ -264,7 -254,7 +264,7 @@@ set g_ca_teams 
  //  capture the flag
  // ==================
  set g_ctf 0 "Capture The Flag: take the enemy flag and bring it to yours at your base to score"
- set g_ctf_oneflag 1 "Allow oneflag CTF mode on maps that support it"
+ set g_ctf_oneflag 0 "Allow oneflag CTF mode on maps that support it"
  set g_ctf_oneflag_reverse 0 "apply reverse mode to oneflag CTF (take flag to enemy bases to cap), overrides g_ctf_reverse only in oneflag, g_ctf_reverse still affects oneflag"
  set g_ctf_flag_return 1 "auto return the flag to base when touched by a teammate"
  set g_ctf_flag_return_carrying 0 "(manual return mode) auto return the flag to base if touched by a flag carrier"
@@@ -370,7 -360,7 +370,7 @@@ set g_domination_disable_frags             0 "play
  set g_domination_point_amt            0 "override: how many points to get per ping"
  set g_domination_point_fullbright     0 "domination point fullbright"
  set g_domination_point_rate           0 "override: how often to give those points"
set g_domination_point_capturetime    0.1 "how long it takes to capture a point (given no interference)"
//set g_domination_point_capturetime  0.1 "how long it takes to capture a point (given no interference)"
  set g_domination_point_glow           0 "domination point glow (warning, slow)"
  set g_domination_roundbased 0 "enable round-based domination (capture all control points to win the round)"
  set g_domination_roundbased_point_limit 5 "capture limit in round-based domination mode"
@@@ -512,7 -502,6 +512,6 @@@ set g_onslaught 0 "Onslaught: take cont
  set g_onslaught_point_limit 1 "Onslaught point limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)"
  set g_onslaught_warmup 5
  set g_onslaught_round_timelimit 280
- set g_onslaught_debug 0 "show debug prints in onslaught"
  set g_onslaught_teleport_radius 200 "Allows teleporting from a control point to another"
  set g_onslaught_teleport_wait 5 "Time before player can teleport again"
  set g_onslaught_spawn_choose 1 "Allow players to choose the control point to be spawned at"
@@@ -544,17 -533,6 +543,17 @@@ set g_race_qualifying_timelimit 
  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)"
  
 +
 +// ===========
 +//  infection
 +// ===========
 +set g_infection 0 "Infection: Infect everyone with your color"
 +set g_infection_round_timelimit 180 "round time limit in seconds"
 +set g_infection_warmup 5 "Time players get to run around before the round starts"
 +set g_infection_teams 4 "Maximum number of teams (from 2 to 15 inclusive)"
 +set g_infection_conversions 1 "Enable stealing when killing an alpha"
 +
 +
  // ==========
  //  invasion
  // ==========
index 92969ce6fb8badecf4b27d982d04974e10d753b1,0705ddfe5c3091cf1fe2b48e72b2437da69dc664..6d0ce7809602baaae98926308657ad4235b253d3
@@@ -108,7 -108,7 +108,7 @@@ void HUD_Mod_CTF(vector pos, vector myS
        float f; // every function should have that
  
        int redflag, blueflag, yellowflag, pinkflag, neutralflag; // current status
-       float redflag_statuschange_elapsedtime, blueflag_statuschange_elapsedtime, yellowflag_statuschange_elapsedtime, pinkflag_statuschange_elapsedtime, neutralflag_statuschange_elapsedtime; // time since the status changed
+       float redflag_statuschange_elapsedtime = 0, blueflag_statuschange_elapsedtime = 0, yellowflag_statuschange_elapsedtime = 0, pinkflag_statuschange_elapsedtime = 0, neutralflag_statuschange_elapsedtime = 0; // time since the status changed
        bool ctf_oneflag; // one-flag CTF mode enabled/disabled
        int stat_items = STAT(CTF_FLAGSTATUS);
        float fs, fs2, fs3, size1, size2;
        }
  
        // when status CHANGES, set old status into prevstatus and current status into status
-       #define X(team) do {                                                                                                                    \
+       #define X(team) MACRO_BEGIN {                                                                                                   \
                if (team##flag != team##flag_prevframe) {                                                                       \
                team##flag_statuschange_time = time;                                                                    \
                team##flag_prevstatus = team##flag_prevframe;                                                   \
                team##flag_prevframe = team##flag;                                                                              \
        }                                                                                                                                                       \
        team##flag_statuschange_elapsedtime = time - team##flag_statuschange_time;      \
-     } while (0)
+     } MACRO_END
        X(red);
        X(blue);
        X(yellow);
        const float BLINK_FREQ = 5; // circle frequency, = 2*pi*frequency in hertz
  
        #define X(team, cond) \
-       string team##_icon, team##_icon_prevstatus; \
+       string team##_icon = string_null, team##_icon_prevstatus = string_null; \
        int team##_alpha, team##_alpha_prevstatus; \
        team##_alpha = team##_alpha_prevstatus = 1; \
-       do { \
+       MACRO_BEGIN { \
                switch (team##flag) { \
                        case 1: team##_icon = "flag_" #team "_taken"; break; \
                        case 2: team##_icon = "flag_" #team "_lost"; break; \
                                } \
                                break; \
                } \
-       } while (0)
+       } MACRO_END
        X(red, myteam != NUM_TEAM_1);
        X(blue, myteam != NUM_TEAM_2);
        X(yellow, myteam != NUM_TEAM_3);
        neutralflag_pos = pos;
        flag_size = e1 * fs * size1 + e2 * size2;
  
-       #define X(team) do { \
+       #define X(team) MACRO_BEGIN { \
                f = bound(0, team##flag_statuschange_elapsedtime * 2, 1); \
                if (team##_icon_prevstatus && f < 1) \
                        drawpic_aspect_skin_expanding(team##flag_pos, team##_icon_prevstatus, flag_size, '1 1 1', panel_fg_alpha * team##_alpha_prevstatus, DRAWFLAG_NORMAL, f); \
                if (team##_icon) \
                        drawpic_aspect_skin(team##flag_pos, team##_icon, flag_size, '1 1 1', panel_fg_alpha * team##_alpha * f, DRAWFLAG_NORMAL); \
-       } while (0)
+       } MACRO_END
        X(red);
        X(blue);
        X(yellow);
@@@ -399,14 -399,6 +399,14 @@@ void HUD_Mod_KH(vector pos, vector mySi
                drawpic_aspect_skin(KH_SLOTS[i++], "kh_dropped", slot_size, '1 1 1', alpha, DRAWFLAG_NORMAL);
  }
  
 +void HUD_Mod_Infection(vector pos, vector mySize)
 +{
 +      mod_active = 1; // Infection should always show the mod HUD
 +      int f = stof(getplayerkeyvalue(player_localentnum - 1, "colors"));
 +      vector color = colormapPaletteColor(floor(f / 16), 0);
 +      drawpic_aspect_skin(pos, "player_neutral", mySize, color, panel_fg_alpha, DRAWFLAG_NORMAL);
 +}
 +
  // Keepaway HUD mod icon
  int kaball_prevstatus; // last remembered status
  float kaball_statuschange_time; // time when the status changed
@@@ -423,7 -415,7 +423,7 @@@ void HUD_Mod_Keepaway(vector pos, vecto
        float BLINK_FREQ = 5;
        float kaball_alpha = BLINK_BASE + BLINK_FACTOR * cos(time * BLINK_FREQ);
  
-       int stat_items = getstati(STAT_ITEMS, 0, 24);
+       int stat_items = STAT(ITEMS);
        int kaball = (stat_items/IT_KEY1) & 1;
  
        if(kaball != kaball_prevstatus)
@@@ -459,7 -451,7 +459,7 @@@ void HUD_Mod_NexBall(vector pos, vecto
        float nb_pb_starttime, dt, p;
        int stat_items;
  
-       stat_items = getstati(STAT_ITEMS, 0, 24);
+       stat_items = STAT(ITEMS);
        nb_pb_starttime = STAT(NB_METERSTART);
  
        if (stat_items & IT_KEY1)
@@@ -497,7 -489,7 +497,7 @@@ void HUD_Mod_Race(vector pos, vector my
        mod_active = 1; // race should never hide the mod icons panel
        entity me;
        me = playerslots[player_localnum];
-       float t, score;
+       float score;
        float f; // yet another function has this
        score = me.(scores[ps_primary]);
  
                rr = CTS_RECORD;
        else
                rr = RACE_RECORD;
-       t = stof(db_get(ClientProgsDB, strcat(shortmapname, rr, "time")));
+       float t = stof(db_get(ClientProgsDB, strcat(shortmapname, rr, "time")));
  
        if(score && (score < t || !t)) {
                db_put(ClientProgsDB, strcat(shortmapname, rr, "time"), ftos(score));
@@@ -737,7 -729,6 +737,7 @@@ void HUD_ModIcons_SetFunc(
                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;
        }
  }
  
diff --combined qcsrc/common/mapinfo.qc
index 73bf7b7c652dc82f02b2b4b128640e0df35dd541,d853eeac3cc2fe690d164226c8757c096f61f280..92f3f1200d11b4b630adf8517a83be44c3e0e39c
@@@ -1,12 -1,12 +1,12 @@@
  #if defined(CSQC)
      #include "../client/defs.qh"
      #include "util.qh"
-     #include "weapons/all.qh"
+     #include <common/weapons/all.qh>
      #include "mapinfo.qh"
  #elif defined(MENUQC)
  #elif defined(SVQC)
      #include "util.qh"
-     #include "monsters/all.qh"
+     #include <common/monsters/all.qh>
      #include "mapinfo.qh"
  #endif
  
@@@ -390,7 -390,7 +390,7 @@@ float _MapInfo_Generate(string pFilenam
        }
        if(inWorldspawn)
        {
-               LOG_INFO(fn, " ended still in worldspawn, BUG\n");
+               LOG_MAPWARN(fn, " ended still in worldspawn, BUG\n");
                return 0;
        }
        diameter = vlen(mapMaxs - mapMins);
@@@ -538,13 -538,6 +538,13 @@@ void _MapInfo_Map_ApplyGametype(string 
                        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)
@@@ -635,7 -628,7 +635,7 @@@ void _MapInfo_Map_ApplyGametypeEx(strin
                if (sa == "") continue;
                int p = strstrofs(sa, "=", 0);
                if (p < 0) {
-                       LOG_WARNINGF("Invalid gametype setting in mapinfo for gametype %s: %s\n", MapInfo_Type_ToString(pWantedType), sa);
+                       LOG_MAPWARNF("Invalid gametype setting in mapinfo for gametype %s: %s\n", MapInfo_Type_ToString(pWantedType), sa);
                        continue;
                }
                string k = substring(sa, 0, p);
                }
                FOREACH(Gametypes, true, LAMBDA(handled |= it.m_parse_mapinfo(k, v)));
                if (!handled)
-             LOG_WARNINGF("Invalid gametype setting in mapinfo for gametype %s: %s\n", MapInfo_Type_ToString(pWantedType), sa);
+             LOG_MAPWARNF("Invalid gametype setting in mapinfo for gametype %s: %s\n", MapInfo_Type_ToString(pWantedType), sa);
        }
  
        if (pWantedType == MAPINFO_TYPE_RACE && cvar("g_race_teams") >= 2)
@@@ -698,13 -691,13 +698,13 @@@ Gametype MapInfo_Type(int t
  
  int MapInfo_Type_FromString(string t)
  {
- #define deprecate(from, to) do { \
+ #define deprecate(from, to) MACRO_BEGIN { \
        if (t == #from) { \
                string replacement = #to; \
-               LOG_WARNINGF("MapInfo_Type_FromString (probably %s): using deprecated name '%s'. Should use '%s'.\n", MapInfo_Map_bspname, t, replacement); \
+               LOG_MAPWARNF("MapInfo_Type_FromString (probably %s): using deprecated name '%s'. Should use '%s'.\n", MapInfo_Map_bspname, t, replacement); \
                t = replacement; \
        } \
- } while (0)
+ } MACRO_END
        deprecate(nexball, nb);
        deprecate(freezetag, ft);
        deprecate(keepaway, ka);
@@@ -777,7 -770,7 +777,7 @@@ void _MapInfo_Parse_Settemp(string pFil
                {
                        fh = fopen(s, FILE_READ);
                        if(fh < 0)
-                               LOG_INFO("Map ", pFilename, " references not existing config file ", s, "\n");
+                               LOG_MAPWARN("Map ", pFilename, " references not existing config file ", s, "\n");
                        else
                        {
                                for (;;)
                        }
                }
                else
-                       LOG_INFO("Map ", pFilename, " uses too many levels of inclusion\n");
+                       LOG_MAPWARN("Map ", pFilename, " uses too many levels of inclusion\n");
        }
        else if(t == "")
-               LOG_INFO("Map ", pFilename, " contains a potentially harmful setting, ignored\n");
+               LOG_MAPWARN("Map ", pFilename, " contains a potentially harmful setting, ignored\n");
        else if (!cvar_value_issafe(t))
-               LOG_INFO("Map ", pFilename, " contains a potentially harmful setting, ignored\n");
+               LOG_MAPWARN("Map ", pFilename, " contains a potentially harmful setting, ignored\n");
        else if (!cvar_value_issafe(s))
-               LOG_INFO("Map ", pFilename, " contains a potentially harmful setting, ignored\n");
+               LOG_MAPWARN("Map ", pFilename, " contains a potentially harmful setting, ignored\n");
        else if(matchacl(MAPINFO_SETTEMP_ACL_SYSTEM, t) <= 0)
-               LOG_INFO("Map ", pFilename, " contains a potentially harmful setting, ignored\n");
+               LOG_MAPWARN("Map ", pFilename, " contains a potentially harmful setting, ignored\n");
        else if(matchacl(acl, t) <= 0)
-               LOG_INFO("Map ", pFilename, " contains a denied setting, ignored\n");
+               LOG_MAPWARN("Map ", pFilename, " contains a denied setting, ignored\n");
        else
        {
                if(type == 0) // server set
@@@ -864,7 -857,7 +864,7 @@@ float MapInfo_isRedundant(string fn, st
  }
  
  // load info about a map by name into the MapInfo_Map_* globals
 -float MapInfo_Get_ByName_NoFallbacks(string pFilename, int pAllowGenerate, int pGametypeToSet)
 +float MapInfo_Get_ByName_NoFallbacks(string pFilename, bool pAllowGenerate, int pGametypeToSet)
  {
        string fn;
        string s, t;
  
        if(strstrofs(pFilename, "/", 0) >= 0)
        {
-               LOG_INFO("Invalid character in map name, ignored\n");
+               LOG_MAPWARN("Invalid character in map name, ignored\n");
                return 0;
        }
  
                                error("... but I just wrote it!");
                }
  
-               LOG_INFO("WARNING: autogenerated mapinfo file ", fn, " has been loaded; please edit that file and move it to maps/", pFilename, ".mapinfo\n");
+               LOG_MAPWARN("autogenerated mapinfo file ", fn, " has been loaded; please edit that file and move it to maps/", pFilename, ".mapinfo\n");
        }
  
        _MapInfo_Map_Reset();
                        else if(t == "monsters") MapInfo_Map_supportedFeatures |= MAPINFO_FEATURE_MONSTERS;
                        else if(t == "new_toys") MapInfo_Map_supportedFeatures |= MAPINFO_FEATURE_WEAPONS;
                        else
-                               LOG_TRACE("Map ", pFilename, " supports unknown feature ", t, ", ignored\n");
+                               LOG_MAPWARN("Map ", pFilename, " supports unknown feature ", t, ", ignored\n");
                }
                else if(t == "hidden")
                {
                {
                        t = car(s); s = cdr(s);
                        f = MapInfo_Type_FromString(t);
-                       LOG_WARNING("Map ", pFilename, " contains the legacy 'type' keyword which is deprecated and will be removed in the future. Please migrate the mapinfo file to 'gametype'.\n");
+                       LOG_MAPWARN("Map ", pFilename, " contains the legacy 'type' keyword which is deprecated and will be removed in the future. Please migrate the mapinfo file to 'gametype'.\n");
                        if(f)
                                _MapInfo_Map_ApplyGametype (s, pGametypeToSet, f, true);
                        else
-                               LOG_TRACE("Map ", pFilename, " supports unknown game type ", t, ", ignored\n");
+                               LOG_MAPWARN("Map ", pFilename, " supports unknown game type ", t, ", ignored\n");
                }
                else if(t == "gametype")
                {
                        if(f)
                                _MapInfo_Map_ApplyGametypeEx (s, pGametypeToSet, f);
                        else
-                               LOG_TRACE("Map ", pFilename, " supports unknown game type ", t, ", ignored\n");
+                               LOG_MAPWARN("Map ", pFilename, " supports unknown game type ", t, ", ignored\n");
                }
                else if(t == "size")
                {
                        t = car(s); s = cdr(s); d = stof(t);
                        t = car(s); s = cdr(s); e = stof(t);
                        if(s == "")
-                               LOG_INFO("Map ", pFilename, " contains an incorrect size line (not enough params), syntax: size mins_x mins_y mins_z maxs_x maxs_y maxs_z\n");
+                               LOG_MAPWARN("Map ", pFilename, " contains an incorrect size line (not enough params), syntax: size mins_x mins_y mins_z maxs_x maxs_y maxs_z\n");
                        else
                        {
                                t = car(s); s = cdr(s); f = stof(t);
                                if(s != "")
-                                       LOG_INFO("Map ", pFilename, " contains an incorrect size line (too many params), syntax: size mins_x mins_y mins_z maxs_x maxs_y maxs_z\n");
+                                       LOG_MAPWARN("Map ", pFilename, " contains an incorrect size line (too many params), syntax: size mins_x mins_y mins_z maxs_x maxs_y maxs_z\n");
                                else
                                {
                                        if(a >= d || b >= e || c >= f)
-                                               LOG_INFO("Map ", pFilename, " contains an incorrect size line, mins have to be < maxs\n");
+                                               LOG_MAPWARN("Map ", pFilename, " contains an incorrect size line, mins have to be < maxs\n");
                                        else
                                        {
                                                MapInfo_Map_mins.x = a;
                        }
                        else
                        {
-                               LOG_TRACE("Map ", pFilename, " has a setting for unknown game type ", t, ", ignored\n");
+                               LOG_MAPWARN("Map ", pFilename, " has a setting for unknown game type ", t, ", ignored\n");
                        }
                }
                else if(t == "clientsettemp_for_type")
                        }
                        else
                        {
-                               LOG_TRACE("Map ", pFilename, " has a client setting for unknown game type ", t, ", ignored\n");
+                               LOG_MAPWARN("Map ", pFilename, " has a client setting for unknown game type ", t, ", ignored\n");
                        }
                }
                else if(t == "fog")
                {
                        if (!cvar_value_issafe(s))
-                               LOG_INFO("Map ", pFilename, " contains a potentially harmful fog setting, ignored\n");
+                               LOG_MAPWARN("Map ", pFilename, " contains a potentially harmful fog setting, ignored\n");
                        else
                                MapInfo_Map_fog = s;
                }
                        if(pGametypeToSet)
                        {
                                if (!cvar_value_issafe(t))
-                                       LOG_INFO("Map ", pFilename, " contains a potentially harmful cdtrack, ignored\n");
+                                       LOG_MAPWARN("Map ", pFilename, " contains a potentially harmful cdtrack, ignored\n");
                                else
                                        MapInfo_Map_clientstuff = strcat(
                                                MapInfo_Map_clientstuff, "cd loop \"", t, "\"\n"
                        }
                }
                else
-                       LOG_TRACE("Map ", pFilename, " provides unknown info item ", t, ", ignored\n");
+                       LOG_MAPWARN("Map ", pFilename, " provides unknown info item ", t, ", ignored\n");
        }
        fclose(fh);
  
        MapInfo_Cache_Store();
        if(MapInfo_Map_supportedGametypes != 0)
                return r;
-       LOG_TRACE("Map ", pFilename, " supports no game types, ignored\n");
+       LOG_MAPWARN("Map ", pFilename, " supports no game types, ignored\n");
        return 0;
  }
 -float MapInfo_Get_ByName(string pFilename, float pAllowGenerate, int pGametypeToSet)
 +float MapInfo_Get_ByName(string pFilename, bool pAllowGenerate, int pGametypeToSet)
  {
        float r = MapInfo_Get_ByName_NoFallbacks(pFilename, pAllowGenerate, pGametypeToSet);
  
                        if(MapInfo_Map_supportedGametypes & MAPINFO_TYPE_DEATHMATCH)
                                _MapInfo_Map_ApplyGametypeEx ("", pGametypeToSet, MAPINFO_TYPE_TEAM_DEATHMATCH);
        }
 +      _MapInfo_Map_ApplyGametypeEx("", pGametypeToSet, MAPINFO_TYPE_INFECTION);  // infection always works
  
        if(pGametypeToSet)
        {
@@@ -1249,15 -1241,15 +1249,15 @@@ int MapInfo_CurrentGametype(
        return MAPINFO_TYPE_DEATHMATCH;
  }
  
 -float _MapInfo_CheckMap(string s) // returns 0 if the map can't be played with the current settings, 1 otherwise
 +bool _MapInfo_CheckMap(string s) // returns false if the map can't be played with the current settings, true otherwise
  {
 -      if(!MapInfo_Get_ByName(s, 1, 0))
 -              return 0;
 +      if(!MapInfo_Get_ByName(s, true, 0))
 +              return false;
        if((MapInfo_Map_supportedGametypes & MapInfo_CurrentGametype()) == 0)
 -              return 0;
 +              return false;
        if((MapInfo_Map_supportedFeatures & MapInfo_CurrentFeatures()) != MapInfo_CurrentFeatures())
 -              return 0;
 -      return 1;
 +              return false;
 +      return true;
  }
  
  float MapInfo_CheckMap(string s) // returns 0 if the map can't be played with the current settings, 1 otherwise
@@@ -1334,21 -1326,23 +1334,21 @@@ void MapInfo_LoadMapSettings_SaveGameTy
  
  void MapInfo_LoadMapSettings(string s) // to be called from worldspawn
  {
 -      float t;
 -
 -      t = MapInfo_CurrentGametype();
 +      int t = MapInfo_CurrentGametype();
        MapInfo_LoadMapSettings_SaveGameType(t);
  
        if(!_MapInfo_CheckMap(s)) // with underscore, it keeps temps
        {
                if(cvar("g_mapinfo_allow_unsupported_modes_and_let_stuff_break"))
                {
-                       LOG_INFO("EMERGENCY: can't play the selected map in the given game mode. Working with only the override settings.\n");
+                       LOG_SEVERE("can't play the selected map in the given game mode. Working with only the override settings.\n");
                        _MapInfo_Map_ApplyGametypeEx("", t, t);
                        return; // do not call Get_ByName!
                }
  
                if(MapInfo_Map_supportedGametypes == 0)
                {
-                       LOG_INFO("Mapinfo system is not functional at all. Assuming deathmatch.\n");
+                       LOG_SEVERE("Mapinfo system is not functional at all. Assuming deathmatch.\n");
                        MapInfo_Map_supportedGametypes = MAPINFO_TYPE_DEATHMATCH;
                        MapInfo_LoadMapSettings_SaveGameType(MAPINFO_TYPE_DEATHMATCH);
                        _MapInfo_Map_ApplyGametypeEx("", MAPINFO_TYPE_DEATHMATCH, MAPINFO_TYPE_DEATHMATCH);
                }
  
                // t is now a supported mode!
-               LOG_INFO("EMERGENCY: can't play the selected map in the given game mode. Falling back to a supported mode.\n");
+               LOG_WARNING("can't play the selected map in the given game mode. Falling back to a supported mode.\n");
                MapInfo_LoadMapSettings_SaveGameType(t);
        }
        MapInfo_Get_ByName(s, 1, t);
diff --combined qcsrc/common/mapinfo.qh
index 6e2949914b88de00518ed873d1e920cfd1b8e024,ab7233ef269cd60db277aa23c81588a9260b83df..e3dfc4c01cce68f4f17fc736a4a625664a0bc7c2
@@@ -1,6 -1,11 +1,11 @@@
  #ifndef MAPINFO_H
  #define MAPINFO_H
  
+ bool autocvar_developer_mapper;
+ #define LOG_MAPWARN(...) MACRO_BEGIN { if (autocvar_developer_mapper) LOG_WARNING(__VA_ARGS__); } MACRO_END
+ #define LOG_MAPWARNF(...) MACRO_BEGIN { if (autocvar_developer_mapper) LOG_WARNINGF(__VA_ARGS__); } MACRO_END
  #include "util.qh"
  
  CLASS(Gametype, Object)
@@@ -42,7 -47,7 +47,7 @@@
      }
  ENDCLASS(Gametype)
  
 -REGISTRY(Gametypes, BITS(4))
 +REGISTRY(Gametypes, BITS(5))
  #define Gametypes_from(i) _Gametypes_from(i, NULL)
  REGISTER_REGISTRY(Gametypes)
  REGISTRY_CHECK(Gametypes)
@@@ -174,8 -179,6 +179,8 @@@ REGISTER_GAMETYPE(_("Invasion"),inv,g_i
      }
  }
  
 +REGISTER_GAMETYPE(_("Infection"),inf,g_infection,INFECTION,true,"","timelimit=20",_("Survive against the infection"));
 +
  const int MAPINFO_FEATURE_WEAPONS       = 1; // not defined for instagib-only maps
  const int MAPINFO_FEATURE_VEHICLES      = 2;
  const int MAPINFO_FEATURE_TURRETS       = 4;
index 34bc2264b5beb10f57910e2019018b51180cfb06,c2a53db939e341a67afcfdcc4536604bae2ad918..21a54af7d10bacd3e83e1f7e4f739906711d7fd9
@@@ -3,11 -3,11 +3,11 @@@
  #include "../item.qc"
  
  #include "../menu.qh"
- #include "../../common/campaign_common.qh"
- #include "../../common/constants.qh"
- #include "../../common/mapinfo.qh"
- #include "../../common/util.qh"
- #include "../../common/command/generic.qh"
+ #include <common/campaign_common.qh>
+ #include <common/constants.qh>
+ #include <common/mapinfo.qh>
+ #include <common/util.qh>
+ #include <common/command/generic.qh>
  
  float GL_CheckExtension(string ext)
  {
@@@ -672,7 -672,6 +672,7 @@@ float updateCompression(
        GAMETYPE(MAPINFO_TYPE_CTF) \
        GAMETYPE(MAPINFO_TYPE_CA) \
        GAMETYPE(MAPINFO_TYPE_FREEZETAG) \
 +      GAMETYPE(MAPINFO_TYPE_INFECTION) \
        GAMETYPE(MAPINFO_TYPE_KEEPAWAY) \
        GAMETYPE(MAPINFO_TYPE_KEYHUNT) \
        GAMETYPE(MAPINFO_TYPE_LMS) \
index 3fb2a03c2fb66c9bc02a8736cbf6772ed41ae0c1,0000000000000000000000000000000000000000..80ff18ea437328e03949cd0a207c77253626a94b
mode 100644,000000..100644
--- /dev/null
@@@ -1,286 -1,0 +1,273 @@@
-       entity e;
-       FOR_EACH_PLAYER(e)
-       {
-               if (me.infectioncolor == e.infectioncolor_original)
-               {
-                       color_owner_green = sprintf("%s^2's", e.netname);
-                       color_owner_red = sprintf("%s^1's", e.netname);
-                       break;
-               }
-       }
 +#ifdef IMPLEMENTATION
 +
 +float autocvar_g_infection_round_timelimit;
 +float autocvar_g_infection_warmup;
 +int autocvar_g_infection_teams;
 +bool autocvar_g_infection_conversions;
 +
 +int infection_players_count;
 +
 +.int infectioncolor;
 +.int infectioncolor_original;
 +
 +const int INFECTIONTEAM_NONE = -1;
 +const int INFECTIONTEAM_UNDEFINED = -2;
 +
 +// safe team comparisons
 +#define INF_SAMETEAM(a, b) (a.infectioncolor == b.infectioncolor)
 +#define INF_DIFFTEAM(a, b) (a.infectioncolor != b.infectioncolor)
 +
 +void infection_SetColor(entity e, int _color)
 +{
 +      e.infectioncolor = _color;
 +      setcolor(e, (_color << 4) | _color);
 +}
 +
 +string color_owner_green, color_owner_red;
 +// find whose color a player is carrying, true if it's his own, otherwise set color_owner_* to the other owner
 +void infection_GetColorOwner(entity me)
 +{
 +      if (me.infectioncolor == me.infectioncolor_original)
 +      {
 +              color_owner_green = "^2your own";
 +              color_owner_red = "^1their own";
 +              return;
 +      }
-               entity e;
-               FOR_EACH_PLAYER(e)
-               {
++      FOREACH_CLIENT(IS_PLAYER(it) && me.infectioncolor == it.infectioncolor_original, {
++        color_owner_green = sprintf("%s^2's", it.netname);
++        color_owner_red = sprintf("%s^1's", it.netname);
++        break;
++      });
 +}
 +
 +void infection_Assign(bool late)
 +{
++    SELFPARAM();
 +      if (!late)
 +      {
 +              int infection_coloridx = 0;
-                               e.infectioncolor_original = infection_coloridx;
-                       infection_SetColor(e, infection_coloridx++ % bound(0, autocvar_g_infection_teams, 15));
-               }
++              FOREACH_CLIENT(IS_PLAYER(it), {
 +                      if (infection_coloridx < autocvar_g_infection_teams)  // Limit alphas
-               entity e;
-               FOR_EACH_PLAYER(e)
-               {
-                       if (e == self || IS_OBSERVER(e)) continue;
-                       if (!skip-- > 0) break;
-               }
-               dprintf("[INFECTION] copying %s's color\n", e.netname);
++                              it.infectioncolor_original = infection_coloridx;
++                      infection_SetColor(it, infection_coloridx++ % bound(0, autocvar_g_infection_teams, 15));
++              });
 +      }
 +      else
 +      {
 +              // Spawn too late, give player a random color
 +              int color = 15;
 +              int skip = rint(random() * (infection_players_count - 1));  // Ignore self
-               self.infectioncolor_original = INFECTIONTEAM_NONE;  // Can't win if player didn't spawn during the round delay
-               infection_SetColor(self, color);
++              entity e = NULL;
++              FOREACH_CLIENT(IS_PLAYER(it), {
++                      if (it == this || IS_OBSERVER(it)) continue;
++                      if (!skip-- > 0) {
++                          e = it;
++                break;
++                      }
++              });
++              LOG_DEBUGF("[INFECTION] copying %s's color", e.netname);
 +              color = e.infectioncolor;
-       entity e;
-       FOR_EACH_PLAYER(e)
-       {
++              this.infectioncolor_original = INFECTIONTEAM_NONE;  // Can't win if player didn't spawn during the round delay
++              infection_SetColor(this, color);
 +      }
 +}
 +
 +bool infection_CheckTeams()
 +{
 +      return infection_players_count > 1;
 +}
 +
 +bool infection_CheckWinner()
 +{
 +      if (infection_players_count <= 1) return false;                           // There can be no winner
 +
 +      if (0 < round_handler_GetEndTime() && round_handler_GetEndTime() <= time) // Round over
 +      {
 +              Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_ROUND_OVER);
 +              Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_ROUND_OVER);
 +              round_handler_Init(5, autocvar_g_infection_warmup, autocvar_g_infection_round_timelimit);
 +              return true;
 +      }
 +
 +      // Check if only one color remains
 +      int previnfectioncolor = -1;
 +      bool we_have_a_winner = true;  // until loop below proves us wrong
-               if (previnfectioncolor != -1 && previnfectioncolor != e.infectioncolor)
++      FOREACH_CLIENT(IS_PLAYER(it), {
 +              // All infection colors are the same if we have a winner
-               previnfectioncolor = e.infectioncolor;
-       }
++              if (previnfectioncolor != -1 && previnfectioncolor != it.infectioncolor)
 +              {
 +                      // In this case we still have more than one color alive
 +                      we_have_a_winner = false;
 +                      break;
 +              }
-       FOR_EACH_PLAYER(e)
-       {
-               if (e.infectioncolor == e.infectioncolor_original)
-               {
-                       UpdateFrags(e, 10);  // Bonus points
-                       Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_ROUND_PLAYER_WIN, e.netname);
-                       Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_ROUND_PLAYER_WIN, e.netname);
-               }
-       }
++              previnfectioncolor = it.infectioncolor;
++      });
 +
 +      if (!we_have_a_winner) return false;
 +
 +      // Who is it?
-       if (!IS_PLAYER(self)) return true;  // Wasn't playing
++      FOREACH_CLIENT(IS_PLAYER(it) && it.infectioncolor == it.infectioncolor_original, {
++        UpdateFrags(it, 10);  // Bonus points
++        Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_ROUND_PLAYER_WIN, it.netname);
++        Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_ROUND_PLAYER_WIN, it.netname);
++      });
 +
 +      round_handler_Init(5, autocvar_g_infection_warmup, autocvar_g_infection_round_timelimit);
 +      return true;
 +}
 +
 +void infection_RoundStart()
 +{
 +      infection_Assign(false);
 +      infection_CheckWinner();
 +}
 +
 +MUTATOR_HOOKFUNCTION(inf, PlayerDies)
 +{
 +      infection_CheckWinner();
 +      return true;
 +}
 +
 +bool inf_RemovePlayer();
 +MUTATOR_HOOKFUNCTION(inf, MakePlayerObserver)
 +{
 +      return inf_RemovePlayer();
 +}
 +MUTATOR_HOOKFUNCTION(inf, ClientDisconnect)
 +{
 +      return inf_RemovePlayer();
 +}
 +bool inf_RemovePlayer()
 +{
-       self.infectioncolor_original = INFECTIONTEAM_UNDEFINED;
++    SELFPARAM();
++      if (!IS_PLAYER(this)) return true;  // Wasn't playing
 +
 +      infection_players_count--;
 +
-       entity e;
-       FOR_EACH_PLAYER(e)
-       {
-               if (e.infectioncolor == self.infectioncolor_original)
-               {
-                       e.infectioncolor_original = self.infectioncolor;
-                       centerprint(e, "^2You are now an alpha.\n");
-                       break;
-               }
-       }
++      this.infectioncolor_original = INFECTIONTEAM_UNDEFINED;
 +
 +      // Grant alpha status to next of kin
-       if (self.infectioncolor_original != INFECTIONTEAM_UNDEFINED) return true;  // Wasn't observing
++      FOREACH_CLIENT(IS_PLAYER(it) && it.infectioncolor == this.infectioncolor_original, {
++        it.infectioncolor_original = this.infectioncolor;
++        centerprint(it, "^2You are now an alpha.\n");
++        break;
++      });
 +
 +      infection_CheckWinner();
 +      return true;
 +}
 +
 +MUTATOR_HOOKFUNCTION(inf, PlayerSpawn)
 +{
-               entity e;
-               FOR_EACH_PLAYER(e)                    // check other players...
-               {
-                       if (INF_SAMETEAM(e, frag_target)) // And see if they have our original infection color
-                       {  // 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.
-                               centerprint(e, sprintf("^1Your alpha ^7%s^1 was infected by ^7%s^1 with ^7%s^1 color.\n",
-                                       frag_target.netname, frag_attacker.netname, color_owner_red));
-                               infection_SetColor(e, frag_attacker.infectioncolor);
-                               frag_score++;
-                       }
-               }
++    SELFPARAM();
++      if (this.infectioncolor_original != INFECTIONTEAM_UNDEFINED) return true;  // Wasn't observing
 +      infection_players_count++;
 +
 +      infection_Assign(true);
 +
 +      return true;
 +}
 +
 +MUTATOR_HOOKFUNCTION(inf, GiveFragsForKill, CBC_ORDER_FIRST)
 +{
 +      frag_score = 0;
 +      infection_GetColorOwner(frag_attacker);
 +      // If this is the first time we die... (our infectioncolor remained unchanged)
 +      if (autocvar_g_infection_conversions && frag_target.infectioncolor == frag_target.infectioncolor_original)
 +      {
-       if (IS_PLAYER(self))
++          // check other players and see if they have our original infection color
++              FOREACH_CLIENT(IS_PLAYER(it) && INF_SAMETEAM(it, frag_target), {
++            // 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.
++            centerprint(it, sprintf("^1Your alpha ^7%s^1 was infected by ^7%s^1 with ^7%s^1 color.\n",
++                frag_target.netname, frag_attacker.netname, color_owner_red));
++            infection_SetColor(it, frag_attacker.infectioncolor);
++            frag_score++;
++              });
 +      }
 +      else
 +      {
 +              infection_SetColor(frag_target, frag_attacker.infectioncolor);
 +              frag_score++;
 +      }
 +
 +      string target = frag_target.netname, attacker = frag_attacker.netname;
 +
 +      centerprint(frag_attacker, sprintf("^2You infected ^7%s^2 with ^7%s^2 color.\n", target, color_owner_green));
 +      centerprint(frag_target, sprintf("^1You were infected by ^7%s^1 with ^7%s^1 color.\n", attacker, color_owner_red));
 +
 +      bprint(sprintf("^7%s^1 was infected by ^7%s^1 with ^7%s^1 color.\n", frag_target.netname, attacker,
 +              color_owner_red));
 +
 +      return true;
 +}
 +
 +MUTATOR_HOOKFUNCTION(inf, PlayerPreThink, CBC_ORDER_FIRST)
 +{
-               infection_SetColor(self, round_handler_IsRoundStarted()
-                       ? self.infectioncolor : 15
-                                 );
++    SELFPARAM();
++      if (IS_PLAYER(this))
 +      {
 +              // Prevent cheating by changing player colors
-       return INF_SAMETEAM(checkentity, self);
++              infection_SetColor(this, round_handler_IsRoundStarted()
++                      ? this.infectioncolor
++                      : 15);
 +      }
 +      return true;
 +}
 +
 +MUTATOR_HOOKFUNCTION(inf, PlayerDamage_Calculate)
 +{
 +      if (IS_PLAYER(frag_attacker)                    // Allow environment damage
 +          && frag_attacker != frag_target             // Allow self damage
 +          && INF_SAMETEAM(frag_attacker, frag_target) // Block friendly fire
 +         )
 +      {
 +              frag_damage = 0;
 +              frag_force = '0 0 0';
 +      }
 +      return false;
 +}
 +
 +MUTATOR_HOOKFUNCTION(inf, BotShouldAttack)
 +{
-       self.infectioncolor_original = INFECTIONTEAM_UNDEFINED;
-       stuffcmd(self, "settemp cl_forceplayercolors 0\n");
++    SELFPARAM();
++      return INF_SAMETEAM(checkentity, this);
 +}
 +
 +MUTATOR_HOOKFUNCTION(inf, ClientConnect)
 +{
++    SELFPARAM();
++      this.infectioncolor_original = INFECTIONTEAM_UNDEFINED;
++      stuffcmd(this, "settemp cl_forceplayercolors 0\n");
 +
 +      return false;
 +}
 +
 +REGISTER_MUTATOR(inf, false)
 +{
 +      MUTATOR_ONADD
 +      {
 +              if (time > 1)  // game loads at time 1
 +                      error("This is a game type and it cannot be added at runtime.");
 +              infection_players_count = 0;
 +              round_handler_Spawn(infection_CheckTeams, infection_CheckWinner, infection_RoundStart);
 +              round_handler_Init(5, autocvar_g_infection_warmup, autocvar_g_infection_round_timelimit);
 +      }
 +
 +      MUTATOR_ONROLLBACK_OR_REMOVE
 +      {
 +              // we actually cannot roll back inf_Initialize here
 +              // BUT: we don't need to! If this gets called, adding always
 +              // succeeds.
 +      }
 +
 +      MUTATOR_ONREMOVE
 +      {
 +              print("This is a game type and it cannot be removed at runtime.");
 +              return -1;
 +      }
 +
 +      return 0;
 +}
 +
 +#endif