#include "spawnpoints.qh"
-#include "mutators/_mod.qh"
+#include <server/mutators/_mod.qh>
#include "g_world.qh"
+#include "miscfunctions.qh"
#include "race.qh"
+#include "defs.qh"
#include "../common/constants.qh"
#include <common/net_linked.qh>
#include "../common/teams.qh"
-#include "../common/triggers/subs.qh"
-#include "../common/triggers/target/spawnpoint.qh"
+#include <common/mapinfo.qh>
+#include "../common/mapobjects/subs.qh"
+#include "../common/mapobjects/target/spawnpoint.qh"
#include "../common/util.qh"
#include "../lib/warpzone/common.qh"
#include "../lib/warpzone/util_server.qh"
+#include <server/utils.qh>
bool SpawnPoint_Send(entity this, entity to, int sf)
{
{
this.team = actor.team;
some_spawn_has_been_used = true;
+ this.SendFlags |= 1; // update team on the client side
}
//LOG_INFO("spawnpoint was used!\n");
}
+void spawnpoint_reset(entity this)
+{
+ this.SendFlags |= 1; // update team since it was restored during reset
+}
+
+void link_spawnpoint(entity this)
+{
+ bool anypoint = (autocvar_g_spawn_useallspawns || (teamplay && have_team_spawns <= 0)); // TODO: check if available teams is equal to spawnpoints available
+
+ // Don't show team spawns in non-team matches,
+ // and don't show non-team spawns in team matches.
+ // (Unless useallspawns is activated)
+ if(anypoint || !((teamplay && !Team_IsValidTeam(this.team)) || (!teamplay && Team_IsValidTeam(this.team))))
+ Net_LinkEntity(this, false, 0, SpawnPoint_Send);
+}
+
void relocate_spawnpoint(entity this)
{
// nudge off the floor
this.use = spawnpoint_use;
setthink(this, spawnpoint_think);
this.nextthink = time + 0.5 + random() * 2; // shouldn't need it for a little second
+ this.reset2 = spawnpoint_reset; // restores team, allows re-sending the spawnpoint
this.team_saved = this.team;
IL_PUSH(g_saved_team, this);
if (!this.cnt)
if (autocvar_r_showbboxes)
{
// show where spawnpoints point at too
- makevectors(this.angles);
+ vector forward, right, up;
+ MAKE_VECTORS(this.angles, forward, right, up);
entity e = new(info_player_foo);
- setorigin(e, this.origin + v_forward * 24);
+ setorigin(e, this.origin + forward * 24);
setsize(e, '-8 -8 -8', '8 8 8');
e.solid = SOLID_TRIGGER;
}
- // Don't show team spawns in non-team matches,
- // and don't show non-team spawns in team matches.
- // (Unless useallspawns is activated)
- if(
- !(
- ( // if this passes, there is a DM spawn on a team match
- teamplay
- && (this.team != NUM_TEAM_1)
- && (this.team != NUM_TEAM_2)
- && (this.team != NUM_TEAM_3)
- && (this.team != NUM_TEAM_4)
- )
- ||
- ( // if this passes, there is a team spawn on a DM match
- !teamplay
- &&
- (
- (this.team == NUM_TEAM_1)
- || (this.team == NUM_TEAM_2)
- || (this.team == NUM_TEAM_3)
- || (this.team == NUM_TEAM_4)
- )
- )
- )
- ||
- autocvar_g_spawn_useallspawns
- )
- { Net_LinkEntity(this, false, 0, SpawnPoint_Send); }
+ // network it after all spawnpoints are setup, so that we can check if team spawnpoints are used
+ InitializeEntity(this, link_spawnpoint, INITPRIO_FINDTARGET);
}
spawnfunc(info_player_survivor)
// Returns:
// _x: prio (-1 if unusable)
// _y: weight
-vector Spawn_Score(entity this, entity spot, float mindist, float teamcheck)
+vector Spawn_Score(entity this, entity spot, float mindist, float teamcheck, bool targetcheck)
{
// filter out spots for the wrong team
if(teamcheck >= 0)
return '-1 0 0';
if(race_spawns)
- if(spot.target == "")
+ if(!spot.target || spot.target == "")
return '-1 0 0';
if(IS_REAL_CLIENT(this))
float prio = 0;
float shortest = vlen(world.maxs - world.mins);
- FOREACH_CLIENT(IS_PLAYER(it) && it != this, {
+ FOREACH_CLIENT(IS_PLAYER(it) && !IS_DEAD(it) && it != this, {
float thisdist = vlen(it.origin - spot.origin);
if (thisdist < shortest)
shortest = thisdist;
vector spawn_score = prio * '1 0 0' + shortest * '0 1 0';
// filter out spots for assault
- if(spot.target != "")
+ if(spot.target && spot.target != "" && targetcheck)
{
int found = 0;
for(entity targ = findchain(targetname, spot.target); targ; targ = targ.chain)
}
}
- if(!found)
+ if(!found && !g_cts)
{
LOG_TRACE("WARNING: spawnpoint at ", vtos(spot.origin), " could not find its target ", spot.target);
return '-1 0 0';
return spawn_score;
}
-void Spawn_ScoreAll(entity this, entity firstspot, float mindist, float teamcheck)
+void Spawn_ScoreAll(entity this, entity firstspot, float mindist, float teamcheck, bool targetcheck)
{
entity spot;
for(spot = firstspot; spot; spot = spot.chain)
- spot.spawnpoint_score = Spawn_Score(this, spot, mindist, teamcheck);
+ spot.spawnpoint_score = Spawn_Score(this, spot, mindist, teamcheck, targetcheck);
}
-entity Spawn_FilterOutBadSpots(entity this, entity firstspot, float mindist, float teamcheck)
+entity Spawn_FilterOutBadSpots(entity this, entity firstspot, float mindist, float teamcheck, bool targetcheck)
{
entity spot, spotlist, spotlistend;
spotlist = NULL;
spotlistend = NULL;
- Spawn_ScoreAll(this, firstspot, mindist, teamcheck);
+ Spawn_ScoreAll(this, firstspot, mindist, teamcheck, targetcheck);
for(spot = firstspot; spot; spot = spot.chain)
{
Finds a point to respawn
=============
*/
+bool testspawn_checked;
+entity testspawn_point;
entity SelectSpawnPoint(entity this, bool anypoint)
{
float teamcheck;
- entity spot, firstspot;
+ entity spot = NULL;
+
+ if(!testspawn_checked)
+ {
+ testspawn_point = find(NULL, classname, "testplayerstart");
+ testspawn_checked = true;
+ }
- spot = find(NULL, classname, "testplayerstart");
- if (spot)
- return spot;
+ if(testspawn_point)
+ return testspawn_point;
if(this.spawnpoint_targ)
return this.spawnpoint_targ;
// get the entire list of spots
- firstspot = findchain(classname, "info_player_deathmatch");
+ //entity firstspot = findchain(classname, "info_player_deathmatch");
+ entity firstspot = IL_FIRST(g_spawnpoints);
+ entity prev = NULL;
+ IL_EACH(g_spawnpoints, true,
+ {
+ if(prev)
+ prev.chain = it;
+ it.chain = NULL;
+ prev = it;
+ });
// filter out the bad ones
// (note this returns the original list if none survived)
if(anypoint)
}
else
{
- firstspot = Spawn_FilterOutBadSpots(this, firstspot, 100, teamcheck);
+ firstspot = Spawn_FilterOutBadSpots(this, firstspot, 100, teamcheck, true);
+
+ // emergency fallback! double check without targets
+ // fixes some crashes with improperly repacked maps
+ if(!firstspot)
+ {
+ firstspot = IL_FIRST(g_spawnpoints);
+ prev = NULL;
+ IL_EACH(g_spawnpoints, true,
+ {
+ if(prev)
+ prev.chain = it;
+ it.chain = NULL;
+ prev = it;
+ });
+ firstspot = Spawn_FilterOutBadSpots(this, firstspot, 100, teamcheck, false);
+ }
// there is 50/50 chance of choosing a random spot or the furthest spot
// (this means that roughly every other spawn will be furthest, so you