]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blobdiff - qcsrc/client/main.qc
Merge branch 'master' into TimePath/scoreboard_elo
[xonotic/xonotic-data.pk3dir.git] / qcsrc / client / main.qc
index 0dcf6811dec488dd5306781bafd43956d6f5ba84..680e65ac05e50e33bd5b19832bb664cad6480b25 100644 (file)
@@ -4,7 +4,7 @@
 #include "hud/all.qh"
 #include "mapvoting.qh"
 #include "mutators/events.qh"
-#include "quickmenu.qh"
+#include "hud/panel/quickmenu.qh"
 #include "scoreboard.qh"
 #include "shownames.qh"
 #include <common/t_items.qh>
 
 #define DP_CSQC_ENTITY_REMOVE_IS_B0RKED
 
+void draw_cursor(vector pos, vector ofs, string img, vector col, float a)
+{
+       ofs = eX * (ofs.x * SIZE_CURSOR.x) + eY * (ofs.y * SIZE_CURSOR.y);
+       drawpic(pos - ofs, strcat(draw_currentSkin, img), SIZE_CURSOR, col, a, DRAWFLAG_NORMAL);
+}
+
+void draw_cursor_normal(vector pos, vector col, float a)
+{
+       draw_cursor(pos, OFFSET_CURSOR, "/cursor", col, a);
+}
+
+void LoadMenuSkinValues()
+{
+       int fh = -1;
+       if(cvar_string("menu_skin") != "")
+       {
+               draw_currentSkin = strcat("gfx/menu/", cvar_string("menu_skin"));
+               fh = fopen(strcat(draw_currentSkin, "/skinvalues.txt"), FILE_READ);
+       }
+       if(fh < 0 && cvar_defstring("menu_skin") != "")
+       {
+               cvar_set("menu_skin", cvar_defstring("menu_skin"));
+               draw_currentSkin = strcat("gfx/menu/", cvar_string("menu_skin"));
+               fh = fopen(strcat(draw_currentSkin, "/skinvalues.txt"), FILE_READ);
+       }
+       if(fh < 0)
+       {
+               draw_currentSkin = "gfx/menu/default";
+               fh = fopen(strcat(draw_currentSkin, "/skinvalues.txt"), FILE_READ);
+       }
+
+       draw_currentSkin = strzone(draw_currentSkin);
+
+       if(fh >= 0)
+       {
+               string s;
+               while((s = fgets(fh)))
+               {
+                       int n = tokenize_console(s);
+                       if (n < 2)
+                               continue;
+                       if(substring(argv(0), 0, 2) == "//")
+                               continue;
+                       if(argv(0) == "SIZE_CURSOR")
+                               SIZE_CURSOR = stov(substring(s, argv_start_index(1), argv_end_index(-1) - argv_start_index(1)));
+                       else if(argv(0) == "OFFSET_CURSOR")
+                               OFFSET_CURSOR = stov(substring(s, argv_start_index(1), argv_end_index(-1) - argv_start_index(1)));
+               }
+               fclose(fh);
+       }
+}
+
 // CSQC_Init : Called every time the CSQC code is initialized (essentially at map load)
 // Useful for precaching things
 
@@ -75,7 +127,7 @@ void CSQC_Init()
        registercvar("cl_jumpspeedcap_min", "");
        registercvar("cl_jumpspeedcap_max", "");
 
-       registercvar("cl_multijump", "0");
+       registercvar("cl_multijump", "1");
 
        registercvar("cl_spawn_near_teammate", "1");
 
@@ -125,26 +177,7 @@ void CSQC_Init()
        }
 
        hud_skin_path = strzone(strcat("gfx/hud/", autocvar_hud_skin));
-
-       int fh = -1;
-       if(cvar_string("menu_skin") != "")
-       {
-               draw_currentSkin = strcat("gfx/menu/", cvar_string("menu_skin"));
-               fh = fopen(strcat(draw_currentSkin, "/skinvalues.txt"), FILE_READ);
-       }
-       if(fh < 0 && cvar_defstring("menu_skin") != "")
-       {
-               cvar_set("menu_skin", cvar_defstring("menu_skin"));
-               draw_currentSkin = strcat("gfx/menu/", cvar_string("menu_skin"));
-               fh = fopen(strcat(draw_currentSkin, "/skinvalues.txt"), FILE_READ);
-       }
-       if(fh < 0)
-       {
-               draw_currentSkin = "gfx/menu/default";
-               fh = fopen(strcat(draw_currentSkin, "/skinvalues.txt"), FILE_READ);
-       }
-
-       draw_currentSkin = strzone(draw_currentSkin);
+       LoadMenuSkinValues();
 }
 
 // CSQC_Shutdown : Called every time the CSQC code is shutdown (changing maps, quitting, etc)
