]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blobdiff - qcsrc/server/miscfunctions.qc
Merge remote branch 'refs/remotes/origin/fruitiex/racefixes'
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / miscfunctions.qc
index 479a69106fa9f39956457ecb12bf511304786593..a20c31dd63e18ee5d133bf46dc62b970b1414e8d 100644 (file)
@@ -16,9 +16,6 @@ void WarpZone_crosshair_trace(entity pl)
 
 void() spawnfunc_info_player_deathmatch; // needed for the other spawnpoints
 void() spawnpoint_use;
-float race_GetTime(float pos);
-string race_GetName(float pos);
-string race_PlaceName(float pos);
 string GetMapname();
 string ColoredTeamName(float t);
 
@@ -100,9 +97,9 @@ string GetAdvancedDeathReports(entity enPlayer) // Extra fragmessage information
 
        if(cvar("sv_fragmessage_information_ping")) {
                if(clienttype(enPlayer) == CLIENTTYPE_BOT) // Bots have no ping
-                       strMessage = strcat(strMessage, "\n^7(^2Bot");
+                       strMessage = strcat(strMessage, " ^7(^2Bot");
                else
-                       strMessage = strcat(strMessage, "\n^7(Ping ", strPlayerPingColor, ftos(nPlayerPing), "ms");
+                       strMessage = strcat(strMessage, " ^7(Ping ", strPlayerPingColor, ftos(nPlayerPing), "ms");
                if(cvar("sv_fragmessage_information_handicap"))
                        if(cvar("sv_fragmessage_information_handicap") == 2)
                                if(nPlayerHandicap <= 1)
@@ -229,12 +226,6 @@ void relocate_spawnpoint()
         }
     }
 
-    if (cvar("g_spawnpoints_autodrop"))
-    {
-        setsize(self, PL_MIN, PL_MAX);
-        droptofloor();
-    }
-
     self.use = spawnpoint_use;
     self.team_saved = self.team;
     if (!self.cnt)
@@ -534,7 +525,7 @@ void GetCvars_handleString(string thisname, float f, .string field, string name)
                }
        }
        else
-               stuffcmd(self, strcat("sendcvar ", name, "\n"));
+               stuffcmd(self, strcat("cl_cmd sendcvar ", name, "\n"));
 }
 void GetCvars_handleString_Fixup(string thisname, float f, .string field, string name, string(string) func)
 {
@@ -562,7 +553,7 @@ void GetCvars_handleFloat(string thisname, float f, .float field, string name)
                        self.field = stof(argv(f + 1));
        }
        else
-               stuffcmd(self, strcat("sendcvar ", name, "\n"));
+               stuffcmd(self, strcat("cl_cmd sendcvar ", name, "\n"));
 }
 void GetCvars_handleFloatOnce(string thisname, float f, .float field, string name)
 {
@@ -584,12 +575,22 @@ void GetCvars_handleFloatOnce(string thisname, float f, .float field, string nam
        else
        {
                if(!self.field)
-                       stuffcmd(self, strcat("sendcvar ", name, "\n"));
+                       stuffcmd(self, strcat("cl_cmd sendcvar ", name, "\n"));
        }
 }
-string W_FixWeaponOrder_ForceComplete(string s);
-string W_FixWeaponOrder_AllowIncomplete(string s);
 float w_getbestweapon(entity e);
+string W_FixWeaponOrder_ForceComplete_AndBuildImpulseList(string wo)
+{
+       string o;
+       o = W_FixWeaponOrder_ForceComplete(wo);
+       if(self.weaponorder_byimpulse)
+       {
+               strunzone(self.weaponorder_byimpulse);
+               self.weaponorder_byimpulse = string_null;
+       }
+       self.weaponorder_byimpulse = strzone(W_FixWeaponOrder_BuildImpulseList(o));
+       return o;
+}
 void GetCvars(float f)
 {
        string s;
@@ -606,7 +607,7 @@ void GetCvars(float f)
        GetCvars_handleFloat(s, f, cvar_cl_shownames, "cl_shownames");
        GetCvars_handleString(s, f, cvar_g_xonoticversion, "g_xonoticversion");
        GetCvars_handleFloat(s, f, cvar_cl_handicap, "cl_handicap");
-       GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriority, "cl_weaponpriority", W_FixWeaponOrder_ForceComplete);
+       GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriority, "cl_weaponpriority", W_FixWeaponOrder_ForceComplete_AndBuildImpulseList);
        GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[0], "cl_weaponpriority0", W_FixWeaponOrder_AllowIncomplete);
        GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[1], "cl_weaponpriority1", W_FixWeaponOrder_AllowIncomplete);
        GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[2], "cl_weaponpriority2", W_FixWeaponOrder_AllowIncomplete);
