]> 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 2bd9f9668bc443bd021a7499656be2040c55e606..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
                                }
                        });
                }
@@ -548,38 +564,11 @@ void race_ClearTime(entity e)
        });
 }
 
-void dumpsurface(entity e)
-{
-       float n, si, ni;
-       vector norm, vec;
-       LOG_INFO("Surfaces of ", etos(e), ":\n");
-
-       LOG_INFO("TEST = ", ftos(getsurfacenearpoint(e, '0 0 0')), "\n");
-
-       for(si = 0; ; ++si)
-       {
-               n = getsurfacenumpoints(e, si);
-               if(n <= 0)
-                       break;
-               LOG_INFO("  Surface ", ftos(si), ":\n");
-               norm = getsurfacenormal(e, si);
-               LOG_INFO("    Normal = ", vtos(norm), "\n");
-               for(ni = 0; ni < n; ++ni)
-               {
-                       vec = getsurfacepoint(e, si, ni);
-                       LOG_INFO("    Point ", ftos(ni), " = ", vtos(vec), " (", ftos(norm * vec), ")\n");
-               }
-       }
-}
-
 void checkpoint_passed(entity this, entity player)
 {
        if(player.personal && autocvar_g_allow_checkpoints)
                return; // practice mode!
 
-       string oldmsg;
-       entity cp;
-
        if(player.classname == "porto")
        {
                // do not allow portalling through checkpoints
@@ -588,6 +577,8 @@ void checkpoint_passed(entity this, entity player)
                return;
        }
 
+       string oldmsg; // used twice
+
        /*
         * Trigger targets
         */
@@ -616,30 +607,37 @@ void checkpoint_passed(entity this, entity player)
                        this.race_checkpoint = player.race_checkpoint;
                }
 
-               float largest_cp_id = 0;
-               float cp_amount = 0;
-               for(cp = NULL; (cp = find(cp, classname, "target_checkpoint"));)
+               int cp_amount = 0, largest_cp_id = 0;
+               IL_EACH(g_race_targets, it.classname == "target_checkpoint",
                {
                        cp_amount += 1;
-                       if(cp.race_checkpoint > largest_cp_id) // update the finish id if someone hit a new checkpoint
+                       if(it.race_checkpoint > largest_cp_id) // update the finish id if someone hit a new checkpoint
                        {
-                               largest_cp_id = cp.race_checkpoint;
-                               for(cp = NULL; (cp = find(cp, classname, "target_stopTimer"));)
-                                       cp.race_checkpoint = largest_cp_id + 1; // finish line
-                               race_highest_checkpoint = largest_cp_id + 1;
-                               race_timed_checkpoint = largest_cp_id + 1;
-
-                               for(cp = NULL; (cp = find(cp, classname, "target_checkpoint"));)
+                               if(!largest_cp_id)
                                {
-                                       if(cp.race_checkpoint == -2) // set defragcpexists to -1 so that the cp id file will be rewritten when someone finishes
-                                               defragcpexists = -1;
+                                       IL_EACH(g_race_targets, it.classname == "target_checkpoint",
+                                       {
+                                               if(it.race_checkpoint == -2) // set defragcpexists to -1 so that the cp id file will be rewritten when someone finishes
+                                                       defragcpexists = -1;
+                                       });
                                }
+
+                               largest_cp_id = it.race_checkpoint;
+                               IL_EACH(g_race_targets, it.classname == "target_stopTimer",
+                               {
+                                       it.race_checkpoint = largest_cp_id + 1; // finish line
+                               });
+                               race_highest_checkpoint = largest_cp_id + 1;
+                               race_timed_checkpoint = largest_cp_id + 1;
                        }
-               }
-               if(cp_amount == 0)
+               });
+
+               if(!cp_amount)
                {
-                       for(cp = NULL; (cp = find(cp, classname, "target_stopTimer"));)
-                               cp.race_checkpoint = 1;
+                       IL_EACH(g_race_targets, it.classname == "target_stopTimer",
+                       {
+                               it.race_checkpoint = 1;
+                       });
                        race_highest_checkpoint = 1;
                        race_timed_checkpoint = 1;
                }
@@ -695,8 +693,10 @@ void checkpoint_passed(entity this, entity player)
                        defragcpexists = fh = fopen(strcat("maps/", GetMapname(), ".defragcp"), FILE_WRITE);
                        if(fh >= 0)
                        {
-                               for(cp = NULL; (cp = find(cp, classname, "target_checkpoint"));)
-                               fputs(fh, strcat(cp.targetname, " ", ftos(cp.race_checkpoint), "\n"));
+                               IL_EACH(g_race_targets, it.classname == "target_checkpoint",
+                               {
+                                       fputs(fh, strcat(it.targetname, " ", ftos(it.race_checkpoint), "\n"));
+                               });
                        }
                        fclose(fh);
                }
@@ -708,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);
 }
 
@@ -723,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);
 }
 
@@ -818,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,
                {
@@ -836,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") {
@@ -993,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;
@@ -1037,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);
@@ -1049,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)
@@ -1084,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)
@@ -1178,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;