]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blobdiff - qcsrc/server/race.qc
Merge branch 'master' into bones_was_here/q3compat
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / race.qc
index 183da23f9a6944ce0c5424ee1e17ff94086c5edf..ebad6157d8750edb24e2f589f1eb4887b0beab2d 100644 (file)
@@ -1,27 +1,74 @@
 #include "race.qh"
 
-#include <server/defs.qh>
-#include <server/miscfunctions.qh>
-#include "client.qh"
-#include "portals.qh"
-#include "scores.qh"
-#include "spawnpoints.qh"
-#include "bot/api.qh"
-#include "command/getreplies.qh"
-#include "../common/deathtypes/all.qh"
-#include "../common/notifications/all.qh"
-#include "../common/mapinfo.qh"
+#include <common/deathtypes/all.qh>
+#include <common/gamemodes/_mod.qh>
 #include <common/gamemodes/rules.qh>
+#include <common/mapobjects/subs.qh>
+#include <common/mapobjects/triggers.qh>
+#include <common/mutators/mutator/waypoints/waypointsprites.qh>
 #include <common/net_linked.qh>
+#include <common/notifications/all.qh>
 #include <common/state.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"
+#include <common/stats.qh>
+#include <common/vehicles/sv_vehicles.qh>
+#include <common/weapons/_all.qh>
+#include <common/weapons/weapon/porto.qh>
+#include <lib/warpzone/common.qh>
+#include <lib/warpzone/util_server.qh>
+#include <server/bot/api.qh>
+#include <server/cheats.qh>
+#include <server/client.qh>
+#include <server/command/getreplies.qh>
+#include <server/damage.qh>
+#include <server/gamelog.qh>
+#include <server/intermission.qh>
+#include <server/main.qh>
+#include <server/mutators/_mod.qh>
+#include <server/portals.qh>
+#include <server/scores.qh>
+#include <server/spawnpoints.qh>
+#include <server/weapons/common.qh>
+#include <server/world.qh>
+
+string uid2name(string myuid)
+{
+       string s = db_get(ServerProgsDB, strcat("/uid2name/", myuid));
+
+       // FIXME remove this later after 0.6 release
+       // convert old style broken records to correct style
+       if(s == "")
+       {
+               s = db_get(ServerProgsDB, strcat("uid2name", myuid));
+               if(s != "")
+               {
+                       db_put(ServerProgsDB, strcat("/uid2name/", myuid), s);
+                       db_remove(ServerProgsDB, strcat("uid2name", myuid));
+               }
+       }
+
+       if(s == "")
+               s = "^1Unregistered Player";
+       return s;
+}
+
+void write_recordmarker(entity pl, float tstart, float dt)
+{
+    GameLogEcho(strcat(":recordset:", ftos(pl.playerid), ":", ftos(dt)));
+
+    // also write a marker into demo files for demotc-race-record-extractor to find
+    stuffcmd(pl,
+             strcat(
+                 strcat("//", strconv(2, 0, 0, GetGametype()), " RECORD SET ", TIME_ENCODED_TOSTRING(TIME_ENCODE(dt))),
+                 " ", ftos(tstart), " ", ftos(dt), "\n"));
+}
 
 IntrusiveList g_race_targets;
-STATIC_INIT(g_race_targets) { g_race_targets = IL_NEW(); }
+IntrusiveList g_racecheckpoints;
+STATIC_INIT(g_race)
+{
+       g_race_targets = IL_NEW();
+       g_racecheckpoints = IL_NEW();
+}
 
 void race_InitSpectator()
 {
@@ -30,8 +77,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));
@@ -85,8 +130,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));
+                       }
                }
        }
 
