Xonotic QuakeC
The free, fast arena FPS with crisp movement and a wide array of weapons
sv_spawn_near_teammate.qc File Reference
Include dependency graph for sv_spawn_near_teammate.qc:
This graph shows which files directly or indirectly include this file:

Go to the source code of this file.

Functions

 MUTATOR_HOOKFUNCTION (spawn_near_teammate, PlayerSpawn)
 MUTATOR_HOOKFUNCTION (spawn_near_teammate, Spawn_Score)
 REGISTER_MUTATOR (spawn_near_teammate, expr_evaluate(autocvar_g_spawn_near_teammate))

Variables

string autocvar_g_spawn_near_teammate
float autocvar_g_spawn_near_teammate_distance
int autocvar_g_spawn_near_teammate_ignore_spawnpoint
bool autocvar_g_spawn_near_teammate_ignore_spawnpoint_check_health
bool autocvar_g_spawn_near_teammate_ignore_spawnpoint_closetodeath
float autocvar_g_spawn_near_teammate_ignore_spawnpoint_delay
float autocvar_g_spawn_near_teammate_ignore_spawnpoint_delay_death
int autocvar_g_spawn_near_teammate_ignore_spawnpoint_max
entity msnt_lookat
float msnt_timer
vector snt_ofs [6]

Function Documentation

◆ MUTATOR_HOOKFUNCTION() [1/2]

MUTATOR_HOOKFUNCTION ( spawn_near_teammate ,
PlayerSpawn  )

Definition at line 58 of file sv_spawn_near_teammate.qc.