@@ -617,6 +618,7 @@ void GetCvars(float f)
        GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[7], "cl_weaponpriority7", W_FixWeaponOrder_AllowIncomplete);
        GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[8], "cl_weaponpriority8", W_FixWeaponOrder_AllowIncomplete);
        GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[9], "cl_weaponpriority9", W_FixWeaponOrder_AllowIncomplete);
+       GetCvars_handleFloat(s, f, cvar_cl_weaponimpulsemode, "cl_weaponimpulsemode");
        GetCvars_handleFloat(s, f, cvar_cl_autotaunt, "cl_autotaunt");
        GetCvars_handleFloat(s, f, cvar_cl_noantilag, "cl_noantilag");
        GetCvars_handleFloat(s, f, cvar_cl_voice_directional, "cl_voice_directional");
@@ -633,6 +635,7 @@ void GetCvars(float f)
        GetCvars_handleFloat(s, f, cvar_cl_forceplayermodelsfromxonotic, "cl_forceplayermodelsfromxonotic");
 #endif
        GetCvars_handleFloatOnce(s, f, cvar_cl_gunalign, "cl_gunalign");
+       GetCvars_handleFloat(s, f, cvar_cl_allow_uid2name, "cl_allow_uid2name");
 
        // fixup of switchweapon (needed for LMS or when spectating is disabled, as PutClientInServer comes too early)
        if (f > 0)
@@ -824,20 +827,30 @@ float g_pickup_fuel_jetpack;
 float g_pickup_fuel_max;
 float g_pickup_armorsmall;
 float g_pickup_armorsmall_max;
+float g_pickup_armorsmall_anyway;
 float g_pickup_armormedium;
 float g_pickup_armormedium_max;
+float g_pickup_armormedium_anyway;
 float g_pickup_armorbig;
 float g_pickup_armorbig_max;
+float g_pickup_armorbig_anyway;
 float g_pickup_armorlarge;
 float g_pickup_armorlarge_max;
+float g_pickup_armorlarge_anyway;
 float g_pickup_healthsmall;
 float g_pickup_healthsmall_max;
+float g_pickup_healthsmall_anyway;
 float g_pickup_healthmedium;
 float g_pickup_healthmedium_max;
+float g_pickup_healthmedium_anyway;
 float g_pickup_healthlarge;
 float g_pickup_healthlarge_max;
+float g_pickup_healthlarge_anyway;
 float g_pickup_healthmega;
 float g_pickup_healthmega_max;
+float g_pickup_healthmega_anyway;
+float g_pickup_ammo_anyway;
+float g_pickup_weapons_anyway;
 float g_weaponarena;
 float g_weaponarena_random;
 string g_weaponarena_list;
