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