]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blobdiff - qcsrc/server/race.qc
If a vehicle is activating a checkpoint, count the owner as passing, fixes race with...
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / race.qc
index ef82cc3d603c6751f6811bcdab5a75353ade2268..350872ce77148f529726459d93c35954ef8140cb 100644 (file)
@@ -1,5 +1,7 @@
 #include "race.qh"
 
+#include <server/defs.qh>
+#include <server/miscfunctions.qh>
 #include "client.qh"
 #include "portals.qh"
 #include "scores.qh"
@@ -9,8 +11,12 @@
 #include "../common/deathtypes/all.qh"
 #include "../common/notifications/all.qh"
 #include "../common/mapinfo.qh"
+#include <common/gamemodes/rules.qh>
 #include <common/net_linked.qh>
-#include "../common/triggers/subs.qh"
+#include <common/state.qh>
+#include <common/weapons/weapon/porto.qh>
+#include "../common/mapobjects/subs.qh"
+#include <common/mapobjects/triggers.qh>
 #include "../lib/warpzone/util_server.qh"
 #include "../lib/warpzone/common.qh"
 #include "../common/mutators/mutator/waypoints/waypointsprites.qh"
@@ -25,8 +31,6 @@ void race_InitSpectator()
                        race_SendNextCheckpoint(msg_entity.enemy, 1);
 }
 
-void W_Porto_Fail(entity this, float failhard);
-
 float race_readTime(string map, float pos)
 {
        string rr = ((g_cts) ? CTS_RECORD : ((g_ctf) ? CTF_RECORD : RACE_RECORD));
@@ -80,8 +84,11 @@ void race_writeTime(string map, float t, string myuid)
                // player has no ranked record yet
                for (i = RANKINGS_CNT; i > newpos; --i)
                {
-                       db_put(ServerProgsDB, strcat(map, rr, "time", ftos(i)), ftos(race_readTime(map, i - 1)));
-                       db_put(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(i)), race_readUID(map, i - 1));
+                       float other_time = race_readTime(map, i - 1);
+                       if (other_time) {
+                               db_put(ServerProgsDB, strcat(map, rr, "time", ftos(i)), ftos(other_time));
+                               db_put(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(i)), race_readUID(map, i - 1));
+                       }
                }
        }
 
@@ -100,8 +107,6 @@ string race_readName(string map, float pos)
 
 const float MAX_CHECKPOINTS = 255;
 
-spawnfunc(target_checkpoint);
-
 .float race_penalty;
 .float race_penalty_accumulator;
 .string race_penalty_reason;
@@ -116,6 +121,8 @@ float race_checkpoint_lasttimes[MAX_CHECKPOINTS];
 float race_checkpoint_lastlaps[MAX_CHECKPOINTS];
 entity race_checkpoint_lastplayers[MAX_CHECKPOINTS];
 
+.float race_checkpoint_record[MAX_CHECKPOINTS];
+
 float race_highest_checkpoint;
 float race_timed_checkpoint;
 
@@ -158,16 +165,13 @@ float race_CheckpointNetworkID(float f)
 
 void race_SendNextCheckpoint(entity e, float spec) // qualifying only
 {
-       float recordtime;
-       string recordholder;
-       float cp;
-
        if(!e.race_laptime)
                return;
 
-       cp = e.race_checkpoint;
-       recordtime = race_checkpoint_records[cp];
-       recordholder = race_checkpoint_recordholders[cp];
+       int cp = e.race_checkpoint;
+       float recordtime = race_checkpoint_records[cp];
+       float myrecordtime = e.race_checkpoint_record[cp];
+       string recordholder = race_checkpoint_recordholders[cp];
        if(recordholder == e.netname)
                recordholder = "";
 
@@ -188,6 +192,8 @@ void race_SendNextCheckpoint(entity e, float spec) // qualifying only
                        WriteByte(MSG_ONE, RACE_NET_CHECKPOINT_NEXT_QUALIFYING);
                WriteByte(MSG_ONE, race_CheckpointNetworkID(cp)); // checkpoint the player will be at next
                WriteInt24_t(MSG_ONE, recordtime);
+               if(!spec)
+                       WriteInt24_t(MSG_ONE, myrecordtime);
                WriteString(MSG_ONE, recordholder);
        });
 }
@@ -219,6 +225,14 @@ void race_send_speedaward_alltimebest(float msg)
        WriteString(msg, speedaward_alltimebest_holder);
 }
 