@@ -984,7 +997,7 @@ void readplayerstartcvars()
        if (g_weaponarena)
        {
                start_weapons = g_weaponarena;
-               if (g_weaponarena & (WEPBIT_GRENADE_LAUNCHER | WEPBIT_HAGAR | WEPBIT_ROCKET_LAUNCHER))
+               if (g_weaponarena & (WEPBIT_GRENADE_LAUNCHER | WEPBIT_MINE_LAYER | WEPBIT_HAGAR | WEPBIT_ROCKET_LAUNCHER))
                        start_ammo_rockets = 999;
                if (g_weaponarena & WEPBIT_SHOTGUN)
                        start_ammo_shells = 999;
@@ -1167,6 +1180,10 @@ void readlevelcvars(void)
        // first load all the mutators
        if(cvar("g_nix"))
                MUTATOR_ADD(mutator_nix);
+       if(cvar("g_dodging"))
+               MUTATOR_ADD(mutator_dodging);
+       if(cvar("g_rocket_flying") || (cvar("g_balance_rocketlauncher_detonatedelay") == 0 && stof(cvar_defstring("g_balance_rocketlauncher_detonatedelay")) != 0))
+               MUTATOR_ADD(mutator_rocketflying);
 
     g_bugrigs = cvar("g_bugrigs");
     g_bugrigs_planar_movement = cvar("g_bugrigs_planar_movement");
@@ -1216,7 +1233,6 @@ void readlevelcvars(void)
        sv_maxidle = cvar("sv_maxidle");
        sv_maxidle_spectatorsareidle = cvar("sv_maxidle_spectatorsareidle");
        sv_pogostick = cvar("sv_pogostick");
-       sv_doublejump = cvar("sv_doublejump");
        g_ctf_reverse = cvar("g_ctf_reverse");
        sv_autotaunt = cvar("sv_autotaunt");
        sv_taunt = cvar("sv_taunt");
@@ -1261,20 +1277,31 @@ void readlevelcvars(void)
        g_pickup_fuel_max = cvar("g_pickup_fuel_max");
        g_pickup_armorsmall = cvar("g_pickup_armorsmall");
        g_pickup_armorsmall_max = cvar("g_pickup_armorsmall_max");
+       g_pickup_armorsmall_anyway = cvar("g_pickup_armorsmall_anyway");
        g_pickup_armormedium = cvar("g_pickup_armormedium");
        g_pickup_armormedium_max = cvar("g_pickup_armormedium_max");
+       g_pickup_armormedium_anyway = cvar("g_pickup_armormedium_anyway");
        g_pickup_armorbig = cvar("g_pickup_armorbig");
        g_pickup_armorbig_max = cvar("g_pickup_armorbig_max");
+       g_pickup_armorbig_anyway = cvar("g_pickup_armorbig_anyway");
        g_pickup_armorlarge = cvar("g_pickup_armorlarge");
        g_pickup_armorlarge_max = cvar("g_pickup_armorlarge_max");
+       g_pickup_armorlarge_anyway = cvar("g_pickup_armorlarge_anyway");
        g_pickup_healthsmall = cvar("g_pickup_healthsmall");
        g_pickup_healthsmall_max = cvar("g_pickup_healthsmall_max");
+       g_pickup_healthsmall_anyway = cvar("g_pickup_healthsmall_anyway");
        g_pickup_healthmedium = cvar("g_pickup_healthmedium");
        g_pickup_healthmedium_max = cvar("g_pickup_healthmedium_max");
+       g_pickup_healthmedium_anyway = cvar("g_pickup_healthmedium_anyway");
        g_pickup_healthlarge = cvar("g_pickup_healthlarge");
        g_pickup_healthlarge_max = cvar("g_pickup_healthlarge_max");
+       g_pickup_healthlarge_anyway = cvar("g_pickup_healthlarge_anyway");
        g_pickup_healthmega = cvar("g_pickup_healthmega");
        g_pickup_healthmega_max = cvar("g_pickup_healthmega_max");
+       g_pickup_healthmega_anyway = cvar("g_pickup_healthmega_anyway");
+
+       g_pickup_ammo_anyway = cvar("g_pickup_ammo_anyway");
+       g_pickup_weapons_anyway = cvar("g_pickup_weapons_anyway");
 
        g_pinata = cvar("g_pinata");
 
@@ -1512,30 +1539,50 @@ void play2all(string samp)
 }
 
 void PrecachePlayerSounds(string f);