@@ -105,8 +153,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;
@@ -566,6 +612,9 @@ void race_ClearTime(entity e)
 
 void checkpoint_passed(entity this, entity player)
 {
+       if(IS_VEHICLE(player) && player.owner)
+               player = player.owner;
+
        if(player.personal && autocvar_g_allow_checkpoints)
                return; // practice mode!
 
@@ -740,9 +789,35 @@ bool race_waypointsprite_visible_for_player(entity this, entity player, entity v
                return false;
 }
 
+void defrag_waypointsprites(entity targeted, entity checkpoint)
+{
+       for(entity t = findchain(target, targeted.targetname); t; t = t.chain)
+       {
+               if(t.modelindex)
+               {
+                       entity s = WP_RaceStart;
+
+                       if(checkpoint.classname == "target_checkpoint")
+                               s = WP_RaceCheckpoint;
+                       else if(checkpoint.classname == "target_stopTimer")
+                               s = WP_RaceFinish;
+
+                       vector o = (t.absmin + t.absmax) * 0.5;
+
+                       WaypointSprite_SpawnFixed(s, o, t, sprite, RADARICON_NONE);
+
+                       t.sprite.realowner = checkpoint;
+                       t.sprite.waypointsprite_visible_for_player = race_waypointsprite_visible_for_player;
+               }
+
+               if(t.targetname)
+                       defrag_waypointsprites(t, checkpoint);
+       }
+}
+
 void trigger_race_checkpoint_verify(entity this)
 {
-    static bool have_verified;
+       static bool have_verified;
        if (have_verified) return;
        have_verified = true;
 
@@ -758,25 +833,25 @@ void trigger_race_checkpoint_verify(entity this)
                        // race only (middle of the race)
                        g_race_qualifying = false;
                        pl_race_place = 0;
-                       if (!Spawn_FilterOutBadSpots(this, findchain(classname, "info_player_deathmatch"), 0, false)) {
+                       if (!Spawn_FilterOutBadSpots(this, findchain(classname, "info_player_deathmatch"), 0, false, true)) {
                                error(strcat("Checkpoint ", ftos(i), " misses a spawnpoint with race_place==", ftos(pl_race_place), " (used for respawning in race) - bailing out"));
-            }
+                       }
 
                        if (i == 0) {
                                // qualifying only
                                g_race_qualifying = 1;
                                pl_race_place = race_lowest_place_spawn;
-                               if (!Spawn_FilterOutBadSpots(this, findchain(classname, "info_player_deathmatch"), 0, false)) {
+                               if (!Spawn_FilterOutBadSpots(this, findchain(classname, "info_player_deathmatch"), 0, false, true)) {
                                        error(strcat("Checkpoint ", ftos(i), " misses a spawnpoint with race_place==", ftos(pl_race_place), " (used for qualifying) - bailing out"));
-                }
+                               }
 
                                // race only (initial spawn)
                                g_race_qualifying = 0;
                                for (int p = 1; p <= race_highest_place_spawn; ++p) {
                                        pl_race_place = p;
-                                       if (!Spawn_FilterOutBadSpots(this, findchain(classname, "info_player_deathmatch"), 0, false)) {
+                                       if (!Spawn_FilterOutBadSpots(this, findchain(classname, "info_player_deathmatch"), 0, false, true)) {
                                                error(strcat("Checkpoint ", ftos(i), " misses a spawnpoint with race_place==", ftos(pl_race_place), " (used for initially spawning in race) - bailing out"));
-                    }
+                                       }
                                }
                        }
                }
@@ -785,9 +860,9 @@ void trigger_race_checkpoint_verify(entity this)
                pl_race_checkpoint = race_NextCheckpoint(0);
                g_race_qualifying = 1;
                pl_race_place = race_lowest_place_spawn;
-               if (!Spawn_FilterOutBadSpots(this, findchain(classname, "info_player_deathmatch"), 0, false)) {
+               if (!Spawn_FilterOutBadSpots(this, findchain(classname, "info_player_deathmatch"), 0, false, true)) {
                        error(strcat("Checkpoint 0 misses a spawnpoint with race_place==", ftos(pl_race_place), " (used for qualifying) - bailing out"));
-        }
+               }
        } else {
                pl_race_checkpoint = race_NextCheckpoint(0);
                g_race_qualifying = 1;
@@ -809,8 +884,8 @@ void trigger_race_checkpoint_verify(entity this)
                                for (entity cp = NULL; (cp = find(cp, classname, "target_checkpoint"));) {
                                        if (argv(0) == cp.targetname) {
                                                cp.race_checkpoint = stof(argv(1));
-                    }
-                }
+                                       }
+                               }
                        }
                        fclose(fh);
                }
@@ -818,37 +893,12 @@ void trigger_race_checkpoint_verify(entity this)
 
        g_race_qualifying = qual;
 
-       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,
-               {
-                       vector org = (it.absmin + it.absmax) * 0.5;
-                       if(cpt.race_checkpoint == 0)
-                               WaypointSprite_SpawnFixed(WP_RaceStart, org, it, sprite, RADARICON_NONE);
-                       else
-                               WaypointSprite_SpawnFixed(WP_RaceCheckpoint, org, it, sprite, RADARICON_NONE);
-
-                       it.sprite.realowner = cpt;
-                       it.sprite.waypointsprite_visible_for_player = race_waypointsprite_visible_for_player;
-               });
-       });
-
        if (race_timed_checkpoint) {
                if (defrag_ents) {
                        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, {
-                                               if(it.sprite)
-                                                       WaypointSprite_UpdateSprites(it.sprite, ((cpt.classname == "target_startTimer") ? WP_RaceStart : WP_RaceFinish), WP_Null, WP_Null);
-                                       });
-                               }
+                               defrag_waypointsprites(it, it);
+
                                if(it.classname == "target_checkpoint") {
                                        if(it.race_checkpoint == -2)
                                                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
@@ -859,17 +909,17 @@ void trigger_race_checkpoint_verify(entity this)
                                for (entity cp = NULL; (cp = find(cp, classname, "target_checkpoint"));) {
                                        if (cp.race_checkpoint > largest_cp_id) {
                                                largest_cp_id = cp.race_checkpoint;
-                    }
-                }
+                                       }
+                               }
                                for (entity 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;
                        } else {
                                for (entity cp = NULL; (cp = find(cp, classname, "target_stopTimer"));) {
                                        cp.race_checkpoint = 255; // finish line
-                }
+                               }
                                race_highest_checkpoint = 255;
                                race_timed_checkpoint = 255;
                        }
@@ -878,14 +928,14 @@ void trigger_race_checkpoint_verify(entity this)
                        {
                                if (it.race_checkpoint == 0) {
                                        WaypointSprite_UpdateSprites(it.sprite, WP_RaceStart, WP_Null, WP_Null);
-                } else if (it.race_checkpoint == race_timed_checkpoint) {
+                               } else if (it.race_checkpoint == race_timed_checkpoint) {
                                        WaypointSprite_UpdateSprites(it.sprite, WP_RaceFinish, WP_Null, WP_Null);
                                }
-            });
+                       });
                }
        }
 
-       if (defrag_ents) {
+       if (defrag_ents) { /* The following hack shall be removed when per-player trigger_multiple.wait is implemented for cts */
                for (entity trigger = NULL; (trigger = find(trigger, classname, "trigger_multiple")); ) {
                        for (entity targ = NULL; (targ = find(targ, targetname, trigger.target)); ) {
                                if (targ.classname == "target_checkpoint" || targ.classname == "target_startTimer" || targ.classname == "target_stopTimer") {