]> 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 4b0f0546e5ef488b01fe2fd9dd0c50bb312f4a30..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)
 {
@@ -92,13 +99,11 @@ bool ctf_CheckPassDirection(vector head_center, vector passer_center, vector pas
                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);
@@ -121,9 +126,9 @@ bool ctf_CheckPassDirection(vector head_center, vector passer_center, vector pas
 
 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;
@@ -367,7 +372,7 @@ void ctf_Handle_Throw(entity player, entity receiver, int droptype)
                {
                        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 = (('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;
@@ -449,7 +454,7 @@ void ctf_Handle_Capture(entity flag, entity toucher, int 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
@@ -470,7 +475,7 @@ void ctf_Handle_Capture(entity flag, entity toucher, int 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);
        }
@@ -603,7 +608,7 @@ void ctf_Handle_Pickup(entity flag, entity player, int 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); }
@@ -693,11 +698,11 @@ void ctf_CheckStalemate(void)
 
        if(ctf_oneflag && stale_flags == 1)
                ctf_stalemate = true;
-       else if(stale_flags == ctf_teams)
+       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)
+       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
@@ -722,7 +727,7 @@ void ctf_CheckStalemate(void)
        }
 }
 
-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))
        {
@@ -901,7 +906,7 @@ void ctf_FlagTouch()
        if(trace_dphitcontents & (DPCONTENTS_PLAYERCLIP | DPCONTENTS_MONSTERCLIP)) { return; }
 
        entity toucher = other, tmp_entity;
-       bool 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())
@@ -918,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
@@ -934,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;
                }
@@ -1076,9 +1081,16 @@ void ctf_DelayedFlagSetup(void) // called after a flag is placed on a map by ctf
        ctf_CaptureShield_Spawn(self);
 }
 
+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
@@ -1112,21 +1124,21 @@ void ctf_FlagSetup(int teamnumber, entity flag) // called when spawning a flag e
        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
-
-       // 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);
@@ -1147,7 +1159,14 @@ void ctf_FlagSetup(int teamnumber, entity flag) // called when spawning a flag e
 
        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;
        }
@@ -1162,7 +1181,7 @@ void ctf_FlagSetup(int teamnumber, entity flag) // called when spawning a flag e
                        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;
                }
        }
        
@@ -1825,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)
@@ -2118,6 +2137,8 @@ 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);
@@ -2143,6 +2164,7 @@ MUTATOR_HOOKFUNCTION(ctf_VehicleExit)
                setorigin(vh_player.flagcarried, FLAG_CARRY_OFFSET);
                vh_player.flagcarried.scale = FLAG_SCALE;
                vh_player.flagcarried.angles = '0 0 0';
+               vh_player.flagcarried.nodrawtoclient = world;
                return true;
        }
 
@@ -2378,13 +2400,16 @@ 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);
@@ -2399,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;