59{
60 if (!teamplay) return;
61
62 entity player = M_ARGV(0, entity);
63 entity spawn_spot = M_ARGV(1, entity);
64
65 int num_red = 0, num_blue = 0, num_yellow = 0, num_pink = 0;
67 {
68 switch(it.team)
69 {
70 case NUM_TEAM_1: ++num_red; break;
71 case NUM_TEAM_2: ++num_blue; break;
72 case NUM_TEAM_3: ++num_yellow; break;
73 case NUM_TEAM_4: ++num_pink; break;
74 }
75 });
76
77 if(num_red == 1 || num_blue == 1 || num_yellow == 1 || num_pink == 1)
78 return; // at least 1 team has only 1 player, let's not give the bigger team too much of an advantage!
79
80 // Note: when entering this, fixangle is already set.
82 {
85
86 entity best_mate = NULL;
87 vector best_pos = '0 0 0';
88 float best_dist2 = FLOAT_MAX;
89 int tested = 0;
92
93 if (PHYS_INPUT_BUTTON_CHAT(it)) continue;
94 if (DIFF_TEAM(player, it)) continue;
96 if (IS_DEAD(it)) continue;
97 if (time < it.msnt_timer) continue;
98 if (StatusEffects_active(STATUSEFFECT_SpawnShield, it)) continue;
99 if (weaponLocked(it)) continue;
100 if (it == player) continue;
101
102 tested++; // i consider a teammate to be available when they pass the checks above
103
104 vector horiz_vel = vec2(it.velocity);
105 // when walking slowly sideways, we assume the player wants a clear shot ahead - spawn behind them according to where they're looking
106 // when running fast, spawn behind them according to their direction of movement to prevent colliding with the newly spawned player
107 vector forward = '0 0 0'; vector right = '0 0 0'; vector up = '0 0 0';
108 if (vdist(horiz_vel, >, autocvar_sv_maxspeed + 50))
109 {
110 FIXED_MAKE_VECTORS(vectoangles(horiz_vel), forward, right, up);
111 }
112 else
113 {
114 FIXED_MAKE_VECTORS(it.angles, forward, right, up);
115 }
116
117 // test different spots close to mate - trace upwards so it works on uneven surfaces
118 // don't spawn in front of player or directly behind to avoid players shooting each other
119 // test the potential spots in pairs (first pair is better than second and so on) but don't prefer one side
120 snt_ofs[0] = up * 64 + right * 128 - forward * 64;
121 snt_ofs[1] = up * 64 - right * 128 - forward * 64;
122 snt_ofs[2] = up * 64 + right * 192;
123 snt_ofs[3] = up * 64 - right * 192;
124 snt_ofs[4] = up * 64 + right * 64 - forward * 128;
125 snt_ofs[5] = up * 64 - right * 64 - forward * 128;
127 for(int i = 0; i < 6; ++i)
128 {
129 tracebox(it.origin, STAT(PL_MIN, player), STAT(PL_MAX, player), it.origin + snt_ofs[i], MOVE_NOMONSTERS, it);
130
131 vector horizontal_trace_endpos = trace_endpos;
132 //te_lightning1(NULL, it.origin, horizontal_trace_endpos);
133 if (trace_fraction != 1.0) goto skip;
134
135 // 400 is about the height of a typical laser jump (in overkill)
136 // not traceline because we need space for the whole player, not just their origin
137 tracebox(horizontal_trace_endpos, STAT(PL_MIN, player), STAT(PL_MAX, player), horizontal_trace_endpos - 400 * up, MOVE_NORMAL, it);
138 vector vectical_trace_endpos = trace_endpos;
139 //te_lightning1(NULL, horizontal_trace_endpos, vectical_trace_endpos);
140 if (trace_startsolid) goto skip; // inside another player
141 if (trace_fraction == 1.0) goto skip; // above void or too high
143 if (pointcontents(vectical_trace_endpos) != CONTENT_EMPTY) goto skip; // no lava or slime (or water which i assume would be annoying anyway)
144 if (tracebox_hits_trigger_hurt(horizontal_trace_endpos, STAT(PL_MIN, player), STAT(PL_MAX, player), vectical_trace_endpos)) goto skip;
145
146 // make sure the spawned player will have floor ahead (or at least a wall - they shouldn't fall as soon as they start moving)
147 // top front of player's bbox - highest point we know is not inside solid
148 vector floor_test_start = vectical_trace_endpos + up * STAT(PL_MAX, player).z + forward * STAT(PL_MAX, player).x;
149 traceline(floor_test_start, floor_test_start + forward * 100 - up * 128, MOVE_NOMONSTERS, it);
150 //te_beam(NULL, floor_test_start, trace_endpos);
151 if (trace_fraction == 1.0) goto skip;
152
153 if (autocvar_g_nades) {
154 bool nade_in_range = false;
155 IL_EACH(g_projectiles, it.classname == "nade",
156 {
157 if (vdist(it.origin - vectical_trace_endpos, <, autocvar_g_nades_nade_radius)) {
158 nade_in_range = true;
159 goto skip;
160 }
161 });
162 if (nade_in_range) goto skip;
163 }
164
165 // here, we know we found a good spot
166 RandomSelection_Add(it, 0, string_null, vectical_trace_endpos, 1, 1);
167 //te_lightning1(NULL, vectical_trace_endpos, vectical_trace_endpos + forward * 10);
168
169 LABEL(skip)
170 if (i % 2 == 1 && RandomSelection_chosen_ent)
171 {
173 {
174 float dist2 = vlen2(RandomSelection_chosen_ent.origin - player.death_origin);
175 if (dist2 < best_dist2)
176 {
177 best_dist2 = dist2;
179 best_mate = RandomSelection_chosen_ent;
180 }
181 }
182 else
183 {
184 setorigin(player, RandomSelection_chosen_vec);
185 player.angles = RandomSelection_chosen_ent.angles;
186 player.angles_z = 0; // never spawn tilted even if the spot says to
188 return;
189 }
190 break; // don't test the other spots near this teammate, go to the next one
191 }
192 }
193 });
194
196 if(best_mate)
197 {
198 setorigin(player, best_pos);
199 player.angles = best_mate.angles;
200 player.angles_z = 0; // never spawn tilted even if the spot says to
202 }
203 }
204 else if(spawn_spot.msnt_lookat)
205 {
206 player.angles = vectoangles(spawn_spot.msnt_lookat.origin - player.origin);
207 player.angles_x = -player.angles.x;
208 player.angles_z = 0; // never spawn tilted even if the spot says to
209 /*
210 sprint(player, "You should be looking at ", spawn_spot.msnt_lookat.netname, "^7.\n");
211 sprint(player, "distance: ", vtos(spawn_spot.msnt_lookat.origin - player.origin), "\n");
212 sprint(player, "angles: ", vtos(player.angles), "\n");
213 */
214 }
215}
#define FIXED_MAKE_VECTORS(angles, forward, right, up)
var entity(vector mins, vector maxs,.entity tofield) findbox_tofield_OrFallback
float GetResource(entity e, Resource res_type)
Returns the current amount of resource the given entity has.
#define M_ARGV(x, type)
Definition events.qh:17
#define IS_DEAD(s)
Definition player.qh:245
#define PHYS_INPUT_BUTTON_CHAT(s)
Definition player.qh:159
float autocvar_sv_maxspeed
Definition player.qh:53
#define IS_PLAYER(s)
Definition player.qh:243
#define LABEL(id)
Definition compiler.qh:34
float Q3SURFACEFLAG_SKY
const float MOVE_NOMONSTERS
const float MOVE_NORMAL
float time
vector trace_endpos
float trace_startsolid
float trace_dphitq3surfaceflags
float trace_fraction
const float CONTENT_EMPTY
const float FLOAT_MAX
Definition float.qh:3
bool tracebox_hits_trigger_hurt(vector start, vector e_min, vector e_max, vector end)
Definition hurt.qc:79
#define IL_EACH(this, cond, body)
#define STAT(...)
Definition stats.qh:82
vector vectoangles(vector v)
string string_null
Definition nil.qh:9
#define NULL
Definition post.qh:14
ERASEABLE void RandomSelection_Add(entity e, float f, string s, vector v, float weight, float priority)
Definition random.qc:14
ERASEABLE void RandomSelection_Init()
Definition random.qc:4
entity RandomSelection_chosen_ent
Definition random.qh:5
vector RandomSelection_chosen_vec
Definition random.qh:8
vector
Definition self.qh:92
IntrusiveList g_projectiles
Definition common.qh:58
#define CS_CVAR(this)
Definition state.qh:51
bool StatusEffects_active(StatusEffect this, entity actor)
bool autocvar_g_nades
Definition sv_nades.qh:5
float autocvar_g_balance_health_regenstable
bool autocvar_g_spawn_near_teammate_ignore_spawnpoint_check_health
vector snt_ofs[6]
int autocvar_g_spawn_near_teammate_ignore_spawnpoint_max
float autocvar_g_spawn_near_teammate_ignore_spawnpoint_delay_death
float autocvar_g_spawn_near_teammate_ignore_spawnpoint_delay
int autocvar_g_spawn_near_teammate_ignore_spawnpoint
bool autocvar_g_spawn_near_teammate_ignore_spawnpoint_closetodeath
const int NUM_TEAM_2
Definition teams.qh:14
const int NUM_TEAM_4
Definition teams.qh:16
const int NUM_TEAM_3
Definition teams.qh:15
bool teamplay
Definition teams.qh:59
#define DIFF_TEAM(a, b)
Definition teams.qh:242
const int NUM_TEAM_1
Definition teams.qh:13
#define FOREACH_CLIENT_RANDOM(cond, body)
Definition utils.qh:56
#define FOREACH_CLIENT(cond, body)
Definition utils.qh:50
#define vlen2(v)
Definition vector.qh:4
#define vdist(v, cmp, f)
Vector distance comparison, avoids sqrt()
Definition vector.qh:8
#define vec2(...)
Definition vector.qh:90
bool weaponLocked(entity player)