@@ -152,8 +185,8 @@ void Shutdown()
 {
        WarpZone_Shutdown();
 
-       remove(teams);
-       remove(players);
+       delete(teams);
+       delete(players);
        db_close(binddb);
        db_close(tempdb);
        if(autocvar_cl_db_saveasdump)
@@ -169,6 +202,8 @@ void Shutdown()
        if(autocvar_chase_active < 0)
                cvar_set("chase_active", "0");
 
+       cvar_set("slowmo", cvar_defstring("slowmo")); // reset it back to 'default'
+
        if (!isdemo())
        {
                if (!(calledhooks & HOOK_START))
@@ -178,12 +213,13 @@ void Shutdown()
        }
 
        deactivate_minigame();
-       HUD_MinigameMenu_Close();
+       HUD_MinigameMenu_Close(NULL, NULL, NULL);
 }
 
 .float has_team;
 float SetTeam(entity o, int Team)
 {
+    TC(int, Team);
        devassert_once(Team);
        entity tm;
        if(teamplay)
@@ -197,7 +233,7 @@ float SetTeam(entity o, int Team)
                        case NUM_TEAM_4:
                                break;
                        default:
-                               if(GetTeam(Team, false) == world)
+                               if(GetTeam(Team, false) == NULL)
                                {
                                        LOG_TRACEF("trying to switch to unsupported team %d\n", Team);
                                        Team = NUM_SPECTATOR;
@@ -213,7 +249,7 @@ float SetTeam(entity o, int Team)
                        case 0:
                                break;
                        default:
-                               if(GetTeam(Team, false) == world)
+                               if(GetTeam(Team, false) == NULL)
                                {
                                        LOG_TRACEF("trying to switch to unsupported team %d\n", Team);
                                        Team = NUM_SPECTATOR;
@@ -254,9 +290,8 @@ float SetTeam(entity o, int Team)
        return false;
 }
 
-void Playerchecker_Think()
+void Playerchecker_Think(entity this)
 {
-       SELFPARAM();
     int i;
        entity e;
        for(i = 0; i < maxclients; ++i)
@@ -269,7 +304,7 @@ void Playerchecker_Think()
                                // player disconnected
                                SetTeam(e, -1);
                                RemovePlayer(e);
-                               e.sort_prev = world;
+                               e.sort_prev = NULL;
                                //e.gotscores = 0;
                        }
                }
@@ -280,8 +315,7 @@ void Playerchecker_Think()
                                // player connected
                                if (!e)
                                {
-                                       playerslots[i] = e = new(playerslot);
-                                       make_pure(e);
+                                       playerslots[i] = e = new_pure(playerslot);
                                }
                                e.sv_entnum = i;
                                e.ping = 0;
@@ -298,16 +332,13 @@ void Playerchecker_Think()
        this.nextthink = time + 0.2;
 }
 
