]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blobdiff - qcsrc/server/mutators/gamemode_ctf.qc
Merge branch 'master' into Mario/vehicles
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / mutators / gamemode_ctf.qc
index ae657b4c68152158a5312141fa73502389a2f728..31d21116a483e1a1a0b0d6dabfd340427cef08df 100644 (file)
@@ -1,7 +1,14 @@
-// ================================================================
-//  Official capture the flag game mode coding, reworked by Samual
-//  Last updated: September, 2012
-// ================================================================
+#include "gamemode_ctf.qh"
+#include "../_all.qh"
+
+#include "gamemode.qh"
+
+#ifdef SVQC
+#include "../../common/vehicles/sv_vehicles.qh"
+#endif
+
+#include "../../warpzonelib/common.qh"
+#include "../../warpzonelib/mathlib.qh"
 
 void ctf_FakeTimeLimit(entity e, float t)
 {
@@ -14,7 +21,7 @@ void ctf_FakeTimeLimit(entity e, float t)
                WriteCoord(MSG_ONE, (t + 1) / 60);
 }
 
-void ctf_EventLog(string mode, float flagteam, entity actor) // use an alias for easy changing and quick editing later
+void ctf_EventLog(string mode, int flagteam, entity actor) // use an alias for easy changing and quick editing later
 {
        if(autocvar_sv_eventlog)
                GameLogEcho(sprintf(":ctf:%s:%d:%d:%s", mode, flagteam, actor.team, ((actor != world) ? ftos(actor.playerid) : "")));
@@ -46,7 +53,7 @@ void ctf_CaptureRecord(entity flag, entity player)
 
 void ctf_FlagcarrierWaypoints(entity player)
 {
-       WaypointSprite_Spawn("flagcarrier", 0, 0, player, FLAG_WAYPOINT_OFFSET, world, player.team, player, wps_flagcarrier, TRUE, RADARICON_FLAG, WPCOLOR_FLAGCARRIER(player.team));
+       WaypointSprite_Spawn("flagcarrier", 0, 0, player, FLAG_WAYPOINT_OFFSET, world, player.team, player, wps_flagcarrier, true, RADARICON_FLAG, WPCOLOR_FLAGCARRIER(player.team));
        WaypointSprite_UpdateMaxHealth(player.wps_flagcarrier, '1 0 0' * healtharmor_maxdamage(start_health, start_armorvalue, autocvar_g_balance_armor_blockpercent, DEATH_WEAPON) * 2);
        WaypointSprite_UpdateHealth(player.wps_flagcarrier, '1 0 0' * healtharmor_maxdamage(player.health, player.armorvalue, autocvar_g_balance_armor_blockpercent, DEATH_WEAPON));
        WaypointSprite_UpdateTeamRadar(player.wps_flagcarrier, RADARICON_FLAGCARRIER, WPCOLOR_FLAGCARRIER(player.team));
@@ -54,7 +61,7 @@ void ctf_FlagcarrierWaypoints(entity player)
 
 void ctf_CalculatePassVelocity(entity flag, vector to, vector from, float turnrate)
 {
-       float current_distance = vlen((('1 0 0' * to_x) + ('0 1 0' * to_y)) - (('1 0 0' * from_x) + ('0 1 0' * from_y))); // for the sake of this check, exclude Z axis
+       float current_distance = vlen((('1 0 0' * to.x) + ('0 1 0' * to.y)) - (('1 0 0' * from.x) + ('0 1 0' * from.y))); // for the sake of this check, exclude Z axis
        float initial_height = min(autocvar_g_ctf_pass_arc_max, (flag.pass_distance * tanh(autocvar_g_ctf_pass_arc)));
        float current_height = (initial_height * min(1, (current_distance / flag.pass_distance)));
        //print("current_height = ", ftos(current_height), ", initial_height = ", ftos(initial_height), ".\n");
@@ -83,7 +90,7 @@ void ctf_CalculatePassVelocity(entity flag, vector to, vector from, float turnra
        else { flag.velocity = (desired_direction * autocvar_g_ctf_pass_velocity); }
 }
 
-float ctf_CheckPassDirection(vector head_center, vector passer_center, vector passer_angle, vector nearest_to_passer)
+bool ctf_CheckPassDirection(vector head_center, vector passer_center, vector passer_angle, vector nearest_to_passer)
 {
        if(autocvar_g_ctf_pass_directional_max || autocvar_g_ctf_pass_directional_min)
        {
@@ -92,13 +99,11 @@ float ctf_CheckPassDirection(vector head_center, vector passer_center, vector pa
                makevectors(passer_angle);
 
                // find the closest point on the enemy to the center of the attack
-               float ang; // angle between shotdir and h
                float h; // hypotenuse, which is the distance between attacker to head
                float a; // adjacent side, which is the distance between attacker and the point on w_shotdir that is closest to head.origin
 
                h = vlen(head_center - passer_center);
-               ang = acos(dotproduct(normalize(head_center - passer_center), v_forward));
-               a = h * cos(ang);
+               a = h * (normalize(head_center - passer_center) * v_forward);
 
                vector nearest_on_line = (passer_center + a * v_forward);
                float distance_from_line = vlen(nearest_to_passer - nearest_on_line);
@@ -107,11 +112,11 @@ float ctf_CheckPassDirection(vector head_center, vector passer_center, vector pa
                spreadlimit = (autocvar_g_ctf_pass_directional_min * (1 - spreadlimit) + autocvar_g_ctf_pass_directional_max * spreadlimit);
 
                if(spreadlimit && (distance_from_line <= spreadlimit) && ((vlen(normalize(head_center - passer_center) - v_forward) * RAD2DEG) <= 90))
-                       { return TRUE; }
+                       { return true; }
                else
-                       { return FALSE; }
+                       { return false; }
        }
-       else { return TRUE; }
+       else { return true; }
 }
 
 
@@ -119,14 +124,14 @@ float ctf_CheckPassDirection(vector head_center, vector passer_center, vector pa
 // CaptureShield Functions
 // =======================
 
-float ctf_CaptureShield_CheckStatus(entity p)
+bool ctf_CaptureShield_CheckStatus(entity p)
 {
-       float s, s2, s3, s4, se, se2, se3, se4, sr, ser;
+       int s, s2, s3, s4, se, se2, se3, se4, sr, ser;
        entity e;
-       float players_worseeq, players_total;
+       int players_worseeq, players_total;
 
        if(ctf_captureshield_max_ratio <= 0)
-               return FALSE;
+               return false;
 
        s = PlayerScore_Add(p, SP_CTF_CAPS, 0);
        s2 = PlayerScore_Add(p, SP_CTF_PICKUPS, 0);
@@ -136,7 +141,7 @@ float ctf_CaptureShield_CheckStatus(entity p)
        sr = ((s - s2) + (s3 + s4));
 
        if(sr >= -ctf_captureshield_min_negscore)
-               return FALSE;
+               return false;
 
        players_total = players_worseeq = 0;
        FOR_EACH_PLAYER(e)
@@ -159,14 +164,14 @@ float ctf_CaptureShield_CheckStatus(entity p)
        // use this rule here
 
        if(players_worseeq >= players_total * ctf_captureshield_max_ratio)
-               return FALSE;
+               return false;
 
-       return TRUE;
+       return true;
 }
 
-void ctf_CaptureShield_Update(entity player, float wanted_status)
+void ctf_CaptureShield_Update(entity player, bool wanted_status)
 {
-       float updated_status = ctf_CaptureShield_CheckStatus(player);
+       bool updated_status = ctf_CaptureShield_CheckStatus(player);
        if((wanted_status == player.ctf_captureshielded) && (updated_status != wanted_status)) // 0: shield only, 1: unshield only
        {
                Send_Notification(NOTIF_ONE, player, MSG_CENTER, ((updated_status) ? CENTER_CTF_CAPTURESHIELD_SHIELDED : CENTER_CTF_CAPTURESHIELD_FREE));
@@ -174,28 +179,16 @@ void ctf_CaptureShield_Update(entity player, float wanted_status)
        }
 }
 
-float ctf_CaptureShield_Customize()
+bool ctf_CaptureShield_Customize()
 {
-       if(self.enemy.active != ACTIVE_ACTIVE) { return TRUE; }
-       if(!other.ctf_captureshielded) { return FALSE; }
-       if(CTF_SAMETEAM(self, other)) { return FALSE; }
+       if(!other.ctf_captureshielded) { return false; }
+       if(CTF_SAMETEAM(self, other)) { return false; }
 
-       return TRUE;
+       return true;
 }
 
 void ctf_CaptureShield_Touch()
 {
-       if(self.enemy.active != ACTIVE_ACTIVE)
-       {
-               vector mymid = (self.absmin + self.absmax) * 0.5;
-               vector othermid = (other.absmin + other.absmax) * 0.5;
-
-               Damage(other, self, self, 0, DEATH_HURTTRIGGER, mymid, normalize(othermid - mymid) * ctf_captureshield_force);
-               if(IS_REAL_CLIENT(other)) { Send_Notification(NOTIF_ONE, other, MSG_CENTER, CENTER_CTF_CAPTURESHIELD_INACTIVE); }
-
-               return;
-       }
-
        if(!other.ctf_captureshielded) { return; }
        if(CTF_SAMETEAM(self, other)) { return; }
 
@@ -231,7 +224,7 @@ void ctf_CaptureShield_Spawn(entity flag)
 // Drop/Pass/Throw Code
 // ====================
 
-void ctf_Handle_Drop(entity flag, entity player, float droptype)
+void ctf_Handle_Drop(entity flag, entity player, int droptype)
 {
        // declarations
        player = (player ? player : flag.pass_sender);
@@ -256,7 +249,7 @@ void ctf_Handle_Drop(entity flag, entity player, float droptype)
 
        // waypoints
        if(autocvar_g_ctf_flag_dropped_waypoint)
-               WaypointSprite_Spawn("flagdropped", 0, 0, flag, FLAG_WAYPOINT_OFFSET, world, ((autocvar_g_ctf_flag_dropped_waypoint == 2) ? 0 : player.team), flag, wps_flagdropped, TRUE, RADARICON_FLAG, WPCOLOR_DROPPEDFLAG(flag.team));
+               WaypointSprite_Spawn("flagdropped", 0, 0, flag, FLAG_WAYPOINT_OFFSET, world, ((autocvar_g_ctf_flag_dropped_waypoint == 2) ? 0 : player.team), flag, wps_flagdropped, true, RADARICON_FLAG, WPCOLOR_DROPPEDFLAG(flag.team));
 
        if(autocvar_g_ctf_flag_return_time || (autocvar_g_ctf_flag_return_damage && autocvar_g_ctf_flag_health))
        {
@@ -326,7 +319,7 @@ void ctf_Handle_Retrieve(entity flag, entity player)
        flag.pass_target = world;
 }
 
-void ctf_Handle_Throw(entity player, entity receiver, float droptype)
+void ctf_Handle_Throw(entity player, entity receiver, int droptype)
 {
        entity flag = player.flagcarried;
        vector targ_origin, flag_velocity;
@@ -358,8 +351,8 @@ void ctf_Handle_Throw(entity player, entity receiver, float droptype)
                        WarpZone_RefSys_AddInverse(flag, receiver); // wz1^-1 ... wzn^-1 receiver
                        targ_origin = WarpZone_RefSys_TransformOrigin(receiver, flag, (0.5 * (receiver.absmin + receiver.absmax))); // this is target origin as seen by the flag
 
-                       flag.pass_distance = vlen((('1 0 0' * targ_origin_x) + ('0 1 0' * targ_origin_y)) - (('1 0 0' *  player.origin_x) + ('0 1 0' *  player.origin_y))); // for the sake of this check, exclude Z axis
-                       ctf_CalculatePassVelocity(flag, targ_origin, player.origin, FALSE);
+                       flag.pass_distance = vlen((('1 0 0' * targ_origin.x) + ('0 1 0' * targ_origin.y)) - (('1 0 0' *  player.origin.x) + ('0 1 0' *  player.origin.y))); // for the sake of this check, exclude Z axis
+                       ctf_CalculatePassVelocity(flag, targ_origin, player.origin, false);
 
                        // main
                        flag.movetype = MOVETYPE_FLY;
@@ -377,10 +370,10 @@ void ctf_Handle_Throw(entity player, entity receiver, float droptype)
 
                case DROP_THROW:
                {
-                       makevectors((player.v_angle_y * '0 1 0') + (bound(autocvar_g_ctf_throw_angle_min, player.v_angle_x, autocvar_g_ctf_throw_angle_max) * '1 0 0'));
+                       makevectors((player.v_angle.y * '0 1 0') + (bound(autocvar_g_ctf_throw_angle_min, player.v_angle.x, autocvar_g_ctf_throw_angle_max) * '1 0 0'));
 
-                       flag_velocity = (('0 0 1' * autocvar_g_ctf_throw_velocity_up) + ((v_forward * autocvar_g_ctf_throw_velocity_forward) * ((player.items & IT_STRENGTH) ? autocvar_g_ctf_throw_strengthmultiplier : 1)));
-                       flag.velocity = W_CalculateProjectileVelocity(player.velocity, flag_velocity, FALSE);
+                       flag_velocity = (('0 0 1' * autocvar_g_ctf_throw_velocity_up) + ((v_forward * autocvar_g_ctf_throw_velocity_forward) * ((player.items & ITEM_Strength.m_itemid) ? autocvar_g_ctf_throw_strengthmultiplier : 1)));
+                       flag.velocity = W_CalculateProjectileVelocity(player.velocity, flag_velocity, false);
                        ctf_Handle_Drop(flag, player, droptype);
                        break;
                }
@@ -394,7 +387,7 @@ void ctf_Handle_Throw(entity player, entity receiver, float droptype)
                default:
                case DROP_NORMAL:
                {
-                       flag.velocity = W_CalculateProjectileVelocity(player.velocity, (('0 0 1' * autocvar_g_ctf_drop_velocity_up) + ((('0 1 0' * crandom()) + ('1 0 0' * crandom())) * autocvar_g_ctf_drop_velocity_side)), FALSE);
+                       flag.velocity = W_CalculateProjectileVelocity(player.velocity, (('0 0 1' * autocvar_g_ctf_drop_velocity_up) + ((('0 1 0' * crandom()) + ('1 0 0' * crandom())) * autocvar_g_ctf_drop_velocity_side)), false);
                        ctf_Handle_Drop(flag, player, droptype);
                        break;
                }
@@ -416,7 +409,7 @@ void ctf_Handle_Throw(entity player, entity receiver, float droptype)
 // Event Handlers
 // ==============
 
-void ctf_Handle_Capture(entity flag, entity toucher, float capturetype)
+void ctf_Handle_Capture(entity flag, entity toucher, int capturetype)
 {
        entity enemy_flag = ((capturetype == CAPTURE_NORMAL) ? toucher.flagcarried : toucher);
        entity player = ((capturetype == CAPTURE_NORMAL) ? toucher : enemy_flag.ctf_dropper);
@@ -461,7 +454,7 @@ void ctf_Handle_Capture(entity flag, entity toucher, float capturetype)
                PlayerScore_Add(player, SP_CTF_CAPTIME, new_time - old_time);
 
        // effects
-       pointparticles(particleeffectnum(flag.capeffect), flag.origin, '0 0 0', 1);
+       Send_Effect(flag.capeffect, flag.origin, '0 0 0', 1);
        //shockwave_spawn("models/ctf/shockwavetransring.md3", flag.origin - '0 0 15', -0.8, 0, 1);
 
        // other
@@ -482,7 +475,7 @@ void ctf_Handle_Capture(entity flag, entity toucher, float capturetype)
 void ctf_Handle_Return(entity flag, entity player)
 {
        // messages and sounds
-       if(player.flags & FL_MONSTER)
+       if(IS_MONSTER(player))
        {
                Send_Notification(NOTIF_ALL, world, MSG_INFO, APP_TEAM_ENT_4(flag, INFO_CTF_RETURN_MONSTER_), player.monster_name);
        }
@@ -520,7 +513,7 @@ void ctf_Handle_Return(entity flag, entity player)
        ctf_RespawnFlag(flag);
 }
 
-void ctf_Handle_Pickup(entity flag, entity player, float pickuptype)
+void ctf_Handle_Pickup(entity flag, entity player, int pickuptype)
 {
        // declarations
        float pickup_dropped_score; // used to calculate dropped pickup score
@@ -615,7 +608,7 @@ void ctf_Handle_Pickup(entity flag, entity player, float pickuptype)
        }
 
        // effects
-       pointparticles(particleeffectnum(flag.toucheffect), player.origin, '0 0 0', 1);
+       Send_Effect(flag.toucheffect, player.origin, '0 0 0', 1);
 
        // waypoints
        if(pickuptype == PICKUP_DROPPED) { WaypointSprite_Kill(flag.wps_flagdropped); }
@@ -628,7 +621,7 @@ void ctf_Handle_Pickup(entity flag, entity player, float pickuptype)
 // Main Flag Functions
 // ===================
 
-void ctf_CheckFlagReturn(entity flag, float returntype)
+void ctf_CheckFlagReturn(entity flag, int returntype)
 {
        if((flag.ctf_status == FLAG_DROPPED) || (flag.ctf_status == FLAG_PASSING))
        {
@@ -654,7 +647,7 @@ void ctf_CheckFlagReturn(entity flag, float returntype)
        }
 }
 
-float ctf_Stalemate_Customize()
+bool ctf_Stalemate_Customize()
 {
        // make spectators see what the player would see
        entity e, wp_owner;
@@ -662,17 +655,17 @@ float ctf_Stalemate_Customize()
        wp_owner = self.owner;
 
        // team waypoints
-       if(CTF_SAMETEAM(wp_owner.flagcarried, wp_owner)) { return FALSE; }
-       if(SAME_TEAM(wp_owner, e)) { return FALSE; }
-       if(!IS_PLAYER(e)) { return FALSE; }
+       if(CTF_SAMETEAM(wp_owner.flagcarried, wp_owner)) { return false; }
+       if(SAME_TEAM(wp_owner, e)) { return false; }
+       if(!IS_PLAYER(e)) { return false; }
 
-       return TRUE;
+       return true;
 }
 
 void ctf_CheckStalemate(void)
 {
        // declarations
-       float stale_flags = 0, stale_red_flags = 0, stale_blue_flags = 0, stale_yellow_flags = 0, stale_pink_flags = 0, stale_neutral_flags = 0;
+       int stale_flags = 0, stale_red_flags = 0, stale_blue_flags = 0, stale_yellow_flags = 0, stale_pink_flags = 0, stale_neutral_flags = 0;
        entity tmp_entity;
 
        entity ctf_staleflaglist = world; // reset the list, we need to build the list each time this function runs
@@ -704,13 +697,13 @@ void ctf_CheckStalemate(void)
                stale_flags = (stale_red_flags >= 1) + (stale_blue_flags >= 1) + (stale_yellow_flags >= 1) + (stale_pink_flags >= 1);
 
        if(ctf_oneflag && stale_flags == 1)
-               ctf_stalemate = TRUE;
-       else if(stale_flags == ctf_teams)
-               ctf_stalemate = TRUE;
+               ctf_stalemate = true;
+       else if(stale_flags >= 2)
+               ctf_stalemate = true;
        else if(stale_flags == 0 && autocvar_g_ctf_stalemate_endcondition == 2)
-               { ctf_stalemate = FALSE; wpforenemy_announced = FALSE; }
-       else if(stale_flags < ctf_teams && autocvar_g_ctf_stalemate_endcondition == 1)
-               { ctf_stalemate = FALSE; wpforenemy_announced = FALSE; }
+               { ctf_stalemate = false; wpforenemy_announced = false; }
+       else if(stale_flags < 2 && autocvar_g_ctf_stalemate_endcondition == 1)
+               { ctf_stalemate = false; wpforenemy_announced = false; }
 
        // if sufficient stalemate, then set up the waypointsprite and announce the stalemate if necessary
        if(ctf_stalemate)
@@ -719,7 +712,7 @@ void ctf_CheckStalemate(void)
                {
                        if((tmp_entity.owner) && (!tmp_entity.owner.wps_enemyflagcarrier))
                        {
-                               WaypointSprite_Spawn(((ctf_oneflag) ? "flagcarrier" : "enemyflagcarrier"), 0, 0, tmp_entity.owner, FLAG_WAYPOINT_OFFSET, world, 0, tmp_entity.owner, wps_enemyflagcarrier, TRUE, RADARICON_FLAG, WPCOLOR_ENEMYFC(tmp_entity.owner.team));
+                               WaypointSprite_Spawn(((ctf_oneflag) ? "flagcarrier" : "enemyflagcarrier"), 0, 0, tmp_entity.owner, FLAG_WAYPOINT_OFFSET, world, 0, tmp_entity.owner, wps_enemyflagcarrier, true, RADARICON_FLAG, WPCOLOR_ENEMYFC(tmp_entity.owner.team));
                                tmp_entity.owner.wps_enemyflagcarrier.customizeentityforclient = ctf_Stalemate_Customize;
                        }
                }
@@ -729,18 +722,18 @@ void ctf_CheckStalemate(void)
                        FOR_EACH_REALPLAYER(tmp_entity)
                                Send_Notification(NOTIF_ONE, tmp_entity, MSG_CENTER, ((tmp_entity.flagcarried) ? CENTER_CTF_STALEMATE_CARRIER : CENTER_CTF_STALEMATE_OTHER));
 
-                       wpforenemy_announced = TRUE;
+                       wpforenemy_announced = true;
                }
        }
 }
 
-void ctf_FlagDamage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
+void ctf_FlagDamage(entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force)
 {
        if(ITEM_DAMAGE_NEEDKILL(deathtype))
        {
                if(autocvar_g_ctf_flag_return_damage_delay)
                {
-                       self.ctf_flagdamaged = TRUE;
+                       self.ctf_flagdamaged = true;
                }
                else
                {
@@ -894,7 +887,7 @@ void ctf_FlagThink()
                        else
                        {
                                // still a viable target, go for it
-                               ctf_CalculatePassVelocity(self, targ_origin, self.origin, TRUE);
+                               ctf_CalculatePassVelocity(self, targ_origin, self.origin, true);
                        }
                        return;
                }
@@ -910,11 +903,10 @@ void ctf_FlagThink()
 void ctf_FlagTouch()
 {
        if(gameover) { return; }
-       if(self.active != ACTIVE_ACTIVE) { return; }
        if(trace_dphitcontents & (DPCONTENTS_PLAYERCLIP | DPCONTENTS_MONSTERCLIP)) { return; }
 
        entity toucher = other, tmp_entity;
-       float is_not_monster = (!(toucher.flags & FL_MONSTER)), num_perteam = 0;
+       bool is_not_monster = (!IS_MONSTER(toucher)), num_perteam = 0;
 
        // automatically kill the flag and return it if it touched lava/slime/nodrop surfaces
        if(ITEM_TOUCH_NEEDKILL())
@@ -931,14 +923,14 @@ void ctf_FlagTouch()
 
        // special touch behaviors
        if(toucher.frozen) { return; }
-       else if(toucher.vehicle_flags & VHF_ISVEHICLE)
+       else if(IS_VEHICLE(toucher))
        {
                if(autocvar_g_ctf_allow_vehicle_touch && toucher.owner)
                        toucher = toucher.owner; // the player is actually the vehicle owner, not other
                else
                        return; // do nothing
        }
-       else if(toucher.flags & FL_MONSTER)
+       else if(IS_MONSTER(toucher))
        {
                if(!autocvar_g_ctf_allow_monster_touch)
                        return; // do nothing
@@ -947,7 +939,7 @@ void ctf_FlagTouch()
        {
                if(time > self.wait) // if we haven't in a while, play a sound/effect
                {
-                       pointparticles(particleeffectnum(self.toucheffect), self.origin, '0 0 0', 1);
+                       Send_Effect(self.toucheffect, self.origin, '0 0 0', 1);
                        sound(self, CH_TRIGGER, self.snd_flag_touch, VOL_BASE, ATTEN_NORM);
                        self.wait = time + FLAG_TOUCHRATE;
                }
@@ -1063,22 +1055,6 @@ void ctf_Reset()
        ctf_RespawnFlag(self);
 }
 
-void ctf_Use()
-{
-       if(self.ctf_status != FLAG_BASE) { return; }
-
-       self.active = ((self.active) ? ACTIVE_NOT : ACTIVE_ACTIVE);
-
-       if(self.active == ACTIVE_ACTIVE)
-               WaypointSprite_Ping(self.wps_flagbase);
-}
-
-float ctf_FlagWaypoint_Customize()
-{
-       if(self.owner.active != ACTIVE_ACTIVE) { return FALSE; }
-       return TRUE;
-}
-
 void ctf_DelayedFlagSetup(void) // called after a flag is placed on a map by ctf_FlagSetup()
 {
        // bot waypoints
@@ -1099,16 +1075,22 @@ void ctf_DelayedFlagSetup(void) // called after a flag is placed on a map by ctf
        }
 
        WaypointSprite_SpawnFixed(basename, self.origin + FLAG_WAYPOINT_OFFSET, self, wps_flagbase, RADARICON_FLAG, ((self.team) ? Team_ColorRGB(self.team) : '1 1 1'));
-       WaypointSprite_UpdateTeamRadar(self.wps_flagbase, RADARICON_FLAG, ((self.team) ? colormapPaletteColor(self.team - 1, FALSE) : '1 1 1'));
-       self.wps_flagbase.customizeentityforclient = ctf_FlagWaypoint_Customize;
+       WaypointSprite_UpdateTeamRadar(self.wps_flagbase, RADARICON_FLAG, ((self.team) ? colormapPaletteColor(self.team - 1, false) : '1 1 1'));
 
        // captureshield setup
        ctf_CaptureShield_Spawn(self);
 }
 
-void ctf_FlagSetup(float teamnumber, entity flag) // called when spawning a flag entity on the map as a spawnfunc
+void set_flag_string(entity flag, .string field, string value, string teamname)
+{
+       if(flag.field == "")
+               flag.field = strzone(sprintf(value,teamname));
+}
+
+void ctf_FlagSetup(int teamnumber, entity flag) // called when spawning a flag entity on the map as a spawnfunc
 {
        // declarations
+       string teamname = Static_Team_ColorName_Lower(teamnumber);
        self = flag; // for later usage with droptofloor()
 
        // main setup
@@ -1128,7 +1110,7 @@ void ctf_FlagSetup(float teamnumber, entity flag) // called when spawning a flag
        flag.max_flag_health = ((autocvar_g_ctf_flag_return_damage && autocvar_g_ctf_flag_health) ? autocvar_g_ctf_flag_health : 100);
        flag.health = flag.max_flag_health;
        flag.event_damage = ctf_FlagDamage;
-       flag.pushable = TRUE;
+       flag.pushable = true;
        flag.teleportable = TELEPORT_NORMAL;
        flag.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_PLAYERCLIP | DPCONTENTS_BOTCLIP;
        flag.damagedbytriggers = autocvar_g_ctf_flag_return_when_unreachable;
@@ -1136,29 +1118,27 @@ void ctf_FlagSetup(float teamnumber, entity flag) // called when spawning a flag
        flag.velocity = '0 0 0';
        flag.mangle = flag.angles;
        flag.reset = ctf_Reset;
-       flag.use = ctf_Use;
        flag.touch = ctf_FlagTouch;
        flag.think = ctf_FlagThink;
        flag.nextthink = time + FLAG_THINKRATE;
        flag.ctf_status = FLAG_BASE;
 
        // appearence
-       if(flag.model == "")       { flag.model = ((teamnumber == NUM_TEAM_1) ? autocvar_g_ctf_flag_red_model : ((teamnumber == NUM_TEAM_2) ? autocvar_g_ctf_flag_blue_model : ((teamnumber == NUM_TEAM_3) ? autocvar_g_ctf_flag_yellow_model : ((teamnumber == NUM_TEAM_4) ? autocvar_g_ctf_flag_pink_model : autocvar_g_ctf_flag_neutral_model)))); }
-       if(!flag.scale)            { flag.scale = FLAG_SCALE; }
-       if(!flag.skin)             { flag.skin = ((teamnumber == NUM_TEAM_1) ? autocvar_g_ctf_flag_red_skin : ((teamnumber == NUM_TEAM_2) ? autocvar_g_ctf_flag_blue_skin : ((teamnumber == NUM_TEAM_3) ? autocvar_g_ctf_flag_yellow_skin : ((teamnumber == NUM_TEAM_4) ? autocvar_g_ctf_flag_pink_skin : autocvar_g_ctf_flag_neutral_skin)))); }
-       if(flag.toucheffect == "") { flag.toucheffect = ((teamnumber == NUM_TEAM_1) ? "redflag_touch" : ((teamnumber == NUM_TEAM_2) ? "blueflag_touch" : ((teamnumber == NUM_TEAM_3) ? "yellowflag_touch" : ((teamnumber == NUM_TEAM_4) ? "pinkflag_touch" : "neutralflag_touch")))); }
-       if(flag.passeffect == "")  { flag.passeffect = ((teamnumber == NUM_TEAM_1) ? "red_pass" : ((teamnumber == NUM_TEAM_2) ? "blue_pass" : ((teamnumber == NUM_TEAM_3) ? "yellow_pass" : ((teamnumber == NUM_TEAM_4) ? "pink_pass" : "neutral_pass")))); }
-       if(flag.capeffect == "")   { flag.capeffect = ((teamnumber == NUM_TEAM_1) ? "red_cap" : ((teamnumber == NUM_TEAM_2) ? "blue_cap" : ((teamnumber == NUM_TEAM_3) ? "yellow_cap" : ((teamnumber == NUM_TEAM_4) ? "pink_cap" : "")))); } // neutral flag cant be capped itself
-       if(!flag.active)           { flag.active = ACTIVE_ACTIVE; }
-
-       // sound
-       if(flag.snd_flag_taken == "")    { flag.snd_flag_taken = ((teamnumber == NUM_TEAM_1) ? "ctf/red_taken.wav" : ((teamnumber == NUM_TEAM_2) ? "ctf/blue_taken.wav" : ((teamnumber == NUM_TEAM_3) ? "ctf/yellow_taken.wav" : ((teamnumber == NUM_TEAM_4) ? "ctf/pink_taken.wav" : "ctf/neutral_taken.wav")))); }
-       if(flag.snd_flag_returned == "") { flag.snd_flag_returned = ((teamnumber == NUM_TEAM_1) ? "ctf/red_returned.wav" : ((teamnumber == NUM_TEAM_2) ? "ctf/blue_returned.wav" : ((teamnumber == NUM_TEAM_3) ? "ctf/yellow_returned.wav" : ((teamnumber == NUM_TEAM_4) ? "ctf/pink_returned.wav" : "")))); } // neutral flag can't be returned by players
-       if(flag.snd_flag_capture == "")  { flag.snd_flag_capture = ((teamnumber == NUM_TEAM_1) ? "ctf/red_capture.wav" : ((teamnumber == NUM_TEAM_2) ? "ctf/blue_capture.wav" : ((teamnumber == NUM_TEAM_3) ? "ctf/yellow_capture.wav" : ((teamnumber == NUM_TEAM_4) ? "ctf/pink_capture.wav" : "")))); } // again can't be captured
-       if(flag.snd_flag_respawn == "")  { flag.snd_flag_respawn = "ctf/flag_respawn.wav"; } // if there is ever a team-based sound for this, update the code to match.
-       if(flag.snd_flag_dropped == "")  { flag.snd_flag_dropped = ((teamnumber == NUM_TEAM_1) ? "ctf/red_dropped.wav" : ((teamnumber == NUM_TEAM_2) ? "ctf/blue_dropped.wav" : ((teamnumber == NUM_TEAM_3) ? "ctf/yellow_dropped.wav" : ((teamnumber == NUM_TEAM_4) ? "ctf/pink_dropped.wav" : "ctf/neutral_dropped.wav")))); }
-       if(flag.snd_flag_touch == "")    { flag.snd_flag_touch = "ctf/touch.wav"; } // again has no team-based sound
-       if(flag.snd_flag_pass == "")     { flag.snd_flag_pass = "ctf/pass.wav"; } // same story here
+       if(!flag.scale)                         { flag.scale = FLAG_SCALE; }
+       if(flag.skin == 0)                      { flag.skin = cvar(sprintf("g_ctf_flag_%s_skin", teamname)); }
+       if(flag.model == "")            { flag.model = cvar_string(sprintf("g_ctf_flag_%s_model", teamname)); }
+       set_flag_string(flag, toucheffect,      "%sflag_touch", teamname);
+       set_flag_string(flag, passeffect,       "%s_pass",              teamname);
+       set_flag_string(flag, capeffect,        "%s_cap",               teamname);
+
+       // sounds
+       set_flag_string(flag, snd_flag_taken,           "ctf/%s_taken.wav",     teamname);
+       set_flag_string(flag, snd_flag_returned,        "ctf/%s_returned.wav",  teamname);
+       set_flag_string(flag, snd_flag_capture,         "ctf/%s_capture.wav",   teamname);
+       set_flag_string(flag, snd_flag_dropped,         "ctf/%s_dropped.wav",   teamname);
+       if(flag.snd_flag_respawn == "")         { flag.snd_flag_respawn = "ctf/flag_respawn.wav"; } // if there is ever a team-based sound for this, update the code to match.
+       if(flag.snd_flag_touch == "")           { flag.snd_flag_touch = "ctf/touch.wav"; } // again has no team-based sound
+       if(flag.snd_flag_pass == "")            { flag.snd_flag_pass = "ctf/pass.wav"; } // same story here
 
        // precache
        precache_sound(flag.snd_flag_taken);
@@ -1179,7 +1159,14 @@ void ctf_FlagSetup(float teamnumber, entity flag) // called when spawning a flag
 
        if(autocvar_g_ctf_flag_glowtrails)
        {
-               flag.glow_color = ((teamnumber == NUM_TEAM_1) ? 251 : ((teamnumber == NUM_TEAM_2) ? 210 : ((teamnumber == NUM_TEAM_3) ? 110 : ((teamnumber == NUM_TEAM_4) ? 145 : 254))));
+               switch(teamnumber)
+               {
+                       case NUM_TEAM_1: flag.glow_color = 251; break;
+                       case NUM_TEAM_2: flag.glow_color = 210; break;
+                       case NUM_TEAM_3: flag.glow_color = 110; break;
+                       case NUM_TEAM_4: flag.glow_color = 145; break;
+                       default:                 flag.glow_color = 254; break;
+               }
                flag.glow_size = 25;
                flag.glow_trail = 1;
        }
@@ -1194,7 +1181,7 @@ void ctf_FlagSetup(float teamnumber, entity flag) // called when spawning a flag
                        case NUM_TEAM_2: flag.effects |= EF_BLUE; break;
                        case NUM_TEAM_3: flag.effects |= EF_DIMLIGHT; break;
                        case NUM_TEAM_4: flag.effects |= EF_RED; break;
-                       default: flag.effects |= EF_DIMLIGHT; break;
+                       default:                 flag.effects |= EF_DIMLIGHT; break;
                }
        }
        
@@ -1202,12 +1189,12 @@ void ctf_FlagSetup(float teamnumber, entity flag) // called when spawning a flag
        if((flag.spawnflags & 1) || flag.noalign) // don't drop to floor, just stay at fixed location
        {
                flag.dropped_origin = flag.origin;
-               flag.noalign = TRUE;
+               flag.noalign = true;
                flag.movetype = MOVETYPE_NONE;
        }
        else // drop to floor, automatically find a platform and set that as spawn origin
        {
-               flag.noalign = FALSE;
+               flag.noalign = false;
                self = flag;
                droptofloor();
                flag.movetype = MOVETYPE_TOSS;
@@ -1283,12 +1270,12 @@ entity havocbot_ctf_find_enemy_flag(entity bot)
        return world;
 }
 
-float havocbot_ctf_teamcount(entity bot, vector org, float tc_radius)
+int havocbot_ctf_teamcount(entity bot, vector org, float tc_radius)
 {
        if (!teamplay)
                return 0;
 
-       float c = 0;
+       int c = 0;
        entity head;
 
        FOR_EACH_PLAYER(head)
@@ -1418,7 +1405,7 @@ void havocbot_goalrating_ctf_carrieritems(float ratingscale, vector org, float s
 {
        entity head;
        float t;
-       head = findchainfloat(bot_pickup, TRUE);
+       head = findchainfloat(bot_pickup, true);
        while (head)
        {
                // gather health and armor only
@@ -1769,7 +1756,7 @@ void havocbot_role_ctf_middle()
                vector org;
 
                org = havocbot_ctf_middlepoint;
-               org_z = self.origin_z;
+               org.z = self.origin.z;
 
                self.bot_strategytime = time + autocvar_bot_ai_strategyinterval;
                navigation_goalrating_start();
@@ -1857,7 +1844,7 @@ void havocbot_role_ctf_defense()
        }
 }
 
-void havocbot_role_ctf_setrole(entity bot, float role)
+void havocbot_role_ctf_setrole(entity bot, int role)
 {
        dprint(strcat(bot.netname," switched to "));
        switch(role)
@@ -1910,8 +1897,7 @@ void havocbot_role_ctf_setrole(entity bot, float role)
 MUTATOR_HOOKFUNCTION(ctf_PlayerPreThink)
 {
        entity flag;
-
-       float t = 0, t2 = 0, t3 = 0;
+       int t = 0, t2 = 0, t3 = 0;
 
        // initially clear items so they can be set as necessary later.
        self.ctf_flagstatus &= ~(CTF_RED_FLAG_CARRYING          | CTF_RED_FLAG_TAKEN            | CTF_RED_FLAG_LOST 
@@ -1957,7 +1943,7 @@ MUTATOR_HOOKFUNCTION(ctf_PlayerPreThink)
        if(self.wps_flagcarrier)
                WaypointSprite_UpdateHealth(self.wps_flagcarrier, '1 0 0' * healtharmor_maxdamage(self.health, self.armorvalue, autocvar_g_balance_armor_blockpercent, DEATH_WEAPON));
 
-       return FALSE;
+       return false;
 }
 
 MUTATOR_HOOKFUNCTION(ctf_PlayerDamage) // for changing damage and force values that are applied to players in g_damage.qc
@@ -1985,7 +1971,7 @@ MUTATOR_HOOKFUNCTION(ctf_PlayerDamage) // for changing damage and force values t
                }
                // todo: add notification for when flag carrier needs help?
        }
-       return FALSE;
+       return false;
 }
 
 MUTATOR_HOOKFUNCTION(ctf_PlayerDies)
@@ -2003,7 +1989,7 @@ MUTATOR_HOOKFUNCTION(ctf_PlayerDies)
                tmp_entity.ctf_dropper = world;
        }
 
-       return FALSE;
+       return false;
 }
 
 MUTATOR_HOOKFUNCTION(ctf_GiveFragsForKill)
@@ -2026,7 +2012,7 @@ MUTATOR_HOOKFUNCTION(ctf_RemovePlayer)
                if(flag.ctf_dropper == self) { flag.ctf_dropper = world; }
        }
 
-       return FALSE;
+       return false;
 }
 
 MUTATOR_HOOKFUNCTION(ctf_PortalTeleport)
@@ -2035,12 +2021,12 @@ MUTATOR_HOOKFUNCTION(ctf_PortalTeleport)
        if(!autocvar_g_ctf_portalteleport)
                { ctf_Handle_Throw(self, world, DROP_NORMAL); }
 
-       return FALSE;
+       return false;
 }
 
 MUTATOR_HOOKFUNCTION(ctf_PlayerUseKey)
 {
-       if(MUTATOR_RETURNVALUE || gameover) { return FALSE; }
+       if(MUTATOR_RETURNVALUE || gameover) { return false; }
 
        entity player = self;
 
@@ -2050,7 +2036,7 @@ MUTATOR_HOOKFUNCTION(ctf_PlayerUseKey)
                if(autocvar_g_ctf_pass)
                {
                        entity head, closest_target = world;
-                       head = WarpZone_FindRadius(player.origin, autocvar_g_ctf_pass_radius, TRUE);
+                       head = WarpZone_FindRadius(player.origin, autocvar_g_ctf_pass_radius, true);
 
                        while(head) // find the closest acceptable target to pass to
                        {
@@ -2077,7 +2063,7 @@ MUTATOR_HOOKFUNCTION(ctf_PlayerUseKey)
                                                                Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_CTF_PASS_REQUESTING, head.netname);
                                                        }
                                                        player.throw_antispam = time + autocvar_g_ctf_pass_wait;
-                                                       return TRUE;
+                                                       return true;
                                                }
                                                else if(player.flagcarried)
                                                {
@@ -2094,7 +2080,7 @@ MUTATOR_HOOKFUNCTION(ctf_PlayerUseKey)
                                head = head.chain;
                        }
 
-                       if(closest_target) { ctf_Handle_Throw(player, closest_target, DROP_PASS); return TRUE; }
+                       if(closest_target) { ctf_Handle_Throw(player, closest_target, DROP_PASS); return true; }
                }
 
                // throw the flag in front of you
@@ -2107,12 +2093,12 @@ MUTATOR_HOOKFUNCTION(ctf_PlayerUseKey)
                                        player.throw_prevtime = time;
                                        player.throw_count = 1;
                                        ctf_Handle_Throw(player, world, DROP_THROW);
-                                       return TRUE;
+                                       return true;
                                }
                                else
                                {
                                        Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_CTF_FLAG_THROW_PUNISH, rint((player.throw_prevtime + autocvar_g_ctf_throw_punish_delay) - time));
-                                       return FALSE;
+                                       return false;
                                }
                        }
                        else
@@ -2123,12 +2109,12 @@ MUTATOR_HOOKFUNCTION(ctf_PlayerUseKey)
 
                                player.throw_prevtime = time;
                                ctf_Handle_Throw(player, world, DROP_THROW);
-                               return TRUE;
+                               return true;
                        }
                }
        }
 
