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)
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 entity s = WP_RaceCheckpoint;
886 if (checkpoint.classname == "target_startTimer")
887 s = WP_RaceStart;
888 else if (checkpoint.classname == "target_stopTimer")
889 s = WP_RaceFinish;
890
891 for (entity t = findchain(target, targeted.targetname); t; t = t.chain)
892 {
893 if (t.modelindex)
894 {
895 WaypointSprite_SpawnFixed(s, (t.absmin + t.absmax) * 0.5, t, sprite, RADARICON_NONE);
896 t.sprite.realowner = checkpoint;
897 t.sprite.waypointsprite_visible_for_player = race_waypointsprite_visible_for_player;
898 }
899
900 if (t.loopdetector & 2)
901 {
903 LOG_WARNF("MAP BUG: target field of %s \"%s\" loops back to %s \"%s\"\n", t.classname, t.targetname, targeted.classname, targeted.targetname);
904 }
905 else if (t.targetname)
906 {
907 targeted.loopdetector |= 2;
908 defrag_waypointsprites(t, checkpoint);
909 targeted.loopdetector &= ~2;
910 }
911 }
912}
913
915{
916 static bool have_verified;
917 if (have_verified) return;
918 have_verified = true;
919
920 int qual = g_race_qualifying;
921
922 int pl_race_checkpoint = 0;
923 int pl_race_place = 0;
924
925 if (g_race) {
926 for (int i = 0; i <= race_highest_checkpoint; ++i) {
927 pl_race_checkpoint = race_NextCheckpoint(i);
928
929 // race only (middle of the race)
931 pl_race_place = 0;
932 if (!Spawn_FilterOutBadSpots(this, findchain(classname, "info_player_deathmatch"), 0, false, true)) {
933 error(strcat("Checkpoint ", ftos(i), " misses a spawnpoint with race_place==", ftos(pl_race_place), " (used for respawning in race) - bailing out"));
934 }
935
936 if (i == 0) {
937 // qualifying only
939 pl_race_place = race_lowest_place_spawn;
940 if (!Spawn_FilterOutBadSpots(this, findchain(classname, "info_player_deathmatch"), 0, false, true)) {
941 error(strcat("Checkpoint ", ftos(i), " misses a spawnpoint with race_place==", ftos(pl_race_place), " (used for qualifying) - bailing out"));
942 }
943
944 // race only (initial spawn)
946 for (int p = 1; p <= race_highest_place_spawn; ++p) {
947 pl_race_place = p;
948 if (!Spawn_FilterOutBadSpots(this, findchain(classname, "info_player_deathmatch"), 0, false, true)) {
949 error(strcat("Checkpoint ", ftos(i), " misses a spawnpoint with race_place==", ftos(pl_race_place), " (used for initially spawning in race) - bailing out"));
950 }
951 }
952 }
953 }
954 } else if (!defrag_ents) {
955 // qualifying only
956 pl_race_checkpoint = race_NextCheckpoint(0);
958 pl_race_place = race_lowest_place_spawn;
959 if (!Spawn_FilterOutBadSpots(this, findchain(classname, "info_player_deathmatch"), 0, false, true)) {
960 error(strcat("Checkpoint 0 misses a spawnpoint with race_place==", ftos(pl_race_place), " (used for qualifying) - bailing out"));
961 }
962 } else {
963 pl_race_checkpoint = race_NextCheckpoint(0);
965 pl_race_place = 0; // there's only one spawn on defrag maps
966
967 // check if a defragcp file already exists, then read it and apply the checkpoint order
968 float fh;
969 float len;
970 bool broken_defragcp = false;
971 string l;
972
973 defragcpexists = fh = fopen(strcat("maps/", GetMapname(), ".defragcp"), FILE_READ);
974 if (fh >= 0) {
975 while ((l = fgets(fh))) {
976 len = tokenize_console(l);
977 if (len != 2) {
978 broken_defragcp = true;
979 break;
980 }
981 for (entity cp = NULL; (cp = find(cp, classname, "target_checkpoint"));) {
982 if (argv(0) == cp.targetname) {
983 if(cp.race_checkpoint != -2 && cp.race_checkpoint != stof(argv(1))) {
984 broken_defragcp = true; // cp had been previously set with diff order nr defragcp file is broken
985 break;
986 }
987 cp.race_checkpoint = stof(argv(1));
988 }
989 }
990 if(broken_defragcp) break; // no point to keep going we'll rebuild the whole order
991 }
992 fclose(fh);
993 if(broken_defragcp) {
994 // something's wrong in the defrag cp file, set defragcpexists to -1 so that it will be rewritten when someone finishes
995 // also clear any set cp order to make sure map is completable
996 defragcpexists = -1;
997 for (entity cp = NULL; (cp = find(cp, classname, "target_checkpoint"));) {
998 cp.race_checkpoint = -2;
999 }
1000 }
1001 }
1002 }
1003
1004 g_race_qualifying = qual;
1005
1007 if (defrag_ents) {
1008 IL_EACH(g_race_targets, it.classname == "target_checkpoint" || it.classname == "target_startTimer" || it.classname == "target_stopTimer",
1009 {
1010 defrag_waypointsprites(it, it);
1011
1012 if(it.classname == "target_checkpoint") {
1013 if(it.race_checkpoint == -2)
1014 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
1015 }
1016 });
1017 if (defragcpexists != -1) {
1018 float largest_cp_id = 0;
1019 for (entity cp = NULL; (cp = find(cp, classname, "target_checkpoint"));) {
1020 if (cp.race_checkpoint > largest_cp_id) {
1021 largest_cp_id = cp.race_checkpoint;
1022 }
1023 }
1024 for (entity cp = NULL; (cp = find(cp, classname, "target_stopTimer"));) {
1025 cp.race_checkpoint = largest_cp_id + 1; // finish line
1026 }
1027 race_highest_checkpoint = largest_cp_id + 1;
1028 race_timed_checkpoint = largest_cp_id + 1;
1029 } else {
1030 for (entity cp = NULL; (cp = find(cp, classname, "target_stopTimer"));) {
1031 cp.race_checkpoint = 255; // finish line
1032 }
1035 }
1036 } else {
1037 IL_EACH(g_racecheckpoints, it.sprite,
1038 {
1039 if (it.race_checkpoint == 0) {
1040 WaypointSprite_UpdateSprites(it.sprite, WP_RaceStart, WP_Null, WP_Null);
1041 } else if (it.race_checkpoint == race_timed_checkpoint) {
1042 WaypointSprite_UpdateSprites(it.sprite, WP_RaceFinish, WP_Null, WP_Null);
1043 }
1044 });
1045 }
1046 }
1047}
1048
1050{
1052 {
1053 // spawn at first
1054 if(this.race_checkpoint != 0)
1055 return '-1 0 0';
1056 if(spot.race_place != race_lowest_place_spawn)
1057 return '-1 0 0';
1058 }
1059 else
1060 {
1061 if(this.race_checkpoint != player.race_respawn_checkpoint)
1062 return '-1 0 0';
1063 // try reusing the previous spawn
1064 if(this == player.race_respawn_spotref || spot == player.race_respawn_spotref)
1065 current.x += SPAWN_PRIO_RACE_PREVIOUS_SPAWN;
1066 if(this.race_checkpoint == 0)
1067 {
1068 int pl = player.race_place;
1070 pl = 0;
1071 if(pl == 0 && !player.race_started)
1072 pl = race_highest_place_spawn; // use last place if they have not even touched finish yet
1073 if(spot.race_place != pl)
1074 return '-1 0 0';
1075 }
1076 }
1077 return current;
1078}
1079
1080spawnfunc(trigger_race_checkpoint)
1081{
1082 vector o;
1083 if(!g_race && !g_cts) { delete(this); return; }
1084
1086
1087 this.use = checkpoint_use;
1088 if (!(this.spawnflags & 1))
1090
1091 o = (this.absmin + this.absmax) * 0.5;
1092 tracebox(o, PL_MIN_CONST, PL_MAX_CONST, o - '0 0 1' * (o.z - this.absmin.z), MOVE_NORMAL, this);
1094 this.nearestwaypointtimeout = -1;
1095
1096 if(this.message == "")
1097 this.message = "went backwards";
1098 if (this.message2 == "")
1099 this.message2 = "was pushed backwards by";
1100 if (this.race_penalty_reason == "")
1101 this.race_penalty_reason = "missing a checkpoint";
1102
1103 this.race_checkpoint = this.cnt;
1104
1106 {
1108 if(this.spawnflags & 8)
1110 else
1112 }
1113
1114 if(!this.race_penalty)
1115 {
1116 if(this.race_checkpoint)
1117 WaypointSprite_SpawnFixed(WP_RaceCheckpoint, o, this, sprite, RADARICON_NONE);
1118 else
1119 WaypointSprite_SpawnFixed(WP_RaceStartFinish, o, this, sprite, RADARICON_NONE);
1120 }
1121
1122 this.sprite.waypointsprite_visible_for_player = race_waypointsprite_visible_for_player;
1124
1125 if (!g_racecheckpoints)
1128
1129 // trigger_race_checkpoint_verify checks this list too
1130 if (!g_race_targets)
1132
1134}
1135
1137{
1138 if(!g_race && !g_cts) { delete(this); return; }
1139 defrag_ents = 1;
1140
1141 if (this.targetname == "")
1142 objerror(this, sprintf("^3%s without a targetname at %v", this.classname, this.origin));
1143
1144 this.spawnflags = 0;
1145 this.use = checkpoint_use;
1146
1147 if(this.classname == "target_startTimer")
1148 this.race_checkpoint = 0;
1149 else
1150 this.race_checkpoint = -2;
1151
1153
1154 if (!g_race_targets)
1156 IL_PUSH(g_race_targets, this);
1157
1158 // trigger_race_checkpoint_verify checks this list too
1159 if (!g_racecheckpoints)
1161
1163}
1164
1165// Q3DF entity names
1166spawnfunc(target_startTimer) { target_checkpoint_setup(this); }
1167spawnfunc(target_stopTimer) { target_checkpoint_setup(this); }
1168spawnfunc(target_checkpoint) { target_checkpoint_setup(this); }
1169
1171{
1173 {
1174 CS(p).race_completed = 1;
1176 Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_RACE_ABANDONED, p.netname);
1177 ClientData_Touch(p, true);
1178 }
1179}
1180
1182{
1183 race_completing = 1;
1185}
1186
1188{
1189 race_ClearTime(this);
1190 this.race_place = 0;
1191 this.race_started = 0;
1192 this.race_respawn_checkpoint = 0;
1194}
1195
1197{
1198 if(!g_race && !g_cts)
1199 return;
1201 race_ClearTime(this);
1203}
1204
1205spawnfunc(info_player_race)
1206{
1207 if(!g_race && !g_cts) { delete(this); return; }
1208 ++race_spawns;
1209 spawnfunc_info_player_deathmatch(this);
1210
1215}
1216
1218{
1219 for(int i = 0; i < MAX_CHECKPOINTS; ++i)
1220 {
1221 player.race_checkpoint_record[i] = 0;
1222 player.race_checkpoint_recordspeed[i] = 0;
1223 }
1224}
1225
1227{
1228 for(int j = 0; j < MAX_CHECKPOINTS; ++j)
1229 {
1233 }
1234
1235 FOREACH_CLIENT(true, {
1236 float p = it.race_place;
1238 it.race_place = p;
1239 });
1240}
1241
1242void race_ImposePenaltyTime(entity pl, float penalty, string reason)
1243{
1245 {
1246 pl.race_penalty_accumulator += penalty;
1247 if(IS_REAL_CLIENT(pl))
1248 {
1249 msg_entity = pl;
1250 WRITESPECTATABLE_MSG_ONE(msg_entity, {
1251 WriteHeader(MSG_ONE, TE_CSQC_RACE);
1253 WriteShort(MSG_ONE, TIME_ENCODE(penalty));
1254 WriteString(MSG_ONE, reason);
1255 });
1256 }
1257 }
1258 else
1259 {
1260 pl.race_penalty = time + penalty;
1261 if(IS_REAL_CLIENT(pl))
1262 {
1263 msg_entity = pl;
1264 WRITESPECTATABLE_MSG_ONE(msg_entity, {
1265 WriteHeader(MSG_ONE, TE_CSQC_RACE);
1267 WriteShort(MSG_ONE, TIME_ENCODE(penalty));
1268 WriteString(MSG_ONE, reason);
1269 });
1270 }
1271 }
1272}
1273
1275{
1277 if(toucher.race_lastpenalty != this)
1278 {
1279 toucher.race_lastpenalty = this;
1281 }
1282}
1283
1284void penalty_use(entity this, entity actor, entity trigger)
1285{
1287}
1288
1289spawnfunc(trigger_race_penalty)
1290{
1291 // TODO: find out why this wasnt done:
1292 //if(!g_cts && !g_race) { remove(this); return; }
1293
1295
1296 this.use = penalty_use;
1297 if (!(this.spawnflags & 1))
1298 settouch(this, penalty_touch);
1299
1300 if (this.race_penalty_reason == "")
1301 this.race_penalty_reason = "missing a checkpoint";
1302 if (!this.race_penalty)
1303 this.race_penalty = 5;
1304}
1305
1307{
1308 // interesting metrics (idea by KrimZon) to maybe sort players in the
1309 // scoreboard, immediately updates when overtaking
1310 //
1311 // requires the track to be built so you never get farther away from the
1312 // next checkpoint, though, and current Xonotic race maps are not built that
1313 // way
1314 //
1315 // also, this code is slow and would need optimization (i.e. "next CP"
1316 // links on CP entities)
1317
1318 float l = GameRules_scoring_add(e, RACE_LAPS, 0);
1319 if(CS(e).race_completed)
1320 return l; // not fractional
1321
1322 vector o0, o1;
1323 float bestfraction, fraction;
1324 entity lastcp;
1325 float nextcpindex, lastcpindex;
1326
1327 nextcpindex = max(e.race_checkpoint, 0);
1328 lastcpindex = e.race_respawn_checkpoint;
1329 lastcp = e.race_respawn_spotref;
1330
1331 if(nextcpindex == lastcpindex)
1332 return l; // finish
1333
1334 bestfraction = 1;
1336 {
1337 if(it.race_checkpoint != lastcpindex)
1338 continue;
1339 if(lastcp)
1340 if(it != lastcp)
1341 continue;
1342 o0 = (it.absmin + it.absmax) * 0.5;
1344 {
1345 if(it.race_checkpoint != nextcpindex)
1346 continue;
1347 o1 = (it.absmin + it.absmax) * 0.5;
1348 if(o0 == o1)
1349 continue;
1350 fraction = bound(0.0001, sqrt(vlen2(e.origin - o1) / vlen2(o0 - o1)), 1);
1351 if(fraction < bestfraction)
1352 bestfraction = fraction;
1353 });
1354 });
1355
1356 // we are at CP "nextcpindex - bestfraction"
1357 // race_timed_checkpoint == 4: then nextcp==4 means 0.9999x, nextcp==0 means 0.0000x
1358 // race_timed_checkpoint == 0: then nextcp==0 means 0.9999x
1359 float c, nc;
1360 nc = race_highest_checkpoint + 1;
1361 c = ((nextcpindex - race_timed_checkpoint + nc + nc - 1) % nc) + 1 - bestfraction;
1362
1363 return l + c / nc;
1364}
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(name,...)
Definition base.qh:186
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:168
#define TIME_ENCODE(t)
Definition util.qh:107
#define TIME_ENCODED_TOSTRING(n, compact)
Definition util.qh:103
#define TIME_DECODE(n)
Definition util.qh:108
const vector PL_MIN_CONST
Definition constants.qh:53
const int RANKINGS_CNT
Definition constants.qh:32
const int INITPRIO_FINDTARGET
Definition constants.qh:89
const vector PL_MAX_CONST
Definition constants.qh:52
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:484
#define DMG_NOWEP
Definition damage.qh:104
#define tokenize_console
WriteString(chan, ent.netname)
WriteByte(chan, ent.angles.y/DEC_FACTOR)
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:353
#define WriteHeader(to, id)
Definition net.qh:265
#define EXACTTRIGGER_TOUCH(e, t)
Definition common.qh:133
#define EXACTTRIGGER_INIT
Definition common.qh:134
#define LOG_WARNF(...)
Definition log.qh:59
#define db_remove(db, key)
Definition map.qh:100
ERASEABLE string db_get(int db, string key)
Definition map.qh:93
ERASEABLE void db_put(int db, string key, string value)
Definition map.qh:103
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)
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)
float sqrt(float f)
void WriteCoord(float data, float dest, float desto)
float min(float f,...)
vector normalize(vector v)
string ftos(float f)
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:17
@ RACE_NET_SPEED_AWARD_BEST
Definition net_linked.qh:23
@ RACE_NET_CHECKPOINT_NEXT_QUALIFYING
Definition net_linked.qh:15
@ RACE_NET_PENALTY_QUALIFYING
Definition net_linked.qh:20
@ RACE_NET_SPEED_AWARD
Definition net_linked.qh:22
@ RACE_NET_CHECKPOINT_CLEAR
Definition net_linked.qh:14
@ RACE_NET_SERVER_RANKINGS
Definition net_linked.qh:24
@ RACE_NET_RANKINGS_CNT
Definition net_linked.qh:28
@ RACE_NET_PENALTY_RACE
Definition net_linked.qh:19
@ RACE_NET_CHECKPOINT_HIT_RACE
Definition net_linked.qh:16
@ RACE_NET_SERVER_RECORD
Definition net_linked.qh:21
@ RACE_NET_SERVER_STATUS
Definition net_linked.qh:25
@ RACE_NET_CHECKPOINT_NEXT_SPEC_QUALIFYING
Definition net_linked.qh:18
@ RACE_NET_CHECKPOINT_HIT_QUALIFYING
Definition net_linked.qh:13
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:1500
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
#define objerror
Definition pre.qh:8
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:96
entity entity toucher
Definition self.qh:76
#define settouch(e, f)
Definition self.qh:77
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:1187
void race_ClearRecords()
Definition race.qc:1226
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:1049
string race_readName(string map, float pos)
Definition race.qc:128
void defrag_waypointsprites(entity targeted, entity checkpoint)
Definition race.qc:879
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:1181
float race_GetFractionalLapCount(entity e)
Definition race.qc:1306
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:1196
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
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:1170
void penalty_use(entity this, entity actor, entity trigger)
Definition race.qc:1284
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:1136
void race_ClearPlayerRecords(entity player)
Definition race.qc:1217
void race_deleteTime(string map, float pos)
Definition race.qc:461
void trigger_race_checkpoint_verify(entity this)
Definition race.qc:914
float race_checkpoint_lasttimes[MAX_CHECKPOINTS]
Definition race.qc:161
void race_ImposePenaltyTime(entity pl, float penalty, string reason)
Definition race.qc:1242
void penalty_touch(entity this, entity toucher)
Definition race.qc:1274
bool autocvar_g_allow_checkpoints
Definition race.qh:3
float race_spawns
Definition race.qh:16
bool autocvar_g_race_cptimes_onlyself
Definition race.qh:9
float speedaward_alltimebest
Definition race.qh:61
float race_lowest_place_spawn
Definition race.qh:18
float speedaward_speed
Definition race.qh:57
entity race_respawn_spotref
Definition race.qh:33
float speedaward_lastupdate
Definition race.qh:14
float race_place
Definition race.qh:22
float race_respawn_checkpoint
Definition race.qh:32
string speedaward_alltimebest_uid
Definition race.qh:63
float race_highest_place_spawn
Definition race.qh:17
float race_started
Definition race.qh:23
float race_completed
Definition race.qh:24
int g_race_qualifying
Definition race.qh:11
int autocvar_g_cts_send_rankings_cnt
Definition race.qh:8
string speedaward_uid
Definition race.qh:59
float race_completing
Definition race.qh:26
float speedaward_lastsent
Definition race.qh:13
string speedaward_holder
Definition race.qh:58
string speedaward_alltimebest_holder
Definition race.qh:62
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:107
#define CS_CVAR(this)
Definition state.qh:51
ClientState CS(Client this)
Definition state.qh:47
#define strfree(this)
Definition string.qh:57
#define strcpy(this, s)
Definition string.qh:51
entity sprite
Definition sv_assault.qc:11
if(frag_attacker.flagcarried)
Definition sv_ctf.qc:2319
#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 vlen2(v)
Definition vector.qh:4
#define vdist(v, cmp, f)
Vector distance comparison, avoids sqrt().
Definition vector.qh:8
#define vec2(...)
Definition vector.qh:95
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:2201
string record_type
Definition world.qh:55
bool autocvar_sv_dedicated
Definition world.qh:41
float ServerProgsDB
Definition world.qh:127