-void precache_all_models(string pattern)
+void precache_playermodel(string m)
 {
-    float globhandle, i, n;
-    string f;
+       float globhandle, i, n;
+       string f;
 
-    globhandle = search_begin(pattern, TRUE, FALSE);
-    if (globhandle < 0)
-        return;
-    n = search_getsize(globhandle);
-    for (i = 0; i < n; ++i)
-    {
+       if(substring(m, -9,5) == "_lod1")
+               return;
+       if(substring(m, -9,5) == "_lod2")
+               return;
+       precache_model(m);
+       if(sv_loddistance1)
+       {
+               precache_model(strcat(substring(m, 0, -5), "_lod1", substring(m, -4, -1)));
+               precache_model(strcat(substring(m, 0, -5), "_lod2", substring(m, -4, -1)));
+       }
+
+       globhandle = search_begin(strcat(m, "_*.sounds"), TRUE, FALSE);
+       if (globhandle < 0)
+               return;
+       n = search_getsize(globhandle);
+       for (i = 0; i < n; ++i)
+       {
                //print(search_getfilename(globhandle, i), "\n");
                f = search_getfilename(globhandle, i);
-               if(sv_loddistance1)
-                       precache_model(f);
-               if(substring(f, -9,5) == "_lod1")
-                       continue;
-               if(substring(f, -9,5) == "_lod2")
-                       continue;
-               if(!sv_loddistance1)
-                       precache_model(f);
-               PrecachePlayerSounds(strcat(f, ".sounds"));
-    }
-    search_end(globhandle);
+               PrecachePlayerSounds(f);
+       }
+       search_end(globhandle);
+}
+void precache_all_playermodels(string pattern)
+{
+       float globhandle, i, n;
+       string f;
+
+       globhandle = search_begin(pattern, TRUE, FALSE);
+       if (globhandle < 0)
+               return;
+       n = search_getsize(globhandle);
+       for (i = 0; i < n; ++i)
+       {
+               //print(search_getfilename(globhandle, i), "\n");
+               f = search_getfilename(globhandle, i);
+               precache_playermodel(f);
+       }
+       search_end(globhandle);
 }
 
 void precache()
@@ -1558,25 +1605,11 @@ void precache()
     if (cvar("sv_precacheplayermodels"))
     {
         PrecachePlayerSounds("sound/player/default.sounds");
-        precache_all_models("models/player/*.zym");
-        precache_all_models("models/player/*.dpm");
-        precache_all_models("models/player/*.md3");
-        precache_all_models("models/player/*.psk");
-        //precache_model("models/player/carni.zym");
-        //precache_model("models/player/crash.zym");
-        //precache_model("models/player/grunt.zym");
-        //precache_model("models/player/headhunter.zym");
-        //precache_model("models/player/insurrectionist.zym");
-        //precache_model("models/player/jeandarc.zym");
-        //precache_model("models/player/lurk.zym");
-        //precache_model("models/player/lycanthrope.zym");
-        //precache_model("models/player/marine.zym");
-        //precache_model("models/player/nexus.zym");
-        //precache_model("models/player/pyria.zym");
-        //precache_model("models/player/shock.zym");
-        //precache_model("models/player/skadi.zym");
-        //precache_model("models/player/specop.zym");
-        //precache_model("models/player/visitant.zym");
+        precache_all_playermodels("models/player/*.zym");
+        precache_all_playermodels("models/player/*.dpm");
+        precache_all_playermodels("models/player/*.md3");
+        precache_all_playermodels("models/player/*.psk");
+        precache_all_playermodels("models/player/*.iqm");
     }
 
     if (cvar("sv_defaultcharacter"))
@@ -1584,34 +1617,19 @@ void precache()
         string s;
         s = cvar_string("sv_defaultplayermodel_red");
         if (s != "")
-        {
-            precache_model(s);
-            PrecachePlayerSounds(strcat(s, ".sounds"));
-        }
+            precache_playermodel(s);
         s = cvar_string("sv_defaultplayermodel_blue");
         if (s != "")
-        {
-            precache_model(s);
-            PrecachePlayerSounds(strcat(s, ".sounds"));
-        }
+            precache_playermodel(s);
         s = cvar_string("sv_defaultplayermodel_yellow");
         if (s != "")
-        {
-            precache_model(s);
-            PrecachePlayerSounds(strcat(s, ".sounds"));
-        }
+            precache_playermodel(s);
         s = cvar_string("sv_defaultplayermodel_pink");
         if (s != "")
-        {
-            precache_model(s);
-            PrecachePlayerSounds(strcat(s, ".sounds"));
-        }
+            precache_playermodel(s);
         s = cvar_string("sv_defaultplayermodel");
         if (s != "")
-        {
-            precache_model(s);
-            PrecachePlayerSounds(strcat(s, ".sounds"));
-        }
+            precache_playermodel(s);
     }
 
     if (g_footsteps)