+void race_send_rankings_cnt(float msg)
+{
+       WriteHeader(msg, TE_CSQC_RACE);
+       WriteByte(msg, RACE_NET_RANKINGS_CNT);
+       int m = min(RANKINGS_CNT, autocvar_g_cts_send_rankings_cnt);
+       WriteByte(msg, m);
+}
+
 void race_SendRankings(float pos, float prevpos, float del, float msg)
 {
        WriteHeader(msg, TE_CSQC_RACE);
@@ -311,9 +325,7 @@ void race_setTime(string map, float t, string myuid, string mynetname, entity e,
        }
 
        race_SendRankings(newpos, player_prevpos, 0, MSG_ALL);
-       if(rankings_reply)
-               strunzone(rankings_reply);
-       rankings_reply = strzone(getrankings());
+       strcpy(rankings_reply, getrankings());
 
        if(newpos == player_prevpos)
        {
@@ -361,9 +373,7 @@ void race_deleteTime(string map, float pos)
        if(pos == 1)
                race_send_recordtime(MSG_ALL);
 
-       if(rankings_reply)
-               strunzone(rankings_reply);
-       rankings_reply = strzone(getrankings());
+       strcpy(rankings_reply, getrankings());
 }
 
 void race_SendTime(entity e, float cp, float t, float tvalid)
@@ -377,25 +387,25 @@ void race_SendTime(entity e, float cp, float t, float tvalid)
 
        if(tvalid)
        if(cp == race_timed_checkpoint) // finish line
-       if (!e.race_completed)
+       if (!CS(e).race_completed)
        {
                float s;
                if(g_race_qualifying)
                {
-                       s = PlayerScore_Add(e, SP_RACE_FASTEST, 0);
+                       s = GameRules_scoring_add(e, RACE_FASTEST, 0);
                        if(!s || t < s)
-                               PlayerScore_Add(e, SP_RACE_FASTEST, t - s);
+                               GameRules_scoring_add(e, RACE_FASTEST, t - s);
                }
                else
                {
-                       s = PlayerScore_Add(e, SP_RACE_FASTEST, 0);
+                       s = GameRules_scoring_add(e, RACE_FASTEST, 0);
                        if(!s || t < s)
-                               PlayerScore_Add(e, SP_RACE_FASTEST, t - s);
+                               GameRules_scoring_add(e, RACE_FASTEST, t - s);
 
-                       s = PlayerScore_Add(e, SP_RACE_TIME, 0);
+                       s = GameRules_scoring_add(e, RACE_TIME, 0);
                        snew = TIME_ENCODE(time - game_starttime);
-                       PlayerScore_Add(e, SP_RACE_TIME, snew - s);
-                       l = PlayerTeamScore_Add(e, SP_RACE_LAPS, ST_RACE_LAPS, 1);
+                       GameRules_scoring_add(e, RACE_TIME, snew - s);
+                       l = GameRules_scoring_add_team(e, RACE_LAPS, 1);
 
                        if(autocvar_fraglimit)
                                if(l >= autocvar_fraglimit)
@@ -403,7 +413,7 @@ void race_SendTime(entity e, float cp, float t, float tvalid)
 
                        if(race_completing)
                        {
-                               e.race_completed = 1;
+                               CS(e).race_completed = 1;
                                MAKE_INDEPENDENT_PLAYER(e);
                                Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_RACE_FINISHED, e.netname);
                                ClientData_Touch(e);
@@ -411,13 +421,15 @@ void race_SendTime(entity e, float cp, float t, float tvalid)
                }
        }
 
-       float recordtime;
-       string recordholder;
        if(g_race_qualifying)
        {
+               float recordtime;
+               string recordholder;
+
                if(tvalid)
                {
                        recordtime = race_checkpoint_records[cp];
+                       float myrecordtime = e.race_checkpoint_record[cp];
                        recordholder = strcat1(race_checkpoint_recordholders[cp]); // make a tempstring copy, as we'll possibly strunzone it!
                        if(recordholder == e.netname)
                                recordholder = "";
@@ -429,17 +441,17 @@ void race_SendTime(entity e, float cp, float t, float tvalid)
                                        race_setTime(GetMapname(), t, e.crypto_idfp, e.netname, e, true);
                                        MUTATOR_CALLHOOK(Race_FinalCheckpoint, e);
                                }
+                               if(t < myrecordtime || myrecordtime == 0)
+                                       e.race_checkpoint_record[cp] = t; // resending done below
+
                                if(t < recordtime || recordtime == 0)
                                {
                                        race_checkpoint_records[cp] = t;
-                                       if(race_checkpoint_recordholders[cp])
-                                               strunzone(race_checkpoint_recordholders[cp]);
-                                       race_checkpoint_recordholders[cp] = strzone(e.netname);
+                                       strcpy(race_checkpoint_recordholders[cp], e.netname);
                                        if(g_race_qualifying)
-                                       {
-                                               FOREACH_CLIENT(IS_PLAYER(it) && IS_REAL_CLIENT(it) && it.race_checkpoint == cp, LAMBDA(race_SendNextCheckpoint(it, 0)));
-                                       }
+                                               FOREACH_CLIENT(IS_PLAYER(it) && IS_REAL_CLIENT(it) && it.race_checkpoint == cp, { race_SendNextCheckpoint(it, 0); });
                                }
+
                        }
                }
                else
