From: Samual Lenks Date: Mon, 29 Apr 2013 18:52:10 +0000 (-0400) Subject: Merge remote-tracking branch 'origin/master' into samual/respawn_improvements X-Git-Tag: xonotic-v0.7.0~50^2~11 X-Git-Url: https://de.git.xonotic.org/?p=xonotic%2Fxonotic-data.pk3dir.git;a=commitdiff_plain;h=fc0e4c6f26156858a71e0ec62eb974fa11eec2f9;hp=27e0dcee207c9b4ab3e36bde11dee4dcb260bd13 Merge remote-tracking branch 'origin/master' into samual/respawn_improvements Conflicts: qcsrc/client/Main.qc qcsrc/common/constants.qh --- diff --git a/qcsrc/client/Main.qc b/qcsrc/client/Main.qc index ecc2000d7c..9843033a1d 100644 --- a/qcsrc/client/Main.qc +++ b/qcsrc/client/Main.qc @@ -696,6 +696,34 @@ void Ent_ReadAccuracy(void) } } +void Spawn_Draw(void) +{ + pointparticles(particleeffectnum("EF_STARDUST"), self.origin, '0 0 2', bound(0, frametime, 0.1)); +} + +void Ent_ReadSpawnPoint(float is_new) +{ + self.team = ReadByte(); + self.origin_x = ReadShort(); + self.origin_y = ReadShort(); + self.origin_z = ReadShort(); + + //self.colormod = colormapPaletteColor(self.team - 1, FALSE); + + //setsize(self, PL_MIN, PL_MAX); + + //droptofloor(); + + //self.mdl = "models/domination/dom_unclaimed.md3"; + //precache_model(self.mdl); + //setmodel(self, self.mdl); + self.drawmask = MASK_NORMAL; + self.movetype = MOVETYPE_NOCLIP; + self.draw = Spawn_Draw; + + print(ftos(is_new), " - read a spawnpoint at ", vtos(self.origin), ", bitches.\n"); +} + // CSQC_Ent_Update : Called every frame that the server has indicated an update to the SSQC / CSQC entity has occured. // The only parameter reflects if the entity is "new" to the client, meaning it just came into the client's PVS. void Ent_RadarLink(); @@ -781,7 +809,9 @@ void CSQC_Ent_Update(float bIsNewEntity) case ENT_CLIENT_MODEL: CSQCModel_Read(bIsNewEntity); break; case ENT_CLIENT_ITEM: ItemRead(bIsNewEntity); break; case ENT_CLIENT_BUMBLE_RAYGUN: bumble_raygun_read(bIsNewEntity); break; + case ENT_CLIENT_SPAWNPOINT: Ent_ReadSpawnPoint(bIsNewEntity); break; case ENT_CLIENT_NOTIFICATION: Read_Notification(bIsNewEntity); break; + default: //error(strcat(_("unknown entity type in CSQC_Ent_Update: %d\n"), self.enttype)); error(sprintf(_("Unknown entity type in CSQC_Ent_Update (enttype: %d, edict: %d, classname: %s)\n"), self.enttype, num_for_edict(self), self.classname)); diff --git a/qcsrc/common/constants.qh b/qcsrc/common/constants.qh index ffec62095b..a54a284563 100644 --- a/qcsrc/common/constants.qh +++ b/qcsrc/common/constants.qh @@ -95,7 +95,8 @@ const float ENT_CLIENT_WARPZONE_TELEPORTED = 32; const float ENT_CLIENT_MODEL = 33; const float ENT_CLIENT_ITEM = 34; const float ENT_CLIENT_BUMBLE_RAYGUN = 35; -const float ENT_CLIENT_NOTIFICATION = 36; +const float ENT_CLIENT_SPAWNPOINT = 36; +const float ENT_CLIENT_NOTIFICATION = 37; const float ENT_CLIENT_TURRET = 40; const float ENT_CLIENT_AUXILIARYXHAIR = 50; diff --git a/qcsrc/server/cl_client.qc b/qcsrc/server/cl_client.qc index 7f16dfd74b..1dcd95ff95 100644 --- a/qcsrc/server/cl_client.qc +++ b/qcsrc/server/cl_client.qc @@ -74,240 +74,8 @@ void ClientData_Touch(entity e) } } - -.vector spawnpoint_score; .string netname_previous; -void spawnfunc_info_player_survivor (void) -{ - spawnfunc_info_player_deathmatch(); -} - -void spawnfunc_info_player_start (void) -{ - spawnfunc_info_player_deathmatch(); -} - -void spawnfunc_info_player_deathmatch (void) -{ - self.classname = "info_player_deathmatch"; - relocate_spawnpoint(); -} - -void spawnpoint_use() -{ - if(teamplay) - if(have_team_spawns > 0) - { - self.team = activator.team; - some_spawn_has_been_used = 1; - } -} - -// Returns: -// _x: prio (-1 if unusable) -// _y: weight -vector Spawn_Score(entity spot, float mindist, float teamcheck) -{ - float shortest, thisdist; - float prio; - entity player; - - prio = 0; - - // filter out spots for the wrong team - if(teamcheck >= 0) - if(spot.team != teamcheck) - return '-1 0 0'; - - if(race_spawns) - if(spot.target == "") - return '-1 0 0'; - - if(clienttype(self) == CLIENTTYPE_REAL) - { - if(spot.restriction == 1) - return '-1 0 0'; - } - else - { - if(spot.restriction == 2) - return '-1 0 0'; - } - - shortest = vlen(world.maxs - world.mins); - FOR_EACH_PLAYER(player) if (player != self) - { - thisdist = vlen(player.origin - spot.origin); - if (thisdist < shortest) - shortest = thisdist; - } - if(shortest > mindist) - prio += SPAWN_PRIO_GOOD_DISTANCE; - - spawn_score = prio * '1 0 0' + shortest * '0 1 0'; - spawn_spot = spot; - - // filter out spots for assault - if(spot.target != "") { - entity ent; - float found; - - found = 0; - for(ent = world; (ent = find(ent, targetname, spot.target)); ) - { - ++found; - if(ent.spawn_evalfunc) - { - entity oldself = self; - self = ent; - spawn_score = ent.spawn_evalfunc(oldself, spot, spawn_score); - self = oldself; - if(spawn_score_x < 0) - return spawn_score; - } - } - - if(!found) - { - dprint("WARNING: spawnpoint at ", vtos(spot.origin), " could not find its target ", spot.target, "\n"); - return '-1 0 0'; - } - } - - MUTATOR_CALLHOOK(Spawn_Score); - return spawn_score; -} - -void Spawn_ScoreAll(entity firstspot, float mindist, float teamcheck) -{ - entity spot; - for(spot = firstspot; spot; spot = spot.chain) - spot.spawnpoint_score = Spawn_Score(spot, mindist, teamcheck); -} - -entity Spawn_FilterOutBadSpots(entity firstspot, float mindist, float teamcheck) -{ - entity spot, spotlist, spotlistend; - - spotlist = world; - spotlistend = world; - - Spawn_ScoreAll(firstspot, mindist, teamcheck); - - for(spot = firstspot; spot; spot = spot.chain) - { - if(spot.spawnpoint_score_x >= 0) // spawning allowed here - { - if(spotlistend) - spotlistend.chain = spot; - spotlistend = spot; - if(!spotlist) - spotlist = spot; - } - } - if(spotlistend) - spotlistend.chain = world; - - return spotlist; -} - -entity Spawn_WeightedPoint(entity firstspot, float lower, float upper, float exponent) -{ - // weight of a point: bound(lower, mindisttoplayer, upper)^exponent - // multiplied by spot.cnt (useful if you distribute many spawnpoints in a small area) - entity spot; - - RandomSelection_Init(); - for(spot = firstspot; spot; spot = spot.chain) - RandomSelection_Add(spot, 0, string_null, pow(bound(lower, spot.spawnpoint_score_y, upper), exponent) * spot.cnt, (spot.spawnpoint_score_y >= lower) * 0.5 + spot.spawnpoint_score_x); - - return RandomSelection_chosen_ent; -} - -/* -============= -SelectSpawnPoint - -Finds a point to respawn -============= -*/ -entity SelectSpawnPoint (float anypoint) -{ - float teamcheck; - entity spot, firstspot; - - spot = find (world, classname, "testplayerstart"); - if (spot) - return spot; - - if(anypoint || autocvar_g_spawn_useallspawns) - teamcheck = -1; - else if(have_team_spawns > 0) - { - if(have_team_spawns_forteam[self.team] == 0) - { - // we request a spawn for a team, and we have team - // spawns, but that team has no spawns? - if(have_team_spawns_forteam[0]) - // try noteam spawns - teamcheck = 0; - else - // if not, any spawn has to do - teamcheck = -1; - } - else - teamcheck = self.team; // MUST be team - } - else if(have_team_spawns == 0 && have_team_spawns_forteam[0]) - teamcheck = 0; // MUST be noteam - else - teamcheck = -1; - // if we get here, we either require team spawns but have none, or we require non-team spawns and have none; use any spawn then - - - // get the entire list of spots - firstspot = findchain(classname, "info_player_deathmatch"); - // filter out the bad ones - // (note this returns the original list if none survived) - if(anypoint) - { - spot = Spawn_WeightedPoint(firstspot, 1, 1, 1); - } - else - { - float mindist; - if (arena_roundbased && !g_ca) - mindist = 800; - else - mindist = 100; - firstspot = Spawn_FilterOutBadSpots(firstspot, mindist, teamcheck); - - // 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 - // usually won't get fragged at spawn twice in a row) - if (random() > autocvar_g_spawn_furthest) - spot = Spawn_WeightedPoint(firstspot, 1, 1, 1); - else - spot = Spawn_WeightedPoint(firstspot, 1, 5000, 5); // chooses a far far away spawnpoint - } - - if (!spot) - { - if(autocvar_spawn_debug) - GotoNextMap(0); - else - { - if(some_spawn_has_been_used) - return world; // team can't spawn any more, because of actions of other team - else - error("Cannot find a spawn point - please fix the map!"); - } - } - - return spot; -} - /* ============= CheckPlayerModel diff --git a/qcsrc/server/miscfunctions.qc b/qcsrc/server/miscfunctions.qc index 545aa31b25..d57b28624a 100644 --- a/qcsrc/server/miscfunctions.qc +++ b/qcsrc/server/miscfunctions.qc @@ -155,63 +155,6 @@ void GameLogClose() } } -float spawnpoint_nag; -void relocate_spawnpoint() -{ - // nudge off the floor - setorigin(self, self.origin + '0 0 1'); - - tracebox(self.origin, PL_MIN, PL_MAX, self.origin, TRUE, self); - if (trace_startsolid) - { - vector o; - o = self.origin; - self.mins = PL_MIN; - self.maxs = PL_MAX; - if (!move_out_of_solid(self)) - objerror("could not get out of solid at all!"); - print("^1NOTE: this map needs FIXING. Spawnpoint at ", vtos(o - '0 0 1')); - print(" needs to be moved out of solid, e.g. by '", ftos(self.origin_x - o_x)); - print(" ", ftos(self.origin_y - o_y)); - print(" ", ftos(self.origin_z - o_z), "'\n"); - if (autocvar_g_spawnpoints_auto_move_out_of_solid) - { - if (!spawnpoint_nag) - print("\{1}^1NOTE: this map needs FIXING (it contains spawnpoints in solid, see server log)\n"); - spawnpoint_nag = 1; - } - else - { - setorigin(self, o); - self.mins = self.maxs = '0 0 0'; - objerror("player spawn point in solid, mapper sucks!\n"); - return; - } - } - - self.use = spawnpoint_use; - self.team_saved = self.team; - if (!self.cnt) - self.cnt = 1; - - if (have_team_spawns != 0) - if (self.team) - have_team_spawns = 1; - have_team_spawns_forteam[self.team] = 1; - - if (autocvar_r_showbboxes) - { - // show where spawnpoints point at too - makevectors(self.angles); - entity e; - e = spawn(); - e.classname = "info_player_foo"; - setorigin(e, self.origin + v_forward * 24); - setsize(e, '-8 -8 -8', '8 8 8'); - e.solid = SOLID_TRIGGER; - } -} - #define strstr strstrofs /* // NOTE: DO NOT USE THIS FUNCTION TOO OFTEN. diff --git a/qcsrc/server/progs.src b/qcsrc/server/progs.src index 157aa38a43..2bfab37782 100644 --- a/qcsrc/server/progs.src +++ b/qcsrc/server/progs.src @@ -77,6 +77,8 @@ w_laser.qh scores.qh +spawnpoints.qh + ipban.qh race.qh @@ -192,6 +194,8 @@ vehicles/vehicles.qh scores.qc +spawnpoints.qc + portals.qc target_spawn.qc diff --git a/qcsrc/server/spawnpoints.qc b/qcsrc/server/spawnpoints.qc new file mode 100644 index 0000000000..c0846b7427 --- /dev/null +++ b/qcsrc/server/spawnpoints.qc @@ -0,0 +1,300 @@ +float Spawn_Send(entity to, float sf) +{ + WriteByte(MSG_ENTITY, ENT_CLIENT_SPAWNPOINT); + WriteByte(MSG_ENTITY, self.team); + WriteShort(MSG_ENTITY, self.origin_x); + WriteShort(MSG_ENTITY, self.origin_y); + WriteShort(MSG_ENTITY, self.origin_z); + return TRUE; +} + +void spawnpoint_use() +{ + if(teamplay) + if(have_team_spawns > 0) + { + self.team = activator.team; + some_spawn_has_been_used = 1; + } +} + +void relocate_spawnpoint() +{ + // nudge off the floor + setorigin(self, self.origin + '0 0 1'); + + tracebox(self.origin, PL_MIN, PL_MAX, self.origin, TRUE, self); + if (trace_startsolid) + { + vector o; + o = self.origin; + self.mins = PL_MIN; + self.maxs = PL_MAX; + if (!move_out_of_solid(self)) + objerror("could not get out of solid at all!"); + print("^1NOTE: this map needs FIXING. Spawnpoint at ", vtos(o - '0 0 1')); + print(" needs to be moved out of solid, e.g. by '", ftos(self.origin_x - o_x)); + print(" ", ftos(self.origin_y - o_y)); + print(" ", ftos(self.origin_z - o_z), "'\n"); + if (autocvar_g_spawnpoints_auto_move_out_of_solid) + { + if (!spawnpoint_nag) + print("\{1}^1NOTE: this map needs FIXING (it contains spawnpoints in solid, see server log)\n"); + spawnpoint_nag = 1; + } + else + { + setorigin(self, o); + self.mins = self.maxs = '0 0 0'; + objerror("player spawn point in solid, mapper sucks!\n"); + return; + } + } + + self.use = spawnpoint_use; + self.team_saved = self.team; + if (!self.cnt) + self.cnt = 1; + + if (have_team_spawns != 0) + if (self.team) + have_team_spawns = 1; + have_team_spawns_forteam[self.team] = 1; + + if (autocvar_r_showbboxes) + { + // show where spawnpoints point at too + makevectors(self.angles); + entity e; + e = spawn(); + e.classname = "info_player_foo"; + setorigin(e, self.origin + v_forward * 24); + setsize(e, '-8 -8 -8', '8 8 8'); + e.solid = SOLID_TRIGGER; + } + + //self.think = Spawn_Send_Think; + //self.nextthink = time; + + Net_LinkEntity(self, FALSE, 0, Spawn_Send); +} + +void spawnfunc_info_player_survivor (void) +{ + spawnfunc_info_player_deathmatch(); +} + +void spawnfunc_info_player_start (void) +{ + spawnfunc_info_player_deathmatch(); +} + +void spawnfunc_info_player_deathmatch (void) +{ + self.classname = "info_player_deathmatch"; + relocate_spawnpoint(); +} + +// Returns: +// _x: prio (-1 if unusable) +// _y: weight +vector Spawn_Score(entity spot, float mindist, float teamcheck) +{ + float shortest, thisdist; + float prio; + entity player; + + prio = 0; + + // filter out spots for the wrong team + if(teamcheck >= 0) + if(spot.team != teamcheck) + return '-1 0 0'; + + if(race_spawns) + if(spot.target == "") + return '-1 0 0'; + + if(clienttype(self) == CLIENTTYPE_REAL) + { + if(spot.restriction == 1) + return '-1 0 0'; + } + else + { + if(spot.restriction == 2) + return '-1 0 0'; + } + + shortest = vlen(world.maxs - world.mins); + FOR_EACH_PLAYER(player) if (player != self) + { + thisdist = vlen(player.origin - spot.origin); + if (thisdist < shortest) + shortest = thisdist; + } + if(shortest > mindist) + prio += SPAWN_PRIO_GOOD_DISTANCE; + + spawn_score = prio * '1 0 0' + shortest * '0 1 0'; + spawn_spot = spot; + + // filter out spots for assault + if(spot.target != "") { + entity ent; + float found; + + found = 0; + for(ent = world; (ent = find(ent, targetname, spot.target)); ) + { + ++found; + if(ent.spawn_evalfunc) + { + entity oldself = self; + self = ent; + spawn_score = ent.spawn_evalfunc(oldself, spot, spawn_score); + self = oldself; + if(spawn_score_x < 0) + return spawn_score; + } + } + + if(!found) + { + dprint("WARNING: spawnpoint at ", vtos(spot.origin), " could not find its target ", spot.target, "\n"); + return '-1 0 0'; + } + } + + MUTATOR_CALLHOOK(Spawn_Score); + return spawn_score; +} + +void Spawn_ScoreAll(entity firstspot, float mindist, float teamcheck) +{ + entity spot; + for(spot = firstspot; spot; spot = spot.chain) + spot.spawnpoint_score = Spawn_Score(spot, mindist, teamcheck); +} + +entity Spawn_FilterOutBadSpots(entity firstspot, float mindist, float teamcheck) +{ + entity spot, spotlist, spotlistend; + + spotlist = world; + spotlistend = world; + + Spawn_ScoreAll(firstspot, mindist, teamcheck); + + for(spot = firstspot; spot; spot = spot.chain) + { + if(spot.spawnpoint_score_x >= 0) // spawning allowed here + { + if(spotlistend) + spotlistend.chain = spot; + spotlistend = spot; + if(!spotlist) + spotlist = spot; + } + } + if(spotlistend) + spotlistend.chain = world; + + return spotlist; +} + +entity Spawn_WeightedPoint(entity firstspot, float lower, float upper, float exponent) +{ + // weight of a point: bound(lower, mindisttoplayer, upper)^exponent + // multiplied by spot.cnt (useful if you distribute many spawnpoints in a small area) + entity spot; + + RandomSelection_Init(); + for(spot = firstspot; spot; spot = spot.chain) + RandomSelection_Add(spot, 0, string_null, pow(bound(lower, spot.spawnpoint_score_y, upper), exponent) * spot.cnt, (spot.spawnpoint_score_y >= lower) * 0.5 + spot.spawnpoint_score_x); + + return RandomSelection_chosen_ent; +} + +/* +============= +SelectSpawnPoint + +Finds a point to respawn +============= +*/ +entity SelectSpawnPoint (float anypoint) +{ + float teamcheck; + entity spot, firstspot; + + spot = find (world, classname, "testplayerstart"); + if (spot) + return spot; + + if(anypoint || autocvar_g_spawn_useallspawns) + teamcheck = -1; + else if(have_team_spawns > 0) + { + if(have_team_spawns_forteam[self.team] == 0) + { + // we request a spawn for a team, and we have team + // spawns, but that team has no spawns? + if(have_team_spawns_forteam[0]) + // try noteam spawns + teamcheck = 0; + else + // if not, any spawn has to do + teamcheck = -1; + } + else + teamcheck = self.team; // MUST be team + } + else if(have_team_spawns == 0 && have_team_spawns_forteam[0]) + teamcheck = 0; // MUST be noteam + else + teamcheck = -1; + // if we get here, we either require team spawns but have none, or we require non-team spawns and have none; use any spawn then + + + // get the entire list of spots + firstspot = findchain(classname, "info_player_deathmatch"); + // filter out the bad ones + // (note this returns the original list if none survived) + if(anypoint) + { + spot = Spawn_WeightedPoint(firstspot, 1, 1, 1); + } + else + { + float mindist; + if (arena_roundbased && !g_ca) + mindist = 800; + else + mindist = 100; + firstspot = Spawn_FilterOutBadSpots(firstspot, mindist, teamcheck); + + // 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 + // usually won't get fragged at spawn twice in a row) + if (random() > autocvar_g_spawn_furthest) + spot = Spawn_WeightedPoint(firstspot, 1, 1, 1); + else + spot = Spawn_WeightedPoint(firstspot, 1, 5000, 5); // chooses a far far away spawnpoint + } + + if (!spot) + { + if(autocvar_spawn_debug) + GotoNextMap(0); + else + { + if(some_spawn_has_been_used) + return world; // team can't spawn any more, because of actions of other team + else + error("Cannot find a spawn point - please fix the map!"); + } + } + + return spot; +} diff --git a/qcsrc/server/spawnpoints.qh b/qcsrc/server/spawnpoints.qh new file mode 100644 index 0000000000..b256f4c368 --- /dev/null +++ b/qcsrc/server/spawnpoints.qh @@ -0,0 +1,12 @@ +.vector spawnpoint_score; +float spawnpoint_nag; +//void spawnpoint_use(); +//void relocate_spawnpoint(); +//void spawnfunc_info_player_survivor (void); +//void spawnfunc_info_player_start (void); +//void spawnfunc_info_player_deathmatch (void); +//vector Spawn_Score(entity spot, float mindist, float teamcheck); +//void Spawn_ScoreAll(entity firstspot, float mindist, float teamcheck); +entity Spawn_FilterOutBadSpots(entity firstspot, float mindist, float teamcheck); +//entity Spawn_WeightedPoint(entity firstspot, float lower, float upper, float exponent); +//entity SelectSpawnPoint (float anypoint);