-void Porto_Init();
 void TrueAim_Init();
 void PostInit()
 {
-       entity playerchecker = new(playerchecker);
-       make_pure(playerchecker);
-       playerchecker.think = Playerchecker_Think;
+       entity playerchecker = new_pure(playerchecker);
+       setthink(playerchecker, Playerchecker_Think);
        playerchecker.nextthink = time + 0.2;
 
-       Porto_Init();
        TrueAim_Init();
 
        postinit = true;
@@ -320,8 +351,9 @@ void PostInit()
 // In the case of keyboard input, nPrimary is the ascii code, and nSecondary is 0.
 // In the case of mouse input, nPrimary is xdelta, nSecondary is ydelta.
 // In the case of mouse input after a setcursormode(1) call, nPrimary is xpos, nSecondary is ypos.
-float CSQC_InputEvent(float bInputType, float nPrimary, float nSecondary)
+float CSQC_InputEvent(int bInputType, float nPrimary, float nSecondary)
 {
+    TC(int, bInputType);
        if (HUD_Panel_InputEvent(bInputType, nPrimary, nSecondary))
                return true;
 
@@ -346,19 +378,6 @@ float CSQC_InputEvent(float bInputType, float nPrimary, float nSecondary)
 // --------------------------------------------------------------------------
 // BEGIN OPTIONAL CSQC FUNCTIONS
 
-.void(entity) predraw_qc;
-void PreDraw_self()
-{
-       SELFPARAM();
-       if (this.predraw_qc) this.predraw_qc(this);
-}
-
-void setpredraw(entity this, void(entity) pdfunc)
-{
-       this.predraw = PreDraw_self;
-       this.predraw_qc = pdfunc;
-}
-
 void Ent_Remove(entity this);
 
 void Ent_RemovePlayerScore(entity this)
@@ -366,23 +385,21 @@ void Ent_RemovePlayerScore(entity this)
        if(this.owner) {
                SetTeam(this.owner, -1);
                this.owner.gotscores = 0;
-               for(int i = 0; i < MAX_SCORE; ++i) {
-                       this.owner.(scores[i]) = 0; // clear all scores
-               }
+               FOREACH(Scores, true, {
+                       this.owner.(scores(it)) = 0; // clear all scores
+               });
        }
 }
 
 NET_HANDLE(ENT_CLIENT_SCORES, bool isnew)
 {
        make_pure(this);
-       int i, n;
-       bool isNew;
        entity o;
 
        // damnit -.- don't want to go change every single .sv_entnum in hud.qc AGAIN
        // (no I've never heard of M-x replace-string, sed, or anything like that)
-       isNew = !this.owner; // workaround for DP bug
-       n = ReadByte()-1;
+       bool isNew = !this.owner; // workaround for DP bug
+       int n = ReadByte()-1;
 
 #ifdef DP_CSQC_ENTITY_REMOVE_IS_B0RKED
        if(!isNew && n != this.sv_entnum)
@@ -399,8 +416,7 @@ NET_HANDLE(ENT_CLIENT_SCORES, bool isnew)
        o = playerslots[this.sv_entnum];
        if (!o)
        {
-               o = playerslots[this.sv_entnum] = new(playerslot);
-               make_pure(o);
+               o = playerslots[this.sv_entnum] = new_pure(playerslot);
        }
        this.owner = o;
        o.sv_entnum = this.sv_entnum;
@@ -411,22 +427,18 @@ NET_HANDLE(ENT_CLIENT_SCORES, bool isnew)
        //playerchecker will do this for us later, if it has not already done so
 
     int sf, lf;
-#if MAX_SCORE <= 8
-       sf = ReadByte();
-       lf = ReadByte();
-#else
        sf = ReadShort();
        lf = ReadShort();
-#endif
-    int p;
-       for(i = 0, p = 1; i < MAX_SCORE; ++i, p *= 2)
-               if(sf & p)
+       FOREACH(Scores, true, {
+        int p = 1 << (i % 16);
+               if (sf & p)
                {
-                       if(lf & p)
-                               o.(scores[i]) = ReadInt24_t();
+                       if (lf & p)
+                               o.(scores(it)) = ReadInt24_t();
                        else
-                               o.(scores[i]) = ReadChar();
+                               o.(scores(it)) = ReadChar();
                }
+    });
 
        return = true;
 
@@ -458,9 +470,9 @@ NET_HANDLE(ENT_CLIENT_TEAMSCORES, bool isnew)
                if(sf & p)
                {
                        if(lf & p)
-                               o.(teamscores[i]) = ReadInt24_t();
+                               o.(teamscores(i)) = ReadInt24_t();
                        else
-                               o.(teamscores[i]) = ReadChar();
+                               o.(teamscores(i)) = ReadChar();
                }
 
        return = true;
@@ -498,6 +510,22 @@ NET_HANDLE(ENT_CLIENT_CLIENTDATA, bool isnew)
        else
                angles_held_status = 0;
 
+       if(f & 16)
+       {
+               num_spectators = ReadByte();
+
+               float i, slot;
+
+               for(i = 0; i < MAX_SPECTATORS; ++i)
+                       spectatorlist[i] = 0; // reset list first
+
+               for(i = 0; i < num_spectators; ++i)
+               {
+                       slot = ReadByte();
+                       spectatorlist[i] = slot - 1;
+               }
+       }
+
        return = true;
 
        if(newspectatee_status != spectatee_status)
@@ -505,6 +533,7 @@ NET_HANDLE(ENT_CLIENT_CLIENTDATA, bool isnew)
                // clear race stuff
                race_laptime = 0;
                race_checkpointtime = 0;
+               hud_dynamic_shake_factor = -1;
        }
        if (autocvar_hud_panel_healtharmor_progressbar_gfx)
        {
@@ -641,13 +670,31 @@ void Spawn_Draw(entity this)
        __pointparticles(this.cnt, this.origin + '0 0 28', '0 0 2', bound(0, frametime, 0.1));
 }
 
+void Spawn_PreDraw(entity this)
+{
+       float alph;
+       vector org = getpropertyvec(VF_ORIGIN);
+       if(this.fade_start)
+               alph = bound(0, (this.fade_end - vlen(org - this.origin - 0.5 * (this.mins + this.maxs))) / (this.fade_end - this.fade_start), 1);
+       else
+               alph = 1;
+       //printf("%v <-> %v\n", view_origin, this.origin + 0.5 * (this.mins + this.maxs));
+       this.alpha = alph;
+       if(alph <= 0)
+               this.drawmask = 0;
+       else
+               this.drawmask = MASK_NORMAL;
+}
+
 NET_HANDLE(ENT_CLIENT_SPAWNPOINT, bool is_new)
 {
        float teamnum = (ReadByte() - 1);
        vector spn_origin;
-       spn_origin.x = ReadShort();
-       spn_origin.y = ReadShort();
-       spn_origin.z = ReadShort();
+       spn_origin.x = ReadCoord();
+       spn_origin.y = ReadCoord();
+       spn_origin.z = ReadCoord();
+
+       this.team = (teamnum + 1);
 
        //if(is_new)
        //{
@@ -662,8 +709,9 @@ NET_HANDLE(ENT_CLIENT_SPAWNPOINT, bool is_new)
                        precache_model(this.mdl);
                        setmodel(this, this.mdl);
                        this.drawmask = MASK_NORMAL;
-                       //this.movetype = MOVETYPE_NOCLIP;
+                       //this.move_movetype = MOVETYPE_NOCLIP;
                        //this.draw = Spawn_Draw;
+                       IL_PUSH(g_drawables, this);
                }*/
                if(autocvar_cl_spawn_point_particles)
                {
@@ -681,6 +729,10 @@ NET_HANDLE(ENT_CLIENT_SPAWNPOINT, bool is_new)
                        else { this.cnt = particleeffectnum(EFFECT_SPAWNPOINT_NEUTRAL); }
 
                        this.draw = Spawn_Draw;
+                       if (is_new) IL_PUSH(g_drawables, this);
+                       setpredraw(this, Spawn_PreDraw);
+                       this.fade_start = autocvar_cl_spawn_point_dist_min;
+                       this.fade_end = autocvar_cl_spawn_point_dist_max;
                }
        //}
 
@@ -697,9 +749,9 @@ NET_HANDLE(ENT_CLIENT_SPAWNEVENT, bool is_new)
 
        if(entnum)
        {
-               this.origin_x = ReadShort();
-               this.origin_y = ReadShort();
-               this.origin_z = ReadShort();
+               this.origin_x = ReadCoord();
+               this.origin_y = ReadCoord();
+               this.origin_z = ReadCoord();
 
                if(is_new)
                {
@@ -742,9 +794,8 @@ NET_HANDLE(ENT_CLIENT_SPAWNEVENT, bool is_new)
 
 // CSQC_Ent_Update : Called every frame that the server has indicated an update to the SSQC / CSQC entity has occured.
 // The only parameter reflects if the entity is "new" to the client, meaning it just came into the client's PVS.
-void CSQC_Ent_Update(bool isnew)
+void CSQC_Ent_Update(entity this, bool isnew)
 {
-       SELFPARAM();
        this.sourceLoc = __FILE__ ":" STR(__LINE__);
        int t = ReadByte();
 
@@ -783,13 +834,14 @@ void CSQC_Ent_Update(bool isnew)
 #endif
        this.enttype = t;
        bool done = false;
-       FOREACH(LinkedEntities, it.m_id == t, LAMBDA(
+       FOREACH(LinkedEntities, it.m_id == t, {
                if (isnew) this.classname = it.netname;
                if (autocvar_developer_csqcentities)
             LOG_INFOF("CSQC_Ent_Update(%d) at %f with this=%i {.entnum=%d, .enttype=%d} t=%s (%d)\n", isnew, savetime, this, this.entnum, this.enttype, this.classname, t);
                done = it.m_read(this, NULL, isnew);
+               MUTATOR_CALLHOOK(Ent_Update, this, isnew);
                break;
-       ));
+       });
        time = savetime;
        if (!done)
        {
@@ -823,9 +875,8 @@ void Ent_Remove(entity this)
        // TODO possibly set more stuff to defaults
 }
 // CSQC_Ent_Remove : Called when the server requests a SSQC / CSQC entity to be removed.  Essentially call remove(this) as well.
-void CSQC_Ent_Remove()
+void CSQC_Ent_Remove(entity this)
 {
-       SELFPARAM();
        if (autocvar_developer_csqcentities) LOG_INFOF("CSQC_Ent_Remove() with this=%i {.entnum=%d, .enttype=%d}\n", this, this.entnum, this.enttype);
        if (wasfreed(this))
        {
@@ -833,7 +884,7 @@ void CSQC_Ent_Remove()
                return;
        }
        if (this.enttype) Ent_Remove(this);
-       remove(this);
+       delete(this);
 }
 
 void Gamemode_Init()
@@ -873,11 +924,11 @@ bool CSQC_Parse_TempEntity()
        // Acquire TE ID
        int nTEID = ReadByte();
 
-       FOREACH(TempEntities, it.m_id == nTEID, LAMBDA(
+       FOREACH(TempEntities, it.m_id == nTEID, {
                if (autocvar_developer_csqcentities)
                        LOG_INFOF("CSQC_Parse_TempEntity() nTEID=%s (%d)\n", it.netname, nTEID);
                return it.m_read(NULL, NULL, true);
-       ));
+       });
 
        if (autocvar_developer_csqcentities)
                LOG_INFOF("CSQC_Parse_TempEntity() with nTEID=%d\n", nTEID);
@@ -886,7 +937,6 @@ bool CSQC_Parse_TempEntity()
        return false;
 }
 
-/** TODO somehow thwart prvm_globalset client ... */
 string forcefog;
 void Fog_Force()
 {
@@ -902,17 +952,16 @@ NET_HANDLE(ENT_CLIENT_SCORES_INFO, bool isnew)
        make_pure(this);
        gametype = ReadInt24_t();
        HUD_ModIcons_SetFunc();
-       for (int i = 0; i < MAX_SCORE; ++i)
-       {
-               if (scores_label[i]) strunzone(scores_label[i]);
-               scores_label[i] = strzone(ReadString());
-               scores_flags[i] = ReadByte();
-       }
+       FOREACH(Scores, true, {
+               if (scores_label(it)) strunzone(scores_label(it));
+               scores_label(it) = strzone(ReadString());
+               scores_flags(it) = ReadByte();
+       });
        for (int i = 0; i < MAX_TEAMSCORE; ++i)
        {
-               if (teamscores_label[i]) strunzone(teamscores_label[i]);
-               teamscores_label[i] = strzone(ReadString());
-               teamscores_flags[i] = ReadByte();
+               if (teamscores_label(i)) strunzone(teamscores_label(i));
+               teamscores_label(i) = strzone(ReadString());
+               teamscores_flags(i) = ReadByte();
        }
        return = true;
        HUD_InitScores();
@@ -948,6 +997,42 @@ NET_HANDLE(ENT_CLIENT_INIT, bool isnew)
        if (!postinit) PostInit();
 }
 
+float GetSpeedUnitFactor(int speed_unit)
+{
+       switch(speed_unit)
+       {
+               default:
+               case 1:
+                       return 1.0;
+               case 2:
+                       return 0.0254;
+               case 3:
+                       return 0.0254 * 3.6;
+               case 4:
+                       return 0.0254 * 3.6 * 0.6213711922;
+               case 5:
+                       return 0.0254 * 1.943844492; // 1 m/s = 1.943844492 knots, because 1 knot = 1.852 km/h
+       }
+}
+
+string GetSpeedUnit(int speed_unit)
+{
+       switch(speed_unit)
+       {
+               default:
+               case 1:
+                       return _(" qu/s");
+               case 2:
+                       return _(" m/s");
+               case 3:
+                       return _(" km/h");
+               case 4:
+                       return _(" mph");
+               case 5:
+                       return _(" knots");
+       }
+}
+
 NET_HANDLE(TE_CSQC_RACE, bool isNew)
 {
        int b = ReadByte();
@@ -1036,16 +1121,22 @@ NET_HANDLE(TE_CSQC_RACE, bool isNew)
                        race_server_record = ReadInt24_t();
                        break;
                case RACE_NET_SPEED_AWARD:
-                       race_speedaward = ReadInt24_t();
+                       race_speedaward = ReadInt24_t() * GetSpeedUnitFactor(autocvar_hud_panel_physics_speed_unit);
                        if(race_speedaward_holder)
                                strunzone(race_speedaward_holder);
                        race_speedaward_holder = strzone(ReadString());
+                       if(race_speedaward_unit)
+                               strunzone(race_speedaward_unit);
+                       race_speedaward_unit = strzone(GetSpeedUnit(autocvar_hud_panel_physics_speed_unit));
                        break;
                case RACE_NET_SPEED_AWARD_BEST:
-                       race_speedaward_alltimebest = ReadInt24_t();
+                       race_speedaward_alltimebest = ReadInt24_t() * GetSpeedUnitFactor(autocvar_hud_panel_physics_speed_unit);
                        if(race_speedaward_alltimebest_holder)
                                strunzone(race_speedaward_alltimebest_holder);
                        race_speedaward_alltimebest_holder = strzone(ReadString());
+                       if(race_speedaward_alltimebest_unit)
+                               strunzone(race_speedaward_alltimebest_unit);
+                       race_speedaward_alltimebest_unit = strzone(GetSpeedUnit(autocvar_hud_panel_physics_speed_unit));
                        break;
                case RACE_NET_SERVER_RANKINGS:
                        float prevpos, del;
@@ -1140,27 +1231,32 @@ NET_HANDLE(TE_CSQC_WEAPONCOMPLAIN, bool isNew)
        }
 }
 
-string getcommandkey(string text, string command)
+string _getcommandkey(string cmd_name, string command, bool forcename)
 {
        string keys;
        float n, j, k, l = 0;
 
        if (!autocvar_hud_showbinds)
-               return text;
+               return cmd_name;
 
        keys = db_get(binddb, command);
        if (keys == "")
        {
+               bool joy_detected = cvar("joy_detected");
                n = tokenize(findkeysforcommand(command, 0)); // uses '...' strings
                for(j = 0; j < n; ++j)
                {
                        k = stof(argv(j));
                        if(k != -1)
                        {
-                               if ("" == keys)
-                                       keys = keynumtostring(k);
+                               string key = keynumtostring(k);
+                               if(!joy_detected && substring(key, 0, 3) == "JOY")
+                                       continue;
+
+                               if (keys == "")
+                                       keys = key;
                                else
-                                       keys = strcat(keys, ", ", keynumtostring(k));
+                                       keys = strcat(keys, ", ", key);
 
                                ++l;
                                if (autocvar_hud_showbinds_limit > 0 && autocvar_hud_showbinds_limit <= l)
@@ -1175,12 +1271,12 @@ string getcommandkey(string text, string command)
 
        if (keys == "NO_KEY") {
                if (autocvar_hud_showbinds > 1)
-                       return sprintf(_("%s (not bound)"), text);
+                       return sprintf(_("%s (not bound)"), cmd_name);
                else
-                       return text;
+                       return cmd_name;
        }
-       else if (autocvar_hud_showbinds > 1)
-               return sprintf("%s (%s)", text, keys);
+       else if (autocvar_hud_showbinds > 1 || forcename)
+               return sprintf("%s (%s)", cmd_name, keys);
        else
                return keys;
 }