Xonotic QuakeC
The free, fast arena FPS with crisp movement and a wide array of weapons
race.qc
Go to the documentation of this file.
1#include "race.qh"
2
11#include <common/playerstats.qh>
12#include <common/state.qh>
13#include <common/stats.qh>
19#include <server/bot/api.qh>
20#include <server/cheats.qh>
21#include <server/client.qh>
23#include <server/damage.qh>
24#include <server/gamelog.qh>
26#include <server/main.qh>
28#include <server/portals.qh>
29#include <server/scores.qh>
30#include <server/spawnpoints.qh>
32#include <server/world.qh>
33
34.string stored_netname; // TODO: store this information independently of race-based gametypes
35
36string uid2name(string myuid)
37{
38 string s = db_get(ServerProgsDB, strcat("/uid2name/", myuid));
39
40 // FIXME remove this later after 0.6 release
41 // convert old style broken records to correct style
42 if(s == "")
43 {
44 s = db_get(ServerProgsDB, strcat("uid2name", myuid));
45 if(s != "")
46 {
47 db_put(ServerProgsDB, strcat("/uid2name/", myuid), s);
48 db_remove(ServerProgsDB, strcat("uid2name", myuid));
49 }
50 }
51
52 if(s == "")
53 s = "^1Unregistered Player";
54 return s;
55}
56
57void write_recordmarker(entity pl, float tstart, float dt)
58{
59 GameLogEcho(strcat(":recordset:", ftos(pl.playerid), ":", ftos(dt)));
60 // also write a marker into demo files for demotc-race-record-extractor to find
61 stuffcmd(pl,
62 sprintf("//%s RECORD SET %s %f %f\n", strconv(2, 0, 0, GetGametype()),
63 TIME_ENCODED_TOSTRING(TIME_ENCODE(dt), false), tstart, dt));
64}
65
68
69float race_readTime(string map, float pos)
70{
71 return stof(db_get(ServerProgsDB, strcat(map, record_type, "time", ftos(pos))));
72}
73
74string race_readUID(string map, float pos)
75{
76 return db_get(ServerProgsDB, strcat(map, record_type, "crypto_idfp", ftos(pos)));
77}
78
79float race_readPos(string map, float t)
80{
81 for(int i = 1; i <= RANKINGS_CNT; ++i)
82 {
83 int mytime = race_readTime(map, i);
84 if(!mytime || mytime > t)
85 return i;
86 }
87
88 return 0; // pos is zero if unranked
89}
90
91void race_writeTime(string map, float t, string myuid)
92{
93 float newpos;
94 newpos = race_readPos(map, t);
95
96 float i, prevpos = 0;
97 for(i = 1; i <= RANKINGS_CNT; ++i)
98 {
99 if(race_readUID(map, i) == myuid)
100 prevpos = i;
101 }
102 if (prevpos)
103 {
104 // player improved their existing record, only have to iterate on ranks between new and old recs
105 for (i = prevpos; i > newpos; --i)
106 {
107 db_put(ServerProgsDB, strcat(map, record_type, "time", ftos(i)), ftos(race_readTime(map, i - 1)));
108 db_put(ServerProgsDB, strcat(map, record_type, "crypto_idfp", ftos(i)), race_readUID(map, i - 1));
109 }
110 }
111 else
112 {
113 // player has no ranked record yet
114 for (i = RANKINGS_CNT; i > newpos; --i)
115 {
116 float other_time = race_readTime(map, i - 1);
117 if (other_time) {
118 db_put(ServerProgsDB, strcat(map, record_type, "time", ftos(i)), ftos(other_time));
119 db_put(ServerProgsDB, strcat(map, record_type, "crypto_idfp", ftos(i)), race_readUID(map, i - 1));
120 }
121 }
122 }
123
124 // store new time itself
125 db_put(ServerProgsDB, strcat(map, record_type, "time", ftos(newpos)), ftos(t));
126 db_put(ServerProgsDB, strcat(map, record_type, "crypto_idfp", ftos(newpos)), myuid);
127}
128
129string race_readName(string map, float pos)
130{
131 return uid2name(db_get(ServerProgsDB, strcat(map, record_type, "crypto_idfp", ftos(pos))));
132}
133
135{
136 if(CS_CVAR(player).cvar_cl_allow_uidtracking == 1 && CS_CVAR(player).cvar_cl_allow_uid2name == 1)
137 {
138 if (!player.stored_netname)
139 player.stored_netname = strzone(uid2name(player.crypto_idfp));
140 if(player.stored_netname != player.netname)
141 {
142 db_put(ServerProgsDB, strcat("/uid2name/", player.crypto_idfp), player.netname);
143 strcpy(player.stored_netname, player.netname);
144 }
145 }
146}
147
148
149const float MAX_CHECKPOINTS = 255;
150
154.float race_checkpoint; // player: next checkpoint that has to be reached
156
157.entity sprite;
158
165
168
171
174
176{
178 return 0;
179 else
180 return f + 1;
181}
182
184{
185 if(f == -1)
186 return 0;
187 else if(f == 0)
189 else
190 return f - 1;
191}
192
193// encode as:
194// 0 = common start/finish
195// 254 = start
196// 255 = finish
198{
200 {
201 if(f == 0)
202 return 254; // start
203 else if(f == race_timed_checkpoint)
204 return 255; // finish
205 }
206 return f;
207}
208
209void race_SendNextCheckpoint(entity e, float spec) // qualifying only
210{
211 if(!e.race_laptime)
212 return;
213
214 int cp = e.race_checkpoint;
215 float recordtime;
216 float recordspeed;
217 float myrecordtime = e.race_checkpoint_record[cp];
218 float myrecordspeed = e.race_checkpoint_recordspeed[cp];
219 string recordholder;
221 recordtime = myrecordtime;
222 recordspeed = myrecordspeed;
223 recordholder = "";
224 } else {
225 recordtime = race_checkpoint_records[cp];
226 recordspeed = race_checkpoint_recordspeeds[cp];
227 recordholder = race_checkpoint_recordholders[cp];
228
229 if(recordholder == e.netname)
230 recordholder = "";
231 }
232
233 if(!IS_REAL_CLIENT(e))
234 return;
235
236 if(!spec)
237 msg_entity = e;
238 WRITESPECTATABLE_MSG_ONE(msg_entity, {
239 WriteHeader(MSG_ONE, TE_CSQC_RACE);
240 if(spec)
241 {
243 //WriteCoord(MSG_ONE, e.race_laptime - e.race_penalty_accumulator);
244 WriteCoord(MSG_ONE, time - e.race_movetime - e.race_penalty_accumulator);
245 }
246 else
248 WriteByte(MSG_ONE, race_CheckpointNetworkID(cp)); // checkpoint the player will be at next
249 WriteInt24_t(MSG_ONE, recordtime);
250 if(!spec)
251 WriteInt24_t(MSG_ONE, myrecordtime);
252 WriteString(MSG_ONE, recordholder);
253 // All speeds networked as floats to have decimal precision
254 WriteFloat(MSG_ONE, recordspeed);
255 if(!spec)
256 WriteFloat(MSG_ONE, myrecordspeed);
257 });
258}
259
260void race_send_recordtime(float msg)
261{
262 // send the server best time
263 WriteHeader(msg, TE_CSQC_RACE);
265 WriteInt24_t(msg, race_readTime(GetMapname(), 1));
266}
267
268void race_send_speedaward(float msg)
269{
270 // send the best speed of the round
271 WriteHeader(msg, TE_CSQC_RACE);
273 WriteInt24_t(msg, floor(speedaward_speed+0.5));
275}
276
278{
279 // send the best speed
280 WriteHeader(msg, TE_CSQC_RACE);
282 WriteInt24_t(msg, floor(speedaward_alltimebest+0.5));
284}
285
287{
288 WriteHeader(msg, TE_CSQC_RACE);
291 WriteByte(msg, m);
292}
293
294void race_SendRanking(float pos, float prevpos, float del, float msg)
295{
296 WriteHeader(msg, TE_CSQC_RACE);
298 WriteShort(msg, pos);
299 WriteShort(msg, prevpos);
300 WriteShort(msg, del);
302 WriteInt24_t(msg, race_readTime(GetMapname(), pos));
303}
304
306{
307 if (IS_OBSERVER(player))
308 return;
309
310 if(vdist(player.velocity - player.velocity_z * '0 0 1', >, speedaward_speed))
311 {
312 speedaward_speed = vlen(player.velocity - player.velocity_z * '0 0 1');
313 speedaward_holder = player.netname;
314 speedaward_uid = player.crypto_idfp;
316 }
318 {
322 {
329 }
330 }
331}
332
333void race_SendAll(entity player, bool only_rankings)
334{
335 if(!IS_REAL_CLIENT(player))
336 return;
337
338 msg_entity = player;
339 if (!only_rankings)
340 {
343
347 }
348
351 for (int i = 1; i <= m; ++i)
352 race_SendRanking(i, 0, 0, MSG_ONE);
353}
354
355void race_SendStatus(float id, entity e)
356{
357 if(!IS_REAL_CLIENT(e))
358 return;
359
360 float msg;
361 if (id == 0)
362 msg = MSG_ONE;
363 else
364 msg = MSG_ALL;
365 msg_entity = e;
366 WRITESPECTATABLE_MSG_ONE(msg_entity, {
367 WriteHeader(msg, TE_CSQC_RACE);
369 WriteShort(msg, id);
370 WriteString(msg, e.netname);
371 });
372}
373
374void race_setTime(string map, float t, string myuid, string mynetname, entity e, bool showmessage)
375{
376 // netname only used TEMPORARILY for printing
377 int newpos = race_readPos(map, t);
378
379 int player_prevpos = 0;
380 for(int i = 1; i <= RANKINGS_CNT; ++i)
381 {
382 if(race_readUID(map, i) == myuid)
383 player_prevpos = i;
384 }
385
386 float oldrec;
387 string oldrec_holder;
388 if (player_prevpos && (player_prevpos < newpos || !newpos))
389 {
390 oldrec = race_readTime(GetMapname(), player_prevpos);
391 race_SendStatus(0, e); // "fail"
392 if(showmessage)
393 Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_RACE_FAIL_RANKED, mynetname, player_prevpos, t, oldrec);
394 return;
395 }
396 else if (!newpos)
397 {
398 // no ranking, time worse than the worst ranked
400 race_SendStatus(0, e); // "fail"
401 if(showmessage)
402 Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_RACE_FAIL_UNRANKED, mynetname, RANKINGS_CNT, t, oldrec);
403 return;
404 }
405
406 // if we didn't hit a return yet, we have a new record!
407
408 // if the player does not have a UID we can unfortunately not store the record, as the rankings system relies on UIDs
409 if(myuid == "")
410 {
411 if(showmessage)
412 Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_RACE_NEW_MISSING_UID, mynetname, t);
413 return;
414 }
415
416 if(CS_CVAR(e).cvar_cl_allow_uidtracking != 1 || CS_CVAR(e).cvar_cl_allow_uid2name != 1
417 || uid2name(myuid) == "^1Unregistered Player")
418 {
419 if(showmessage)
420 Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_RACE_NEW_MISSING_NAME, mynetname, t);
421 return;
422 }
423
424 oldrec = race_readTime(GetMapname(), newpos);
425 oldrec_holder = race_readName(GetMapname(), newpos);
426
427 // store new ranking
428 race_writeTime(GetMapname(), t, myuid);
429
430 if (newpos == 1 && showmessage)
431 {
434 }
435
436 race_SendRanking(newpos, player_prevpos, 0, MSG_ALL);
438
439 if(newpos == player_prevpos)
440 {
441 if(showmessage)
442 Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_RACE_NEW_IMPROVED, mynetname, newpos, t, oldrec);
443 if(newpos == 1) { race_SendStatus(3, e); } // "new server record"
444 else { race_SendStatus(1, e); } // "new time"
445 }
446 else if(oldrec == 0)
447 {
448 if(showmessage)
449 Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_RACE_NEW_SET, mynetname, newpos, t);
450 if(newpos == 1) { race_SendStatus(3, e); } // "new server record"
451 else { race_SendStatus(2, e); } // "new rank"
452 }
453 else
454 {
455 if(showmessage)
456 Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_RACE_NEW_BROKEN, mynetname, oldrec_holder, newpos, t, oldrec);
457 if(newpos == 1) { race_SendStatus(3, e); } // "new server record"
458 else { race_SendStatus(2, e); } // "new rank"
459 }
460}
461
462void race_deleteTime(string map, float pos)
463{
464 for(int i = pos; i <= RANKINGS_CNT; ++i)
465 {
466 string therank = ftos(i);
467 if (i == RANKINGS_CNT)
468 {
469 db_remove(ServerProgsDB, strcat(map, record_type, "time", therank));
470 db_remove(ServerProgsDB, strcat(map, record_type, "crypto_idfp", therank));
471 }
472 else
473 {
474 db_put(ServerProgsDB, strcat(map, record_type, "time", therank), ftos(race_readTime(GetMapname(), i+1)));
475 db_put(ServerProgsDB, strcat(map, record_type, "crypto_idfp", therank), race_readUID(GetMapname(), i+1));
476 }
477 }
478
479 race_SendRanking(pos, 0, 1, MSG_ALL);
480 if(pos == 1)
482
484}
485
486void race_SendTime(entity e, float cp, float t, float tvalid)
487{
489 t += e.race_penalty_accumulator;
490
491 t = TIME_ENCODE(t); // make integer
492
493 if(tvalid)
494 if(cp == race_timed_checkpoint) // finish line
495 if (!CS(e).race_completed)
496 {
497 int s = GameRules_scoring_add(e, RACE_FASTEST, 0);
498 if(!s || t < s)
499 GameRules_scoring_add(e, RACE_FASTEST, t - s);
501 {
502 s = GameRules_scoring_add(e, RACE_TIME, 0);
503 int snew = TIME_ENCODE(time - game_starttime);
504 GameRules_scoring_add(e, RACE_TIME, snew - s);
505 int l = GameRules_scoring_add_team(e, RACE_LAPS, 1);
506
508 if(l >= autocvar_fraglimit)
510
512 {
513 CS(e).race_completed = 1;
515 if(e.bot_attack)
517 e.bot_attack = false;
518 Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_RACE_FINISHED, e.netname);
519 ClientData_Touch(e, true);
520 }
521 }
522 }
523
525 {
526 float recordtime;
527 float recordtimespeed;
528 string recordholder;
529
530 if(tvalid)
531 {
533 {
534 // return their own checkpoint time
535 recordtime = e.race_checkpoint_record[cp];
536 recordtimespeed = e.race_checkpoint_recordspeed[cp];
537 recordholder = "";
538 }
539 else
540 {
541 recordtime = race_checkpoint_records[cp];
542 recordtimespeed = race_checkpoint_recordspeeds[cp];
543 // make a tempstring copy, as we'll possibly strunzone it
544 recordholder = strcat(race_checkpoint_recordholders[cp]);
545
546 if(recordholder == e.netname)
547 recordholder = "";
548 }
549
550 if(t != 0)
551 {
552 if(cp == race_timed_checkpoint)
553 {
554 race_setTime(GetMapname(), t, e.crypto_idfp, e.netname, e, true);
555 MUTATOR_CALLHOOK(Race_FinalCheckpoint, e);
556 }
557
558 if(t < recordtime || recordtime == 0)
559 {
561 race_checkpoint_recordspeeds[cp] = vlen(vec2(e.velocity));
564 FOREACH_CLIENT(IS_PLAYER(it) && IS_REAL_CLIENT(it) && it.race_checkpoint == cp, { race_SendNextCheckpoint(it, 0); });
565 }
566
567 }
568 }
569 else
570 {
571 // dummies
572 t = 0;
573 recordtime = 0;
574 recordtimespeed = 0;
575 recordholder = "";
576 }
577
578 if(IS_REAL_CLIENT(e))
579 {
581 {
583 {
584 if(it == e || (IS_SPEC(it) && it.enemy == e))
585 {
586 msg_entity = it;
587 WriteHeader(MSG_ONE, TE_CSQC_RACE);
589 WriteByte(MSG_ONE, race_CheckpointNetworkID(cp)); // checkpoint the player now is at
590 WriteInt24_t(MSG_ONE, t); // time to that checkpoint
591 WriteInt24_t(MSG_ONE, recordtime); // previously best time
592 WriteInt24_t(MSG_ONE, ((tvalid) ? it.race_checkpoint_record[cp] : 0)); // previously best time
593 WriteString(MSG_ONE, recordholder); // record holder
594 // All speeds networked as floats to have decimal precision
595 WriteFloat(MSG_ONE, vlen(vec2(e.velocity))); // speed of cp hit
596 WriteFloat(MSG_ONE, recordtimespeed); // speed of previous best cp hit
597 WriteFloat(MSG_ONE, ((tvalid) ? it.race_checkpoint_recordspeed[cp] : 0)); // speed of my previous best cp hit
598 }
599 });
600 }
601 }
602 // check if new pb should be set for a checkpoint
603 if(tvalid)
604 {
605 float myrecordtime = e.race_checkpoint_record[cp];
606 if(t != 0)
607 if(t < myrecordtime || myrecordtime == 0)
608 {
609 e.race_checkpoint_record[cp] = t;
610 e.race_checkpoint_recordspeed[cp] = vlen(vec2(e.velocity));
611 }
612 }
613 }
614 else // RACE! Not Qualifying
615 {
616 float mylaps, lother, othtime;
618 if(oth)
619 {
620 mylaps = GameRules_scoring_add(e, RACE_LAPS, 0);
621 lother = race_checkpoint_lastlaps[cp];
622 othtime = race_checkpoint_lasttimes[cp];
623 }
624 else
625 mylaps = lother = othtime = 0;
626
627 if(IS_REAL_CLIENT(e))
628 {
629 msg_entity = e;
630 WRITESPECTATABLE_MSG_ONE(msg_entity, {
631 WriteHeader(MSG_ONE, TE_CSQC_RACE);
633 WriteByte(MSG_ONE, race_CheckpointNetworkID(cp)); // checkpoint the player now is at
634 if(e == oth)
635 {
636 WriteInt24_t(MSG_ONE, 0);
637 WriteByte(MSG_ONE, 0);
638 WriteByte(MSG_ONE, 0);
639 }
640 else
641 {
643 WriteByte(MSG_ONE, mylaps - lother);
644 WriteByte(MSG_ONE, etof(oth)); // record holder
645 }
646 });
647 }
648
651 race_checkpoint_lastlaps[cp] = mylaps;
652
653 if(IS_REAL_CLIENT(oth))
654 {
655 msg_entity = oth;
656 WRITESPECTATABLE_MSG_ONE(msg_entity, {
657 WriteHeader(MSG_ONE, TE_CSQC_RACE);
659 WriteByte(MSG_ONE, race_CheckpointNetworkID(cp)); // checkpoint the player now is at
660 if(e == oth)
661 {
662 WriteInt24_t(MSG_ONE, 0);
663 WriteByte(MSG_ONE, 0);
664 WriteByte(MSG_ONE, 0);
665 }
666 else
667 {
668 WriteInt24_t(MSG_ONE, TIME_ENCODE(time - othtime));
669 WriteByte(MSG_ONE, lother - mylaps);
670 WriteByte(MSG_ONE, etof(e) - 1); // record holder
671 }
672 });
673 }
674 }
675}
676
678{
679 e.race_checkpoint = 0;
680 e.race_laptime = 0;
681 e.race_movetime = e.race_movetime_frac = e.race_movetime_count = 0;
682 e.race_penalty_accumulator = 0;
683 e.race_lastpenalty = NULL;
684
685 if(!IS_REAL_CLIENT(e))
686 return;
687
688 msg_entity = e;
689 WRITESPECTATABLE_MSG_ONE(msg_entity, {
690 WriteHeader(MSG_ONE, TE_CSQC_RACE);
692 });
693}
694
696{
697 if(IS_VEHICLE(player) && player.owner)
698 player = player.owner;
699
700 if(player.personal && autocvar_g_allow_checkpoints)
701 return; // practice mode!
702
703 if(player.classname == "porto")
704 {
705 // do not allow portalling through checkpoints
706 trace_plane_normal = normalize(-1 * player.velocity);
707 W_Porto_Fail(player, 0);
708 return;
709 }
710
711 string oldmsg; // used twice
712
713 /*
714 * Trigger targets
715 */
716 if (!((this.spawnflags & 2) && (IS_PLAYER(player))))
717 {
718 oldmsg = this.message;
719 this.message = "";
720 SUB_UseTargets(this, player, player);
721 this.message = oldmsg;
722 }
723
724 if (!IS_PLAYER(player))
725 return;
726
727 /*
728 * Remove unauthorized equipment
729 */
730 Portal_ClearAll(player);
731
732 player.porto_forbidden = 2; // decreased by 1 each StartFrame
733
734 if(defrag_ents)
735 {
736 if(this.race_checkpoint == -2)
737 {
738 // duplicate targetname target_checkpoints get the same cp nr
739 IL_EACH(g_race_targets, it.classname == "target_checkpoint" && it.targetname == this.targetname, {
740 if(it.race_checkpoint >= 0)
741 this.race_checkpoint = it.race_checkpoint;
742 });
743 if(this.race_checkpoint == -2)
744 this.race_checkpoint = player.race_checkpoint;
745 }
746
747 int cp_amount = 0, largest_cp_id = 0;
748 IL_EACH(g_race_targets, it.classname == "target_checkpoint",
749 {
750 cp_amount += 1;
751 if(it.race_checkpoint > largest_cp_id) // update the finish id if someone hit a new checkpoint
752 {
753 if(!largest_cp_id)
754 {
755 IL_EACH(g_race_targets, it.classname == "target_checkpoint",
756 {
757 if(it.race_checkpoint == -2) // set defragcpexists to -1 so that the cp id file will be rewritten when someone finishes
758 defragcpexists = -1;
759 });
760 }
761
762 largest_cp_id = it.race_checkpoint;
763 IL_EACH(g_race_targets, it.classname == "target_stopTimer",
764 {
765 it.race_checkpoint = largest_cp_id + 1; // finish line
766 });
767 race_highest_checkpoint = largest_cp_id + 1;
768 race_timed_checkpoint = largest_cp_id + 1;
769 }
770 });
771
772 if(!cp_amount)
773 {
774 IL_EACH(g_race_targets, it.classname == "target_stopTimer",
775 {
776 it.race_checkpoint = 1;
777 });
780 }
781 }
782
783 if((player.race_checkpoint == -1 && this.race_checkpoint == 0) || (player.race_checkpoint == this.race_checkpoint))
784 {
785 if(this.race_penalty)
786 {
787 if(player.race_lastpenalty != this)
788 {
789 player.race_lastpenalty = this;
791 }
792 }
793
794 if(player.race_penalty)
795 return;
796
797 /*
798 * Trigger targets
799 */
800 if(this.spawnflags & 2)
801 {
802 oldmsg = this.message;
803 this.message = "";
804 SUB_UseTargets(this, player, player); // TODO: should we be using other for the trigger here?
805 this.message = oldmsg;
806 }
807
808 if(player.race_respawn_checkpoint != this.race_checkpoint || !player.race_started)
809 player.race_respawn_spotref = this; // this is not a spot but a CP, but spawnpoint selection will deal with that
810 player.race_respawn_checkpoint = this.race_checkpoint;
811 player.race_checkpoint = race_NextCheckpoint(this.race_checkpoint);
812 player.race_started = 1;
813
814 race_SendTime(player, this.race_checkpoint, player.race_movetime, boolean(player.race_laptime));
815
816 if(!this.race_checkpoint) // start line
817 {
818 player.race_laptime = time;
819 player.race_movetime = player.race_movetime_frac = player.race_movetime_count = 0;
820 player.race_penalty_accumulator = 0;
821 player.race_lastpenalty = NULL;
822 }
823
825 race_SendNextCheckpoint(player, 0);
826
827 if(defrag_ents && defragcpexists < 0 && this.classname == "target_stopTimer")
828 {
829 float fh;
830 defragcpexists = fh = fopen(strcat("maps/", GetMapname(), ".defragcp"), FILE_WRITE);
831 if(fh >= 0)
832 {
833 IL_EACH(g_race_targets, it.classname == "target_checkpoint",
834 {
835 fputs(fh, strcat(it.targetname, " ", ftos(it.race_checkpoint), "\n"));
836 });
837 }
838 fclose(fh);
839 }
840 }
841 else if(player.race_checkpoint == race_NextCheckpoint(this.race_checkpoint))
842 {
843 // ignored
844 }
845 else
846 {
847 if(this.spawnflags & 4)
848 Damage (player, this, this, 10000, DEATH_HURTTRIGGER.m_id, DMG_NOWEP, player.origin, '0 0 0');
849 }
850}
851
857
858void checkpoint_use(entity this, entity actor, entity trigger)
859{
860 if(trigger.classname == "info_player_deathmatch") // a spawn, a spawn
861 return;
862
863 checkpoint_passed(this, actor);
864}
865
867{
868 entity own = this.owner;
869 if(this.realowner)
870 own = this.realowner; // target support
871
872 if(view.race_checkpoint == -1 || own.race_checkpoint == -2)
873 return true;
874 else if(view.race_checkpoint == own.race_checkpoint)
875 return true;
876 else
877 return false;
878}
879
880void defrag_waypointsprites(entity targeted, entity checkpoint, int recursionlevel)
881{
882 // bones_was_here: spawn a waypoint for every entity with a bmodel
883 // that directly or indirectly targets this checkpoint
884 // (anything a player could touch or shoot to activate this cp)
885
886 // spam a few warnings so that larger link chain loops can be found
887 if (recursionlevel > 400)
888 {
889 LOG_WARNF("df cp wp loop: ^4\"target\" ^3\"%s\"^7, ^4\"targetname\" ^3\"%s\"^7",
890 targeted.classname,
891 targeted.targetname);
892 }
893 // avoid a stack overflow
894 if (recursionlevel > 420)
895 {
896 LOG_WARNF("Aborted creating defrag checkpoint waypointsprites for ^2%s^7 due to a loop",
897 checkpoint.classname);
898 return;
899 }
900
901 entity s = WP_RaceCheckpoint;
902 if (checkpoint.classname == "target_startTimer")
903 s = WP_RaceStart;
904 else if (checkpoint.classname == "target_stopTimer")
905 s = WP_RaceFinish;
906
907 for (entity t = findchain(target, targeted.targetname); t; t = t.chain)
908 {
909 if (t.modelindex)
910 {
911 WaypointSprite_SpawnFixed(s, (t.absmin + t.absmax) * 0.5, t, sprite, RADARICON_NONE);
912 t.sprite.realowner = checkpoint;
913 t.sprite.waypointsprite_visible_for_player = race_waypointsprite_visible_for_player;
914 }
915
916 if (t.targetname && t != checkpoint)
917 defrag_waypointsprites(t, checkpoint, ++recursionlevel);
918 }
919}
920
922{
923 static bool have_verified;
924 if (have_verified) return;
925 have_verified = true;
926
927 int qual = g_race_qualifying;
928
929 int pl_race_checkpoint = 0;
930 int pl_race_place = 0;
931
932 if (g_race) {
933 for (int i = 0; i <= race_highest_checkpoint; ++i) {
934 pl_race_checkpoint = race_NextCheckpoint(i);
935
936 // race only (middle of the race)
938 pl_race_place = 0;
939 if (!Spawn_FilterOutBadSpots(this, findchain(classname, "info_player_deathmatch"), 0, false, true)) {
940 error(strcat("Checkpoint ", ftos(i), " misses a spawnpoint with race_place==", ftos(pl_race_place), " (used for respawning in race) - bailing out"));
941 }
942
943 if (i == 0) {
944 // qualifying only
946 pl_race_place = race_lowest_place_spawn;
947 if (!Spawn_FilterOutBadSpots(this, findchain(classname, "info_player_deathmatch"), 0, false, true)) {
948 error(strcat("Checkpoint ", ftos(i), " misses a spawnpoint with race_place==", ftos(pl_race_place), " (used for qualifying) - bailing out"));
949 }
950
951 // race only (initial spawn)
953 for (int p = 1; p <= race_highest_place_spawn; ++p) {
954 pl_race_place = p;
955 if (!Spawn_FilterOutBadSpots(this, findchain(classname, "info_player_deathmatch"), 0, false, true)) {
956 error(strcat("Checkpoint ", ftos(i), " misses a spawnpoint with race_place==", ftos(pl_race_place), " (used for initially spawning in race) - bailing out"));
957 }
958 }
959 }
960 }
961 } else if (!defrag_ents) {
962 // qualifying only
963 pl_race_checkpoint = race_NextCheckpoint(0);
965 pl_race_place = race_lowest_place_spawn;
966 if (!Spawn_FilterOutBadSpots(this, findchain(classname, "info_player_deathmatch"), 0, false, true)) {
967 error(strcat("Checkpoint 0 misses a spawnpoint with race_place==", ftos(pl_race_place), " (used for qualifying) - bailing out"));
968 }
969 } else {
970 pl_race_checkpoint = race_NextCheckpoint(0);
972 pl_race_place = 0; // there's only one spawn on defrag maps
973
974 // check if a defragcp file already exists, then read it and apply the checkpoint order
975 float fh;
976 float len;
977 bool broken_defragcp = false;
978 string l;
979
980 defragcpexists = fh = fopen(strcat("maps/", GetMapname(), ".defragcp"), FILE_READ);
981 if (fh >= 0) {
982 while ((l = fgets(fh))) {
983 len = tokenize_console(l);
984 if (len != 2) {
985 broken_defragcp = true;
986 break;
987 }
988 for (entity cp = NULL; (cp = find(cp, classname, "target_checkpoint"));) {
989 if (argv(0) == cp.targetname) {
990 if(cp.race_checkpoint != -2 && cp.race_checkpoint != stof(argv(1))) {
991 broken_defragcp = true; // cp had been previously set with diff order nr defragcp file is broken
992 break;
993 }
994 cp.race_checkpoint = stof(argv(1));
995 }
996 }
997 if(broken_defragcp) break; // no point to keep going we'll rebuild the whole order
998 }
999 fclose(fh);
1000 if(broken_defragcp) {
1001 // something's wrong in the defrag cp file, set defragcpexists to -1 so that it will be rewritten when someone finishes
1002 // also clear any set cp order to make sure map is completable
1003 defragcpexists = -1;
1004 for (entity cp = NULL; (cp = find(cp, classname, "target_checkpoint"));) {
1005 cp.race_checkpoint = -2;
1006 }
1007 }
1008 }
1009 }
1010
1011 g_race_qualifying = qual;
1012
1014 if (defrag_ents) {
1015 IL_EACH(g_race_targets, it.classname == "target_checkpoint" || it.classname == "target_startTimer" || it.classname == "target_stopTimer",
1016 {
1017 defrag_waypointsprites(it, it, 0);
1018
1019 if(it.classname == "target_checkpoint") {
1020 if(it.race_checkpoint == -2)
1021 defragcpexists = -1; // something's wrong with the defrag cp file or it has not been written yet, set defragcpexists to -1 so that it will be rewritten when someone finishes
1022 }
1023 });
1024 if (defragcpexists != -1) {
1025 float largest_cp_id = 0;
1026 for (entity cp = NULL; (cp = find(cp, classname, "target_checkpoint"));) {
1027 if (cp.race_checkpoint > largest_cp_id) {
1028 largest_cp_id = cp.race_checkpoint;
1029 }
1030 }
1031 for (entity cp = NULL; (cp = find(cp, classname, "target_stopTimer"));) {
1032 cp.race_checkpoint = largest_cp_id + 1; // finish line
1033 }
1034 race_highest_checkpoint = largest_cp_id + 1;
1035 race_timed_checkpoint = largest_cp_id + 1;
1036 } else {
1037 for (entity cp = NULL; (cp = find(cp, classname, "target_stopTimer"));) {
1038 cp.race_checkpoint = 255; // finish line
1039 }
1042 }
1043 } else {
1044 IL_EACH(g_racecheckpoints, it.sprite,
1045 {
1046 if (it.race_checkpoint == 0) {
1047 WaypointSprite_UpdateSprites(it.sprite, WP_RaceStart, WP_Null, WP_Null);
1048 } else if (it.race_checkpoint == race_timed_checkpoint) {
1049 WaypointSprite_UpdateSprites(it.sprite, WP_RaceFinish, WP_Null, WP_Null);
1050 }
1051 });
1052 }
1053 }
1054
1055 if (defrag_ents) { /* The following hack shall be removed when per-player trigger_multiple.wait is implemented for cts */
1056 for (entity trigger = NULL; (trigger = find(trigger, classname, "trigger_multiple")); ) {
1057 for (entity targ = NULL; (targ = find(targ, targetname, trigger.target)); ) {
1058 if (targ.classname == "target_checkpoint" || targ.classname == "target_startTimer" || targ.classname == "target_stopTimer") {
1059 trigger.wait = 0;
1060 trigger.delay = 0;
1061 targ.wait = 0;
1062 targ.delay = 0;
1063
1064 // These just make the game crash on some maps with oddly shaped triggers.
1065 // (on the other hand they used to fix the case when two players ran through a checkpoint at once,
1066 // and often one of them just passed through without being registered. Hope it's fixed in a better way now.
1067 // (happened on item triggers too)
1068 //
1069 //targ.wait = -2;
1070 //targ.delay = 0;
1071
1072 //setsize(targ, trigger.mins, trigger.maxs);
1073 //setorigin(targ, trigger.origin);
1074 //remove(trigger);
1075 }
1076 }
1077 }
1078 }
1079}
1080
1082{
1084 {
1085 // spawn at first
1086 if(this.race_checkpoint != 0)
1087 return '-1 0 0';
1088 if(spot.race_place != race_lowest_place_spawn)
1089 return '-1 0 0';
1090 }
1091 else
1092 {
1093 if(this.race_checkpoint != player.race_respawn_checkpoint)
1094 return '-1 0 0';
1095 // try reusing the previous spawn
1096 if(this == player.race_respawn_spotref || spot == player.race_respawn_spotref)
1097 current.x += SPAWN_PRIO_RACE_PREVIOUS_SPAWN;
1098 if(this.race_checkpoint == 0)
1099 {
1100 int pl = player.race_place;
1102 pl = 0;
1103 if(pl == 0 && !player.race_started)
1104 pl = race_highest_place_spawn; // use last place if they have not even touched finish yet
1105 if(spot.race_place != pl)
1106 return '-1 0 0';
1107 }
1108 }
1109 return current;
1110}
1111
1112spawnfunc(trigger_race_checkpoint)
1113{
1114 vector o;
1115 if(!g_race && !g_cts) { delete(this); return; }
1116
1118
1119 this.use = checkpoint_use;
1120 if (!(this.spawnflags & 1))
1122
1123 o = (this.absmin + this.absmax) * 0.5;
1124 tracebox(o, PL_MIN_CONST, PL_MAX_CONST, o - '0 0 1' * (o.z - this.absmin.z), MOVE_NORMAL, this);
1126 this.nearestwaypointtimeout = -1;
1127
1128 if(this.message == "")
1129 this.message = "went backwards";
1130 if (this.message2 == "")
1131 this.message2 = "was pushed backwards by";
1132 if (this.race_penalty_reason == "")
1133 this.race_penalty_reason = "missing a checkpoint";
1134
1135 this.race_checkpoint = this.cnt;
1136
1138 {
1140 if(this.spawnflags & 8)
1142 else
1144 }
1145
1146 if(!this.race_penalty)
1147 {
1148 if(this.race_checkpoint)
1149 WaypointSprite_SpawnFixed(WP_RaceCheckpoint, o, this, sprite, RADARICON_NONE);
1150 else
1151 WaypointSprite_SpawnFixed(WP_RaceStartFinish, o, this, sprite, RADARICON_NONE);
1152 }
1153
1154 this.sprite.waypointsprite_visible_for_player = race_waypointsprite_visible_for_player;
1156
1157 if (!g_racecheckpoints)
1160
1161 // trigger_race_checkpoint_verify checks this list too
1162 if (!g_race_targets)
1164
1166}
1167
1169{
1170 if(!g_race && !g_cts) { delete(this); return; }
1171 defrag_ents = 1;
1172
1173 // if this is targeted, then it probably isn't a trigger
1174 bool is_trigger = this.targetname == "";
1175
1176 if(is_trigger)
1178
1179 this.use = checkpoint_use;
1180 if (is_trigger && !(this.spawnflags & 1))
1182
1183 vector org = this.origin;
1184
1185 // bots should only pathfind to this if it is a valid touchable trigger
1186 if(is_trigger)
1187 {
1188 org = (this.absmin + this.absmax) * 0.5;
1189 tracebox(org, PL_MIN_CONST, PL_MAX_CONST, org - '0 0 1' * (org.z - this.absmin.z), MOVE_NORMAL, this);
1191 this.nearestwaypointtimeout = -1;
1192 }
1193
1194 if(this.message == "")
1195 this.message = "went backwards";
1196 if (this.message2 == "")
1197 this.message2 = "was pushed backwards by";
1198 if (this.race_penalty_reason == "")
1199 this.race_penalty_reason = "missing a checkpoint";
1200
1201 if(this.classname == "target_startTimer")
1202 this.race_checkpoint = 0;
1203 else
1204 this.race_checkpoint = -2;
1205
1207
1208 if (!g_race_targets)
1210 IL_PUSH(g_race_targets, this);
1211
1212 // trigger_race_checkpoint_verify checks this list too
1213 if (!g_racecheckpoints)
1215
1217}
1218
1219spawnfunc(target_checkpoint)
1220{
1221 // xonotic defrag entity
1223}
1224
1225// compatibility entity names
1226spawnfunc(target_startTimer) { target_checkpoint_setup(this); }
1227spawnfunc(target_stopTimer) { target_checkpoint_setup(this); }
1228
1230{
1232 {
1233 CS(p).race_completed = 1;
1235 Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_RACE_ABANDONED, p.netname);
1236 ClientData_Touch(p, true);
1237 }
1238}
1239
1241{
1242 race_completing = 1;
1244}
1245
1247{
1248 race_ClearTime(this);
1249 this.race_place = 0;
1250 this.race_started = 0;
1251 this.race_respawn_checkpoint = 0;
1253}
1254
1256{
1257 if(!g_race && !g_cts)
1258 return;
1260 race_ClearTime(this);
1262}
1263
1264spawnfunc(info_player_race)
1265{
1266 if(!g_race && !g_cts) { delete(this); return; }
1267 ++race_spawns;
1268 spawnfunc_info_player_deathmatch(this);
1269
1274}
1275
1277{
1278 for(int i = 0; i < MAX_CHECKPOINTS; ++i)
1279 {
1280 player.race_checkpoint_record[i] = 0;
1281 player.race_checkpoint_recordspeed[i] = 0;
1282 }
1283}
1284
1286{
1287 for(int j = 0; j < MAX_CHECKPOINTS; ++j)
1288 {
1292 }
1293
1294 FOREACH_CLIENT(true, {
1295 float p = it.race_place;
1297 it.race_place = p;
1298 });
1299}
1300
1301void race_ImposePenaltyTime(entity pl, float penalty, string reason)
1302{
1304 {
1305 pl.race_penalty_accumulator += penalty;
1306 if(IS_REAL_CLIENT(pl))
1307 {
1308 msg_entity = pl;
1309 WRITESPECTATABLE_MSG_ONE(msg_entity, {
1310 WriteHeader(MSG_ONE, TE_CSQC_RACE);
1312 WriteShort(MSG_ONE, TIME_ENCODE(penalty));
1313 WriteString(MSG_ONE, reason);
1314 });
1315 }
1316 }
1317 else
1318 {
1319 pl.race_penalty = time + penalty;
1320 if(IS_REAL_CLIENT(pl))
1321 {
1322 msg_entity = pl;
1323 WRITESPECTATABLE_MSG_ONE(msg_entity, {
1324 WriteHeader(MSG_ONE, TE_CSQC_RACE);
1326 WriteShort(MSG_ONE, TIME_ENCODE(penalty));
1327 WriteString(MSG_ONE, reason);
1328 });
1329 }
1330 }
1331}
1332
1334{
1336 if(toucher.race_lastpenalty != this)
1337 {
1338 toucher.race_lastpenalty = this;
1340 }
1341}
1342
1343void penalty_use(entity this, entity actor, entity trigger)
1344{
1346}
1347
1348spawnfunc(trigger_race_penalty)
1349{
1350 // TODO: find out why this wasnt done:
1351 //if(!g_cts && !g_race) { remove(this); return; }
1352
1354
1355 this.use = penalty_use;
1356 if (!(this.spawnflags & 1))
1357 settouch(this, penalty_touch);
1358
1359 if (this.race_penalty_reason == "")
1360 this.race_penalty_reason = "missing a checkpoint";
1361 if (!this.race_penalty)
1362 this.race_penalty = 5;
1363}
1364
1366{
1367 // interesting metrics (idea by KrimZon) to maybe sort players in the
1368 // scoreboard, immediately updates when overtaking
1369 //
1370 // requires the track to be built so you never get farther away from the
1371 // next checkpoint, though, and current Xonotic race maps are not built that
1372 // way
1373 //
1374 // also, this code is slow and would need optimization (i.e. "next CP"
1375 // links on CP entities)
1376
1377 float l;
1378 l = GameRules_scoring_add(e, RACE_LAPS, 0);
1379 if(CS(e).race_completed)
1380 return l; // not fractional
1381
1382 vector o0, o1;
1383 float bestfraction, fraction;
1384 entity lastcp;
1385 float nextcpindex, lastcpindex;
1386
1387 nextcpindex = max(e.race_checkpoint, 0);
1388 lastcpindex = e.race_respawn_checkpoint;
1389 lastcp = e.race_respawn_spotref;
1390
1391 if(nextcpindex == lastcpindex)
1392 return l; // finish
1393
1394 bestfraction = 1;
1396 {
1397 if(it.race_checkpoint != lastcpindex)
1398 continue;
1399 if(lastcp)
1400 if(it != lastcp)
1401 continue;
1402 o0 = (it.absmin + it.absmax) * 0.5;
1404 {
1405 if(it.race_checkpoint != nextcpindex)
1406 continue;
1407 o1 = (it.absmin + it.absmax) * 0.5;
1408 if(o0 == o1)
1409 continue;
1410 fraction = bound(0.0001, vlen(e.origin - o1) / vlen(o0 - o1), 1);
1411 if(fraction < bestfraction)
1412 bestfraction = fraction;
1413 });
1414 });
1415
1416 // we are at CP "nextcpindex - bestfraction"
1417 // race_timed_checkpoint == 4: then nextcp==4 means 0.9999x, nextcp==0 means 0.0000x
1418 // race_timed_checkpoint == 0: then nextcp==0 means 0.9999x
1419 float c, nc;
1420 nc = race_highest_checkpoint + 1;
1421 c = ((nextcpindex - race_timed_checkpoint + nc + nc - 1) % nc) + 1 - bestfraction;
1422
1423 return l + c / nc;
1424}
float nearestwaypointtimeout
Definition api.qh:53
IntrusiveList g_bot_targets
Definition api.qh:149
void waypoint_spawnforitem_force(entity e, vector org)
#define MUTATOR_CALLHOOK(id,...)
Definition base.qh:143
var entity(vector mins, vector maxs,.entity tofield) findbox_tofield_OrFallback
limitations: NULL cannot be present elements can only be present once a maximum of IL_MAX lists can e...
float cnt
Definition powerups.qc:24
string message
Definition powerups.qc:19
entity owner
Definition main.qh:87
#define g_race
Definition race.qh:48
int spawnflags
Definition ammo.qh:15
float race_penalty
Definition player.qh:59
#define IS_DEAD(s)
Definition player.qh:245
#define IS_PLAYER(s)
Definition player.qh:243
#define autocvar_fraglimit
Definition stats.qh:90
float game_starttime
Definition stats.qh:82
string rankings_reply
Definition util.qh:161
#define TIME_ENCODE(t)
Definition util.qh:100
#define TIME_ENCODED_TOSTRING(n, compact)
Definition util.qh:96
#define TIME_DECODE(n)
Definition util.qh:101
const vector PL_MIN_CONST
Definition constants.qh:56
const int RANKINGS_CNT
Definition constants.qh:31
const int INITPRIO_FINDTARGET
Definition constants.qh:96
const vector PL_MAX_CONST
Definition constants.qh:55
string classname
const float MOVE_NORMAL
const float FILE_READ
const float FILE_WRITE
float time
vector trace_endpos
vector absmax
vector origin
vector absmin
vector trace_plane_normal
#define use
#define g_cts
Definition cts.qh:36
void Damage(entity targ, entity inflictor, entity attacker, float damage, int deathtype,.entity weaponentity, vector hitloc, vector force)
Definition damage.qc:503
#define DMG_NOWEP
Definition damage.qh:104
#define tokenize_console
void GameLogEcho(string s)
Definition gamelog.qc:15
string getrankings()
Definition getreplies.qc:46
string GetMapname()
string GetGametype()
bool intermission_running
ERASEABLE void IL_REMOVE(IntrusiveList this, entity it)
Remove any element, anywhere in the list.
ERASEABLE entity IL_PUSH(IntrusiveList this, entity it)
Push to tail.
#define IL_NEW()
#define IL_EACH(this, cond, body)
void SUB_UseTargets(entity this, entity actor, entity trigger)
Definition triggers.qc:344
#define WriteHeader(to, id)
Definition net.qh:221
#define EXACTTRIGGER_TOUCH(e, t)
Definition common.qh:115
#define EXACTTRIGGER_INIT
Definition common.qh:116
#define LOG_WARNF(...)
Definition log.qh:62
#define db_remove(db, key)
Definition map.qh:98
ERASEABLE string db_get(int db, string key)
Definition map.qh:91
ERASEABLE void db_put(int db, string key, string value)
Definition map.qh:101
string fgets(float fhandle)
void fclose(float fhandle)
float MSG_ONE
Definition menudefs.qc:56
float stof(string val,...)
float bound(float min, float value, float max)
void WriteString(string data, float dest, float desto)
float fopen(string filename, float mode)
entity find(entity start,.string field, string match)
float vlen(vector v)
void WriteShort(float data, float dest, float desto)
void WriteCoord(float data, float dest, float desto)
float min(float f,...)
vector normalize(vector v)
string ftos(float f)
void WriteByte(float data, float dest, float desto)
float floor(float f)
string strzone(string s)
string argv(float n)
float MSG_ALL
Definition menudefs.qc:57
float max(float f,...)
#define etof(e)
Definition misc.qh:25
const int RACE_NET_CHECKPOINT_HIT_QUALIFYING
Definition net_linked.qh:11
const int RACE_NET_SERVER_RECORD
Definition net_linked.qh:19
const int RACE_NET_CHECKPOINT_HIT_RACE_BY_OPPONENT
Definition net_linked.qh:15
const int RACE_NET_SPEED_AWARD
Definition net_linked.qh:20
const int RACE_NET_CHECKPOINT_HIT_RACE
Definition net_linked.qh:14
const int RACE_NET_PENALTY_RACE
Definition net_linked.qh:17
const int RACE_NET_SERVER_STATUS
Definition net_linked.qh:23
const int RACE_NET_PENALTY_QUALIFYING
Definition net_linked.qh:18
const int RACE_NET_SPEED_AWARD_BEST
Definition net_linked.qh:21
const int RACE_NET_CHECKPOINT_NEXT_QUALIFYING
Definition net_linked.qh:13
const int RACE_NET_RANKINGS_CNT
Definition net_linked.qh:26
const int RACE_NET_CHECKPOINT_NEXT_SPEC_QUALIFYING
Definition net_linked.qh:16
const int RACE_NET_CHECKPOINT_CLEAR
Definition net_linked.qh:12
const int RACE_NET_SERVER_RANKINGS
Definition net_linked.qh:22
strcat(_("^F4Countdown stopped!"), "\n^BG", _("Teams are too unbalanced."))
void Send_Notification(NOTIF broadcast, entity client, MSG net_type, Notification net_name,...count)
Definition all.qc:1573
void Portal_ClearAll(entity own)
Definition portals.qc:588
void W_Porto_Fail(entity this, float failhard)
Definition porto.qc:108
#define NULL
Definition post.qh:14
#define error
Definition pre.qh:6
entity msg_entity
Definition progsdefs.qc:63
#define stuffcmd(cl,...)
Definition progsdefs.qh:23
float race_checkpoint
Definition racetimer.qh:8
vector
Definition self.qh:92
vector org
Definition self.qh:92
entity entity toucher
Definition self.qh:72
#define settouch(e, f)
Definition self.qh:73
void ClientData_Touch(entity e, bool to_spectators_too)
Definition client.qc:185
#define MAKE_INDEPENDENT_PLAYER(e)
Definition client.qh:313
float race_checkpoint_record[MAX_CHECKPOINTS]
Definition race.qc:166
float race_CheckpointNetworkID(float f)
Definition race.qc:197
void race_SendRanking(float pos, float prevpos, float del, float msg)
Definition race.qc:294
float race_readTime(string map, float pos)
Definition race.qc:69
float race_PreviousCheckpoint(float f)
Definition race.qc:183
float race_highest_checkpoint
Definition race.qc:169
entity race_lastpenalty
Definition race.qc:155
void checkpoint_passed(entity this, entity player)
Definition race.qc:695
string stored_netname
Definition race.qc:34
void race_SendAll(entity player, bool only_rankings)
Definition race.qc:333
float race_checkpoint_recordspeeds[MAX_CHECKPOINTS]
Definition race.qc:160
void race_PreparePlayer(entity this)
Definition race.qc:1246
void race_ClearRecords()
Definition race.qc:1285
float race_checkpoint_lastlaps[MAX_CHECKPOINTS]
Definition race.qc:163
string race_penalty_reason
Definition race.qc:153
void race_SendNextCheckpoint(entity e, float spec)
Definition race.qc:209
void write_recordmarker(entity pl, float tstart, float dt)
Definition race.qc:57
void race_SpeedAwardFrame(entity player)
Definition race.qc:305
float race_checkpoint_recordspeed[MAX_CHECKPOINTS]
Definition race.qc:167
const float MAX_CHECKPOINTS
Definition race.qc:149
float race_timed_checkpoint
Definition race.qc:170
float race_readPos(string map, float t)
Definition race.qc:79
float defragcpexists
Definition race.qc:173
void race_SendTime(entity e, float cp, float t, float tvalid)
Definition race.qc:486
void race_ClearTime(entity e)
Definition race.qc:677
float defrag_ents
Definition race.qc:172
vector trigger_race_checkpoint_spawn_evalfunc(entity this, entity player, entity spot, vector current)
Definition race.qc:1081
string race_readName(string map, float pos)
Definition race.qc:129
bool race_waypointsprite_visible_for_player(entity this, entity player, entity view)
Definition race.qc:866
float race_NextCheckpoint(float f)
Definition race.qc:175
void checkpoint_use(entity this, entity actor, entity trigger)
Definition race.qc:858
string uid2name(string myuid)
Definition race.qc:36
void race_StartCompleting()
Definition race.qc:1240
float race_GetFractionalLapCount(entity e)
Definition race.qc:1365
void race_checkAndWriteName(entity player)
Definition race.qc:134
IntrusiveList g_racecheckpoints
Definition race.qc:67
void race_send_speedaward_alltimebest(float msg)
Definition race.qc:277
void race_send_speedaward(float msg)
Definition race.qc:268
void race_RetractPlayer(entity this)
Definition race.qc:1255
IntrusiveList g_race_targets
Definition race.qc:66
entity race_checkpoint_lastplayers[MAX_CHECKPOINTS]
Definition race.qc:164
void checkpoint_touch(entity this, entity toucher)
Definition race.qc:852
float race_checkpoint_records[MAX_CHECKPOINTS]
Definition race.qc:159
void race_setTime(string map, float t, string myuid, string mynetname, entity e, bool showmessage)
Definition race.qc:374
void race_SendStatus(float id, entity e)
Definition race.qc:355
void race_send_recordtime(float msg)
Definition race.qc:260
void defrag_waypointsprites(entity targeted, entity checkpoint, int recursionlevel)
Definition race.qc:880
string race_checkpoint_recordholders[MAX_CHECKPOINTS]
Definition race.qc:161
string race_readUID(string map, float pos)
Definition race.qc:74
void race_AbandonRaceCheck(entity p)
Definition race.qc:1229
void penalty_use(entity this, entity actor, entity trigger)
Definition race.qc:1343
float race_penalty_accumulator
Definition race.qc:152
void race_writeTime(string map, float t, string myuid)
Definition race.qc:91
void race_send_rankings_cnt(float msg)
Definition race.qc:286
void target_checkpoint_setup(entity this)
Definition race.qc:1168
void race_ClearPlayerRecords(entity player)
Definition race.qc:1276
void race_deleteTime(string map, float pos)
Definition race.qc:462
void trigger_race_checkpoint_verify(entity this)
Definition race.qc:921
float race_checkpoint_lasttimes[MAX_CHECKPOINTS]
Definition race.qc:162
void race_ImposePenaltyTime(entity pl, float penalty, string reason)
Definition race.qc:1301
void penalty_touch(entity this, entity toucher)
Definition race.qc:1333
bool autocvar_g_allow_checkpoints
Definition race.qh:3
float race_spawns
Definition race.qh:18
bool autocvar_g_race_cptimes_onlyself
Definition race.qh:11
float speedaward_alltimebest
Definition race.qh:63
float race_lowest_place_spawn
Definition race.qh:20
float speedaward_speed
Definition race.qh:59
entity race_respawn_spotref
Definition race.qh:35
float speedaward_lastupdate
Definition race.qh:16
float race_place
Definition race.qh:24
float race_respawn_checkpoint
Definition race.qh:34
string speedaward_alltimebest_uid
Definition race.qh:65
float race_highest_place_spawn
Definition race.qh:19
float race_started
Definition race.qh:25
float race_completed
Definition race.qh:26
int g_race_qualifying
Definition race.qh:13
int autocvar_g_cts_send_rankings_cnt
Definition race.qh:10
string speedaward_uid
Definition race.qh:61
float race_completing
Definition race.qh:28
float speedaward_lastsent
Definition race.qh:15
string speedaward_holder
Definition race.qh:60
string speedaward_alltimebest_holder
Definition race.qh:64
entity Spawn_FilterOutBadSpots(entity this, entity firstspot, float mindist, float teamcheck, bool targetcheck)
const int SPAWN_PRIO_RACE_PREVIOUS_SPAWN
spawn_evalfunc_t spawn_evalfunc
#define spawnfunc(id)
Definition spawnfunc.qh:96
#define CS_CVAR(this)
Definition state.qh:51
ClientState CS(Client this)
Definition state.qh:47
#define strfree(this)
Definition string.qh:59
#define strcpy(this, s)
Definition string.qh:52
entity sprite
Definition sv_assault.qc:11
if(frag_attacker.flagcarried)
Definition sv_ctf.qc:2325
#define GameRules_scoring_add(client, fld, value)
Definition sv_rules.qh:85
#define GameRules_scoring_add_team(client, fld, value)
Definition sv_rules.qh:89
entity realowner
string message2
Definition triggers.qh:19
string targetname
Definition triggers.qh:56
string target
Definition triggers.qh:55
#define IS_OBSERVER(v)
Definition utils.qh:11
#define IS_SPEC(v)
Definition utils.qh:10
#define IS_REAL_CLIENT(v)
Definition utils.qh:17
#define FOREACH_CLIENT(cond, body)
Definition utils.qh:50
#define IS_VEHICLE(v)
Definition utils.qh:22
#define vdist(v, cmp, f)
Vector distance comparison, avoids sqrt()
Definition vector.qh:8
#define vec2(...)
Definition vector.qh:90
entity WaypointSprite_SpawnFixed(entity spr, vector ofs, entity own,.entity ownfield, entity icon)
void InitializeEntity(entity e, void(entity this) func, int order)
Definition world.qc:2209
string record_type
Definition world.qh:55
float ServerProgsDB
Definition world.qh:128