Xonotic
sv_cts.qc
Go to the documentation of this file.
1 #include "sv_cts.qh"
2 
3 #include <server/client.qh>
4 #include <server/race.qh>
5 #include <server/world.qh>
6 #include <server/gamelog.qh>
7 #include <server/intermission.qh>
11 
15 
16 // legacy bot roles
19 {
20  if(IS_DEAD(this))
21  return;
22 
24  {
26 
27  bool raw_touch_check = true;
28  int cp = this.race_checkpoint;
29 
30  LABEL(search_racecheckpoints)
32  {
33  if(it.cnt == cp || cp == -1)
34  {
35  // redirect bot to next goal if it touched the waypoint of an untouchable checkpoint
36  // e.g. checkpoint in front of Stormkeep's warpzone
37  // the same workaround is applied in Race game mode
38  if (raw_touch_check && vdist(this.origin - it.nearestwaypoint.origin, <, 30))
39  {
40  cp = race_NextCheckpoint(cp);
41  raw_touch_check = false;
42  goto search_racecheckpoints;
43  }
44  navigation_routerating(this, it, 1000000, 5000);
45  }
46  });
47 
49 
51  }
52 }
53 
55 {
57  GameRules_scoring(0, 0, 0, {
58  if (g_race_qualifying) {
59  field(SP_RACE_FASTEST, "fastest", SFL_SORT_PRIO_PRIMARY | SFL_LOWER_IS_BETTER | SFL_TIME);
60  } else {
61  field(SP_RACE_LAPS, "laps", SFL_SORT_PRIO_PRIMARY);
62  field(SP_RACE_TIME, "time", SFL_SORT_PRIO_SECONDARY | SFL_LOWER_IS_BETTER | SFL_TIME);
63  field(SP_RACE_FASTEST, "fastest", SFL_LOWER_IS_BETTER | SFL_TIME);
64  }
65  });
66 }
67 
68 void cts_EventLog(string mode, entity actor) // use an alias for easy changing and quick editing later
69 {
71  GameLogEcho(strcat(":cts:", mode, ":", ((actor != NULL) ? (strcat(":", ftos(actor.playerid))) : "")));
72 }
73 
74 MUTATOR_HOOKFUNCTION(cts, PlayerPhysics)
75 {
76  entity player = M_ARGV(0, entity);
77  float dt = M_ARGV(1, float);
78 
79  player.race_movetime_frac += dt;
80  float f = floor(player.race_movetime_frac);
81  player.race_movetime_frac -= f;
82  player.race_movetime_count += f;
83  player.race_movetime = player.race_movetime_frac + player.race_movetime_count;
84 
85  if(IS_PLAYER(player))
86  {
87  if (player.race_penalty)
88  if (time > player.race_penalty)
89  player.race_penalty = 0;
90  if(player.race_penalty)
91  {
92  player.velocity = '0 0 0';
93  set_movetype(player, MOVETYPE_NONE);
94  player.disableclientprediction = 2;
95  }
96  }
97 
98  // force kbd movement for fairness
99  float wishspeed;
100  vector wishvel;
101 
102  // if record times matter
103  // ensure nothing EVIL is being done (i.e. div0_evade)
104  // this hinders joystick users though
105  // but it still gives SOME analog control
106  wishvel.x = fabs(CS(player).movement.x);
107  wishvel.y = fabs(CS(player).movement.y);
108  if(wishvel.x != 0 && wishvel.y != 0 && wishvel.x != wishvel.y)
109  {
110  wishvel.z = 0;
111  wishspeed = vlen(wishvel);
112  if(wishvel.x >= 2 * wishvel.y)
113  {
114  // pure X motion
115  if(CS(player).movement.x > 0)
116  CS(player).movement_x = wishspeed;
117  else
118  CS(player).movement_x = -wishspeed;
119  CS(player).movement_y = 0;
120  }
121  else if(wishvel.y >= 2 * wishvel.x)
122  {
123  // pure Y motion
124  CS(player).movement_x = 0;
125  if(CS(player).movement.y > 0)
126  CS(player).movement_y = wishspeed;
127  else
128  CS(player).movement_y = -wishspeed;
129  }
130  else
131  {
132  // diagonal
133  if(CS(player).movement.x > 0)
134  CS(player).movement_x = M_SQRT1_2 * wishspeed;
135  else
136  CS(player).movement_x = -M_SQRT1_2 * wishspeed;
137  if(CS(player).movement.y > 0)
138  CS(player).movement_y = M_SQRT1_2 * wishspeed;
139  else
140  CS(player).movement_y = -M_SQRT1_2 * wishspeed;
141  }
142  }
143 }
144 
145 MUTATOR_HOOKFUNCTION(cts, reset_map_global)
146 {
147  float s;
148 
150 
152  PlayerScore_Sort(race_place, 0, true, false);
153 
154  FOREACH_CLIENT(true, {
155  if(it.race_place)
156  {
157  s = GameRules_scoring_add(it, RACE_FASTEST, 0);
158  if(!s)
159  it.race_place = 0;
160  }
161  cts_EventLog(ftos(it.race_place), it);
162  });
163 
164  if(g_race_qualifying == 2)
165  {
166  g_race_qualifying = 0;
168  cvar_set("fraglimit", ftos(race_fraglimit));
169  cvar_set("leadlimit", ftos(race_leadlimit));
170  cvar_set("timelimit", ftos(race_timelimit));
171  cts_ScoreRules();
172  }
173 }
174 
176 {
177  entity player = M_ARGV(0, entity);
178 
179  race_PreparePlayer(player);
180  player.race_checkpoint = -1;
181 
182  race_SendAll(player, false);
183 }
184 
185 MUTATOR_HOOKFUNCTION(cts, AbortSpeedrun)
186 {
187  entity player = M_ARGV(0, entity);
188 
190  race_PreparePlayer(player); // nice try
191 }
192 
193 MUTATOR_HOOKFUNCTION(cts, MakePlayerObserver)
194 {
195  entity player = M_ARGV(0, entity);
196 
197  if(GameRules_scoring_add(player, RACE_FASTEST, 0))
198  player.frags = FRAGS_PLAYER_OUT_OF_GAME;
199  else
200  player.frags = FRAGS_SPECTATOR;
201 
202  race_PreparePlayer(player);
203  player.race_checkpoint = -1;
204 }
205 
206 MUTATOR_HOOKFUNCTION(cts, PlayerSpawn)
207 {
208  entity player = M_ARGV(0, entity);
209  entity spawn_spot = M_ARGV(1, entity);
210 
211  if(spawn_spot.target == "")
212  // Emergency: this wasn't a real spawnpoint. Can this ever happen?
213  race_PreparePlayer(player);
214 
215  // if we need to respawn, do it right
216  player.race_respawn_checkpoint = player.race_checkpoint;
217  player.race_respawn_spotref = spawn_spot;
218 
219  player.race_place = 0;
220 }
221 
223 {
224  entity player = M_ARGV(0, entity);
225 
226  if(IS_PLAYER(player))
227  if(!game_stopped)
228  {
229  if(CS(player).killcount == FRAGS_SPECTATOR /* initial spawn */ || g_race_qualifying) // spawn
230  race_PreparePlayer(player);
231  else // respawn
232  race_RetractPlayer(player);
233 
234  race_AbandonRaceCheck(player);
235  }
236 }
237 
238 MUTATOR_HOOKFUNCTION(cts, PlayerDamaged)
239 {
240  int frag_deathtype = M_ARGV(5, int);
241  if (frag_deathtype == DEATH_KILL.m_id)
242  return true; // forbid logging damage
243 }
244 
245 MUTATOR_HOOKFUNCTION(cts, PlayerDies)
246 {
247  entity frag_target = M_ARGV(2, entity);
248 
249  frag_target.respawn_flags |= RESPAWN_FORCE;
250  race_AbandonRaceCheck(frag_target);
251 
253  {
254  IL_EACH(g_projectiles, it.owner == frag_target && (it.flags & FL_PROJECTILE),
255  {
256  delete(it);
257  });
258  }
259 }
260 
261 MUTATOR_HOOKFUNCTION(cts, HavocBot_ChooseRole)
262 {
263  entity bot = M_ARGV(0, entity);
264 
265  bot.havocbot_role = havocbot_role_cts;
266  return true;
267 }
268 
270 {
271  entity player = M_ARGV(0, entity);
272 
273  race_checkAndWriteName(player);
274  race_SpeedAwardFrame(player);
275 }
276 
277 MUTATOR_HOOKFUNCTION(cts, ForbidThrowCurrentWeapon)
278 {
279  // no weapon dropping in CTS
280  return true;
281 }
282 
283 MUTATOR_HOOKFUNCTION(cts, FilterItem)
284 {
285  entity item = M_ARGV(0, entity);
286 
287  if (Item_IsLoot(item))
288  {
289  return true;
290  }
291 }
292 
293 MUTATOR_HOOKFUNCTION(cts, Damage_Calculate)
294 {
295  entity frag_attacker = M_ARGV(1, entity);
296  entity frag_target = M_ARGV(2, entity);
297  float frag_deathtype = M_ARGV(3, float);
298  float frag_damage = M_ARGV(4, float);
299 
300  if(frag_target == frag_attacker || frag_deathtype == DEATH_FALL.m_id)
302  {
303  frag_damage = 0;
304  M_ARGV(4, float) = frag_damage;
305  }
306 }
307 
308 MUTATOR_HOOKFUNCTION(cts, ForbidPlayerScore_Clear)
309 {
310  return true; // in CTS, you don't lose score by observing
311 }
312 
313 MUTATOR_HOOKFUNCTION(cts, GetRecords)
314 {
315  int record_page = M_ARGV(0, int);
316  string ret_string = M_ARGV(1, string);
317 
318  for(int i = record_page * 200; i < MapInfo_count && i < record_page * 200 + 200; ++i)
319  {
320  if(MapInfo_Get_ByID(i))
321  {
322  float r = race_readTime(MapInfo_Map_bspname, 1);
323 
324  if(!r)
325  continue;
326 
327  string h = race_readName(MapInfo_Map_bspname, 1);
328  ret_string = strcat(ret_string, strpad(32, MapInfo_Map_bspname), " ", strpad(-8, TIME_ENCODED_TOSTRING(r)), " ", h, "\n");
329  }
330  }
331 
332  M_ARGV(1, string) = ret_string;
333 }
334 
336 {
337  M_ARGV(1, float) = 0; // kill delay
338 }
339 
340 MUTATOR_HOOKFUNCTION(cts, Race_FinalCheckpoint)
341 {
342  entity player = M_ARGV(0, entity);
343 
344  // useful to prevent cheating by running back to the start line and starting out with more speed
347 }
348 
349 MUTATOR_HOOKFUNCTION(cts, HideTeamNagger)
350 {
351  return true; // doesn't work so well (but isn't cts a teamless mode?)
352 }
353 
355 {
356  entity player = M_ARGV(0, entity);
357 
358  stuffcmd(player, "cl_cmd settemp cl_movecliptokeyboard 2\n");
359 }
360 
361 MUTATOR_HOOKFUNCTION(cts, WantWeapon)
362 {
363  M_ARGV(1, float) = (M_ARGV(0, entity) == WEP_SHOTGUN); // want weapon = weapon info
364  M_ARGV(3, bool) = true; // want mutator blocked
365  return true;
366 }
367 
368 MUTATOR_HOOKFUNCTION(cts, ForbidDropCurrentWeapon)
369 {
370  return true;
371 }
372 
374 {
376  cts_ScoreRules();
377 }
void race_SendAll(entity player, bool only_rankings)
Definition: race.qc:324
const int RESPAWN_FORCE
Definition: client.qh:330
#define GameRules_scoring(teams, spprio, stprio, fields)
Definition: sv_rules.qh:53
#define IL_EACH(this, cond, body)
float MOVETYPE_NONE
Definition: progsdefs.qc:246
void navigation_goalrating_start(entity this)
Definition: navigation.qc:1830
float race_place
Definition: race.qh:23
void race_ClearRecords()
Definition: race.qc:1210
void ClientConnect(entity this)
ClientConnect
Definition: client.qc:1096
void navigation_goalrating_end(entity this)
Definition: navigation.qc:1845
float race_checkpoint
Definition: sv_cts.qc:17
void cts_EventLog(string mode, entity actor)
Definition: sv_cts.qc:68
void Score_NicePrint(entity to)
Prints the scores to the console of a player.
Definition: scores.qc:903
void ClientKill(entity this)
Definition: clientkill.qc:208
const int SFL_SORT_PRIO_SECONDARY
Scoring priority (NOTE: PRIMARY is used for fraglimit)
Definition: scores.qh:126
entity() spawn
ClientState CS(Client this)
Definition: state.qh:47
#define FOREACH_CLIENT(cond, body)
Definition: utils.qh:49
#define GameRules_scoring_add(client, fld, value)
Definition: sv_rules.qh:78
void race_checkAndWriteName(entity player)
Definition: race.qc:143
const int SFL_SORT_PRIO_PRIMARY
Definition: scores.qh:127
bool MapInfo_Get_ByID(int i)
Definition: mapinfo.qc:256
const float M_SQRT1_2
Definition: mathlib.qh:115
MUTATOR_HOOKFUNCTION(cts, PlayerPhysics)
Definition: sv_cts.qc:74
void ClientKill_Silent(entity this, float _delay)
Definition: clientkill.qc:196
void GameRules_score_enabled(bool value)
Disabling score disables the "score" column on the scoreboard.
Definition: sv_rules.qc:28
int killcount
Definition: client.qh:317
bool g_race_qualifying
Definition: race.qh:12
#define TIME_ENCODED_TOSTRING(n)
Definition: util.qh:57
const int SFL_LOWER_IS_BETTER
Lower scores are better (e.g.
Definition: scores.qh:98
#define CTS_RECORD
Definition: util.qh:59
void navigation_routerating(entity this, entity e, float f, float rangebias)
Definition: navigation.qc:1220
float autocvar_g_cts_finish_kill_delay
Definition: sv_cts.qc:12
float MapInfo_count
Definition: mapinfo.qh:144
bool autocvar_g_cts_selfdamage
Definition: sv_cts.qc:13
bool navigation_goalrating_timeout(entity this)
Definition: navigation.qc:43
string record_type
Definition: world.qh:49
spree_cen s1 spree_cen s1 spree_cen s1 spree_cen s1 spree_cen s1 spree_cen s1 spree_cen s1 f1 s1 strcat(_("Level %s: "), "^BG%s\3\, _("^BGPress ^F2%s^BG to enter the game"))
void GetPressedKeys(entity this)
Definition: client.qc:1681
void havocbot_role_cts(entity this)
Definition: sv_cts.qc:18
IntrusiveList g_racecheckpoints
Definition: race.qc:69
bool autocvar_g_allow_checkpoints
Definition: race.qh:3
float race_readTime(string map, float pos)
Definition: race.qc:78
void race_SpeedAwardFrame(entity player)
Definition: race.qc:296
void race_PreparePlayer(entity this)
Definition: race.qc:1180
#define NULL
Definition: post.qh:17
void GameLogEcho(string s)
Definition: gamelog.qc:12
#define M_ARGV(x, type)
Definition: events.qh:17
#define IS_DEAD(s)
Definition: utils.qh:26
void navigation_goalrating_timeout_set(entity this)
Definition: navigation.qc:19
entity PlayerScore_Sort(.float field, int teams, bool strict, bool nospectators)
Sorts the players and stores their place in the given field, starting with.
Definition: scores.qc:724
void cts_ScoreRules()
Definition: sv_cts.qc:54
vector movement
#define stuffcmd(cl,...)
Definition: progsdefs.qh:23
vector(float skel, float bonenum) _skel_get_boneabs_hidden
bool independent_players
Definition: client.qh:312
IntrusiveList g_projectiles
Definition: common.qh:46
const int FRAGS_PLAYER_OUT_OF_GAME
Definition: constants.qh:5
string race_readName(string map, float pos)
Definition: race.qc:138
float race_fraglimit
Definition: race.qh:20
string MapInfo_Map_bspname
Definition: mapinfo.qh:6
void cts_Initialize()
Definition: sv_cts.qc:373
const int FRAGS_SPECTATOR
Definition: constants.qh:4
#define LABEL(id)
Definition: compiler.qh:36
void race_RetractPlayer(entity this)
Definition: race.qc:1189
bool Item_IsLoot(entity item)
Returns whether the item is loot.
Definition: spawning.qc:121
bool autocvar_g_cts_removeprojectiles
Definition: sv_cts.qc:14
PutClientInServer(this)
bool autocvar_sv_eventlog
Definition: gamelog.qh:3
Header file that describes the functions related to game items.
float time
Definition: csprogsdefs.qc:16
float race_leadlimit
Definition: race.qh:21
const int SFL_TIME
Display as mm:ss.s, value is stored as 10ths of a second (AND 0 is the worst possible value!) ...
Definition: scores.qh:118
void set_movetype(entity this, int mt)
#define IS_PLAYER(v)
Definition: utils.qh:9
void FixClientCvars(entity e)
Definition: client.qc:947
float race_timelimit
Definition: race.qh:22
void race_AbandonRaceCheck(entity p)
Definition: race.qc:1163