@@ -1662,6 +1680,7 @@ void precache()
     precache_sound ("weapons/weapon_switch.wav");
     precache_sound ("weapons/weaponpickup.wav");
     precache_sound ("weapons/unavailable.wav");
+    precache_sound ("weapons/dryfire.wav");
     if (g_grappling_hook)
     {
         precache_sound ("weapons/hook_fire.wav"); // hook
@@ -1711,7 +1730,6 @@ void precache()
 #define INITPRIO_FIRST              0
 #define INITPRIO_GAMETYPE           0
 #define INITPRIO_GAMETYPE_FALLBACK  1
-#define INITPRIO_CVARS              5
 #define INITPRIO_FINDTARGET        10
 #define INITPRIO_DROPTOFLOOR       20
 #define INITPRIO_SETLOCATION       90
@@ -1903,6 +1921,13 @@ void adaptor_think2use()
     activator = a;
 }
 
+void adaptor_think2use_hittype_splash() // for timed projectile detonation
+{
+       if not(self.flags & FL_ONGROUND) // if onground, we ARE touching something, but HITTYPE_SPLASH is to be networked if the damage causing projectile is not touching ANYTHING
+               self.projectiledeathtype |= HITTYPE_SPLASH;
+       adaptor_think2use();
+}
+
 // deferred dropping
 void DropToFloor_Handler()
 {
@@ -2051,6 +2076,106 @@ void print_to(entity e, string s)
         print(s, "\n");
 }
 
+string uid2name(string myuid) {
+       string s;
+       s = db_get(ServerProgsDB, strcat("uid2name", myuid));
+       
+       if(s == "")
+               s = "^1Unregistered Player";
+       return s;
+}
+
+float race_readTime(string map, float pos)
+{
+       string rr;
+       if(g_cts)
+               rr = CTS_RECORD;
+       else
+               rr = RACE_RECORD;
+
+       return stof(db_get(ServerProgsDB, strcat(map, rr, "time", ftos(pos))));
+}
+
+string race_readUID(string map, float pos)
+{
+       string rr;
+       if(g_cts)
+               rr = CTS_RECORD;
+       else
+               rr = RACE_RECORD;
+
+       return db_get(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(pos)));
+}
+
+float race_readPos(string map, float t) {
+       float i;
+       for (i = 1; i <= RANKINGS_CNT; ++i)
+               if (race_readTime(map, i) == 0 || race_readTime(map, i) > t)
+                       return i;
+
+       return 0; // pos is zero if unranked
+}
+
+void race_writeTime(string map, float t, string myuid)
+{
+       string rr;
+       if(g_cts)
+               rr = CTS_RECORD;
+       else
+               rr = RACE_RECORD;
+
+       float newpos;
+       newpos = race_readPos(map, t);
+
+       float i, prevpos;
+       for(i = 1; i <= RANKINGS_CNT; ++i)
+       {
+               if(race_readUID(map, i) == myuid)
+                       prevpos = i;
+       }
+       if (prevpos) { // player improved his existing record, only have to iterate on ranks between new and old recs
+               for (i = prevpos; 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));
+               }
+       } else { // 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));
+               }
+       }
+
+       // store new time itself
+       db_put(ServerProgsDB, strcat(map, rr, "time", ftos(newpos)), ftos(t));
+       db_put(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(newpos)), myuid);
+}
+
+string race_readName(string map, float pos)
+{
+       string rr;
+       if(g_cts)
+               rr = CTS_RECORD;
+       else
+               rr = RACE_RECORD;
+
+       return uid2name(db_get(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(pos))));
+}
+
+string race_placeName(float pos) {
+       if(floor((mod(pos, 100))/10) * 10 != 10) // examples: 12th, 111th, 213th will not execute this block
+       {
+               if(mod(pos, 10) == 1)
+                       return strcat(ftos(pos), "st");
+               else if(mod(pos, 10) == 2)
+                       return strcat(ftos(pos), "nd");
+               else if(mod(pos, 10) == 3)
+                       return strcat(ftos(pos), "rd");
+               else
+                       return strcat(ftos(pos), "th");
+       }
+       else
+               return strcat(ftos(pos), "th");
+}
 string getrecords(float page) // 50 records per page
 {
     float rec;
@@ -2072,6 +2197,7 @@ string getrecords(float page) // 50 records per page
                 r = stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/captimerecord/time")));
                 if (r == 0)
                     continue;
+               // TODO: uid2name
                 h = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/captimerecord/netname"));
                 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-6, ftos_decimals(r, 2)), " ", h, "\n");
                 ++rec;