-       return FALSE;
+       return false;
 }
 
 MUTATOR_HOOKFUNCTION(ctf_HelpMePing)
@@ -2140,17 +2126,19 @@ MUTATOR_HOOKFUNCTION(ctf_HelpMePing)
        }
        else // create a normal help me waypointsprite
        {
-               WaypointSprite_Spawn("helpme", waypointsprite_deployed_lifetime, waypointsprite_limitedrange, self, FLAG_WAYPOINT_OFFSET, world, self.team, self, wps_helpme, FALSE, RADARICON_HELPME, '1 0.5 0');
+               WaypointSprite_Spawn("helpme", waypointsprite_deployed_lifetime, waypointsprite_limitedrange, self, FLAG_WAYPOINT_OFFSET, world, self.team, self, wps_helpme, false, RADARICON_HELPME, '1 0.5 0');
                WaypointSprite_Ping(self.wps_helpme);
        }
 
-       return TRUE;
+       return true;
 }
 
 MUTATOR_HOOKFUNCTION(ctf_VehicleEnter)
 {
        if(vh_player.flagcarried)
        {
+               vh_player.flagcarried.nodrawtoclient = vh_player; // hide the flag from the driver
+
                if(!autocvar_g_ctf_allow_vehicle_carry && !autocvar_g_ctf_allow_vehicle_touch)
                {
                        ctf_Handle_Throw(vh_player, world, DROP_NORMAL);
@@ -2162,10 +2150,10 @@ MUTATOR_HOOKFUNCTION(ctf_VehicleEnter)
                        vh_player.flagcarried.scale = VEHICLE_FLAG_SCALE;
                        //vh_player.flagcarried.angles = '0 0 0';
                }
-               return TRUE;
+               return true;
        }
 
-       return FALSE;
+       return false;
 }
 
 MUTATOR_HOOKFUNCTION(ctf_VehicleExit)
