]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/server/spawnpoints.qc
On second thought, lets call them "spawnpoints.q*"
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / spawnpoints.qc
1 void spawnpoint_use()
2 {
3         if(teamplay)
4         if(have_team_spawns > 0)
5         {
6                 self.team = activator.team;
7                 some_spawn_has_been_used = 1;
8         }
9 }
10
11 void relocate_spawnpoint()
12 {
13     // nudge off the floor
14     setorigin(self, self.origin + '0 0 1');
15
16     tracebox(self.origin, PL_MIN, PL_MAX, self.origin, TRUE, self);
17     if (trace_startsolid)
18     {
19         vector o;
20         o = self.origin;
21         self.mins = PL_MIN;
22         self.maxs = PL_MAX;
23         if (!move_out_of_solid(self))
24             objerror("could not get out of solid at all!");
25         print("^1NOTE: this map needs FIXING. Spawnpoint at ", vtos(o - '0 0 1'));
26         print(" needs to be moved out of solid, e.g. by '", ftos(self.origin_x - o_x));
27         print(" ", ftos(self.origin_y - o_y));
28         print(" ", ftos(self.origin_z - o_z), "'\n");
29         if (autocvar_g_spawnpoints_auto_move_out_of_solid)
30         {
31             if (!spawnpoint_nag)
32                 print("\{1}^1NOTE: this map needs FIXING (it contains spawnpoints in solid, see server log)\n");
33             spawnpoint_nag = 1;
34         }
35         else
36         {
37             setorigin(self, o);
38             self.mins = self.maxs = '0 0 0';
39             objerror("player spawn point in solid, mapper sucks!\n");
40             return;
41         }
42     }
43
44     self.use = spawnpoint_use;
45     self.team_saved = self.team;
46     if (!self.cnt)
47         self.cnt = 1;
48
49     if (have_team_spawns != 0)
50         if (self.team)
51             have_team_spawns = 1;
52     have_team_spawns_forteam[self.team] = 1;
53
54     if (autocvar_r_showbboxes)
55     {
56         // show where spawnpoints point at too
57         makevectors(self.angles);
58         entity e;
59         e = spawn();
60         e.classname = "info_player_foo";
61         setorigin(e, self.origin + v_forward * 24);
62         setsize(e, '-8 -8 -8', '8 8 8');
63         e.solid = SOLID_TRIGGER;
64     }
65
66     //Net_LinkEntity(self, FALSE, 0, ItemSend);
67 }
68
69 void spawnfunc_info_player_survivor (void)
70 {
71         spawnfunc_info_player_deathmatch();
72 }
73
74 void spawnfunc_info_player_start (void)
75 {
76         spawnfunc_info_player_deathmatch();
77 }
78
79 void spawnfunc_info_player_deathmatch (void)
80 {
81         self.classname = "info_player_deathmatch";
82         relocate_spawnpoint();
83 }
84
85 // Returns:
86 //   _x: prio (-1 if unusable)
87 //   _y: weight
88 vector Spawn_Score(entity spot, float mindist, float teamcheck)
89 {
90         float shortest, thisdist;
91         float prio;
92         entity player;
93
94         prio = 0;
95
96         // filter out spots for the wrong team
97         if(teamcheck >= 0)
98                 if(spot.team != teamcheck)
99                         return '-1 0 0';
100
101         if(race_spawns)
102                 if(spot.target == "")
103                         return '-1 0 0';
104
105         if(clienttype(self) == CLIENTTYPE_REAL)
106         {
107                 if(spot.restriction == 1)
108                         return '-1 0 0';
109         }
110         else
111         {
112                 if(spot.restriction == 2)
113                         return '-1 0 0';
114         }
115
116         shortest = vlen(world.maxs - world.mins);
117         FOR_EACH_PLAYER(player) if (player != self)
118         {
119                 thisdist = vlen(player.origin - spot.origin);
120                 if (thisdist < shortest)
121                         shortest = thisdist;
122         }
123         if(shortest > mindist)
124                 prio += SPAWN_PRIO_GOOD_DISTANCE;
125
126         spawn_score = prio * '1 0 0' + shortest * '0 1 0';
127         spawn_spot = spot;
128
129         // filter out spots for assault
130         if(spot.target != "") {
131                 entity ent;
132                 float found;
133
134                 found = 0;
135                 for(ent = world; (ent = find(ent, targetname, spot.target)); )
136                 {
137                         ++found;
138                         if(ent.spawn_evalfunc)
139                         {
140                                 entity oldself = self;
141                                 self = ent;
142                                 spawn_score = ent.spawn_evalfunc(oldself, spot, spawn_score);
143                                 self = oldself;
144                                 if(spawn_score_x < 0)
145                                         return spawn_score;
146                         }
147                 }
148
149                 if(!found)
150                 {
151                         dprint("WARNING: spawnpoint at ", vtos(spot.origin), " could not find its target ", spot.target, "\n");
152                         return '-1 0 0';
153                 }
154         }
155
156         MUTATOR_CALLHOOK(Spawn_Score);
157         return spawn_score;
158 }
159
160 void Spawn_ScoreAll(entity firstspot, float mindist, float teamcheck)
161 {
162         entity spot;
163         for(spot = firstspot; spot; spot = spot.chain)
164                 spot.spawnpoint_score = Spawn_Score(spot, mindist, teamcheck);
165 }
166
167 entity Spawn_FilterOutBadSpots(entity firstspot, float mindist, float teamcheck)
168 {
169         entity spot, spotlist, spotlistend;
170
171         spotlist = world;
172         spotlistend = world;
173
174         Spawn_ScoreAll(firstspot, mindist, teamcheck);
175
176         for(spot = firstspot; spot; spot = spot.chain)
177         {
178                 if(spot.spawnpoint_score_x >= 0) // spawning allowed here
179                 {
180                         if(spotlistend)
181                                 spotlistend.chain = spot;
182                         spotlistend = spot;
183                         if(!spotlist)
184                                 spotlist = spot;
185                 }
186         }
187         if(spotlistend)
188                 spotlistend.chain = world;
189
190         return spotlist;
191 }
192
193 entity Spawn_WeightedPoint(entity firstspot, float lower, float upper, float exponent)
194 {
195         // weight of a point: bound(lower, mindisttoplayer, upper)^exponent
196         // multiplied by spot.cnt (useful if you distribute many spawnpoints in a small area)
197         entity spot;
198
199         RandomSelection_Init();
200         for(spot = firstspot; spot; spot = spot.chain)
201                 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);
202
203         return RandomSelection_chosen_ent;
204 }
205
206 /*
207 =============
208 SelectSpawnPoint
209
210 Finds a point to respawn
211 =============
212 */
213 entity SelectSpawnPoint (float anypoint)
214 {
215         float teamcheck;
216         entity spot, firstspot;
217
218         spot = find (world, classname, "testplayerstart");
219         if (spot)
220                 return spot;
221
222         if(anypoint || autocvar_g_spawn_useallspawns)
223                 teamcheck = -1;
224         else if(have_team_spawns > 0)
225         {
226                 if(have_team_spawns_forteam[self.team] == 0)
227                 {
228                         // we request a spawn for a team, and we have team
229                         // spawns, but that team has no spawns?
230                         if(have_team_spawns_forteam[0])
231                                 // try noteam spawns
232                                 teamcheck = 0;
233                         else
234                                 // if not, any spawn has to do
235                                 teamcheck = -1;
236                 }
237                 else
238                         teamcheck = self.team; // MUST be team
239         }
240         else if(have_team_spawns == 0 && have_team_spawns_forteam[0])
241                 teamcheck = 0; // MUST be noteam
242         else
243                 teamcheck = -1;
244                 // 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
245
246
247         // get the entire list of spots
248         firstspot = findchain(classname, "info_player_deathmatch");
249         // filter out the bad ones
250         // (note this returns the original list if none survived)
251         if(anypoint)
252         {
253                 spot = Spawn_WeightedPoint(firstspot, 1, 1, 1);
254         }
255         else
256         {
257                 float mindist;
258                 if (arena_roundbased && !g_ca)
259                         mindist = 800;
260                 else
261                         mindist = 100;
262                 firstspot = Spawn_FilterOutBadSpots(firstspot, mindist, teamcheck);
263
264                 // there is 50/50 chance of choosing a random spot or the furthest spot
265                 // (this means that roughly every other spawn will be furthest, so you
266                 // usually won't get fragged at spawn twice in a row)
267                 if (random() > autocvar_g_spawn_furthest)
268                         spot = Spawn_WeightedPoint(firstspot, 1, 1, 1);
269                 else
270                         spot = Spawn_WeightedPoint(firstspot, 1, 5000, 5); // chooses a far far away spawnpoint
271         }
272
273         if (!spot)
274         {
275                 if(autocvar_spawn_debug)
276                         GotoNextMap(0);
277                 else
278                 {
279                         if(some_spawn_has_been_used)
280                                 return world; // team can't spawn any more, because of actions of other team
281                         else
282                                 error("Cannot find a spawn point - please fix the map!");
283                 }
284         }
285
286         return spot;
287 }