]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/commitdiff
Implement directional passing system (only pass to players you aim at)
authorSamual Lenks <samual@xonotic.org>
Thu, 20 Sep 2012 19:09:19 +0000 (15:09 -0400)
committerSamual Lenks <samual@xonotic.org>
Thu, 20 Sep 2012 19:09:19 +0000 (15:09 -0400)
gamemodes.cfg
qcsrc/common/util.qc
qcsrc/common/util.qh
qcsrc/server/autocvars.qh
qcsrc/server/mutators/gamemode_ctf.qc
qcsrc/server/mutators/gamemode_ctf.qh

index 163824253bc3da086be5325324ca3b28697cfe9f..2f35d39a9710610c35e9d0e508f848862fa6c004 100644 (file)
@@ -203,6 +203,8 @@ set g_ctf_throw_angle_min -90 "minimum downwards angle you can throw the flag"
 set g_ctf_drop_velocity_up 200 "upwards velocity when a flag is dropped (i.e. when a flag carrier dies)"
 set g_ctf_drop_velocity_side 100 "randomized sideways velocity when a flag is dropped"
 set g_ctf_pass 1 "allow passing of flags to nearby team mates"
+set g_ctf_pass_directional_max 200 "maximum radius from crosshair for line of sight selection when passing"
+set g_ctf_pass_directional_min 50 "minimum radius from crosshair for line of sight selection when passing"
 set g_ctf_pass_radius 500 "maximum radius that you can pass to a team mate in"
 set g_ctf_pass_wait 2 "delay in seconds between how often players can pass the flag (antispam, essentially)"
 set g_ctf_pass_request 1 "allow players to request the flag carrier to pass the flag to them"
index e81e27bec78de5fb463420f35f2f354e4e3bbaa9..53c14cfb0cd1798a706bb96cb36bce8afd924ba8 100644 (file)
@@ -463,6 +463,11 @@ string ScoreString(float pFlags, float pValue)
        return valstr;
 }
 
+float dotproduct(vector a, vector b)
+{
+       return a_x * b_x + a_y * b_y + a_z * b_z;
+}
+
 vector cross(vector a, vector b)
 {
        return
index 5a89e17a198a6a7af5d32d32a7f585a572b7a06f..49e51b0ffda7a4f95d23a08d41e1d46f6f75a398 100644 (file)
@@ -79,6 +79,7 @@ string mmssss(float t);
 
 string ScoreString(float vflags, float value);
 
+float dotproduct(vector a, vector b);
 vector cross(vector a, vector b);
 
 void compressShortVector_init();
index 41a4e04fb4a60b9c7f05cb7ee3c15077154e7d16..b5481a96fac4e79b8aa92350dae5f3a079afd0e8 100644 (file)
@@ -773,6 +773,8 @@ float autocvar_g_ctf_drop_velocity_up;
 float autocvar_g_ctf_drop_velocity_side;
 float autocvar_g_ctf_portalteleport;
 float autocvar_g_ctf_pass;
+float autocvar_g_ctf_pass_directional_max;
+float autocvar_g_ctf_pass_directional_min;
 float autocvar_g_ctf_pass_radius;
 float autocvar_g_ctf_pass_wait;
 float autocvar_g_ctf_pass_request;
index 38509343ef8ad6e1a040e12ab5e8b9a0bb7e3a14..705db9351823c208d10022cf1e4e0923ffb9be63 100644 (file)
@@ -1736,6 +1736,36 @@ MUTATOR_HOOKFUNCTION(ctf_PortalTeleport)
        return FALSE;
 }
 