References autocvar_g_balance_health_regenstable, autocvar_g_nades, autocvar_g_spawn_near_teammate_ignore_spawnpoint, autocvar_g_spawn_near_teammate_ignore_spawnpoint_check_health, autocvar_g_spawn_near_teammate_ignore_spawnpoint_closetodeath, autocvar_g_spawn_near_teammate_ignore_spawnpoint_delay, autocvar_g_spawn_near_teammate_ignore_spawnpoint_delay_death, autocvar_g_spawn_near_teammate_ignore_spawnpoint_max, autocvar_sv_maxspeed, CONTENT_EMPTY, CS_CVAR, DIFF_TEAM, entity(), FIXED_MAKE_VECTORS, FLOAT_MAX, FOREACH_CLIENT, FOREACH_CLIENT_RANDOM, g_projectiles, GetResource(), IL_EACH, IS_DEAD, IS_PLAYER, LABEL, M_ARGV, MOVE_NOMONSTERS, MOVE_NORMAL, NULL, NUM_TEAM_1, NUM_TEAM_2, NUM_TEAM_3, NUM_TEAM_4, PHYS_INPUT_BUTTON_CHAT, Q3SURFACEFLAG_SKY, RandomSelection_Add(), RandomSelection_chosen_ent, RandomSelection_chosen_vec, RandomSelection_Init(), snt_ofs, STAT, StatusEffects_active(), string_null, teamplay, time, trace_dphitq3surfaceflags, trace_endpos, trace_fraction, trace_startsolid, tracebox_hits_trigger_hurt(), vdist, vec2, vectoangles(), vector, vlen2, and weaponLocked().

◆ MUTATOR_HOOKFUNCTION() [2/2]

MUTATOR_HOOKFUNCTION ( spawn_near_teammate ,
Spawn_Score  )

Definition at line 22 of file sv_spawn_near_teammate.qc.

