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 = race_readPos(map, t);
94
95 float i, prevpos = 0;
96 for(i = 1; i <= RANKINGS_CNT; ++i)
97 {
98 if(race_readUID(map, i) == myuid)
99 prevpos = i;
100 }
101 if (prevpos)
102 {
103 // player improved their existing record, only have to iterate on ranks between new and old recs
104 for (i = prevpos; i > newpos; --i)
105 {
106 db_put(ServerProgsDB, strcat(map, record_type, "time", ftos(i)), ftos(race_readTime(map, i - 1)));
107 db_put(ServerProgsDB, strcat(map, record_type, "crypto_idfp", ftos(i)), race_readUID(map, i - 1));
108 }
109 }
110 else
111 {
112 // player has no ranked record yet
113 for (i = RANKINGS_CNT; i > newpos; --i)
114 {
115 float other_time = race_readTime(map, i - 1);
116 if (other_time) {
117 db_put(ServerProgsDB, strcat(map, record_type, "time", ftos(i)), ftos(other_time));
118 db_put(ServerProgsDB, strcat(map, record_type, "crypto_idfp", ftos(i)), race_readUID(map, i - 1));
119 }
120 }
121 }
122
123 // store new time itself
124 db_put(ServerProgsDB, strcat(map, record_type, "time", ftos(newpos)), ftos(t));
125 db_put(ServerProgsDB, strcat(map, record_type, "crypto_idfp", ftos(newpos)), myuid);
126}
127
128string race_readName(string map, float pos)
129{
130 return uid2name(db_get(ServerProgsDB, strcat(map, record_type, "crypto_idfp", ftos(pos))));
131}
132
134{
135 if(CS_CVAR(player).cvar_cl_allow_uidtracking == 1 && CS_CVAR(player).cvar_cl_allow_uid2name == 1)
136 {
137 if (!player.stored_netname)
138 player.stored_netname = strzone(uid2name(player.crypto_idfp));
139 if(player.stored_netname != player.netname)
140 {
141 db_put(ServerProgsDB, strcat("/uid2name/", player.crypto_idfp), player.netname);
142 strcpy(player.stored_netname, player.netname);
143 }
144 }
145}
146
147
148const float MAX_CHECKPOINTS = 255;
149
153.float race_checkpoint; // player: next checkpoint that has to be reached
155
156.entity sprite;
157
164
167
170
173
175{
177 return 0;
178 else
179 return f + 1;
180}
181
183{
184 if(f == -1)
185 return 0;
186 else if(f == 0)
188 else
189 return f - 1;
190}
191
192// encode as:
193// 0 = common start/finish
194// 254 = start
195// 255 = finish
197{
199 {
200 if(f == 0)
201 return 254; // start
202 else if(f == race_timed_checkpoint)
203 return 255; // finish
204 }
205 return f;
206}
207
208void race_SendNextCheckpoint(entity e, float spec) // qualifying only
209{
210 if(!e.race_laptime)
211 return;
212
213 int cp = e.race_checkpoint;
214 float recordtime;
215 float recordspeed;
216 float myrecordtime = e.race_checkpoint_record[cp];
217 float myrecordspeed = e.race_checkpoint_recordspeed[cp];
218 string recordholder;
220 recordtime = myrecordtime;
221 recordspeed = myrecordspeed;
222 recordholder = "";
223 } else {
224 recordtime = race_checkpoint_records[cp];
225 recordspeed = race_checkpoint_recordspeeds[cp];
226 recordholder = race_checkpoint_recordholders[cp];
227
228 if(recordholder == e.netname)
229 recordholder = "";
230 }
231
232 if(!IS_REAL_CLIENT(e))
233 return;
234
235 if(!spec)
236 msg_entity = e;
237 WRITESPECTATABLE_MSG_ONE(msg_entity, {
238 WriteHeader(MSG_ONE, TE_CSQC_RACE);
239 if(spec)
240 {
242 //WriteCoord(MSG_ONE, e.race_laptime - e.race_penalty_accumulator);
243 WriteCoord(MSG_ONE, time - e.race_movetime - e.race_penalty_accumulator);
244 }
245 else
247 WriteByte(MSG_ONE, race_CheckpointNetworkID(cp)); // checkpoint the player will be at next
248 WriteInt24_t(MSG_ONE, recordtime);
249 if(!spec)
250 WriteInt24_t(MSG_ONE, myrecordtime);
251 WriteString(MSG_ONE, recordholder);
252 // All speeds networked as floats to have decimal precision
253 WriteFloat(MSG_ONE, recordspeed);
254 if(!spec)
255 WriteFloat(MSG_ONE, myrecordspeed);
256 });
257}
258
259void race_send_recordtime(float msg)
260{
261 // send the server best time
262 WriteHeader(msg, TE_CSQC_RACE);
264 WriteInt24_t(msg, race_readTime(GetMapname(), 1));
265}
266
267void race_send_speedaward(float msg)
268{
269 // send the best speed of the round
270 WriteHeader(msg, TE_CSQC_RACE);
272 WriteInt24_t(msg, floor(speedaward_speed+0.5));
274}
275
277{
278 // send the best speed
279 WriteHeader(msg, TE_CSQC_RACE);
281 WriteInt24_t(msg, floor(speedaward_alltimebest+0.5));
283}
284
286{
287 WriteHeader(msg, TE_CSQC_RACE);
290 WriteByte(msg, m);
291}
292
293void race_SendRanking(float pos, float prevpos, float del, float msg)
294{
295 WriteHeader(msg, TE_CSQC_RACE);
297 WriteShort(msg, pos);
298 WriteShort(msg, prevpos);
299 WriteShort(msg, del);
301 WriteInt24_t(msg, race_readTime(GetMapname(), pos));
302}
303
305{
306 if (IS_OBSERVER(player))
307 return;
308
309 if(vdist(player.velocity - player.velocity_z * '0 0 1', >, speedaward_speed))
310 {
311 speedaward_speed = vlen(player.velocity - player.velocity_z * '0 0 1');
312 speedaward_holder = player.netname;
313 speedaward_uid = player.crypto_idfp;
315 }
317 {
321 {
328 }
329 }
330}
331
332void race_SendAll(entity player, bool only_rankings)
333{
334 if(!IS_REAL_CLIENT(player))
335 return;
336
337 msg_entity = player;
338 if (!only_rankings)
339 {
342
346 }
347
350 for (int i = 1; i <= m; ++i)
351 race_SendRanking(i, 0, 0, MSG_ONE);
352}
353
354void race_SendStatus(float id, entity e)
355{
356 if(!IS_REAL_CLIENT(e))
357 return;
358
359 float msg;
360 if (id == 0)
361 msg = MSG_ONE;
362 else
363 msg = MSG_ALL;
364 msg_entity = e;
365 WRITESPECTATABLE_MSG_ONE(msg_entity, {
366 WriteHeader(msg, TE_CSQC_RACE);
368 WriteShort(msg, id);
369 WriteString(msg, e.netname);
370 });
371}
372
373void race_setTime(string map, float t, string myuid, string mynetname, entity e, bool showmessage)
374{
375 // netname only used TEMPORARILY for printing
376 int newpos = race_readPos(map, t);
377
378 int player_prevpos = 0;
379 for(int i = 1; i <= RANKINGS_CNT; ++i)
380 {
381 if(race_readUID(map, i) == myuid)
382 player_prevpos = i;
383 }
384
385 float oldrec;
386 string oldrec_holder;
387 if (player_prevpos && (player_prevpos < newpos || !newpos))
388 {
389 oldrec = race_readTime(GetMapname(), player_prevpos);
390 race_SendStatus(0, e); // "fail"
391 if(showmessage)
392 Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_RACE_FAIL_RANKED, mynetname, player_prevpos, t, oldrec);
393 return;
394 }
395 else if (!newpos)
396 {
397 // no ranking, time worse than the worst ranked
399 race_SendStatus(0, e); // "fail"
400 if(showmessage)
401 Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_RACE_FAIL_UNRANKED, mynetname, RANKINGS_CNT, t, oldrec);
402 return;
403 }
404
405 // if we didn't hit a return yet, we have a new record!
406
407 // if the player does not have a UID we can unfortunately not store the record, as the rankings system relies on UIDs
408 if(myuid == "")
409 {
410 if(showmessage)
411 Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_RACE_NEW_MISSING_UID, mynetname, t);
412 return;
413 }
414
415 if(CS_CVAR(e).cvar_cl_allow_uidtracking != 1 || CS_CVAR(e).cvar_cl_allow_uid2name != 1
416 || uid2name(myuid) == "^1Unregistered Player")
417 {
418 if(showmessage)
419 Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_RACE_NEW_MISSING_NAME, mynetname, t);
420 return;
421 }
422
423 oldrec = race_readTime(GetMapname(), newpos);
424 oldrec_holder = race_readName(GetMapname(), newpos);
425
426 // store new ranking
427 race_writeTime(GetMapname(), t, myuid);
428
429 if (newpos == 1 && showmessage)
430 {
433 }
434
435 race_SendRanking(newpos, player_prevpos, 0, MSG_ALL);
437
438 if(newpos == player_prevpos)
439 {
440 if(showmessage)
441 Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_RACE_NEW_IMPROVED, mynetname, newpos, t, oldrec);
442 if(newpos == 1) { race_SendStatus(3, e); } // "new server record"
443 else { race_SendStatus(1, e); } // "new time"
444 }
445 else if(oldrec == 0)
446 {
447 if(showmessage)
448 Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_RACE_NEW_SET, mynetname, newpos, t);
449 if(newpos == 1) { race_SendStatus(3, e); } // "new server record"
450 else { race_SendStatus(2, e); } // "new rank"
451 }
452 else
453 {
454 if(showmessage)
455 Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_RACE_NEW_BROKEN, mynetname, oldrec_holder, newpos, t, oldrec);
456 if(newpos == 1) { race_SendStatus(3, e); } // "new server record"
457 else { race_SendStatus(2, e); } // "new rank"
458 }
459}
460
461void race_deleteTime(string map, float pos)
462{
463 for(int i = pos; i <= RANKINGS_CNT; ++i)
464 {
465 string therank = ftos(i);
466 if (i == RANKINGS_CNT)
467 {
468 db_remove(ServerProgsDB, strcat(map, record_type, "time", therank));
469 db_remove(ServerProgsDB, strcat(map, record_type, "crypto_idfp", therank));
470 }
471 else
472 {
473 db_put(ServerProgsDB, strcat(map, record_type, "time", therank), ftos(race_readTime(GetMapname(), i+1)));
474 db_put(ServerProgsDB, strcat(map, record_type, "crypto_idfp", therank), race_readUID(GetMapname(), i+1));
475 }
476 }
477
478 race_SendRanking(pos, 0, 1, MSG_ALL);
479 if(pos == 1)
481
483}
484
485void race_SendTime(entity e, float cp, float t, float tvalid)
486{
488 t += e.race_penalty_accumulator;
489
490 t = TIME_ENCODE(t); // make integer
491
492 if(tvalid)
493 if(cp == race_timed_checkpoint) // finish line
494 if (!CS(e).race_completed)
495 {
496 int s = GameRules_scoring_add(e, RACE_FASTEST, 0);
497 if(!s || t < s)
498 GameRules_scoring_add(e, RACE_FASTEST, t - s);
500 {
501 s = GameRules_scoring_add(e, RACE_TIME, 0);
502 int snew = TIME_ENCODE(time - game_starttime);
503 GameRules_scoring_add(e, RACE_TIME, snew - s);
504 int l = GameRules_scoring_add_team(e, RACE_LAPS, 1);
505
507 if(l >= autocvar_fraglimit)
509
511 {
512 CS(e).race_completed = 1;
514 if(e.bot_attack)
516 e.bot_attack = false;
517 Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_RACE_FINISHED, e.netname);
518 ClientData_Touch(e, true);
519 }
520 }
521 }
522
524 {
525 float recordtime;
526 float recordtimespeed;
527 string recordholder;
528
529 if(tvalid)
530 {
532 {
533 // return their own checkpoint time
534 recordtime = e.race_checkpoint_record[cp];
535 recordtimespeed = e.race_checkpoint_recordspeed[cp];
536 recordholder = "";
537 }
538 else
539 {
540 recordtime = race_checkpoint_records[cp];
541 recordtimespeed = race_checkpoint_recordspeeds[cp];
542 // make a tempstring copy, as we'll possibly strunzone it
543 recordholder = strcat(race_checkpoint_recordholders[cp]);
544
545 if(recordholder == e.netname)
546 recordholder = "";
547 }
548
549 if(t != 0)
550 {
551 if(cp == race_timed_checkpoint)
552 {
553 race_setTime(GetMapname(), t, e.crypto_idfp, e.netname, e, true);
554 MUTATOR_CALLHOOK(Race_FinalCheckpoint, e);
555 }
556
557 if(t < recordtime || recordtime == 0)
558 {
560 race_checkpoint_recordspeeds[cp] = vlen(vec2(e.velocity));
563 FOREACH_CLIENT(IS_PLAYER(it) && IS_REAL_CLIENT(it) && it.race_checkpoint == cp, { race_SendNextCheckpoint(it, 0); });
564 }
565
566 }
567 }
568 else
569 {
570 // dummies
571 t = 0;
572 recordtime = 0;
573 recordtimespeed = 0;
574 recordholder = "";
575 }
576
577 if(IS_REAL_CLIENT(e))
578 {
580 {
582 {
583 if(it == e || (IS_SPEC(it) && it.enemy == e))
584 {
585 msg_entity = it;
586 WriteHeader(MSG_ONE, TE_CSQC_RACE);
588 WriteByte(MSG_ONE, race_CheckpointNetworkID(cp)); // checkpoint the player now is at
589 WriteInt24_t(MSG_ONE, t); // time to that checkpoint
590 WriteInt24_t(MSG_ONE, recordtime); // previously best time
591 WriteInt24_t(MSG_ONE, ((tvalid) ? it.race_checkpoint_record[cp] : 0)); // previously best time
592 WriteString(MSG_ONE, recordholder); // record holder
593 // All speeds networked as floats to have decimal precision
594 WriteFloat(MSG_ONE, vlen(vec2(e.velocity))); // speed of cp hit
595 WriteFloat(MSG_ONE, recordtimespeed); // speed of previous best cp hit
596 WriteFloat(MSG_ONE, ((tvalid) ? it.race_checkpoint_recordspeed[cp] : 0)); // speed of my previous best cp hit
597 }
598 });
599 }
600 }
601 // check if new pb should be set for a checkpoint
602 if(tvalid)
603 {
604 float myrecordtime = e.race_checkpoint_record[cp];
605 if(t != 0)
606 if(t < myrecordtime || myrecordtime == 0)
607 {
608 e.race_checkpoint_record[cp] = t;
609 e.race_checkpoint_recordspeed[cp] = vlen(vec2(e.velocity));
610 }
611 }
612 }
613 else // RACE! Not Qualifying
614 {
615 float mylaps, lother, othtime;
617 if(oth)
618 {
619 mylaps = GameRules_scoring_add(e, RACE_LAPS, 0);
620 lother = race_checkpoint_lastlaps[cp];
621 othtime = race_checkpoint_lasttimes[cp];
622 }
623 else
624 mylaps = lother = othtime = 0;
625
626 if(IS_REAL_CLIENT(e))
627 {
628 msg_entity = e;
629 WRITESPECTATABLE_MSG_ONE(msg_entity, {
630 WriteHeader(MSG_ONE, TE_CSQC_RACE);
632 WriteByte(MSG_ONE, race_CheckpointNetworkID(cp)); // checkpoint the player now is at
633 if(e == oth)
634 {
635 WriteInt24_t(MSG_ONE, 0);
636 WriteByte(MSG_ONE, 0);
637 WriteByte(MSG_ONE, 0);
638 }
639 else
640 {
642 WriteByte(MSG_ONE, mylaps - lother);
643 WriteByte(MSG_ONE, etof(oth)); // record holder
644 }
645 });
646 }
647
650 race_checkpoint_lastlaps[cp] = mylaps;
651
652 if(IS_REAL_CLIENT(oth))
653 {
654 msg_entity = oth;
655 WRITESPECTATABLE_MSG_ONE(msg_entity, {
656 WriteHeader(MSG_ONE, TE_CSQC_RACE);
658 WriteByte(MSG_ONE, race_CheckpointNetworkID(cp)); // checkpoint the player now is at
659 if(e == oth)
660 {
661 WriteInt24_t(MSG_ONE, 0);
662 WriteByte(MSG_ONE, 0);
663 WriteByte(MSG_ONE, 0);
664 }
665 else
666 {
667 WriteInt24_t(MSG_ONE, TIME_ENCODE(time - othtime));
668 WriteByte(MSG_ONE, lother - mylaps);
669 WriteByte(MSG_ONE, etof(e) - 1); // record holder
670 }
671 });
672 }
673 }
674}
675
677{
678 e.race_checkpoint = 0;
679 e.race_laptime = 0;
680 e.race_movetime = e.race_movetime_frac = e.race_movetime_count = 0;
681 e.race_penalty_accumulator = 0;
682 e.race_lastpenalty = NULL;
683
684 if(!IS_REAL_CLIENT(e))
685 return;
686
687 msg_entity = e;
688 WRITESPECTATABLE_MSG_ONE(msg_entity, {
689 WriteHeader(MSG_ONE, TE_CSQC_RACE);
691 });
692}
693
695{
696 if(IS_VEHICLE(player) && player.owner)
697 player = player.owner;
698
699 if(player.personal && autocvar_g_allow_checkpoints)
700 return; // practice mode!
701
702 if(player.classname == "porto")
703 {
704 // do not allow portalling through checkpoints
705 trace_plane_normal = normalize(-1 * player.velocity);
706 W_Porto_Fail(player, 0);
707 return;
708 }
709
710 string oldmsg; // used twice
711
712 /*
713 * Trigger targets
714 */
715 if (!((this.spawnflags & 2) && (IS_PLAYER(player))))
716 {
717 oldmsg = this.message;
718 this.message = "";
719 SUB_UseTargets(this, player, player);
720 this.message = oldmsg;
721 }
722
723 if (!IS_PLAYER(player))
724 return;
725
726 /*
727 * Remove unauthorized equipment
728 */
729 Portal_ClearAll(player);
730
731 player.porto_forbidden = 2; // decreased by 1 each StartFrame
732
733 if(defrag_ents)
734 {
735 if(this.race_checkpoint == -2)
736 {
737 // duplicate targetname target_checkpoints get the same cp nr
738 IL_EACH(g_race_targets, it.classname == "target_checkpoint" && it.targetname == this.targetname, {
739 if(it.race_checkpoint >= 0)
740 this.race_checkpoint = it.race_checkpoint;
741 });
742 if(this.race_checkpoint == -2)
743 this.race_checkpoint = player.race_checkpoint;
744 }
745
746 int cp_amount = 0, largest_cp_id = 0;
747 IL_EACH(g_race_targets, it.classname == "target_checkpoint",
748 {
749 ++cp_amount;
750 if(it.race_checkpoint > largest_cp_id) // update the finish id if someone hit a new checkpoint
751 {
752 if(!largest_cp_id)
753 {
754 IL_EACH(g_race_targets, it.classname == "target_checkpoint",
755 {
756 if(it.race_checkpoint == -2) // set defragcpexists to -1 so that the cp id file will be rewritten when someone finishes
757 defragcpexists = -1;
758 });
759 }
760
761 largest_cp_id = it.race_checkpoint;
762 IL_EACH(g_race_targets, it.classname == "target_stopTimer",
763 {
764 it.race_checkpoint = largest_cp_id + 1; // finish line
765 });
766 race_highest_checkpoint = largest_cp_id + 1;
767 race_timed_checkpoint = largest_cp_id + 1;
768 }
769 });
770
771 if(!cp_amount)
772 {
773 IL_EACH(g_race_targets, it.classname == "target_stopTimer",
774 {
775 it.race_checkpoint = 1;
776 });
779 }
780 }
781
782 if((player.race_checkpoint == -1 && this.race_checkpoint == 0) || (player.race_checkpoint == this.race_checkpoint))
783 {
784 if(this.race_penalty)
785 {
786 if(player.race_lastpenalty != this)
787 {
788 player.race_lastpenalty = this;
790 }
791 }
792
793 if(player.race_penalty)
794 return;
795
796 /*
797 * Trigger targets
798 */
799 if(this.spawnflags & 2)
800 {
801 oldmsg = this.message;
802 this.message = "";
803 SUB_UseTargets(this, player, player); // TODO: should we be using other for the trigger here?
804 this.message = oldmsg;
805 }
806
807 if(player.race_respawn_checkpoint != this.race_checkpoint || !player.race_started)
808 player.race_respawn_spotref = this; // this is not a spot but a CP, but spawnpoint selection will deal with that
809 player.race_respawn_checkpoint = this.race_checkpoint;
810 player.race_checkpoint = race_NextCheckpoint(this.race_checkpoint);
811 player.race_started = 1;
812
813 race_SendTime(player, this.race_checkpoint, player.race_movetime, boolean(player.race_laptime));
814
815 if(!this.race_checkpoint) // start line
816 {
817 player.race_laptime = time;
818 player.race_movetime = player.race_movetime_frac = player.race_movetime_count = 0;
819 player.race_penalty_accumulator = 0;
820 player.race_lastpenalty = NULL;
821 }
822
824 race_SendNextCheckpoint(player, 0);
825
826 if(defrag_ents && defragcpexists < 0 && this.classname == "target_stopTimer")
827 {
828 float fh;
829 defragcpexists = fh = fopen(strcat("maps/", GetMapname(), ".defragcp"), FILE_WRITE);
830 if(fh >= 0)
831 {
832 IL_EACH(g_race_targets, it.classname == "target_checkpoint",
833 {
834 fputs(fh, strcat(it.targetname, " ", ftos(it.race_checkpoint), "\n"));
835 });
836 }
837 fclose(fh);
838 }
839 }
840 else if(player.race_checkpoint == race_NextCheckpoint(this.race_checkpoint))
841 {
842 // ignored
843 }
844 else
845 {
846 if(this.spawnflags & 4)
847 Damage (player, this, this, 10000, DEATH_HURTTRIGGER.m_id, DMG_NOWEP, player.origin, '0 0 0');
848 }
849}
850
856
857void checkpoint_use(entity this, entity actor, entity trigger)
858{
859 if(trigger.classname == "info_player_deathmatch") // a spawn, a spawn
860 return;
861
862 checkpoint_passed(this, actor);
863}
864
866{
867 entity own = this.owner;
868 if(this.realowner)
869 own = this.realowner; // target support
870
871 if(view.race_checkpoint == -1 || own.race_checkpoint == -2)
872 return true;
873 else if(view.race_checkpoint == own.race_checkpoint)
874 return true;
875 else
876 return false;
877}
878
879void defrag_waypointsprites(entity targeted, entity checkpoint, int recursionlevel)
880{
881 // bones_was_here: spawn a waypoint for every entity with a bmodel
882 // that directly or indirectly targets this checkpoint
883 // (anything a player could touch or shoot to activate this cp)
884
885 // spam a few warnings so that larger link chain loops can be found
886 if (recursionlevel > 400)
887 {
888 LOG_WARNF("df cp wp loop: ^4\"target\" ^3\"%s\"^7, ^4\"targetname\" ^3\"%s\"^7",
889 targeted.classname,
890 targeted.targetname);
891 }
892 // avoid a stack overflow
893 if (recursionlevel > 420)
894 {
895 LOG_WARNF("Aborted creating defrag checkpoint waypointsprites for ^2%s^7 due to a loop",
896 checkpoint.classname);
897 return;
898 }
899
900 entity s = WP_RaceCheckpoint;
901 if (checkpoint.classname == "target_startTimer")
902 s = WP_RaceStart;
903 else if (checkpoint.classname == "target_stopTimer")
904 s = WP_RaceFinish;
905
906 for (entity t = findchain(target, targeted.targetname); t; t = t.chain)
907 {
908 if (t.modelindex)
909 {
910 WaypointSprite_SpawnFixed(s, (t.absmin + t.absmax) * 0.5, t, sprite, RADARICON_NONE);
911 t.sprite.realowner = checkpoint;
912 t.sprite.waypointsprite_visible_for_player = race_waypointsprite_visible_for_player;
913 }
914
915 if (t.targetname && t != checkpoint)
916 defrag_waypointsprites(t, checkpoint, ++recursionlevel);
917 }
918}
919
921{
922 static bool have_verified;
923 if (have_verified) return;
924 have_verified = true;
925
926 int qual = g_race_qualifying;
927
928 int pl_race_checkpoint = 0;
929 int pl_race_place = 0;
930
931 if (g_race) {
932 for (int i = 0; i <= race_highest_checkpoint; ++i) {
933 pl_race_checkpoint = race_NextCheckpoint(i);
934
935 // race only (middle of the race)
937 pl_race_place = 0;
938 if (!Spawn_FilterOutBadSpots(this, findchain(classname, "info_player_deathmatch"), 0, false, true)) {
939 error(strcat("Checkpoint ", ftos(i), " misses a spawnpoint with race_place==", ftos(pl_race_place), " (used for respawning in race) - bailing out"));
940 }
941
942 if (i == 0) {
943 // qualifying only
945 pl_race_place = race_lowest_place_spawn;
946 if (!Spawn_FilterOutBadSpots(this, findchain(classname, "info_player_deathmatch"), 0, false, true)) {
947 error(strcat("Checkpoint ", ftos(i), " misses a spawnpoint with race_place==", ftos(pl_race_place), " (used for qualifying) - bailing out"));
948 }
949
950 // race only (initial spawn)
952 for (int p = 1; p <= race_highest_place_spawn; ++p) {
953 pl_race_place = p;
954 if (!Spawn_FilterOutBadSpots(this, findchain(classname, "info_player_deathmatch"), 0, false, true)) {
955 error(strcat("Checkpoint ", ftos(i), " misses a spawnpoint with race_place==", ftos(pl_race_place), " (used for initially spawning in race) - bailing out"));
956 }
957 }
958 }
959 }
960 } else if (!defrag_ents) {
961 // qualifying only
962 pl_race_checkpoint = race_NextCheckpoint(0);
964 pl_race_place = race_lowest_place_spawn;
965 if (!Spawn_FilterOutBadSpots(this, findchain(classname, "info_player_deathmatch"), 0, false, true)) {
966 error(strcat("Checkpoint 0 misses a spawnpoint with race_place==", ftos(pl_race_place), " (used for qualifying) - bailing out"));
967 }
968 } else {
969 pl_race_checkpoint = race_NextCheckpoint(0);
971 pl_race_place = 0; // there's only one spawn on defrag maps
972
973 // check if a defragcp file already exists, then read it and apply the checkpoint order
974 float fh;
975 float len;
976 bool broken_defragcp = false;
977 string l;
978
979 defragcpexists = fh = fopen(strcat("maps/", GetMapname(), ".defragcp"), FILE_READ);
980 if (fh >= 0) {
981 while ((l = fgets(fh))) {
982 len = tokenize_console(l);
983 if (len != 2) {
984 broken_defragcp = true;
985 break;
986 }
987 for (entity cp = NULL; (cp = find(cp, classname, "target_checkpoint"));) {
988 if (argv(0) == cp.targetname) {
989 if(cp.race_checkpoint != -2 && cp.race_checkpoint != stof(argv(1))) {
990 broken_defragcp = true; // cp had been previously set with diff order nr defragcp file is broken
991 break;
992 }
993 cp.race_checkpoint = stof(argv(1));
994 }
995 }
996 if(broken_defragcp) break; // no point to keep going we'll rebuild the whole order
997 }
998 fclose(fh);
999 if(broken_defragcp) {
1000 // something's wrong in the defrag cp file, set defragcpexists to -1 so that it will be rewritten when someone finishes
1001 // also clear any set cp order to make sure map is completable
1002 defragcpexists = -1;
1003 for (entity cp = NULL; (cp = find(cp, classname, "target_checkpoint"));) {
1004 cp.race_checkpoint = -2;
1005 }
1006 }
1007 }
1008 }
1009
1010 g_race_qualifying = qual;
1011
1013 if (defrag_ents) {
1014 IL_EACH(g_race_targets, it.classname == "target_checkpoint" || it.classname == "target_startTimer" || it.classname == "target_stopTimer",
1015 {
1016 defrag_waypointsprites(it, it, 0);
1017
1018 if(it.classname == "target_checkpoint") {
1019 if(it.race_checkpoint == -2)
1020 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
1021 }
1022 });
1023 if (defragcpexists != -1) {
1024 float largest_cp_id = 0;
1025 for (entity cp = NULL; (cp = find(cp, classname, "target_checkpoint"));) {
1026 if (cp.race_checkpoint > largest_cp_id) {
1027 largest_cp_id = cp.race_checkpoint;
1028 }
1029 }
1030 for (entity cp = NULL; (cp = find(cp, classname, "target_stopTimer"));) {
1031 cp.race_checkpoint = largest_cp_id + 1; // finish line
1032 }
1033 race_highest_checkpoint = largest_cp_id + 1;
1034 race_timed_checkpoint = largest_cp_id + 1;
1035 } else {
1036 for (entity cp = NULL; (cp = find(cp, classname, "target_stopTimer"));) {
1037 cp.race_checkpoint = 255; // finish line
1038 }
1041 }
1042 } else {
1043 IL_EACH(g_racecheckpoints, it.sprite,
1044 {
1045 if (it.race_checkpoint == 0) {
1046 WaypointSprite_UpdateSprites(it.sprite, WP_RaceStart, WP_Null, WP_Null);
1047 } else if (it.race_checkpoint == race_timed_checkpoint) {
1048 WaypointSprite_UpdateSprites(it.sprite, WP_RaceFinish, WP_Null, WP_Null);
1049 }
1050 });
1051 }
1052 }
1053}
1054
1056{
1058 {
1059 // spawn at first
1060 if(this.race_checkpoint != 0)
1061 return '-1 0 0';
1062 if(spot.race_place != race_lowest_place_spawn)
1063 return '-1 0 0';
1064 }
1065 else
1066 {
1067 if(this.race_checkpoint != player.race_respawn_checkpoint)
1068 return '-1 0 0';
1069 // try reusing the previous spawn
1070 if(this == player.race_respawn_spotref || spot == player.race_respawn_spotref)
1071 current.x += SPAWN_PRIO_RACE_PREVIOUS_SPAWN;
1072 if(this.race_checkpoint == 0)
1073 {
1074 int pl = player.race_place;
1076 pl = 0;
1077 if(pl == 0 && !player.race_started)
1078 pl = race_highest_place_spawn; // use last place if they have not even touched finish yet
1079 if(spot.race_place != pl)
1080 return '-1 0 0';
1081 }
1082 }
1083 return current;
1084}
1085
1086spawnfunc(trigger_race_checkpoint)
1087{
1088 vector o;
1089 if(!g_race && !g_cts) { delete(this); return; }
1090
1092
1093 this.use = checkpoint_use;
1094 if (!(this.spawnflags & 1))
1096
1097 o = (this.absmin + this.absmax) * 0.5;
1098 tracebox(o, PL_MIN_CONST, PL_MAX_CONST, o - '0 0 1' * (o.z - this.absmin.z), MOVE_NORMAL, this);
1100 this.nearestwaypointtimeout = -1;
1101
1102 if(this.message == "")
1103 this.message = "went backwards";
1104 if (this.message2 == "")
1105 this.message2 = "was pushed backwards by";
1106 if (this.race_penalty_reason == "")
1107 this.race_penalty_reason = "missing a checkpoint";
1108
1109 this.race_checkpoint = this.cnt;
1110
1112 {
1114 if(this.spawnflags & 8)
1116 else
1118 }
1119
1120 if(!this.race_penalty)
1121 {
1122 if(this.race_checkpoint)
1123 WaypointSprite_SpawnFixed(WP_RaceCheckpoint, o, this, sprite, RADARICON_NONE);
1124 else
1125 WaypointSprite_SpawnFixed(WP_RaceStartFinish, o, this, sprite, RADARICON_NONE);
1126 }
1127
1128 this.sprite.waypointsprite_visible_for_player = race_waypointsprite_visible_for_player;
1130
1131 if (!g_racecheckpoints)
1134
1135 // trigger_race_checkpoint_verify checks this list too
1136 if (!g_race_targets)
1138
1140}
1141
1143{
1144 if(!g_race && !g_cts) { delete(this); return; }
1145 defrag_ents = 1;
1146
1147 // if this is targeted, then it probably isn't a trigger
1148 bool is_trigger = this.targetname == "";
1149
1150 if(is_trigger)
1152
1153 this.use = checkpoint_use;
1154 if (is_trigger && !(this.spawnflags & 1))
1156
1157 vector org = this.origin;
1158
1159 // bots should only pathfind to this if it is a valid touchable trigger
1160 if(is_trigger)
1161 {
1162 org = (this.absmin + this.absmax) * 0.5;
1163 tracebox(org, PL_MIN_CONST, PL_MAX_CONST, org - '0 0 1' * (org.z - this.absmin.z), MOVE_NORMAL, this);
1165 this.nearestwaypointtimeout = -1;
1166 }
1167
1168 if(this.message == "")
1169 this.message = "went backwards";
1170 if (this.message2 == "")
1171 this.message2 = "was pushed backwards by";
1172 if (this.race_penalty_reason == "")
1173 this.race_penalty_reason = "missing a checkpoint";
1174
1175 if(this.classname == "target_startTimer")
1176 this.race_checkpoint = 0;
1177 else
1178 this.race_checkpoint = -2;
1179
1181
1182 if (!g_race_targets)
1184 IL_PUSH(g_race_targets, this);
1185
1186 // trigger_race_checkpoint_verify checks this list too
1187 if (!g_racecheckpoints)
1189
1191}
1192
1193spawnfunc(target_checkpoint)
1194{
1195 // xonotic defrag entity
1197}
1198
1199// compatibility entity names
1200spawnfunc(target_startTimer) { target_checkpoint_setup(this); }
1201spawnfunc(target_stopTimer) { target_checkpoint_setup(this); }
1202
1204{
1206 {
1207 CS(p).race_completed = 1;
1209 Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_RACE_ABANDONED, p.netname);
1210 ClientData_Touch(p, true);
1211 }
1212}
1213
1215{
1216 race_completing = 1;
1218}
1219
1221{
1222 race_ClearTime(this);
1223 this.race_place = 0;
1224 this.race_started = 0;
1225 this.race_respawn_checkpoint = 0;
1227}
1228
1230{
1231 if(!g_race && !g_cts)
1232 return;
1234 race_ClearTime(this);
1236}
1237
1238spawnfunc(info_player_race)
1239{
1240 if(!g_race && !g_cts) { delete(this); return; }
1241 ++race_spawns;
1242 spawnfunc_info_player_deathmatch(this);
1243
1248}
1249
1251{
1252 for(int i = 0; i < MAX_CHECKPOINTS; ++i)
1253 {
1254 player.race_checkpoint_record[i] = 0;
1255 player.race_checkpoint_recordspeed[i] = 0;
1256 }
1257}
1258
1260{
1261 for(int j = 0; j < MAX_CHECKPOINTS; ++j)
1262 {
1266 }
1267
1268 FOREACH_CLIENT(true, {
1269 float p = it.race_place;
1271 it.race_place = p;
1272 });
1273}
1274
1275void race_ImposePenaltyTime(entity pl, float penalty, string reason)
1276{
1278 {
1279 pl.race_penalty_accumulator += penalty;
1280 if(IS_REAL_CLIENT(pl))
1281 {
1282 msg_entity = pl;
1283 WRITESPECTATABLE_MSG_ONE(msg_entity, {
1284 WriteHeader(MSG_ONE, TE_CSQC_RACE);
1286 WriteShort(MSG_ONE, TIME_ENCODE(penalty));
1287 WriteString(MSG_ONE, reason);
1288 });
1289 }
1290 }
1291 else
1292 {
1293 pl.race_penalty = time + penalty;
1294 if(IS_REAL_CLIENT(pl))
1295 {
1296 msg_entity = pl;
1297 WRITESPECTATABLE_MSG_ONE(msg_entity, {
1298 WriteHeader(MSG_ONE, TE_CSQC_RACE);
1300 WriteShort(MSG_ONE, TIME_ENCODE(penalty));
1301 WriteString(MSG_ONE, reason);
1302 });
1303 }
1304 }
1305}
1306
1308{
1310 if(toucher.race_lastpenalty != this)
1311 {
1312 toucher.race_lastpenalty = this;
1314 }
1315}
1316
1317void penalty_use(entity this, entity actor, entity trigger)
1318{
1320}
1321
1322spawnfunc(trigger_race_penalty)
1323{
1324 // TODO: find out why this wasnt done:
1325 //if(!g_cts && !g_race) { remove(this); return; }
1326
1328
1329 this.use = penalty_use;
1330 if (!(this.spawnflags & 1))
1331 settouch(this, penalty_touch);
1332
1333 if (this.race_penalty_reason == "")
1334 this.race_penalty_reason = "missing a checkpoint";
1335 if (!this.race_penalty)
1336 this.race_penalty = 5;
1337}
1338
1340{
1341 // interesting metrics (idea by KrimZon) to maybe sort players in the
1342 // scoreboard, immediately updates when overtaking
1343 //
1344 // requires the track to be built so you never get farther away from the
1345 // next checkpoint, though, and current Xonotic race maps are not built that
1346 // way
1347 //
1348 // also, this code is slow and would need optimization (i.e. "next CP"
1349 // links on CP entities)
1350
1351 float l = GameRules_scoring_add(e, RACE_LAPS, 0);
1352 if(CS(e).race_completed)
1353 return l; // not fractional
1354
1355 vector o0, o1;
1356 float bestfraction, fraction;
1357 entity lastcp;
1358 float nextcpindex, lastcpindex;
1359
1360 nextcpindex = max(e.race_checkpoint, 0);
1361 lastcpindex = e.race_respawn_checkpoint;
1362 lastcp = e.race_respawn_spotref;
1363
1364 if(nextcpindex == lastcpindex)
1365 return l; // finish
1366
1367 bestfraction = 1;
1369 {
1370 if(it.race_checkpoint != lastcpindex)
1371 continue;
1372 if(lastcp)
1373 if(it != lastcp)
1374 continue;
1375 o0 = (it.absmin + it.absmax) * 0.5;
1377 {
1378 if(it.race_checkpoint != nextcpindex)
1379 continue;
1380 o1 = (it.absmin + it.absmax) * 0.5;
1381 if(o0 == o1)
1382 continue;
1383 fraction = bound(0.0001, vlen(e.origin - o1) / vlen(o0 - o1), 1);
1384 if(fraction < bestfraction)
1385 bestfraction = fraction;
1386 });
1387 });
1388
1389 // we are at CP "nextcpindex - bestfraction"
1390 // race_timed_checkpoint == 4: then nextcp==4 means 0.9999x, nextcp==0 means 0.0000x
1391 // race_timed_checkpoint == 0: then nextcp==0 means 0.9999x
1392 float c, nc;
1393 nc = race_highest_checkpoint + 1;
1394 c = ((nextcpindex - race_timed_checkpoint + nc + nc - 1) % nc) + 1 - bestfraction;
1395
1396 return l + c / nc;
1397}
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:493
#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:265
#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
@ RACE_NET_CHECKPOINT_HIT_RACE_BY_OPPONENT
Definition net_linked.qh:18
@ RACE_NET_SPEED_AWARD_BEST
Definition net_linked.qh:24
@ RACE_NET_CHECKPOINT_NEXT_QUALIFYING
Definition net_linked.qh:16
@ RACE_NET_PENALTY_QUALIFYING
Definition net_linked.qh:21
@ RACE_NET_SPEED_AWARD
Definition net_linked.qh:23
@ RACE_NET_CHECKPOINT_CLEAR
Definition net_linked.qh:15
@ RACE_NET_SERVER_RANKINGS
Definition net_linked.qh:25
@ RACE_NET_RANKINGS_CNT
Definition net_linked.qh:29
@ RACE_NET_PENALTY_RACE
Definition net_linked.qh:20
@ RACE_NET_CHECKPOINT_HIT_RACE
Definition net_linked.qh:17
@ RACE_NET_SERVER_RECORD
Definition net_linked.qh:22
@ RACE_NET_SERVER_STATUS
Definition net_linked.qh:26
@ RACE_NET_CHECKPOINT_NEXT_SPEC_QUALIFYING
Definition net_linked.qh:19
@ RACE_NET_CHECKPOINT_HIT_QUALIFYING
Definition net_linked.qh:14
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:586
void W_Porto_Fail(entity this, bool failhard)
Definition porto.qc:114
#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:165
float race_CheckpointNetworkID(float f)
Definition race.qc:196
void race_SendRanking(float pos, float prevpos, float del, float msg)
Definition race.qc:293
float race_readTime(string map, float pos)
Definition race.qc:69
float race_PreviousCheckpoint(float f)
Definition race.qc:182
float race_highest_checkpoint
Definition race.qc:168
entity race_lastpenalty
Definition race.qc:154
void checkpoint_passed(entity this, entity player)
Definition race.qc:694
string stored_netname
Definition race.qc:34
void race_SendAll(entity player, bool only_rankings)
Definition race.qc:332
float race_checkpoint_recordspeeds[MAX_CHECKPOINTS]
Definition race.qc:159
void race_PreparePlayer(entity this)
Definition race.qc:1220
void race_ClearRecords()
Definition race.qc:1259
float race_checkpoint_lastlaps[MAX_CHECKPOINTS]
Definition race.qc:162
string race_penalty_reason
Definition race.qc:152
void race_SendNextCheckpoint(entity e, float spec)
Definition race.qc:208
void write_recordmarker(entity pl, float tstart, float dt)
Definition race.qc:57
void race_SpeedAwardFrame(entity player)
Definition race.qc:304
float race_checkpoint_recordspeed[MAX_CHECKPOINTS]
Definition race.qc:166
const float MAX_CHECKPOINTS
Definition race.qc:148
float race_timed_checkpoint
Definition race.qc:169
float race_readPos(string map, float t)
Definition race.qc:79
float defragcpexists
Definition race.qc:172
void race_SendTime(entity e, float cp, float t, float tvalid)
Definition race.qc:485
void race_ClearTime(entity e)
Definition race.qc:676
float defrag_ents
Definition race.qc:171
vector trigger_race_checkpoint_spawn_evalfunc(entity this, entity player, entity spot, vector current)
Definition race.qc:1055
string race_readName(string map, float pos)
Definition race.qc:128
bool race_waypointsprite_visible_for_player(entity this, entity player, entity view)
Definition race.qc:865
float race_NextCheckpoint(float f)
Definition race.qc:174
void checkpoint_use(entity this, entity actor, entity trigger)
Definition race.qc:857
string uid2name(string myuid)
Definition race.qc:36
void race_StartCompleting()
Definition race.qc:1214
float race_GetFractionalLapCount(entity e)
Definition race.qc:1339
void race_checkAndWriteName(entity player)
Definition race.qc:133
IntrusiveList g_racecheckpoints
Definition race.qc:67
void race_send_speedaward_alltimebest(float msg)
Definition race.qc:276
void race_send_speedaward(float msg)
Definition race.qc:267
void race_RetractPlayer(entity this)
Definition race.qc:1229
IntrusiveList g_race_targets
Definition race.qc:66
entity race_checkpoint_lastplayers[MAX_CHECKPOINTS]
Definition race.qc:163
void checkpoint_touch(entity this, entity toucher)
Definition race.qc:851
float race_checkpoint_records[MAX_CHECKPOINTS]
Definition race.qc:158
void race_setTime(string map, float t, string myuid, string mynetname, entity e, bool showmessage)
Definition race.qc:373
void race_SendStatus(float id, entity e)
Definition race.qc:354
void race_send_recordtime(float msg)
Definition race.qc:259
void defrag_waypointsprites(entity targeted, entity checkpoint, int recursionlevel)
Definition race.qc:879
string race_checkpoint_recordholders[MAX_CHECKPOINTS]
Definition race.qc:160
string race_readUID(string map, float pos)
Definition race.qc:74
void race_AbandonRaceCheck(entity p)
Definition race.qc:1203
void penalty_use(entity this, entity actor, entity trigger)
Definition race.qc:1317
float race_penalty_accumulator
Definition race.qc:151
void race_writeTime(string map, float t, string myuid)
Definition race.qc:91
void race_send_rankings_cnt(float msg)
Definition race.qc:285
void target_checkpoint_setup(entity this)
Definition race.qc:1142
void race_ClearPlayerRecords(entity player)
Definition race.qc:1250
void race_deleteTime(string map, float pos)
Definition race.qc:461
void trigger_race_checkpoint_verify(entity this)
Definition race.qc:920
float race_checkpoint_lasttimes[MAX_CHECKPOINTS]
Definition race.qc:161
void race_ImposePenaltyTime(entity pl, float penalty, string reason)
Definition race.qc:1275
void penalty_touch(entity this, entity toucher)
Definition race.qc:1307
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:2318
#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:2230
string record_type
Definition world.qh:55
float ServerProgsDB
Definition world.qh:128