@@ -2176,10 +2164,11 @@ MUTATOR_HOOKFUNCTION(ctf_VehicleExit)
                setorigin(vh_player.flagcarried, FLAG_CARRY_OFFSET);
                vh_player.flagcarried.scale = FLAG_SCALE;
                vh_player.flagcarried.angles = '0 0 0';
-               return TRUE;
+               vh_player.flagcarried.nodrawtoclient = world;
+               return true;
        }
 
-       return FALSE;
+       return false;
 }
 
 MUTATOR_HOOKFUNCTION(ctf_AbortSpeedrun)
@@ -2188,10 +2177,10 @@ MUTATOR_HOOKFUNCTION(ctf_AbortSpeedrun)
        {
                Send_Notification(NOTIF_ALL, world, MSG_INFO, ((self.flagcarried.team) ? APP_TEAM_ENT_4(self.flagcarried, INFO_CTF_FLAGRETURN_ABORTRUN_) : INFO_CTF_FLAGRETURN_ABORTRUN_NEUTRAL));
                ctf_RespawnFlag(self.flagcarried);
-               return TRUE;
+               return true;
        }
 
-       return FALSE;
+       return false;
 }
 
 MUTATOR_HOOKFUNCTION(ctf_MatchEnd)
@@ -2209,7 +2198,7 @@ MUTATOR_HOOKFUNCTION(ctf_MatchEnd)
                                flag.movetype = MOVETYPE_NONE;
                                flag.takedamage = DAMAGE_NO;
                                flag.solid = SOLID_NOT;