@@ -2085,10 +2211,10 @@ string getrecords(float page) // 50 records per page
         {
             if (MapInfo_Get_ByID(i))
             {
-                r = stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, RACE_RECORD, "time")));
+               r = race_readTime(MapInfo_Map_bspname, 1);
                 if (r == 0)
                     continue;
-                h = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, RACE_RECORD, "netname"));
+               h = race_readName(MapInfo_Map_bspname, 1);
                 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-8, TIME_ENCODED_TOSTRING(r)), " ", h, "\n");
                 ++rec;
             }
@@ -2101,10 +2227,10 @@ string getrecords(float page) // 50 records per page
         {
             if (MapInfo_Get_ByID(i))
             {
-                r = stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, CTS_RECORD, "time")));
+               r = race_readTime(MapInfo_Map_bspname, 1);
                 if (r == 0)
                     continue;
-                h = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, CTS_RECORD, "netname"));
+               h = race_readName(MapInfo_Map_bspname, 1);
                 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-8, TIME_ENCODED_TOSTRING(r)), " ", h, "\n");
                 ++rec;
             }
@@ -2134,11 +2260,11 @@ string getrankings()
 
     for (i = 1; i <= RANKINGS_CNT; ++i)
     {
-        t = race_GetTime(i);
+        t = race_readTime(map, i);
        if (t == 0)
            continue;
-       n = race_GetName(i);
-       p = race_PlaceName(i);
+       n = race_readName(map, i);
+       p = race_placeName(i);
         s = strcat(s, strpad(8, p), " ", strpad(-8, TIME_ENCODED_TOSTRING(t)), " ", n, "\n");
     }
 
@@ -2150,6 +2276,189 @@ string getrankings()
         return strcat("Records for ", map, ":\n", s);
 }
 
