#include "spawnpoints.qh"
-#include <server/mutators/_mod.qh>
-#include "g_world.qh"
-#include "race.qh"
-#include "defs.qh"
-#include "../common/constants.qh"
+#include <common/constants.qh>
+#include <common/gamemodes/_mod.qh>
+#include <common/mapobjects/subs.qh>
+#include <common/mapobjects/target/spawnpoint.qh>
+#include <common/mapobjects/triggers.qh>
#include <common/net_linked.qh>
-#include "../common/teams.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 <common/stats.qh>
+#include <common/teams.qh>
+#include <common/util.qh>
+#include <common/weapons/_all.qh>
+#include <lib/warpzone/common.qh>
+#include <lib/warpzone/util_server.qh>
+#include <server/command/vote.qh>
+#include <server/intermission.qh>
+#include <server/mutators/_mod.qh>
+#include <server/race.qh>
#include <server/utils.qh>
+#include <server/world.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)
spawnfunc(info_player_deathmatch)
{
- this.classname = "info_player_deathmatch";
IL_PUSH(g_spawnpoints, this);
relocate_spawnpoint(this);
}
// 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 && spot.target != "")
+ if(spot.target && spot.target != "" && targetcheck)
{
int found = 0;
for(entity targ = findchain(targetname, spot.target); targ; targ = targ.chain)
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)
{
}
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