-                               flag.nextthink = FALSE; // stop thinking
+                               flag.nextthink = false; // stop thinking
 
                                //dprint("stopping the ", flag.netname, " from moving.\n");
                                break;
@@ -2225,26 +2214,26 @@ MUTATOR_HOOKFUNCTION(ctf_MatchEnd)
                }
        }
 
-       return FALSE;
+       return false;
 }
 
 MUTATOR_HOOKFUNCTION(ctf_BotRoles)
 {
        havocbot_ctf_reset_role(self);
-       return TRUE;
+       return true;
 }
 
 MUTATOR_HOOKFUNCTION(ctf_GetTeamCount)
 {
        //ret_float = ctf_teams;
        ret_string = "ctf_team";
-       return TRUE;
+       return true;
 }
 
 MUTATOR_HOOKFUNCTION(ctf_SpectateCopy)
 {
        self.ctf_flagstatus = other.ctf_flagstatus;
-       return FALSE;
+       return false;
 }
 
 
@@ -2411,16 +2400,19 @@ void spawnfunc_team_CTF_blueplayer() { spawnfunc_info_player_team2();  }
 void spawnfunc_team_CTF_redspawn()   { spawnfunc_info_player_team1();  }
 void spawnfunc_team_CTF_bluespawn()  { spawnfunc_info_player_team2();  }
 
