Xonotic
sv_spawn_near_teammate.qc
Go to the documentation of this file.
2 
4 
5 #include <lib/float.qh>
6 
15 
17 
18 .entity msnt_lookat;
19 
20 .float msnt_timer;
21 
22 MUTATOR_HOOKFUNCTION(spawn_near_teammate, Spawn_Score)
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 }
56 
58 MUTATOR_HOOKFUNCTION(spawn_near_teammate, PlayerSpawn)
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 he passes 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 him according to where he's looking
106  // when running fast, spawn behind him according to his 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 his 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 - he shouldn't fall as soon as he starts 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;
178  best_pos = RandomSelection_chosen_vec;
179  best_mate = RandomSelection_chosen_ent;
180  }
181  }
182  else
183  {
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 IL_EACH(this, cond, body)
#define PHYS_INPUT_BUTTON_CHAT(s)
Definition: player.qh:155
string string_null
Definition: nil.qh:9
vector RandomSelection_chosen_vec
Definition: random.qh:8
string autocvar_g_spawn_near_teammate
vector Spawn_Score(entity this, entity spot, float mindist, float teamcheck, bool targetcheck)
Definition: spawnpoints.qc:213
float trace_dphitq3surfaceflags
ERASEABLE void RandomSelection_Init()
Definition: random.qc:4
REGISTER_MUTATOR(spawn_near_teammate, expr_evaluate(autocvar_g_spawn_near_teammate))
bool autocvar_g_spawn_near_teammate_ignore_spawnpoint_check_health
entity() spawn
const float MOVE_NORMAL
Definition: csprogsdefs.qc:252
#define FOREACH_CLIENT(cond, body)
Definition: utils.qh:49
const int SPAWN_PRIO_NEAR_TEAMMATE_SAMETEAM
Definition: spawnpoints.qh:11
#define CS_CVAR(this)
Definition: state.qh:51
MUTATOR_HOOKFUNCTION(spawn_near_teammate, Spawn_Score)
float checkpvs(vector viewpos, entity viewee)
bool autocvar_g_spawn_near_teammate_ignore_spawnpoint_closetodeath
#define DIFF_TEAM(a, b)
Definition: teams.qh:240
float autocvar_g_spawn_near_teammate_ignore_spawnpoint_delay_death
float msnt_timer
float autocvar_g_spawn_near_teammate_ignore_spawnpoint_delay
RES_HEALTH
Definition: ent_cs.qc:126
float autocvar_g_balance_health_regenstable
Definition: sv_resources.qh:28
const float CONTENT_EMPTY
Definition: csprogsdefs.qc:236
#define RandomSelection_AddEnt(e, weight, priority)
Definition: random.qh:14
bool weaponLocked(entity player)
float Q3SURFACEFLAG_SKY
const float MOVE_NOMONSTERS
Definition: csprogsdefs.qc:253
#define vlen2(v)
Definition: vector.qh:4
entity RandomSelection_chosen_ent
Definition: random.qh:5
vector snt_ofs[6]
#define NULL
Definition: post.qh:17
vector trace_endpos
Definition: csprogsdefs.qc:37
#define SAME_TEAM(a, b)
Definition: teams.qh:239
#define FIXED_MAKE_VECTORS
float teamplay
Definition: progsdefs.qc:31
#define M_ARGV(x, type)
Definition: events.qh:17
#define IS_DEAD(s)
Definition: utils.qh:26
entity msnt_lookat
vector(float skel, float bonenum) _skel_get_boneabs_hidden
IntrusiveList g_projectiles
Definition: common.qh:46
float GetResource(entity e, Resource res_type)
Returns the current amount of resource the given entity has.
Definition: cl_resources.qc:10
const int SPAWN_PRIO_NEAR_TEAMMATE_FOUND
Definition: spawnpoints.qh:10
#define vdist(v, cmp, f)
Vector distance comparison, avoids sqrt()
Definition: vector.qh:8
#define FOREACH_CLIENT_RANDOM(cond, body)
Definition: utils.qh:55
#define vec2(...)
Definition: vector.qh:90
#define LABEL(id)
Definition: compiler.qh:36
setorigin(ent, v)
float autocvar_g_spawn_near_teammate_distance
float trace_startsolid
Definition: csprogsdefs.qc:35
if(IS_DEAD(this))
Definition: impulse.qc:92
float time
Definition: csprogsdefs.qc:16
int autocvar_g_spawn_near_teammate_ignore_spawnpoint_max
float trace_fraction
Definition: csprogsdefs.qc:36
#define IS_PLAYER(v)
Definition: utils.qh:9
const float FLOAT_MAX
Definition: float.qh:3
ERASEABLE void RandomSelection_Add(entity e, float f, string s, vector v, float weight, float priority)
Definition: random.qc:14
ERASEABLE bool expr_evaluate(string s)
Evaluate an expression of the form: [+ | -]? [var[op]val | [op]var | val | var] ...
Definition: cvar.qh:48
int autocvar_g_spawn_near_teammate_ignore_spawnpoint