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
1057{
1059 {
1060 // spawn at first
1061 if(this.race_checkpoint != 0)
1062 return '-1 0 0';
1063 if(spot.race_place != race_lowest_place_spawn)
1064 return '-1 0 0';
1065 }
1066 else
1067 {
1068 if(this.race_checkpoint != player.race_respawn_checkpoint)
1069 return '-1 0 0';
1070 // try reusing the previous spawn
1071 if(this == player.race_respawn_spotref || spot == player.race_respawn_spotref)
1072 current.x += SPAWN_PRIO_RACE_PREVIOUS_SPAWN;
1073 if(this.race_checkpoint == 0)
1074 {
1075 int pl = player.race_place;
1077 pl = 0;
1078 if(pl == 0 && !player.race_started)
1079 pl = race_highest_place_spawn; // use last place if they have not even touched finish yet
1080 if(spot.race_place != pl)
1081 return '-1 0 0';
1082 }
1083 }
1084 return current;
1085}
1086
1087spawnfunc(trigger_race_checkpoint)
1088{
1089 vector o;
1090 if(!g_race && !g_cts) { delete(this); return; }
1091
1093
1094 this.use = checkpoint_use;
1095 if (!(this.spawnflags & 1))
1097
1098 o = (this.absmin + this.absmax) * 0.5;
1099 tracebox(o, PL_MIN_CONST, PL_MAX_CONST, o - '0 0 1' * (o.z - this.absmin.z), MOVE_NORMAL, this);
1101 this.nearestwaypointtimeout = -1;
1102
1103 if(this.message == "")
1104 this.message = "went backwards";
1105 if (this.message2 == "")
1106 this.message2 = "was pushed backwards by";
1107 if (this.race_penalty_reason == "")
1108 this.race_penalty_reason = "missing a checkpoint";
1109
1110 this.race_checkpoint = this.cnt;
1111
1113 {
1115 if(this.spawnflags & 8)
1117 else
1119 }
1120
1121 if(!this.race_penalty)
1122 {
1123 if(this.race_checkpoint)
1124 WaypointSprite_SpawnFixed(WP_RaceCheckpoint, o, this, sprite, RADARICON_NONE);
1125 else
1126 WaypointSprite_SpawnFixed(WP_RaceStartFinish, o, this, sprite, RADARICON_NONE);
1127 }
1128
1129 this.sprite.waypointsprite_visible_for_player = race_waypointsprite_visible_for_player;
1131
1132 if (!g_racecheckpoints)
1135
1136 // trigger_race_checkpoint_verify checks this list too
1137 if (!g_race_targets)
1139
1141}
1142
1144{
1145 if(!g_race && !g_cts) { delete(this); return; }
1146 defrag_ents = 1;
1147
1148 // if this is targeted, then it probably isn't a trigger
1149 bool is_trigger = this.targetname == "";
1150
1151 if(is_trigger)
1153
1154 this.use = checkpoint_use;
1155 if (is_trigger && !(this.spawnflags & 1))
1157
1158 vector org = this.origin;
1159
1160 // bots should only pathfind to this if it is a valid touchable trigger
1161 if(is_trigger)
1162 {
1163 org = (this.absmin + this.absmax) * 0.5;
1164 tracebox(org, PL_MIN_CONST, PL_MAX_CONST, org - '0 0 1' * (org.z - this.absmin.z), MOVE_NORMAL, this);
1166 this.nearestwaypointtimeout = -1;
1167 }
1168
1169 if(this.message == "")
1170 this.message = "went backwards";
1171 if (this.message2 == "")
1172 this.message2 = "was pushed backwards by";
1173 if (this.race_penalty_reason == "")
1174 this.race_penalty_reason = "missing a checkpoint";
1175
1176 if(this.classname == "target_startTimer")
1177 this.race_checkpoint = 0;
1178 else
1179 this.race_checkpoint = -2;
1180
1182
1183 if (!g_race_targets)
1185 IL_PUSH(g_race_targets, this);
1186
1187 // trigger_race_checkpoint_verify checks this list too
1188 if (!g_racecheckpoints)
1190
1192}
1193
1194spawnfunc(target_checkpoint)
1195{
1196 // xonotic defrag entity
1198}
1199
1200// compatibility entity names
1201spawnfunc(target_startTimer) { target_checkpoint_setup(this); }
1202spawnfunc(target_stopTimer) { target_checkpoint_setup(this); }
1203
1205{
1207 {
1208 CS(p).race_completed = 1;
1210 Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_RACE_ABANDONED, p.netname);
1211 ClientData_Touch(p, true);
1212 }
1213}
1214
1216{
1217 race_completing = 1;
1219}
1220
1222{
1223 race_ClearTime(this);
1224 this.race_place = 0;
1225 this.race_started = 0;
1226 this.race_respawn_checkpoint = 0;
1228}
1229
1231{
1232 if(!g_race && !g_cts)
1233 return;
1235 race_ClearTime(this);
1237}
1238
1239spawnfunc(info_player_race)
1240{
1241 if(!g_race && !g_cts) { delete(this); return; }
1242 ++race_spawns;
1243 spawnfunc_info_player_deathmatch(this);
1244
1249}
1250
1252{
1253 for(int i = 0; i < MAX_CHECKPOINTS; ++i)
1254 {
1255 player.race_checkpoint_record[i] = 0;
1256 player.race_checkpoint_recordspeed[i] = 0;
1257 }
1258}
1259
1261{
1262 for(int j = 0; j < MAX_CHECKPOINTS; ++j)
1263 {
1267 }
1268
1269 FOREACH_CLIENT(true, {
1270 float p = it.race_place;
1272 it.race_place = p;
1273 });
1274}
1275
1276void race_ImposePenaltyTime(entity pl, float penalty, string reason)
1277{
1279 {
1280 pl.race_penalty_accumulator += penalty;
1281 if(IS_REAL_CLIENT(pl))
1282 {
1283 msg_entity = pl;
1284 WRITESPECTATABLE_MSG_ONE(msg_entity, {
1285 WriteHeader(MSG_ONE, TE_CSQC_RACE);
1287 WriteShort(MSG_ONE, TIME_ENCODE(penalty));
1288 WriteString(MSG_ONE, reason);
1289 });
1290 }
1291 }
1292 else
1293 {
1294 pl.race_penalty = time + penalty;
1295 if(IS_REAL_CLIENT(pl))
1296 {
1297 msg_entity = pl;
1298 WRITESPECTATABLE_MSG_ONE(msg_entity, {
1299 WriteHeader(MSG_ONE, TE_CSQC_RACE);
1301 WriteShort(MSG_ONE, TIME_ENCODE(penalty));
1302 WriteString(MSG_ONE, reason);
1303 });
1304 }
1305 }
1306}
1307
1309{
1311 if(toucher.race_lastpenalty != this)
1312 {
1313 toucher.race_lastpenalty = this;
1315 }
1316}
1317
1318void penalty_use(entity this, entity actor, entity trigger)
1319{
1321}
1322
1323spawnfunc(trigger_race_penalty)
1324{
1325 // TODO: find out why this wasnt done:
1326 //if(!g_cts && !g_race) { remove(this); return; }
1327
1329
1330 this.use = penalty_use;
1331 if (!(this.spawnflags & 1))
1332 settouch(this, penalty_touch);
1333
1334 if (this.race_penalty_reason == "")
1335 this.race_penalty_reason = "missing a checkpoint";
1336 if (!this.race_penalty)
1337 this.race_penalty = 5;
1338}
1339
1341{
1342 // interesting metrics (idea by KrimZon) to maybe sort players in the
1343 // scoreboard, immediately updates when overtaking
1344 //
1345 // requires the track to be built so you never get farther away from the
1346 // next checkpoint, though, and current Xonotic race maps are not built that
1347 // way
1348 //
1349 // also, this code is slow and would need optimization (i.e. "next CP"
1350 // links on CP entities)
1351
1352 float l;
1353 l = GameRules_scoring_add(e, RACE_LAPS, 0);
1354 if(CS(e).race_completed)
1355 return l; // not fractional
1356
1357 vector o0, o1;
1358 float bestfraction, fraction;
1359 entity lastcp;
1360 float nextcpindex, lastcpindex;
1361
1362 nextcpindex = max(e.race_checkpoint, 0);
1363 lastcpindex = e.race_respawn_checkpoint;
1364 lastcp = e.race_respawn_spotref;
1365
1366 if(nextcpindex == lastcpindex)
1367 return l; // finish
1368
1369 bestfraction = 1;
1371 {
1372 if(it.race_checkpoint != lastcpindex)
1373 continue;
1374 if(lastcp)
1375 if(it != lastcp)
1376 continue;
1377 o0 = (it.absmin + it.absmax) * 0.5;
1379 {
1380 if(it.race_checkpoint != nextcpindex)
1381 continue;
1382 o1 = (it.absmin + it.absmax) * 0.5;
1383 if(o0 == o1)
1384 continue;
1385 fraction = bound(0.0001, vlen(e.origin - o1) / vlen(o0 - o1), 1);
1386 if(fraction < bestfraction)
1387 bestfraction = fraction;
1388 });
1389 });
1390
1391 // we are at CP "nextcpindex - bestfraction"
1392 // race_timed_checkpoint == 4: then nextcp==4 means 0.9999x, nextcp==0 means 0.0000x
1393 // race_timed_checkpoint == 0: then nextcp==0 means 0.9999x
1394 float c, nc;
1395 nc = race_highest_checkpoint + 1;
1396 c = ((nextcpindex - race_timed_checkpoint + nc + nc - 1) % nc) + 1 - bestfraction;
1397
1398 return l + c / nc;
1399}
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:244
#define IS_PLAYER(s)
Definition player.qh:242
#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:589
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:1221
void race_ClearRecords()
Definition race.qc:1260
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:1056
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:1215
float race_GetFractionalLapCount(entity e)
Definition race.qc:1340
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:1230
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:1204
void penalty_use(entity this, entity actor, entity trigger)
Definition race.qc:1318
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:1143
void race_ClearPlayerRecords(entity player)
Definition race.qc:1251
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:1276
void penalty_touch(entity this, entity toucher)
Definition race.qc:1308
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:52
#define IS_VEHICLE(v)
Definition utils.qh:24
#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:2225
string record_type
Definition world.qh:55
float ServerProgsDB
Definition world.qh:128