+float 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)
+       {
+               float spreadlimit;
+               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);
+
+               vector nearest_on_line = (passer_center + a * v_forward);
+               float distance_from_line = vlen(nearest_to_passer - nearest_on_line);
+
+               spreadlimit = (autocvar_g_ctf_pass_radius ? min(1, (vlen(passer_center - nearest_on_line) / autocvar_g_ctf_pass_radius)) : 1);
+               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; }
+               else
+                       { return FALSE; }
+       }
+       else { return TRUE; }
+}
+
 MUTATOR_HOOKFUNCTION(ctf_PlayerUseKey)
 {
        if(MUTATOR_RETURNVALUE || gameover) { return FALSE; }
@@ -1756,29 +1786,38 @@ MUTATOR_HOOKFUNCTION(ctf_PlayerUseKey)
                                if(head != player && !IsDifferentTeam(head, player))
                                if(!head.speedrunning && !head.vehicle)
                                {
-                                       if(autocvar_g_ctf_pass_request && !player.flagcarried && head.flagcarried) 
-                                       { 
-                                               if(clienttype(head) == CLIENTTYPE_BOT)
-                                               {
-                                                       centerprint(player, strcat("Requesting ", head.netname, " to pass you the ", head.flagcarried.netname)); 
-                                                       ctf_Handle_Throw(head, player, DROP_PASS);
-                                               }
-                                               else
-                                               {
-                                                       centerprint(head, strcat(player.netname, " requests you to pass the ", head.flagcarried.netname)); 
-                                                       centerprint(player, strcat("Requesting ", head.netname, " to pass you the ", head.flagcarried.netname)); 
-                                               }
-                                               player.throw_antispam = time + autocvar_g_ctf_pass_wait; 
-                                               return TRUE; 
-                                       }
-                                       else if(player.flagcarried)
+                                       // if it's a player, use the view origin as reference (stolen from RadiusDamage functions in g_damage.qc) 
+                                       vector head_center = WarpZone_UnTransformOrigin(head, PLAYER_CENTER(head));
+                                       vector passer_center = PLAYER_CENTER(player);
+                                       
+                                       // directional tracing only
+                                       if(ctf_CheckPassDirection(head_center, passer_center, player.v_angle, head.WarpZone_findradius_nearest))
                                        {
-                                               if(closest_target)
+                                               if(autocvar_g_ctf_pass_request && !player.flagcarried && head.flagcarried) 
+                                               { 
+                                                       if(clienttype(head) == CLIENTTYPE_BOT)
+                                                       {
+                                                               centerprint(player, strcat("Requesting ", head.netname, " to pass you the ", head.flagcarried.netname)); 
+                                                               ctf_Handle_Throw(head, player, DROP_PASS);
+                                                       }
+                                                       else
+                                                       {
+                                                               centerprint(head, strcat(player.netname, " requests you to pass the ", head.flagcarried.netname)); 
+                                                               centerprint(player, strcat("Requesting ", head.netname, " to pass you the ", head.flagcarried.netname)); 
+                                                       }
+                                                       player.throw_antispam = time + autocvar_g_ctf_pass_wait; 
+                                                       return TRUE; 
+                                               }
+                                               else if(player.flagcarried)
                                                {
-                                                       if(vlen(player.origin - WarpZone_UnTransformOrigin(head, head.origin)) < vlen(player.origin - WarpZone_UnTransformOrigin(closest_target, closest_target.origin)))
-                                                               { closest_target = head; }
+                                                       if(closest_target)
+                                                       {
+                                                               vector closest_target_center = WarpZone_UnTransformOrigin(closest_target, PLAYER_CENTER(closest_target));
+                                                               if(vlen(passer_center - head_center) < vlen(passer_center - closest_target_center))
+                                                                       { closest_target = head; }
+                                                       }
+                                                       else { closest_target = head; }
                                                }
-                                               else { closest_target = head; }
                                        }
                                }
                                head = head.chain;
index baf55ebd961d76ceb981c997a3da92fd74f76a06..923f83b9f9acbab7c95dd4eec73cfc1ff6942972 100644 (file)
@@ -104,6 +104,9 @@ float ctf_captimerecord; // record time for capturing the flag
 .entity pass_target;
 .float throw_antispam;
 
+// passing macros
+#define PLAYER_CENTER(ent) (ent.origin + ((ent.classname == "player") ? ent.view_ofs : ((ent.mins + ent.maxs) * 0.5)))
+
 // CaptureShield: If the player is too bad to be allowed to capture, shield them from taking the flag.
 .float ctf_captureshielded; // set to 1 if the player is too bad to be allowed to capture
 float ctf_captureshield_min_negscore; // punish at -20 points