+void team_CTF_neutralflag()                     { spawnfunc_item_flag_neutral();  }
+void team_neutralobelisk()                      { spawnfunc_item_flag_neutral();  }
+
 
 // ==============
 // Initialization
 // ==============
 
 // scoreboard setup
-void ctf_ScoreRules(float teams)
+void ctf_ScoreRules(int teams)
 {
        CheckAllowedTeams(world);
-       ScoreRules_basics(teams, SFL_SORT_PRIO_PRIMARY, 0, TRUE);
+       ScoreRules_basics(teams, SFL_SORT_PRIO_PRIMARY, 0, true);
        ScoreInfo_SetLabel_TeamScore  (ST_CTF_CAPS,     "caps",      SFL_SORT_PRIO_PRIMARY);
        ScoreInfo_SetLabel_PlayerScore(SP_CTF_CAPS,     "caps",      SFL_SORT_PRIO_SECONDARY);
        ScoreInfo_SetLabel_PlayerScore(SP_CTF_CAPTIME,  "captime",   SFL_LOWER_IS_BETTER | SFL_TIME);
@@ -2432,7 +2424,7 @@ void ctf_ScoreRules(float teams)
 }
 
 // code from here on is just to support maps that don't have flag and team entities
-void ctf_SpawnTeam (string teamname, float teamcolor)
+void ctf_SpawnTeam (string teamname, int teamcolor)
 {
        entity oldself;
        oldself = self;
@@ -2455,7 +2447,7 @@ void ctf_DelayedInit() // Do this check with a delay so we can wait for teams to
        {
                if(tmp_entity.team == NUM_TEAM_3) { ctf_teams = max(3, ctf_teams); }
                if(tmp_entity.team == NUM_TEAM_4) { ctf_teams = max(4, ctf_teams); }
-               if(tmp_entity.team == 0) { ctf_oneflag = TRUE; }
+               if(tmp_entity.team == 0) { ctf_oneflag = true; }
        }
 
        ctf_teams = bound(2, ctf_teams, 4);