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