]> icculus.org git repositories - divverent/nexuiz.git/blob - data/qcsrc/server/race.qc
finish g_race_qualifying 2 mode (by adding a race display to csqc)
[divverent/nexuiz.git] / data / qcsrc / server / race.qc
1 #define MAX_CHECKPOINTS 255
2
3 .float race_checkpoint; // player: next checkpoint that has to be reached
4 .float race_laptime;
5
6 .entity sprite;
7
8 float race_checkpoint_records[MAX_CHECKPOINTS];
9 string race_checkpoint_recordholders[MAX_CHECKPOINTS];
10 float race_checkpoint_lasttimes[MAX_CHECKPOINTS];
11 float race_checkpoint_lastlaps[MAX_CHECKPOINTS];
12 entity race_checkpoint_lastplayers[MAX_CHECKPOINTS];
13
14 float race_highest_checkpoint;
15 float race_highest_place_spawn;
16
17 float race_NextCheckpoint(float f)
18 {
19         if(f >= race_highest_checkpoint)
20                 return 0;
21         else
22                 return f + 1;
23 }
24
25 float race_PreviousCheckpoint(float f)
26 {
27         if(f == -1)
28                 return 0;
29         else if(f == 0)
30                 return race_highest_checkpoint;
31         else
32                 return f - 1;
33 }
34
35 void race_SendNextCheckpoint(entity e)
36 {
37         float recordtime;
38         string recordholder;
39         float cp;
40
41         if(clienttype(e) != CLIENTTYPE_REAL)
42                 return;
43
44         if(!e.race_laptime)
45                 return;
46
47         cp = e.race_checkpoint;
48         recordtime = race_checkpoint_records[cp];
49         recordholder = race_checkpoint_recordholders[cp];
50         /*
51         recordtime = stof(db_get(ServerProgsDB, strcat(GetMapname(), "/racerecord/", ftos(cp), "/time")));
52         recordholder = db_get(ServerProgsDB, strcat(GetMapname(), "/racerecord/", ftos(cp), "/netname"));
53         */
54         if(recordholder == e.netname)
55                 recordholder = "";
56
57         msg_entity = e;
58         WriteByte(MSG_ONE, SVC_TEMPENTITY);
59         WriteByte(MSG_ONE, TE_CSQC_RACE);
60         WriteByte(MSG_ONE, RACE_NET_CHECKPOINT_NEXT_QUALIFYING);
61         WriteByte(MSG_ONE, cp); // checkpoint the player will be at next
62         WriteShort(MSG_ONE, recordtime);
63         WriteString(MSG_ONE, recordholder);
64 }
65
66 void race_SendTime(entity e, float cp, float t, float tvalid)
67 {
68         t = floor(0.5 + 10 * t); // make integer
69
70         if(tvalid)
71         if(cp == 0) // finish line
72         {
73                 float s;
74                 if(g_race_qualifying)
75                 {
76                         s = PlayerScore_Add(e, SP_RACE_FASTEST, 0);
77                         if(!s || t < s)
78                                 PlayerScore_Add(e, SP_RACE_FASTEST, t - s);
79                 }
80                 else
81                 {
82                         s = PlayerScore_Add(e, SP_RACE_TIME, 0);
83                         PlayerScore_Add(e, SP_RACE_TIME, time - s);
84                         PlayerTeamScore_Add(e, SP_RACE_LAPS, ST_RACE_LAPS, 1);
85                 }
86         }
87
88         float recordtime;
89         string recordholder;
90
91         if(g_race_qualifying)
92         {
93                 if(tvalid)
94                 {
95                         recordtime = race_checkpoint_records[cp];
96                         recordholder = strcat1(race_checkpoint_recordholders[cp]); // make a tempstring copy, as we'll possibly strunzone it!
97                         if(recordholder == e.netname)
98                                 recordholder = "";
99
100                         if(t < recordtime || recordtime == 0)
101                         {
102                                 race_checkpoint_records[cp] = t;
103                                 if(race_checkpoint_recordholders[cp])
104                                         strunzone(race_checkpoint_recordholders[cp]);
105                                 race_checkpoint_recordholders[cp] = strzone(e.netname);
106                                 if(cp == 0)
107                                 {
108                                         float grecordtime;
109                                         string grecordholder;
110                                         grecordtime = stof(db_get(ServerProgsDB, strcat(GetMapname(), "/racerecord/time")));
111                                         grecordholder = db_get(ServerProgsDB, strcat(GetMapname(), "/racerecord/netname"));
112                                         if(grecordholder == e.netname)
113                                                 grecordholder = "";
114                                         if(grecordtime == 0)
115                                         {
116                                                 bprint(e.netname, "^7 set the all-time fastest lap record with ", mmsss(t), "\n");
117                                                 db_put(ServerProgsDB, strcat(GetMapname(), "/racerecord/time"), ftos(t));
118                                                 db_put(ServerProgsDB, strcat(GetMapname(), "/racerecord/netname"), e.netname);
119                                         }
120                                         else if(t < grecordtime)
121                                         {
122                                                 if(grecordholder == "")
123                                                         bprint(e.netname, "^7 broke his all-time fastest lap record with ", mmsss(t), "\n");
124                                                 else
125                                                         bprint(e.netname, "^7 broke ", grecordholder, "^7's all-time fastest lap record with ", mmsss(t), "\n");
126                                                 db_put(ServerProgsDB, strcat(GetMapname(), "/racerecord/time"), ftos(t));
127                                                 db_put(ServerProgsDB, strcat(GetMapname(), "/racerecord/netname"), e.netname);
128                                         }
129                                         else
130                                         {
131                                                 if(grecordholder == "")
132                                                         bprint(e.netname, "^7's new fastest lap could not break his all-time fastest lap record of ", mmsss(grecordtime), "\n");
133                                                 else
134                                                         bprint(e.netname, "^7's new fastest lap could not break ", grecordholder, "^7's all-time fastest lap record of ", mmsss(grecordtime), "\n");
135                                         }
136                                 }
137
138                                 if(g_race_qualifying)
139                                 {
140                                         entity p;
141                                         FOR_EACH_REALPLAYER(p)
142                                                 if(p.race_checkpoint == cp)
143                                                         race_SendNextCheckpoint(p);
144                                 }
145                         }
146                 }
147                 else
148                 {
149                         // dummies
150                         t = 0;
151                         recordtime = 0;
152                         recordholder = "";
153                 }
154
155                 if(clienttype(e) == CLIENTTYPE_REAL)
156                 {
157                         msg_entity = e;
158                         if(g_race_qualifying)
159                         {
160                                 WriteByte(MSG_ONE, SVC_TEMPENTITY);
161                                 WriteByte(MSG_ONE, TE_CSQC_RACE);
162                                 WriteByte(MSG_ONE, RACE_NET_CHECKPOINT_HIT_QUALIFYING);
163                                 WriteByte(MSG_ONE, cp); // checkpoint the player now is at
164                                 WriteShort(MSG_ONE, t); // time to that intermediate
165                                 WriteShort(MSG_ONE, recordtime); // previously best time
166                                 WriteString(MSG_ONE, recordholder); // record holder
167                         }
168                 }
169         }
170         else // RACE! Not Qualifying
171         {
172                 float lself, lother, othtime;
173                 entity oth;
174                 oth = race_checkpoint_lastplayers[cp];
175                 if(oth)
176                 {
177                         lself = PlayerScore_Add(e, SP_RACE_LAPS, 0);
178                         lother = race_checkpoint_lastlaps[cp];
179                         othtime = race_checkpoint_lasttimes[cp];
180                 }
181                 else
182                         lself = lother = othtime = 0;
183
184                 if(clienttype(e) == CLIENTTYPE_REAL)
185                 {
186                         msg_entity = e;
187                         WriteByte(MSG_ONE, SVC_TEMPENTITY);
188                         WriteByte(MSG_ONE, TE_CSQC_RACE);
189                         WriteByte(MSG_ONE, RACE_NET_CHECKPOINT_HIT_RACE);
190                         WriteByte(MSG_ONE, cp); // checkpoint the player now is at
191                         if(e == oth)
192                         {
193                                 WriteShort(MSG_ONE, 0);
194                                 WriteByte(MSG_ONE, 0);
195                                 WriteString(MSG_ONE, "");
196                         }
197                         else
198                         {
199                                 WriteShort(MSG_ONE, floor(10 * (time - race_checkpoint_lasttimes[cp]) + 0.5));
200                                 WriteByte(MSG_ONE, lself - lother);
201                                 WriteString(MSG_ONE, oth.netname); // record holder
202                         }
203                 }
204
205                 race_checkpoint_lastplayers[cp] = e;
206                 race_checkpoint_lasttimes[cp] = time;
207                 race_checkpoint_lastlaps[cp] = lself;
208
209                 if(clienttype(oth) == CLIENTTYPE_REAL)
210                 {
211                         msg_entity = oth;
212                         WriteByte(MSG_ONE, SVC_TEMPENTITY);
213                         WriteByte(MSG_ONE, TE_CSQC_RACE);
214                         WriteByte(MSG_ONE, RACE_NET_CHECKPOINT_HIT_RACE_BY_OPPONENT);
215                         WriteByte(MSG_ONE, cp); // checkpoint the player now is at
216                         if(e == oth)
217                         {
218                                 WriteShort(MSG_ONE, 0);
219                                 WriteByte(MSG_ONE, 0);
220                                 WriteString(MSG_ONE, "");
221                         }
222                         else
223                         {
224                                 WriteShort(MSG_ONE, floor(10 * (time - othtime) + 0.5));
225                                 WriteByte(MSG_ONE, lother - lself);
226                                 WriteString(MSG_ONE, e.netname); // record holder
227                         }
228                 }
229         }
230 }
231
232 void race_ClearTime(entity e)
233 {
234         e.race_checkpoint = -1;
235         e.race_laptime = 0;
236
237         if(clienttype(e) != CLIENTTYPE_REAL)
238                 return;
239
240         msg_entity = e;
241         WriteByte(MSG_ONE, SVC_TEMPENTITY);
242         WriteByte(MSG_ONE, TE_CSQC_RACE);
243         WriteByte(MSG_ONE, RACE_NET_CHECKPOINT_CLEAR); // next
244 }
245
246 void checkpoint_touch()
247 {
248         if(other.classname != "player")
249                 return;
250
251         if(other.race_checkpoint == -1 || other.race_checkpoint == self.race_checkpoint)
252         {
253                 other.race_checkpoint = race_NextCheckpoint(self.race_checkpoint);
254
255                 race_SendTime(other, self.race_checkpoint, time - other.race_laptime, !!other.race_laptime);
256
257                 if(!self.race_checkpoint) // finish line
258                         other.race_laptime = time;
259
260                 if(g_race_qualifying)
261                         race_SendNextCheckpoint(other);
262         }
263         else if(other.race_checkpoint == race_NextCheckpoint(self.race_checkpoint))
264         {
265                 // ignored
266         }
267         else
268         {
269                 if(self.spawnflags & 4)
270                         Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
271         }
272 }
273
274 void checkpoint_use()
275 {
276         other = activator;
277         checkpoint_touch();
278 }
279
280 float race_waypointsprite_for_player(entity e)
281 {
282         if(e.race_checkpoint == -1)
283                 return self.modelindex;
284         else if(e.race_checkpoint == self.owner.race_checkpoint)
285                 return self.modelindex;
286         else
287                 return FALSE;
288 }
289
290 void spawnfunc_trigger_race_checkpoint()
291 {
292         vector o;
293         if(!g_race)
294         {
295                 remove(self);
296                 return;
297         }
298         InitTrigger();
299         self.use = checkpoint_use;
300         if not(self.spawnflags & 1)
301                 self.touch = checkpoint_touch;
302
303         o = (self.absmin + self.absmax) * 0.5;
304         tracebox(o, PL_MIN, PL_MAX, o - '0 0 1' * (o_z - self.absmin_z), MOVE_NORMAL, self);
305         self.nearestwaypoint = waypoint_spawn(trace_endpos, trace_endpos, WAYPOINTFLAG_GENERATED);
306         self.nearestwaypointtimeout = time + 1000000000;
307
308         if(!self.message)
309                 self.message = "went backwards";
310         
311         self.race_checkpoint = self.cnt;
312
313         if(self.race_checkpoint > race_highest_checkpoint)
314                 race_highest_checkpoint = self.race_checkpoint;
315
316         if(self.race_checkpoint)
317         {
318                 precache_model("models/sprites/race-checkpoint.sp2");
319                 WaypointSprite_SpawnFixed("race-checkpoint", o, self, sprite);
320         }
321         else
322         {
323                 precache_model("models/sprites/race-finish.sp2");
324                 WaypointSprite_SpawnFixed("race-finish", o, self, sprite);
325         }
326         self.sprite.waypointsprite_for_player = race_waypointsprite_for_player;
327 }
328
329 void race_PreparePlayer()
330 {
331         race_ClearTime(self);
332         self.race_place = 0;
333 }
334
335 void race_RetractPlayer()
336 {
337         if(!g_race)
338                 return;
339         self.race_checkpoint = race_PreviousCheckpoint(self.race_checkpoint);
340         if(self.race_checkpoint == 0)
341         {
342                 race_ClearTime(self);
343                 self.race_checkpoint = 0;
344         }
345 }
346
347 void race_PreSpawn()
348 {
349         if(!g_race)
350                 return;
351         if(self.killcount == -666 || g_race_qualifying)
352                 race_PreparePlayer();
353 }
354
355 void race_PostSpawn(entity spot)
356 {
357         if(!g_race)
358                 return;
359         if(self.killcount != -666 && !g_race_qualifying)
360         {
361                 if(spot.target == "")
362                         // let the player run without timing, if he did not spawn at a targetting spawnpoint
363                         race_PreparePlayer();
364                 else
365                         race_RetractPlayer();
366         }
367
368         if(spot.target != "" && self.race_checkpoint == -1)
369                 self.race_checkpoint = 0;
370
371         self.race_place = 0;
372 }
373
374 void race_PreSpawnObserver()
375 {
376         if(!g_race)
377                 return;
378         race_PreparePlayer();
379 }
380
381 void spawnfunc_info_player_race (void)
382 {
383         if(!g_race)
384         {
385                 remove(self);
386                 return;
387         }
388         ++race_spawns;
389         spawnfunc_info_player_deathmatch();
390
391         if(self.race_place > race_highest_place_spawn)
392                 race_highest_place_spawn = self.race_place;
393 }
394
395 void race_ClearRecords()
396 {
397         float i;
398         entity e;
399
400         for(i = 0; i < MAX_CHECKPOINTS; ++i)
401         {
402                 race_checkpoint_records[i] = 0;
403                 if(race_checkpoint_recordholders[i])
404                         strunzone(race_checkpoint_recordholders[i]);
405                 race_checkpoint_recordholders[i] = string_null;
406         }
407
408         FOR_EACH_CLIENT(e)
409                 race_ClearTime(e);
410 }
411
412 void race_ReadyRestart()
413 {
414         race_ClearRecords();
415
416         if(g_race_qualifying == 2)
417         {
418                 g_race_qualifying = 0;
419                 independent_players = 0;
420                 ScoreRules_race();
421         }
422
423         if(PlayerScore_Sort(race_place) <= race_highest_place_spawn)
424                 race_place_valid = 1;
425         else
426                 race_place_valid = 0;
427 }