23{
24 if (!teamplay) return;
25
26 entity player = M_ARGV(0, entity);
27 entity spawn_spot = M_ARGV(1, entity);
28 vector spawn_score = M_ARGV(2, vector);
29
31 return;
32
33 spawn_spot.msnt_lookat = NULL;
34
36 FOREACH_CLIENT(IS_PLAYER(it) && it != player && SAME_TEAM(it, player) && !IS_DEAD(it), {
37 if(vdist(spawn_spot.origin - it.origin, >, autocvar_g_spawn_near_teammate_distance))
38 continue;
39 if(vdist(spawn_spot.origin - it.origin, <, 48))
40 continue;
41 if(!checkpvs(spawn_spot.origin, it))
42 continue;
43 RandomSelection_AddEnt(it, 1, 1);
44 });
45
47 {
48 spawn_spot.msnt_lookat = RandomSelection_chosen_ent;
49 spawn_score.x += SPAWN_PRIO_NEAR_TEAMMATE_FOUND;
50 }
51 else if(player.team == spawn_spot.team)
52 spawn_score.x += SPAWN_PRIO_NEAR_TEAMMATE_SAMETEAM; // prefer same team, if we can't find a spawn near teammate
53
54 M_ARGV(2, vector) = spawn_score;
55}
float checkpvs(vector viewpos, entity viewee)
#define RandomSelection_AddEnt(e, weight, priority)
Definition random.qh:14
const int SPAWN_PRIO_NEAR_TEAMMATE_FOUND
const int SPAWN_PRIO_NEAR_TEAMMATE_SAMETEAM
float autocvar_g_spawn_near_teammate_distance
#define SAME_TEAM(a, b)
Definition teams.qh:241

References autocvar_g_spawn_near_teammate_distance, autocvar_g_spawn_near_teammate_ignore_spawnpoint, checkpvs(), CS_CVAR, entity(), FOREACH_CLIENT, IS_DEAD, IS_PLAYER, M_ARGV, NULL, RandomSelection_AddEnt, RandomSelection_chosen_ent, RandomSelection_Init(), SAME_TEAM, SPAWN_PRIO_NEAR_TEAMMATE_FOUND, SPAWN_PRIO_NEAR_TEAMMATE_SAMETEAM, Spawn_Score(), teamplay, vdist, and vector.

◆ REGISTER_MUTATOR()

REGISTER_MUTATOR ( spawn_near_teammate ,
expr_evaluate(autocvar_g_spawn_near_teammate)  )

Variable Documentation

◆ autocvar_g_spawn_near_teammate

string autocvar_g_spawn_near_teammate

Definition at line 7 of file sv_spawn_near_teammate.qc.

Referenced by REGISTER_MUTATOR().

◆ autocvar_g_spawn_near_teammate_distance

float autocvar_g_spawn_near_teammate_distance

Definition at line 8 of file sv_spawn_near_teammate.qc.

Referenced by MUTATOR_HOOKFUNCTION().

◆ autocvar_g_spawn_near_teammate_ignore_spawnpoint

int autocvar_g_spawn_near_teammate_ignore_spawnpoint

Definition at line 9 of file sv_spawn_near_teammate.qc.

Referenced by MUTATOR_HOOKFUNCTION(), and MUTATOR_HOOKFUNCTION().

◆ autocvar_g_spawn_near_teammate_ignore_spawnpoint_check_health

bool autocvar_g_spawn_near_teammate_ignore_spawnpoint_check_health

Definition at line 13 of file sv_spawn_near_teammate.qc.

Referenced by MUTATOR_HOOKFUNCTION().

◆ autocvar_g_spawn_near_teammate_ignore_spawnpoint_closetodeath

bool autocvar_g_spawn_near_teammate_ignore_spawnpoint_closetodeath

Definition at line 14 of file sv_spawn_near_teammate.qc.

Referenced by MUTATOR_HOOKFUNCTION().

◆ autocvar_g_spawn_near_teammate_ignore_spawnpoint_delay

float autocvar_g_spawn_near_teammate_ignore_spawnpoint_delay

Definition at line 11 of file sv_spawn_near_teammate.qc.

Referenced by MUTATOR_HOOKFUNCTION().

◆ autocvar_g_spawn_near_teammate_ignore_spawnpoint_delay_death

float autocvar_g_spawn_near_teammate_ignore_spawnpoint_delay_death

Definition at line 12 of file sv_spawn_near_teammate.qc.

Referenced by MUTATOR_HOOKFUNCTION().

◆ autocvar_g_spawn_near_teammate_ignore_spawnpoint_max

int autocvar_g_spawn_near_teammate_ignore_spawnpoint_max

Definition at line 10 of file sv_spawn_near_teammate.qc.

Referenced by MUTATOR_HOOKFUNCTION().

◆ msnt_lookat

entity msnt_lookat

Definition at line 18 of file sv_spawn_near_teammate.qc.

◆ msnt_timer

float msnt_timer

Definition at line 20 of file sv_spawn_near_teammate.qc.

◆ snt_ofs

vector snt_ofs[6]

Definition at line 57 of file sv_spawn_near_teammate.qc.

Referenced by MUTATOR_HOOKFUNCTION().