+const float LADDER_FIRSTPOINT = 100;
+const float LADDER_CNT = 10; // position X still gives LADDER_FIRSTPOINT/X points
+const float LADDER_SIZE = 30; // ladder shows the top X players
+string top_uids[LADDER_SIZE];
+float top_scores[LADDER_SIZE];
+string getladder()
+{
+    float i, j, k, uidcnt;
+    string s, temp_s;
+
+    s = "";
+    temp_s = "";
+
+    string rr;
+    if(g_cts)
+       rr = CTS_RECORD;
+    else
+       rr = RACE_RECORD;
+
+    string myuid;
+
+    for (k = 0; k < MapInfo_count; ++k)
+    {
+        if (MapInfo_Get_ByID(k))
+       {
+               for (i = 0; i <= LADDER_CNT; ++i) { // i = 0 because it is the speed award
+                       if(i == 0) // speed award
+                       {
+                               if(stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, rr, "speed/speed"))) == 0)
+                                       continue;
+
+                               myuid = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, rr, "speed/crypto_idfp"));
+                       }
+                       else // normal record, if it exists (else break)
+                       {
+                               if(race_readTime(MapInfo_Map_bspname, i) == 0)
+                                       continue;
+
+                               myuid = race_readUID(MapInfo_Map_bspname, i);
+                       }
+
+                       // string s contains:
+                       // arg 0 = # of speed recs
+                       // arg 1 = # of 1st place recs
+                       // arg 2 = # of 2nd place recs
+                       // ... etc
+                       // LADDER_CNT+1 = total points
+
+                       temp_s = db_get(TemporaryDB, strcat("ladder", myuid));
+                       if (temp_s == "")
+                       {
+                           db_put(TemporaryDB, strcat("uid", ftos(uidcnt)), myuid);
+                           ++uidcnt;
+                           for (j = 0; j <= LADDER_CNT + 1; ++j)
+                           {
+                               if(j != LADDER_CNT + 1)
+                                   temp_s = strcat(temp_s, "0 ");
+                               else
+                                   temp_s = strcat(temp_s, "0");
+                           }
+                       }
+
+                       tokenize_console(temp_s);
+                       s = "";
+
+                       if(i == 0) // speed award
+                           for (j = 0; j <= LADDER_CNT; ++j) // loop over each arg in the string
+                           {
+                               if(j == 0) // speed award
+                                   s = strcat(s, ftos(stof(argv(j)) +1)); // add 1 to speed rec count and write
+                               else
+                                   s = strcat(s, " ", argv(j)); // just copy over everything else
+                           }
+                       else // record
+                           for (j = 0; j <= LADDER_CNT; ++j) // loop over each arg in the string
+                           {
+                               if(j == 0)
+                                   s = strcat(s, argv(j)); // speed award, dont prefix with " "
+                               else if(j == i) // wanted rec!
+                                   s = strcat(s, " ", ftos(stof(argv(j)) +1)); // update argv(j)
+                               else
+                                   s = strcat(s, " ", argv(j)); // just copy over everything else
+                           }
+
+                       // total points are (by default) calculated like this:
+                       // speedrec = floor(100 / 10) = 10 points
+                       // 1st place = floor(100 / 1) = 100 points
+                       // 2nd place = floor(100 / 2) = 50 points
+                       // 3rd place = floor(100 / 3) = 33 points
+                       // 4th place = floor(100 / 4) = 25 points
+                       // 5th place = floor(100 / 5) = 20 points
+                       // ... etc
+
+                       if(i == 0)
+                           s = strcat(s, " ", ftos(stof(argv(LADDER_CNT+1)) + LADDER_FIRSTPOINT / 10)); // speed award, add LADDER_FIRSTPOINT / 10 points
+                       else
+                           s = strcat(s, " ", ftos(stof(argv(LADDER_CNT+1)) + floor(LADDER_FIRSTPOINT / i))); // record, add LADDER_FIRSTPOINT / i points
+
+                       db_put(TemporaryDB, strcat("ladder", myuid), s);
+               }
+       }
+    }
+
+    float thiscnt;
+    string thisuid;
+    for (i = 0; i <= uidcnt; ++i) // for each known uid
+    {
+       thisuid = db_get(TemporaryDB, strcat("uid", ftos(i)));
+       temp_s = db_get(TemporaryDB, strcat("ladder", thisuid));
+       tokenize_console(temp_s);
+        thiscnt = stof(argv(LADDER_CNT+1));
+
+       if(thiscnt > top_scores[LADDER_SIZE-1])
+       for (j = 0; j < LADDER_SIZE; ++j) // for each place in ladder
+       {
+           if(thiscnt > top_scores[j])
+           {
+               for (k = LADDER_SIZE-1; k >= j; --k)
+               {
+                   top_uids[k] = top_uids[k-1];
+                   top_scores[k] = top_scores[k-1];
+               }
+               top_uids[j] = thisuid;
+               top_scores[j] = thiscnt;
+               break;
+           }
+       }
+    }
+
+    s = "^3-----------------------\n\n";
+
+    s = strcat(s, "Pos ^3|");
+    s = strcat(s, " ^7Total  ^3|");
+    for (i = 1; i <= LADDER_CNT; ++i)
+    {
+       s = strcat(s, " ^7", race_placeName(i), " ^3|");
+    }
+    s = strcat(s, " ^7Speed awards ^3| ^7Name");
+
+    s = strcat(s, "\n^3----+--------");
+    for (i = 1; i <= min(9, LADDER_CNT); ++i)
+    {
+       s = strcat(s, "+-----");
+    }
+    if(LADDER_CNT > 9)
+           for (i = 1; i <= LADDER_CNT - 9; ++i)
+           {
+               s = strcat(s, "+------");
+           }
+
+    s = strcat(s, "+--------------+--------------------\n");
+
+    for (i = 0; i < LADDER_SIZE; ++i)
+    {
+       temp_s = db_get(TemporaryDB, strcat("ladder", top_uids[i]));
+       tokenize_console(temp_s);
+       if (argv(LADDER_CNT+1) == "") // total is 0, skip
+           continue;
+       s = strcat(s, strpad(4, race_placeName(i+1)), "^3| ^7"); // pos
+       s = strcat(s, strpad(7, argv(LADDER_CNT+1)), "^3| ^7"); // total
+       for (j = 1; j <= min(9, LADDER_CNT); ++j)
+       {
+           s = strcat(s, strpad(4, argv(j)), "^3| ^7"); // 1st, 2nd, 3rd etc cnt
+       }
+       if(LADDER_CNT > 9)
+               for (j = 10; j <= LADDER_CNT; ++j)
+               {
+                   s = strcat(s, strpad(4, argv(j)), " ^3| ^7"); // 1st, 2nd, 3rd etc cnt
+               }
+
+       s = strcat(s, strpad(13, argv(0)), "^3| ^7"); // speed award cnt
+       s = strcat(s, uid2name(top_uids[i]), "\n"); // name
+    }
+
+    MapInfo_ClearTemps();
+
+    if (s == "")
+       return "No ladder on this server!\n";
+    else
+        return strcat("Top ", ftos(LADDER_SIZE), " ladder rankings:\n", s);
+}
+
+
 float MoveToRandomMapLocation(entity e, float goodcontents, float badcontents, float badsurfaceflags, float attempts, float maxaboveground, float minviewdistance)
 {
     float m, i;
@@ -2317,9 +2626,9 @@ void write_recordmarker(entity pl, float tstart, float dt)
                  " ", ftos(tstart), " ", ftos(dt), "\n"));
 }
 
