Merge branch 'master' into terencehill/music_player
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / mutators / gamemode_cts.qc
1 // legacy bot roles
2 .float race_checkpoint;
3 void havocbot_role_cts()
4 {
5         if(self.deadflag != DEAD_NO)
6                 return;
7
8         entity e;
9         if (self.bot_strategytime < time)
10         {
11                 self.bot_strategytime = time + autocvar_bot_ai_strategyinterval;
12                 navigation_goalrating_start();
13
14                 for(e = world; (e = find(e, classname, "trigger_race_checkpoint")) != world; )
15                 {
16                         if(e.cnt == self.race_checkpoint)
17                         {
18                                 navigation_routerating(e, 1000000, 5000);
19                         }
20                         else if(self.race_checkpoint == -1)
21                         {
22                                 navigation_routerating(e, 1000000, 5000);
23                         }
24                 }
25
26                 navigation_goalrating_end();
27         }
28 }
29
30 void cts_ScoreRules()
31 {
32         ScoreRules_basics(0, 0, 0, FALSE);
33         if(g_race_qualifying)
34         {
35                 ScoreInfo_SetLabel_PlayerScore(SP_CTS_FASTEST, "fastest",   SFL_SORT_PRIO_PRIMARY | SFL_LOWER_IS_BETTER | SFL_TIME);
36         }
37         else
38         {
39                 ScoreInfo_SetLabel_PlayerScore(SP_CTS_LAPS,    "laps",      SFL_SORT_PRIO_PRIMARY);
40                 ScoreInfo_SetLabel_PlayerScore(SP_CTS_TIME,    "time",      SFL_SORT_PRIO_SECONDARY | SFL_LOWER_IS_BETTER | SFL_TIME);
41                 ScoreInfo_SetLabel_PlayerScore(SP_CTS_FASTEST, "fastest",   SFL_LOWER_IS_BETTER | SFL_TIME);
42         }
43         ScoreRules_basics_end();
44 }
45
46 void cts_EventLog(string mode, entity actor) // use an alias for easy changing and quick editing later
47 {
48         if(autocvar_sv_eventlog)
49                 GameLogEcho(strcat(":cts:", mode, ":", ((actor != world) ? (strcat(":", ftos(actor.playerid))) : "")));
50 }
51
52 MUTATOR_HOOKFUNCTION(cts_PlayerPhysics)
53 {
54         // force kbd movement for fairness
55         float wishspeed;
56         vector wishvel;
57
58         // if record times matter
59         // ensure nothing EVIL is being done (i.e. div0_evade)
60         // this hinders joystick users though
61         // but it still gives SOME analog control
62         wishvel_x = fabs(self.movement_x);
63         wishvel_y = fabs(self.movement_y);
64         if(wishvel_x != 0 && wishvel_y != 0 && wishvel_x != wishvel_y)
65         {
66                 wishvel_z = 0;
67                 wishspeed = vlen(wishvel);
68                 if(wishvel_x >= 2 * wishvel_y)
69                 {
70                         // pure X motion
71                         if(self.movement_x > 0)
72                                 self.movement_x = wishspeed;
73                         else
74                                 self.movement_x = -wishspeed;
75                         self.movement_y = 0;
76                 }
77                 else if(wishvel_y >= 2 * wishvel_x)
78                 {
79                         // pure Y motion
80                         self.movement_x = 0;
81                         if(self.movement_y > 0)
82                                 self.movement_y = wishspeed;
83                         else
84                                 self.movement_y = -wishspeed;
85                 }
86                 else
87                 {
88                         // diagonal
89                         if(self.movement_x > 0)
90                                 self.movement_x = M_SQRT1_2 * wishspeed;
91                         else
92                                 self.movement_x = -M_SQRT1_2 * wishspeed;
93                         if(self.movement_y > 0)
94                                 self.movement_y = M_SQRT1_2 * wishspeed;
95                         else
96                                 self.movement_y = -M_SQRT1_2 * wishspeed;
97                 }
98         }
99         
100         return FALSE;
101 }
102
103 MUTATOR_HOOKFUNCTION(cts_ResetMap)
104 {
105         float s;
106
107         Score_NicePrint(world);
108
109         race_ClearRecords();
110         PlayerScore_Sort(race_place, 0, 1, 0);
111
112         entity e;
113         FOR_EACH_CLIENT(e)
114         {
115                 if(e.race_place)
116                 {
117                         s = PlayerScore_Add(e, SP_RACE_FASTEST, 0);
118                         if(!s)
119                                 e.race_place = 0;
120                 }
121                 cts_EventLog(ftos(e.race_place), e);
122         }
123
124         if(g_race_qualifying == 2)
125         {
126                 g_race_qualifying = 0;
127                 independent_players = 0;
128                 cvar_set("fraglimit", ftos(race_fraglimit));
129                 cvar_set("leadlimit", ftos(race_leadlimit));
130                 cvar_set("timelimit", ftos(race_timelimit));
131                 cts_ScoreRules();
132         }
133         
134         return FALSE;
135 }
136
137 MUTATOR_HOOKFUNCTION(cts_PlayerPreThink)
138 {
139         if(IS_SPEC(self) || IS_OBSERVER(self))
140         if(g_race_qualifying)
141         if(msg_entity.enemy.race_laptime)
142                 race_SendNextCheckpoint(msg_entity.enemy, 1);
143
144         return FALSE;
145 }
146
147 MUTATOR_HOOKFUNCTION(cts_ClientConnect)
148 {
149         race_PreparePlayer();
150         self.race_checkpoint = -1;
151         
152         if(IS_REAL_CLIENT(self))
153         {
154                 string rr = CTS_RECORD;
155
156                 msg_entity = self;
157                 race_send_recordtime(MSG_ONE);
158                 race_send_speedaward(MSG_ONE);
159
160                 speedaward_alltimebest = stof(db_get(ServerProgsDB, strcat(GetMapname(), rr, "speed/speed")));
161                 speedaward_alltimebest_holder = uid2name(db_get(ServerProgsDB, strcat(GetMapname(), rr, "speed/crypto_idfp")));
162                 race_send_speedaward_alltimebest(MSG_ONE);
163
164                 float i;
165                 for (i = 1; i <= RANKINGS_CNT; ++i)
166                 {
167                         race_SendRankings(i, 0, 0, MSG_ONE);
168                 }
169         }
170
171         return FALSE;
172 }
173
174 MUTATOR_HOOKFUNCTION(cts_MakePlayerObserver)
175 {
176         if(PlayerScore_Add(self, SP_RACE_FASTEST, 0))
177                 self.frags = FRAGS_LMS_LOSER;
178         else
179                 self.frags = FRAGS_SPECTATOR;
180
181         race_PreparePlayer();
182         self.race_checkpoint = -1;
183
184         return FALSE;
185 }
186
187 MUTATOR_HOOKFUNCTION(cts_PlayerSpawn)
188 {
189         if(spawn_spot.target == "")
190                 // Emergency: this wasn't a real spawnpoint. Can this ever happen?
191                 race_PreparePlayer();
192
193         // if we need to respawn, do it right
194         self.race_respawn_checkpoint = self.race_checkpoint;
195         self.race_respawn_spotref = spawn_spot;
196
197         self.race_place = 0;
198         
199         return FALSE;
200 }
201
202 MUTATOR_HOOKFUNCTION(cts_PutClientInServer)
203 {
204         if(IS_PLAYER(self))
205         if(!gameover)
206         {
207                 if(self.killcount == -666 /* initial spawn */ || g_race_qualifying) // spawn
208                         race_PreparePlayer();
209                 else // respawn
210                         race_RetractPlayer();
211
212                 race_AbandonRaceCheck(self);
213         }
214         return FALSE;
215 }
216
217 MUTATOR_HOOKFUNCTION(cts_PlayerDies)
218 {
219         self.respawn_flags |= RESPAWN_FORCE;
220         race_AbandonRaceCheck(self);
221         return FALSE;
222 }
223
224 MUTATOR_HOOKFUNCTION(cts_BotRoles)
225 {
226         self.havocbot_role = havocbot_role_cts;
227         return TRUE;
228 }
229
230 MUTATOR_HOOKFUNCTION(cts_PlayerPostThink)
231 {
232         if(self.cvar_cl_allow_uidtracking == 1 && self.cvar_cl_allow_uid2name == 1)
233         {
234                 if (!self.stored_netname)
235                         self.stored_netname = strzone(uid2name(self.crypto_idfp));
236                 if(self.stored_netname != self.netname)
237                 {
238                         db_put(ServerProgsDB, strcat("/uid2name/", self.crypto_idfp), self.netname);
239                         strunzone(self.stored_netname);
240                         self.stored_netname = strzone(self.netname);
241                 }
242         }
243
244         return FALSE;
245 }
246
247 MUTATOR_HOOKFUNCTION(cts_ForbidThrowing)
248 {
249         // no weapon dropping in CTS
250         return TRUE;
251 }
252
253 MUTATOR_HOOKFUNCTION(cts_FilterItem)
254 {
255         if(self.classname == "droppedweapon")
256                 return TRUE;
257
258         return FALSE;
259 }
260
261 MUTATOR_HOOKFUNCTION(cts_PlayerDamage)
262 {
263         if(frag_target == frag_attacker || frag_deathtype == DEATH_FALL)
264         if(!autocvar_g_cts_selfdamage)
265                 frag_damage = 0;
266
267         return FALSE;
268 }
269
270 MUTATOR_HOOKFUNCTION(cts_ForbidClearPlayerScore)
271 {
272         return TRUE; // in CTS, you don't lose score by observing
273 }
274
275 MUTATOR_HOOKFUNCTION(cts_SetMods)
276 {
277         g_cloaked = 1; // always enable cloak in CTS
278
279         return FALSE;
280 }
281
282 void cts_Initialize()
283 {
284         cts_ScoreRules();
285 }
286
287 MUTATOR_DEFINITION(gamemode_cts)
288 {
289         MUTATOR_HOOK(PlayerPhysics, cts_PlayerPhysics, CBC_ORDER_ANY);
290         MUTATOR_HOOK(reset_map_global, cts_ResetMap, CBC_ORDER_ANY);
291         MUTATOR_HOOK(PlayerPreThink, cts_PlayerPreThink, CBC_ORDER_ANY);
292         MUTATOR_HOOK(ClientConnect, cts_ClientConnect, CBC_ORDER_ANY);
293         MUTATOR_HOOK(MakePlayerObserver, cts_MakePlayerObserver, CBC_ORDER_ANY);
294         MUTATOR_HOOK(PlayerSpawn, cts_PlayerSpawn, CBC_ORDER_ANY);
295         MUTATOR_HOOK(PutClientInServer, cts_PutClientInServer, CBC_ORDER_ANY);
296         MUTATOR_HOOK(PlayerDies, cts_PlayerDies, CBC_ORDER_ANY);
297         MUTATOR_HOOK(HavocBot_ChooseRole, cts_BotRoles, CBC_ORDER_ANY);
298         MUTATOR_HOOK(GetPressedKeys, cts_PlayerPostThink, CBC_ORDER_ANY);
299         MUTATOR_HOOK(ForbidThrowCurrentWeapon, cts_ForbidThrowing, CBC_ORDER_ANY);
300         MUTATOR_HOOK(FilterItem, cts_FilterItem, CBC_ORDER_ANY);
301         MUTATOR_HOOK(PlayerDamage_Calculate, cts_PlayerDamage, CBC_ORDER_ANY);
302         MUTATOR_HOOK(ForbidPlayerScore_Clear, cts_ForbidClearPlayerScore, CBC_ORDER_ANY);
303         MUTATOR_HOOK(SetModname, cts_SetMods, CBC_ORDER_ANY);
304
305         MUTATOR_ONADD
306         {
307                 if(time > 1) // game loads at time 1
308                         error("This is a game type and it cannot be added at runtime.");
309                 cts_Initialize();
310         }
311
312         MUTATOR_ONROLLBACK_OR_REMOVE
313         {
314                 // we actually cannot roll back cts_Initialize here
315                 // BUT: we don't need to! If this gets called, adding always
316                 // succeeds.
317         }
318
319         MUTATOR_ONREMOVE
320         {
321                 print("This is a game type and it cannot be removed at runtime.");
322                 return -1;
323         }
324
325         return 0;
326 }