From c3d0274357bd1ce7a929630740fc479fa10d3899 Mon Sep 17 00:00:00 2001 From: Samual Lenks Date: Thu, 20 Sep 2012 15:09:19 -0400 Subject: [PATCH] Implement directional passing system (only pass to players you aim at) --- gamemodes.cfg | 2 + qcsrc/common/util.qc | 5 ++ qcsrc/common/util.qh | 1 + qcsrc/server/autocvars.qh | 2 + qcsrc/server/mutators/gamemode_ctf.qc | 79 ++++++++++++++++++++------- qcsrc/server/mutators/gamemode_ctf.qh | 3 + 6 files changed, 72 insertions(+), 20 deletions(-) diff --git a/gamemodes.cfg b/gamemodes.cfg index 163824253..2f35d39a9 100644 --- a/gamemodes.cfg +++ b/gamemodes.cfg @@ -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" diff --git a/qcsrc/common/util.qc b/qcsrc/common/util.qc index e81e27bec..53c14cfb0 100644 --- a/qcsrc/common/util.qc +++ b/qcsrc/common/util.qc @@ -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 diff --git a/qcsrc/common/util.qh b/qcsrc/common/util.qh index 5a89e17a1..49e51b0ff 100644 --- a/qcsrc/common/util.qh +++ b/qcsrc/common/util.qh @@ -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(); diff --git a/qcsrc/server/autocvars.qh b/qcsrc/server/autocvars.qh index 41a4e04fb..b5481a96f 100644 --- a/qcsrc/server/autocvars.qh +++ b/qcsrc/server/autocvars.qh @@ -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; diff --git a/qcsrc/server/mutators/gamemode_ctf.qc b/qcsrc/server/mutators/gamemode_ctf.qc index 38509343e..705db9351 100644 --- a/qcsrc/server/mutators/gamemode_ctf.qc +++ b/qcsrc/server/mutators/gamemode_ctf.qc @@ -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; diff --git a/qcsrc/server/mutators/gamemode_ctf.qh b/qcsrc/server/mutators/gamemode_ctf.qh index baf55ebd9..923f83b9f 100644 --- a/qcsrc/server/mutators/gamemode_ctf.qh +++ b/qcsrc/server/mutators/gamemode_ctf.qh @@ -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 -- 2.39.2