-vector shotorg_adjustfromclient(vector vecs, float y_is_right, float allowcenter)
+vector shotorg_adjustfromclient(vector vecs, float y_is_right, float allowcenter, float algn)
 {
-       switch(self.owner.cvar_cl_gunalign)
+       switch(algn)
        {
                case 1: // right
                        break;
@@ -2334,7 +2643,7 @@ vector shotorg_adjustfromclient(vector vecs, float y_is_right, float allowcenter
                        {
                                // center
                                vecs_y = 0;
-                               vecs_z -= 4;
+                               vecs_z -= 2;
                        }
                        else
                        {
@@ -2347,7 +2656,7 @@ vector shotorg_adjustfromclient(vector vecs, float y_is_right, float allowcenter
                        {
                                // center
                                vecs_y = 0;
-                               vecs_z -= 4;
+                               vecs_z -= 2;
                        }
                        else
                        {
@@ -2359,7 +2668,7 @@ vector shotorg_adjustfromclient(vector vecs, float y_is_right, float allowcenter
        return vecs;
 }
 
-vector shotorg_adjust(vector vecs, float y_is_right, float visual)
+vector shotorg_adjust_values(vector vecs, float y_is_right, float visual, float algn)
 {
        string s;
        vector v;
@@ -2368,7 +2677,7 @@ vector shotorg_adjust(vector vecs, float y_is_right, float visual)
        {
                if (visual)
                {
-                       vecs = shotorg_adjustfromclient(vecs, y_is_right, TRUE);
+                       vecs = shotorg_adjustfromclient(vecs, y_is_right, TRUE, algn);
                }
                else
                {
@@ -2380,12 +2689,12 @@ vector shotorg_adjust(vector vecs, float y_is_right, float visual)
        {
                if (visual)
                {
-                       vecs = shotorg_adjustfromclient(vecs, y_is_right, TRUE);
+                       vecs = shotorg_adjustfromclient(vecs, y_is_right, TRUE, algn);
                }
                else
                {
                        vecs_y = 0;
-                       vecs_z -= 4;
+                       vecs_z -= 2;
                }
        }
        else if ((s = cvar_string("g_shootfromfixedorigin")) != "")
@@ -2400,11 +2709,15 @@ vector shotorg_adjust(vector vecs, float y_is_right, float visual)
        }
        else if (cvar("g_shootfromclient"))
        {
-               vecs = shotorg_adjustfromclient(vecs, y_is_right, (cvar("g_shootfromclient") >= 2));
+               vecs = shotorg_adjustfromclient(vecs, y_is_right, (cvar("g_shootfromclient") >= 2), algn);
        }
        return vecs;
 }
 
+vector shotorg_adjust(vector vecs, float y_is_right, float visual)
+{
+       return shotorg_adjust_values(vecs, y_is_right, visual, self.owner.cvar_cl_gunalign);
+}
 
 
 void attach_sameorigin(entity e, entity to, string tag)