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