@@ -452,16 +464,21 @@ void race_SendTime(entity e, float cp, float t, float tvalid)
 
                if(IS_REAL_CLIENT(e))
                {
-                       msg_entity = e;
                        if(g_race_qualifying)
                        {
-                               WRITESPECTATABLE_MSG_ONE(msg_entity, {
-                                       WriteHeader(MSG_ONE, TE_CSQC_RACE);
-                                       WriteByte(MSG_ONE, RACE_NET_CHECKPOINT_HIT_QUALIFYING);
-                                       WriteByte(MSG_ONE, race_CheckpointNetworkID(cp)); // checkpoint the player now is at
-                                       WriteInt24_t(MSG_ONE, t); // time to that intermediate
-                                       WriteInt24_t(MSG_ONE, recordtime); // previously best time
-                                       WriteString(MSG_ONE, recordholder); // record holder
+                               FOREACH_CLIENT(IS_REAL_CLIENT(it),
+                               {
+                                       if(it == e || (IS_SPEC(it) && it.enemy == e))
+                                       {
+                                               msg_entity = it;
+                                               WriteHeader(MSG_ONE, TE_CSQC_RACE);
+                                               WriteByte(MSG_ONE, RACE_NET_CHECKPOINT_HIT_QUALIFYING);
+                                               WriteByte(MSG_ONE, race_CheckpointNetworkID(cp)); // checkpoint the player now is at
+                                               WriteInt24_t(MSG_ONE, t); // time to that intermediate
+                                               WriteInt24_t(MSG_ONE, recordtime); // previously best time
+                                               WriteInt24_t(MSG_ONE, ((tvalid) ? it.race_checkpoint_record[cp] : 0)); // previously best time
+                                               WriteString(MSG_ONE, recordholder); // record holder
+                                       }
                                });
                        }
                }
@@ -469,11 +486,10 @@ void race_SendTime(entity e, float cp, float t, float tvalid)
        else // RACE! Not Qualifying
        {
                float mylaps, lother, othtime;
-               entity oth;
-               oth = race_checkpoint_lastplayers[cp];
+               entity oth = race_checkpoint_lastplayers[cp];
                if(oth)
                {
-                       mylaps = PlayerScore_Add(e, SP_RACE_LAPS, 0);
+                       mylaps = GameRules_scoring_add(e, RACE_LAPS, 0);
                        lother = race_checkpoint_lastlaps[cp];
                        othtime = race_checkpoint_lasttimes[cp];
                }
@@ -491,13 +507,13 @@ void race_SendTime(entity e, float cp, float t, float tvalid)
                                {
                                        WriteInt24_t(MSG_ONE, 0);
                                        WriteByte(MSG_ONE, 0);
-                                       WriteString(MSG_ONE, "");
+                                       WriteByte(MSG_ONE, 0);
                                }
                                else
                                {
                                        WriteInt24_t(MSG_ONE, TIME_ENCODE(time - race_checkpoint_lasttimes[cp]));
                                        WriteByte(MSG_ONE, mylaps - lother);
-                                       WriteString(MSG_ONE, oth.netname); // record holder
+                                       WriteByte(MSG_ONE, etof(oth)); // record holder
                                }
                        });
                }
@@ -517,13 +533,13 @@ void race_SendTime(entity e, float cp, float t, float tvalid)
                                {
                                        WriteInt24_t(MSG_ONE, 0);
                                        WriteByte(MSG_ONE, 0);
-                                       WriteString(MSG_ONE, "");
+                                       WriteByte(MSG_ONE, 0);
                                }
                                else
                                {
                                        WriteInt24_t(MSG_ONE, TIME_ENCODE(time - othtime));
                                        WriteByte(MSG_ONE, lother - mylaps);
-                                       WriteString(MSG_ONE, e.netname); // record holder
+                                       WriteByte(MSG_ONE, etof(e) - 1); // record holder
                                }
                        });
                }
@@ -692,13 +708,15 @@ void checkpoint_passed(entity this, entity player)
        else
        {
                if(this.spawnflags & 4)
-                       Damage (player, this, this, 10000, DEATH_HURTTRIGGER.m_id, player.origin, '0 0 0');
+                       Damage (player, this, this, 10000, DEATH_HURTTRIGGER.m_id, DMG_NOWEP, player.origin, '0 0 0');
        }
 }
 
 void checkpoint_touch(entity this, entity toucher)
 {
        EXACTTRIGGER_TOUCH(this, toucher);
+       if(IS_VEHICLE(toucher) && toucher.owner)
+               toucher = toucher.owner;
        checkpoint_passed(this, toucher);
 }
 
@@ -707,6 +725,8 @@ void checkpoint_use(entity this, entity actor, entity trigger)
        if(trigger.classname == "info_player_deathmatch") // a spawn, a spawn
                return;
 
+       if(IS_VEHICLE(actor) && actor.owner)
+               actor = actor.owner;
        checkpoint_passed(this, actor);
 }
 
@@ -802,8 +822,10 @@ void trigger_race_checkpoint_verify(entity this)
 
        g_race_qualifying = qual;
 
-       IL_EACH(g_race_targets, true,
+       IL_EACH(g_race_targets, it.classname == "target_checkpoint" || it.classname == "target_startTimer" || it.classname == "target_stopTimer",
        {
+               if(it.targetname == "" || !it.targetname) // somehow this is a case...
+                       continue;
                entity cpt = it;
                FOREACH_ENTITY_STRING(target, cpt.targetname,
                {
@@ -820,12 +842,15 @@ void trigger_race_checkpoint_verify(entity this)
 
        if (race_timed_checkpoint) {
                if (defrag_ents) {
-                       IL_EACH(g_race_targets, true,
+                       IL_EACH(g_race_targets, it.classname == "target_checkpoint" || it.classname == "target_startTimer" || it.classname == "target_stopTimer",
                        {
                                entity cpt = it;
                                if(it.classname == "target_startTimer" || it.classname == "target_stopTimer") {
+                                       if(it.targetname == "" || !it.targetname) // somehow this is a case...
+                                               continue;
                                        FOREACH_ENTITY_STRING(target, cpt.targetname, {
-                                               WaypointSprite_UpdateSprites(it.sprite, ((cpt.classname == "target_startTimer") ? WP_RaceStart : WP_RaceFinish), WP_Null, WP_Null);
+                                               if(it.sprite)
+                                                       WaypointSprite_UpdateSprites(it.sprite, ((cpt.classname == "target_startTimer") ? WP_RaceStart : WP_RaceFinish), WP_Null, WP_Null);
                                        });
                                }
                                if(it.classname == "target_checkpoint") {
@@ -977,7 +1002,7 @@ spawnfunc(target_checkpoint) // defrag entity
        defrag_ents = 1;
 
        // if this is targeted, then it probably isn't a trigger
-       bool is_trigger = !boolean(!this.nottargeted && this.targetname != "");
+       bool is_trigger = this.targetname == "";
 
        if(is_trigger)
                EXACTTRIGGER_INIT;
@@ -1021,9 +1046,9 @@ spawnfunc(target_stopTimer) { spawnfunc_target_checkpoint(this); }
 
 void race_AbandonRaceCheck(entity p)
 {
-       if(race_completing && !p.race_completed)
+       if(race_completing && !CS(p).race_completed)
        {
-               p.race_completed = 1;
+               CS(p).race_completed = 1;
                MAKE_INDEPENDENT_PLAYER(p);
                Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_RACE_ABANDONED, p.netname);
                ClientData_Touch(p);
@@ -1033,7 +1058,7 @@ void race_AbandonRaceCheck(entity p)
 void race_StartCompleting()
 {
        race_completing = 1;
-       FOREACH_CLIENT(IS_PLAYER(it) && IS_DEAD(it), LAMBDA(race_AbandonRaceCheck(it)));
+       FOREACH_CLIENT(IS_PLAYER(it) && IS_DEAD(it), { race_AbandonRaceCheck(it); });
 }
 
 void race_PreparePlayer(entity this)
@@ -1068,21 +1093,17 @@ spawnfunc(info_player_race)
 
 void race_ClearRecords()
 {
-       float i;
-
-       for(i = 0; i < MAX_CHECKPOINTS; ++i)
+       for(int j = 0; j < MAX_CHECKPOINTS; ++j)
        {
-               race_checkpoint_records[i] = 0;
-               if(race_checkpoint_recordholders[i])
-                       strunzone(race_checkpoint_recordholders[i]);
-               race_checkpoint_recordholders[i] = string_null;
+               race_checkpoint_records[j] = 0;
+               strfree(race_checkpoint_recordholders[j]);
        }
 
-       FOREACH_CLIENT(true, LAMBDA(
+       FOREACH_CLIENT(true, {
                float p = it.race_place;
                race_PreparePlayer(it);
                it.race_place = p;
-       ));
+       });
 }
 
 void race_ImposePenaltyTime(entity pl, float penalty, string reason)
@@ -1162,8 +1183,8 @@ float race_GetFractionalLapCount(entity e)
        // links on CP entities)
 
        float l;
-       l = PlayerScore_Add(e, SP_RACE_LAPS, 0);
-       if(e.race_completed)
+       l = GameRules_scoring_add(e, RACE_LAPS, 0);
+       if(CS(e).race_completed)
                return l; // not fractional
 
        vector o0, o1;