]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blobdiff - qcsrc/server/cl_client.qc
Merge remote-tracking branch 'origin/master' into tzork/gm_nexball
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / cl_client.qc
index f41503ab09c790131d48727a08a8af085aeca642..f732600d82f7058eb4fe2d7a66dc38563a2bee48 100644 (file)
@@ -123,7 +123,7 @@ void spawnpoint_use()
 // Returns:
 //   _x: prio (-1 if unusable)
 //   _y: weight
-vector Spawn_Score(entity spot, entity playerlist, float teamcheck, float anypoint)
+vector Spawn_Score(entity spot, float mindist, float teamcheck)
 {
        float shortest, thisdist;
        float prio;
@@ -151,147 +151,80 @@ vector Spawn_Score(entity spot, entity playerlist, float teamcheck, float anypoi
                        return '-1 0 0';
        }
 
+       shortest = vlen(world.maxs - world.mins);
+       FOR_EACH_PLAYER(player) if (player != self)
+       {
+               thisdist = vlen(player.origin - spot.origin);
+               if (thisdist < shortest)
+                       shortest = thisdist;
+       }
+       if(shortest > mindist)
+               prio += SPAWN_PRIO_GOOD_DISTANCE;
+
+       spawn_score = prio * '1 0 0' + shortest * '0 1 0';
+       spawn_spot = spot;
+
        // filter out spots for assault
        if(spot.target != "") {
                entity ent;
-               float good, found;
-               ent = find(world, targetname, spot.target);
+               float found;
 
-               while(ent) {
-                       if(ent.classname == "target_objective")
-                       {
-                               found = 1;
-                               if(ent.health < 0 || ent.health >= ASSAULT_VALUE_INACTIVE)
-                                       return '-1 0 0';
-                               good = 1;
-                       }
-                       else if(ent.classname == "trigger_race_checkpoint")
+               found = 0;
+               for(ent = world; (ent = find(ent, targetname, spot.target)); )
+               {
+                       ++found;
+                       if(ent.spawn_evalfunc)
                        {
-                               found = 1;
-                               if(!anypoint) // spectators may spawn everywhere
-
-                               {
-                                       if(g_race_qualifying)
-                                       {
-                                               // spawn at first
-                                               if(ent.race_checkpoint != 0)
-                                                       return '-1 0 0';
-                                               if(spot.race_place != race_lowest_place_spawn)
-                                                       return '-1 0 0';
-                                       }
-                                       else
-                                       {
-                                               if(ent.race_checkpoint != self.race_respawn_checkpoint)
-                                                       return '-1 0 0';
-                                               // try reusing the previous spawn
-                                               if(ent == self.race_respawn_spotref || spot == self.race_respawn_spotref)
-                                                       prio += 1;
-                                               if(ent.race_checkpoint == 0)
-                                               {
-                                                       float pl;
-                                                       pl = self.race_place;
-                                                       if(pl > race_highest_place_spawn)
-                                                               pl = 0;
-                                                       if(pl == 0 && !self.race_started)
-                                                               pl = race_highest_place_spawn; // use last place if he has not even touched finish yet
-                                                       if(spot.race_place != pl)
-                                                               return '-1 0 0';
-                                               }
-                                       }
-                               }
-                               good = 1;
+                               entity oldself = self;
+                               self = ent;
+                               spawn_score = ent.spawn_evalfunc(oldself, spot, spawn_score);
+                               self = oldself;
+                               if(spawn_score_x < 0)
+                                       return spawn_score;
                        }
-                       ent = find(ent, targetname, spot.target);
                }
 
-               if(found && !good)
+               if(!found)
+               {
+                       dprint("WARNING: spawnpoint at ", vtos(spot.origin), " could not find its target ", spot.target, "\n");
                        return '-1 0 0';
+               }
        }
 
-       player = playerlist;
-       shortest = vlen(world.maxs - world.mins);
-       for(player = playerlist; player; player = player.chain)
-               if (player != self)
-               {
-                       thisdist = vlen(player.origin - spot.origin);
-                       if (thisdist < shortest)
-                               shortest = thisdist;
-               }
-       return prio * '1 0 0' + shortest * '0 1 0';
+       MUTATOR_CALLHOOK(Spawn_Score);
+       return spawn_score;
+}
+
+void Spawn_ScoreAll(entity firstspot, float mindist, float teamcheck)
+{
+       entity spot;
+       for(spot = firstspot; spot; spot = spot.chain)
+               spot.spawnpoint_score = Spawn_Score(spot, mindist, teamcheck);
 }
 
-float spawn_allbad;
-float spawn_allgood;
-entity Spawn_FilterOutBadSpots(entity firstspot, entity playerlist, float mindist, float teamcheck, float anypoint)
+entity Spawn_FilterOutBadSpots(entity firstspot, float mindist, float teamcheck)
 {
        entity spot, spotlist, spotlistend;
-       spawn_allgood = TRUE;
-       spawn_allbad = TRUE;
 
        spotlist = world;
        spotlistend = world;
 
+       Spawn_ScoreAll(firstspot, mindist, teamcheck);
+
        for(spot = firstspot; spot; spot = spot.chain)
        {
-               spot.spawnpoint_score = Spawn_Score(spot, playerlist, teamcheck, anypoint);
-
-               if(autocvar_spawn_debugview)
-               {
-                       setmodel(spot, "models/runematch/rune.mdl");
-                       if(spot.spawnpoint_score_y < mindist)
-                       {
-                               spot.colormod = '1 0 0';
-                               spot.scale = 1;
-                       }
-                       else
-                       {
-                               spot.colormod = '0 1 0';
-                               spot.scale = spot.spawnpoint_score_y / mindist;
-                       }
-               }
-
                if(spot.spawnpoint_score_x >= 0) // spawning allowed here
                {
-                       if(spot.spawnpoint_score_y < mindist)
-                       {
-                               // too short distance
-                               spawn_allgood = FALSE;
-                       }
-                       else
-                       {
-                               // perfect
-                               spawn_allbad = FALSE;
-
-                               if(spotlistend)
-                                       spotlistend.chain = spot;
-                               spotlistend = spot;
-                               if(!spotlist)
-                                       spotlist = spot;
-
-                               /*
-                               if(teamcheck >= 0)
-                               if(spot.team != teamcheck)
-                                       error("invalid spawn added");
-
-                               print("added ", etos(spot), "\n");
-                               */
-                       }
+                       if(spotlistend)
+                               spotlistend.chain = spot;
+                       spotlistend = spot;
+                       if(!spotlist)
+                               spotlist = spot;
                }
        }
        if(spotlistend)
                spotlistend.chain = world;
 
-       /*
-               entity e;
-               if(teamcheck >= 0)
-                       for(e = spotlist; e; e = e.chain)
-                       {
-                               print("seen ", etos(e), "\n");
-                               if(e.team != teamcheck)
-                                       error("invalid spawn found");
-                       }
-       */
-
        return spotlist;
 }
 
@@ -318,14 +251,13 @@ Finds a point to respawn
 entity SelectSpawnPoint (float anypoint)
 {
        float teamcheck;
-       entity firstspot_new;
-       entity spot, firstspot, playerlist;
+       entity spot, firstspot;
 
        spot = find (world, classname, "testplayerstart");
        if (spot)
                return spot;
 
-       if(anypoint)
+       if(anypoint || autocvar_g_spawn_useallspawns)
                teamcheck = -1;
        else if(have_team_spawns > 0)
        {
@@ -350,8 +282,6 @@ entity SelectSpawnPoint (float anypoint)
                // if we get here, we either require team spawns but have none, or we require non-team spawns and have none; use any spawn then
 
 
-       // get the list of players
-       playerlist = findchain(classname, "player");
        // get the entire list of spots
        firstspot = findchain(classname, "info_player_deathmatch");
        // filter out the bad ones
@@ -362,42 +292,26 @@ entity SelectSpawnPoint (float anypoint)
        }
        else
        {
-               firstspot_new = Spawn_FilterOutBadSpots(firstspot, playerlist, 100, teamcheck, anypoint);
-               if(!firstspot_new)
-                       firstspot_new = Spawn_FilterOutBadSpots(firstspot, playerlist, -1, teamcheck, anypoint);
-               firstspot = firstspot_new;
+               float mindist;
+               if (arena_roundbased && !g_ca)
+                       mindist = 800;
+               else
+                       mindist = 100;
+               firstspot = Spawn_FilterOutBadSpots(firstspot, mindist, teamcheck);
 
                // there is 50/50 chance of choosing a random spot or the furthest spot
                // (this means that roughly every other spawn will be furthest, so you
                // usually won't get fragged at spawn twice in a row)
-               if (arena_roundbased && !g_ca)
-               {
-                       firstspot_new = Spawn_FilterOutBadSpots(firstspot, playerlist, 800, teamcheck, anypoint);
-                       if(firstspot_new)
-                               firstspot = firstspot_new;
-                       spot = Spawn_WeightedPoint(firstspot, 1, 1, 1);
-               }
-               else if (random() > autocvar_g_spawn_furthest)
+               if (random() > autocvar_g_spawn_furthest)
                        spot = Spawn_WeightedPoint(firstspot, 1, 1, 1);
                else
                        spot = Spawn_WeightedPoint(firstspot, 1, 5000, 5); // chooses a far far away spawnpoint
        }
 
-       if(autocvar_spawn_debugview)
-       {
-               print("spot mindistance: ", vtos(spot.spawnpoint_score), "\n");
-
-               entity e;
-               if(teamcheck >= 0)
-                       for(e = firstspot; e; e = e.chain)
-                               if(e.team != teamcheck)
-                                       error("invalid spawn found");
-       }
-
        if (!spot)
        {
                if(autocvar_spawn_debug)
-                       GotoNextMap();
+                       GotoNextMap(0);
                else
                {
                        if(some_spawn_has_been_used)
@@ -427,160 +341,36 @@ string CheckPlayerModel(string plyermodel) {
                // to change a cvar default, we'll have a small leak here.
                FallbackPlayerModel = strzone(cvar_defstring("_cl_playermodel"));
        }
-       if(strlen(plyermodel) < 4)
-               return FallbackPlayerModel;
+       // only in right path
        if( substring(plyermodel,0,14) != "models/player/")
                return FallbackPlayerModel;
-       else if(autocvar_sv_servermodelsonly)
+       // only good file extensions
+       if(substring(plyermodel,-4,4) != ".zym")
+       if(substring(plyermodel,-4,4) != ".dpm")
+       if(substring(plyermodel,-4,4) != ".iqm")
+       if(substring(plyermodel,-4,4) != ".md3")
+       if(substring(plyermodel,-4,4) != ".psk")
+               return FallbackPlayerModel;
+       // forbid the LOD models
+       if(substring(plyermodel, -9,5) == "_lod1")
+               return FallbackPlayerModel;
+       if(substring(plyermodel, -9,5) == "_lod2")
+               return FallbackPlayerModel;
+       if(plyermodel != strtolower(plyermodel))
+               return FallbackPlayerModel;
+       // also, restrict to server models
+       if(autocvar_sv_servermodelsonly)
        {
-               if(substring(plyermodel,-4,4) != ".zym")
-               if(substring(plyermodel,-4,4) != ".dpm")
-               if(substring(plyermodel,-4,4) != ".iqm")
-               if(substring(plyermodel,-4,4) != ".md3")
-               if(substring(plyermodel,-4,4) != ".psk")
-                       return FallbackPlayerModel;
-               // forbid the LOD models
-               if(substring(plyermodel, -9,5) == "_lod1")
-                       return FallbackPlayerModel;
-               if(substring(plyermodel, -9,5) == "_lod2")
-                       return FallbackPlayerModel;
-               if(plyermodel != strtolower(plyermodel))
-                       return FallbackPlayerModel;
                if(!fexists(plyermodel))
                        return FallbackPlayerModel;
        }
        return plyermodel;
 }
 
-/*
-=============
-Client_customizeentityforclient
-
-LOD reduction
-=============
-*/
-void Client_uncustomizeentityforclient()
-{
-       if(self.modelindex == 0) // no need to uncustomize then
-               return;
-       self.modelindex = self.modelindex_lod0;
-       self.skin = self.skinindex;
-}
-
-float Client_customizeentityforclient()
-{
-       entity modelsource;
-
-       if(self.modelindex == 0)
-               return TRUE;
-
-       // forcemodel stuff
-
-#ifdef PROFILING
-       float t0;
-       t0 = gettime(GETTIME_HIRES); // reference
-#endif
-
-       modelsource = self;
-
-#ifdef ALLOW_FORCEMODELS
-       if(other.cvar_cl_forceplayermodelsfromxonotic)
-               if not(self.modelindex_lod0_from_xonotic)
-                       modelsource = other;
-       if(other.cvar_cl_forceplayermodels && sv_clforceplayermodels)
-               modelsource = other;
-#endif
-
-       self.skin = modelsource.skinindex;
-
-#if 0
-       if(modelsource == self)
-               self.skin = modelsource.skinindex;
-       else
-               self.skin = mod(modelsource.skinindex, 3); // forbid the fbskins as forced skins
-#endif
-
-       // self: me
-       // other: the player viewing me
-       float distance;
-       float f;
-
-       if(other.cvar_cl_playerdetailreduction <= 0)
-       {
-               if(other.cvar_cl_playerdetailreduction <= -2)
-                       self.modelindex = modelsource.modelindex_lod2;
-               else if(other.cvar_cl_playerdetailreduction <= -1)
-                       self.modelindex = modelsource.modelindex_lod1;
-               else
-                       self.modelindex = modelsource.modelindex_lod0;
-       }
-       else
-       {
-               distance = vlen(self.origin - other.origin);
-               f = (distance + 100.0) * other.cvar_cl_playerdetailreduction;
-               if(f > sv_loddistance2)
-                       self.modelindex = modelsource.modelindex_lod2;
-               else if(f > sv_loddistance1)
-                       self.modelindex = modelsource.modelindex_lod1;
-               else
-                       self.modelindex = modelsource.modelindex_lod0;
-       }
-
-#ifdef PROFILING
-       float t1;
-       t1 = gettime(GETTIME_HIRES); // reference
-       client_cefc_accumulator += (t1 - t0);
-#endif
-
-       return TRUE;
-}
-
-void setmodel_lod(entity e, string modelname)
+void setplayermodel(entity e, string modelname)
 {
-       string s;
-
-       if(sv_loddistance1)
-       {
-               // FIXME: this only supports 3-letter extensions
-               s = strcat(substring(modelname, 0, strlen(modelname)-4), "_lod1", substring(modelname, -4, 4));
-               if(fexists(s))
-               {
-                       setmodel(e, s); // players have high precision
-                       self.modelindex_lod1 = self.modelindex;
-               }
-               else
-                       self.modelindex_lod1 = -1;
-
-               s = strcat(substring(modelname, 0, strlen(modelname)-4), "_lod2", substring(modelname, -4, 4));
-               if(fexists(s))
-               {
-                       setmodel(e, s); // players have high precision
-                       self.modelindex_lod2 = self.modelindex;
-               }
-               else
-                       self.modelindex_lod2 = -1;
-
-               precache_model(modelname);
-               setmodel(e, modelname); // players have high precision
-               self.modelindex_lod0 = self.modelindex;
-
-               if(self.modelindex_lod1 < 0)
-                       self.modelindex_lod1 = self.modelindex;
-
-               if(self.modelindex_lod2 < 0)
-                       self.modelindex_lod2 = self.modelindex;
-       }
-       else
-       {
-               precache_model(modelname);
-               setmodel(e, modelname); // players have high precision
-               self.modelindex_lod0 = self.modelindex;
-                       // save it for possible player model forcing
-       }
-
-       s = whichpack(self.model);
-       self.modelindex_lod0_from_xonotic = ((s == "") || (substring(s, 0, 4) == "data"));
-
+       precache_model(modelname);
+       setmodel(e, modelname);
        player_setupanimsformodel();
        UpdatePlayerSounds();
 }
@@ -631,9 +421,6 @@ void PutObserverInServer (void)
        if(self.flagcarried)
                DropFlag(self.flagcarried, world, world);
 
-       if(self.ballcarried && g_nexball)
-               DropBall(self.ballcarried, self.origin + self.ballcarried.origin, self.velocity);
-
        WaypointSprite_PlayerDead();
 
        if not(g_ca)  // don't reset teams when moving a ca player to the spectators
@@ -695,16 +482,18 @@ void PutObserverInServer (void)
        self.fixangle = TRUE;
        self.crouch = FALSE;
 
-       self.view_ofs = '0 0 0'; // so that your view doesn't go into the ceiling with MOVETYPE_FLY_WORLDONLY, previously "PL_VIEW_OFS"
        setorigin (self, spot.origin);
-       setsize (self, PL_CROUCH_MIN, PL_CROUCH_MAX); // give the spectator some space between walls for MOVETYPE_FLY_WORLDONLY
        self.prevorigin = self.origin;
        self.items = 0;
        self.weapons = 0;
        self.model = "";
        FixPlayermodel();
-       self.model = "";
-       self.modelindex = 0;
+       setmodel(self, "null");
+       self.drawonlytoclient = self;
+
+       setsize (self, PL_CROUCH_MIN, PL_CROUCH_MAX); // give the spectator some space between walls for MOVETYPE_FLY_WORLDONLY
+       self.view_ofs = '0 0 0'; // so that your view doesn't go into the ceiling with MOVETYPE_FLY_WORLDONLY, previously "PL_VIEW_OFS"
+
        self.weapon = 0;
        self.weaponname = "";
        self.switchingweapon = 0;
@@ -719,9 +508,6 @@ void PutObserverInServer (void)
        self.oldvelocity = self.velocity;
        self.fire_endtime = -1;
 
-       if(sv_loddistance1)
-               SetCustomizer(self, Client_customizeentityforclient, Client_uncustomizeentityforclient);
-
        if(g_arena)
        {
                if(self.version_mismatch)
@@ -753,15 +539,17 @@ void PutObserverInServer (void)
                self.frags = FRAGS_SPECTATOR;
 }
 
+.float model_randomizer;
 void FixPlayermodel()
 {
        string defaultmodel;
-       float defaultskin, chmdl, oldskin;
+       float defaultskin, chmdl, oldskin, n, i;
        vector m1, m2;
 
        defaultmodel = "";
 
-       if(autocvar_sv_defaultcharacter == 1) {
+       if(autocvar_sv_defaultcharacter == 1)
+       {
                defaultskin = 0;
 
                if(teamplay)
@@ -780,13 +568,17 @@ void FixPlayermodel()
                        defaultmodel = autocvar_sv_defaultplayermodel;
                        defaultskin = autocvar_sv_defaultplayerskin;
                }
-       }
 
-       if(self.modelindex == 0 && self.deadflag == DEAD_NO)
-       {
-               if(self.model != "")
-                       bprint("\{1}^1Player ", self.netname, "^1 has a zero modelindex, trying to fix...\n");
-               self.model = ""; // force the != checks to return true
+               n = tokenize_console(defaultmodel);
+               if(n > 0)
+                       defaultmodel = argv(floor(n * self.model_randomizer));
+
+               i = strstrofs(defaultmodel, ":", 0);
+               if(i >= 0)
+               {
+                       defaultskin = stof(substring(defaultmodel, i+1, -1));
+                       defaultmodel = substring(defaultmodel, 0, i);
+               }
        }
 
        if(defaultmodel != "")
@@ -795,29 +587,29 @@ void FixPlayermodel()
                {
                        m1 = self.mins;
                        m2 = self.maxs;
-                       setmodel_lod (self, defaultmodel);
+                       setplayermodel (self, defaultmodel);
                        setsize (self, m1, m2);
                        chmdl = TRUE;
                }
 
-               oldskin = self.skinindex;
-               self.skinindex = defaultskin;
+               oldskin = self.skin;
+               self.skin = defaultskin;
        } else {
                if (self.playermodel != self.model || self.playermodel == "")
                {
                        self.playermodel = CheckPlayerModel(self.playermodel); // this is never "", so no endless loop
                        m1 = self.mins;
                        m2 = self.maxs;
-                       setmodel_lod (self, self.playermodel);
+                       setplayermodel (self, self.playermodel);
                        setsize (self, m1, m2);
                        chmdl = TRUE;
                }
 
-               oldskin = self.skinindex;
-               self.skinindex = stof(self.playerskin);
+               oldskin = self.skin;
+               self.skin = stof(self.playerskin);
        }
 
-       if(chmdl || oldskin != self.skinindex)
+       if(chmdl || oldskin != self.skin)
                self.species = player_getspecies(); // model or skin has changed
 
        if(!teamplay)
@@ -861,6 +653,9 @@ void PutClientInServer (void)
                WriteByte(MSG_ONE, SVC_SETVIEW);
                WriteEntity(MSG_ONE, self);
        }
+       
+       // reset player keys
+       self.itemkeys = 0;
 
        // player is dead and becomes observer
        // FIXME fix LMS scoring for new system
@@ -912,11 +707,14 @@ void PutClientInServer (void)
                if(INDEPENDENT_PLAYERS)
                        MAKE_INDEPENDENT_PLAYER(self);
                self.flags = FL_CLIENT;
+               if(autocvar__notarget)
+                       self.flags |= FL_NOTARGET;
                self.takedamage = DAMAGE_AIM;
                if(g_minstagib)
                        self.effects = EF_FULLBRIGHT;
                else
                        self.effects = 0;
+               self.effects |= EF_TELEPORT_BIT | EF_RESTARTANIM_BIT;
                self.air_finished = time + 12;
                self.dmg = 2;
                if(autocvar_g_balance_nex_charge)
@@ -1014,11 +812,9 @@ void PutClientInServer (void)
                        WriteByte(MSG_ONE, TE_CSQC_SPAWN);
                });
 
-               if(sv_loddistance1)
-                       SetCustomizer(self, Client_customizeentityforclient, Client_uncustomizeentityforclient);
-
                self.model = "";
                FixPlayermodel();
+               self.drawonlytoclient = world;
 
                self.crouch = FALSE;
                self.view_ofs = PL_VIEW_OFS;
@@ -1030,7 +826,8 @@ void PutClientInServer (void)
                self.prevorigin = self.origin;
                self.lastrocket = world; // stop rocket guiding, no revenge from the grave!
                self.lastteleporttime = time; // prevent insane speeds due to changing origin
-
+        self.hud = HUD_NORMAL;
+        
                if(g_arena)
                {
                        Spawnqueue_Remove(self);
@@ -1063,12 +860,6 @@ void PutClientInServer (void)
 
                race_PostSpawn(spot);
 
-               if(autocvar_spawn_debug)
-               {
-                       sprint(self, strcat("spawnpoint origin:  ", vtos(spot.origin), "\n"));
-                       remove(spot);   // usefull for checking if there are spawnpoints, that let drop through the floor
-               }
-
                //stuffcmd(self, "chase_active 0");
                //stuffcmd(self, "set viewsize $tmpviewsize \n");
 
@@ -1107,8 +898,15 @@ void PutClientInServer (void)
                        activator = world;
                self = oldself;
 
+               spawn_spot = spot;
                MUTATOR_CALLHOOK(PlayerSpawn);
 
+               if(autocvar_spawn_debug)
+               {
+                       sprint(self, strcat("spawnpoint origin:  ", vtos(spot.origin), "\n"));
+                       remove(spot);   // usefull for checking if there are spawnpoints, that let drop through the floor
+               }
+
                self.switchweapon = w_getbestweapon(self);
                self.cnt = -1; // W_LastWeapon will not complain
                self.weapon = 0;
@@ -1117,6 +915,8 @@ void PutClientInServer (void)
 
                if(!self.alivetime)
                        self.alivetime = time;
+
+               antilag_clear(self);
        } else if(self.classname == "observer" || (g_ca && !allowed_to_spawn)) {
                PutObserverInServer ();
        }
@@ -1313,7 +1113,7 @@ void KillIndicator_Think()
                return;
        }
 
-       if (!self.owner.modelindex)
+       if (self.owner.effects & CSQCMODEL_EF_INVISIBLE)
        {
                self.owner.killindicator = world;
                remove(self);
@@ -1373,13 +1173,13 @@ void ClientKill_TeamChange (float targetteam) // 0 = don't change, -1 = auto, -2
 
     if(!self.killindicator)
        {
-               if(self.modelindex && self.deadflag == DEAD_NO)
+               if(self.deadflag == DEAD_NO)
                {
                        killtime = max(killtime, self.clientkill_nexttime - time);
                        self.clientkill_nexttime = time + killtime + autocvar_g_balance_kill_antispam;
                }
 
-               if(killtime <= 0 || !self.modelindex || self.deadflag != DEAD_NO)
+               if(killtime <= 0 || self.classname != "player" || self.deadflag != DEAD_NO)
                {
                        ClientKill_Now();
                }
@@ -1685,9 +1485,6 @@ void ClientConnect (void)
        // Wazat's grappling hook
        SetGrappleHookBindings();
 
-       // get autoswitch state from player when he toggles it
-       stuffcmd(self, "alias autoswitch \"set cl_autoswitch $1 ; cmd autoswitch $1\"\n"); // default.cfg-ed in 2.4.1
-
        // get version info from player
        stuffcmd(self, "cmd clientversion $gameversion\n");
 
@@ -1789,6 +1586,10 @@ void ClientConnect (void)
 
        if(!autocvar_g_campaign)
                Send_CSQC_Centerprint_Generic(self, CPID_MOTD, getwelcomemessage(), autocvar_welcome_message_time, 0);
+
+       CSQCMODEL_AUTOINIT();
+
+       self.model_randomizer = random();
 }
 
 /*
@@ -1846,8 +1647,6 @@ void ClientDisconnect (void)
        RemoveGrapplingHook(self);
        if(self.flagcarried)
                DropFlag(self.flagcarried, world, world);
-       if(self.ballcarried && g_nexball)
-               DropBall(self.ballcarried, self.origin + self.ballcarried.origin, self.velocity);
 
        // Here, everything has been done that requires this player to be a client.
 
@@ -1896,7 +1695,7 @@ void ClientDisconnect (void)
 void ChatBubbleThink()
 {
        self.nextthink = time;
-       if (!self.owner.modelindex || self.owner.chatbubbleentity != self)
+       if ((self.owner.effects & CSQCMODEL_EF_INVISIBLE) || self.owner.chatbubbleentity != self)
        {
                if(self.owner) // but why can that ever be world?
                        self.owner.chatbubbleentity = world;
@@ -1915,7 +1714,7 @@ void ChatBubbleThink()
 
 void UpdateChatBubble()
 {
-       if (!self.modelindex)
+       if (self.effects & CSQCMODEL_EF_INVISIBLE)
                return;
        // spawn a chatbubble entity if needed
        if (!self.chatbubbleentity)
@@ -1955,7 +1754,7 @@ void UpdateChatBubble()
 .float oldcolormap;
 void respawn(void)
 {
-       if(self.modelindex != 0 && autocvar_g_respawn_ghosts)
+       if(!(self.effects & CSQCMODEL_EF_INVISIBLE) && autocvar_g_respawn_ghosts)
        {
                self.solid = SOLID_NOT;
                self.takedamage = DAMAGE_NO;
@@ -1964,7 +1763,7 @@ void respawn(void)
                self.avelocity = randomvec() * autocvar_g_respawn_ghosts_speed * 3 - randomvec() * autocvar_g_respawn_ghosts_speed * 3;
                self.effects |= EF_ADDITIVE;
                self.oldcolormap = self.colormap;
-               self.colormap = 512;
+               self.colormap = 0; // this originally was 512, but raises a warning in the engine, so get rid of it
                pointparticles(particleeffectnum("respawn_ghost"), self.origin, '0 0 0', 1);
                if(autocvar_g_respawn_ghosts_maxtime)
                        SUB_SetFade (self, time + autocvar_g_respawn_ghosts_maxtime / 2 + random () * (autocvar_g_respawn_ghosts_maxtime - autocvar_g_respawn_ghosts_maxtime / 2), 1.5);
@@ -2004,9 +1803,9 @@ void player_powerups (void)
                self.modelflags &~= MF_ROCKET;
        }
 
-       self.effects &~= (EF_DIMLIGHT | EF_RED | EF_BLUE | EF_ADDITIVE | EF_FULLBRIGHT | EF_FLAME | EF_NODEPTHTEST);
+       self.effects &~= (EF_RED | EF_BLUE | EF_ADDITIVE | EF_FULLBRIGHT | EF_FLAME | EF_NODEPTHTEST);
 
-       if(!self.modelindex || self.deadflag) // don't apply the flags if the player is gibbed
+       if((self.effects & CSQCMODEL_EF_INVISIBLE) || self.deadflag) // don't apply the flags if the player is gibbed
                return;
 
        Fire_ApplyDamage(self);
@@ -2041,7 +1840,7 @@ void player_powerups (void)
                if (self.items & IT_INVINCIBLE)
                {
                        play_countdown(self.invincible_finished, "misc/poweroff.wav");
-                       if (time > self.invincible_finished && autocvar_g_balance_powerup_timer)
+                       if (time > self.invincible_finished)
                        {
                                self.items = self.items - (self.items & IT_INVINCIBLE);
                                sprint(self, "^3Speed has worn off\n");
@@ -2062,7 +1861,7 @@ void player_powerups (void)
                {
                        play_countdown(self.strength_finished, "misc/poweroff.wav");
                        self.effects = self.effects | (EF_BLUE | EF_ADDITIVE | EF_FULLBRIGHT);
-                       if (time > self.strength_finished && autocvar_g_balance_powerup_timer)
+                       if (time > self.strength_finished)
                        {
                                self.items = self.items - (self.items & IT_STRENGTH);
                                sprint(self, "^3Strength has worn off\n");
@@ -2080,7 +1879,7 @@ void player_powerups (void)
                {
                        play_countdown(self.invincible_finished, "misc/poweroff.wav");
                        self.effects = self.effects | (EF_RED | EF_ADDITIVE | EF_FULLBRIGHT);
-                       if (time > self.invincible_finished && autocvar_g_balance_powerup_timer)
+                       if (time > self.invincible_finished)
                        {
                                self.items = self.items - (self.items & IT_INVINCIBLE);
                                sprint(self, "^3Shield has worn off\n");
@@ -2777,7 +2576,7 @@ void PlayerPreThink (void)
 
        MUTATOR_CALLHOOK(PlayerPreThink);
 
-       if(!self.cvar_cl_newusekeysupported)
+       if(!self.cvar_cl_newusekeysupported) // FIXME remove this - it was a stupid idea to begin with, we can JUST use the button
        {
                if(self.BUTTON_USE && !self.usekeypressed)
                        PlayerUseKey();
@@ -2808,42 +2607,26 @@ void PlayerPreThink (void)
 
                if(frametime)
                {
-                       if(self.health <= 0 && autocvar_g_deathglow)
-                       {
-                               if(self.glowmod_x > 0)
-                                       self.glowmod_x -= autocvar_g_deathglow * frametime;
-                               else
-                                       self.glowmod_x = -1;
-                               if(self.glowmod_y > 0)
-                                       self.glowmod_y -= autocvar_g_deathglow * frametime;
-                               else
-                                       self.glowmod_y = -1;
-                               if(self.glowmod_z > 0)
-                                       self.glowmod_z -= autocvar_g_deathglow * frametime;
-                               else
-                                       self.glowmod_z = -1;
-                       }
-                       else
+#ifndef NO_LEGACY_NETWORKING
+                       self.glowmod = colormapPaletteColor(self.clientcolors & 0x0F, TRUE) * 2;
+#endif
+
+                       if(self.weapon == WEP_NEX && autocvar_g_balance_nex_charge)
                        {
-                               // set weapon and player glowmod
-                               self.glowmod = colormapPaletteColor(self.clientcolors & 0x0F, TRUE) * 2;
+                               self.weaponentity_glowmod_x = autocvar_g_weapon_charge_colormod_hdrmultiplier * autocvar_g_weapon_charge_colormod_red_half * min(1, self.nex_charge / autocvar_g_balance_nex_charge_animlimit);
+                               self.weaponentity_glowmod_y = autocvar_g_weapon_charge_colormod_hdrmultiplier * autocvar_g_weapon_charge_colormod_green_half * min(1, self.nex_charge / autocvar_g_balance_nex_charge_animlimit);
+                               self.weaponentity_glowmod_z = autocvar_g_weapon_charge_colormod_hdrmultiplier * autocvar_g_weapon_charge_colormod_blue_half * min(1, self.nex_charge / autocvar_g_balance_nex_charge_animlimit);
 
-                               if(self.weapon == WEP_NEX && autocvar_g_balance_nex_charge)
+                               if(self.nex_charge > autocvar_g_balance_nex_charge_animlimit)
                                {
-                                       self.weaponentity_glowmod_x = autocvar_g_weapon_charge_colormod_hdrmultiplier * autocvar_g_weapon_charge_colormod_red_half * min(1, self.nex_charge / autocvar_g_balance_nex_charge_animlimit);
-                                       self.weaponentity_glowmod_y = autocvar_g_weapon_charge_colormod_hdrmultiplier * autocvar_g_weapon_charge_colormod_green_half * min(1, self.nex_charge / autocvar_g_balance_nex_charge_animlimit);
-                                       self.weaponentity_glowmod_z = autocvar_g_weapon_charge_colormod_hdrmultiplier * autocvar_g_weapon_charge_colormod_blue_half * min(1, self.nex_charge / autocvar_g_balance_nex_charge_animlimit);
-
-                                       if(self.nex_charge > autocvar_g_balance_nex_charge_animlimit)
-                                       {
-                                               self.weaponentity_glowmod_x = self.weaponentity_glowmod_x + autocvar_g_weapon_charge_colormod_hdrmultiplier * autocvar_g_weapon_charge_colormod_red_full * (self.nex_charge - autocvar_g_balance_nex_charge_animlimit) / (1 - autocvar_g_balance_nex_charge_animlimit);
-                                               self.weaponentity_glowmod_y = self.weaponentity_glowmod_y + autocvar_g_weapon_charge_colormod_hdrmultiplier * autocvar_g_weapon_charge_colormod_green_full * (self.nex_charge - autocvar_g_balance_nex_charge_animlimit) / (1 - autocvar_g_balance_nex_charge_animlimit);
-                                               self.weaponentity_glowmod_z = self.weaponentity_glowmod_z + autocvar_g_weapon_charge_colormod_hdrmultiplier * autocvar_g_weapon_charge_colormod_blue_full * (self.nex_charge - autocvar_g_balance_nex_charge_animlimit) / (1 - autocvar_g_balance_nex_charge_animlimit);
-                                       }
+                                       self.weaponentity_glowmod_x = self.weaponentity_glowmod_x + autocvar_g_weapon_charge_colormod_hdrmultiplier * autocvar_g_weapon_charge_colormod_red_full * (self.nex_charge - autocvar_g_balance_nex_charge_animlimit) / (1 - autocvar_g_balance_nex_charge_animlimit);
+                                       self.weaponentity_glowmod_y = self.weaponentity_glowmod_y + autocvar_g_weapon_charge_colormod_hdrmultiplier * autocvar_g_weapon_charge_colormod_green_full * (self.nex_charge - autocvar_g_balance_nex_charge_animlimit) / (1 - autocvar_g_balance_nex_charge_animlimit);
+                                       self.weaponentity_glowmod_z = self.weaponentity_glowmod_z + autocvar_g_weapon_charge_colormod_hdrmultiplier * autocvar_g_weapon_charge_colormod_blue_full * (self.nex_charge - autocvar_g_balance_nex_charge_animlimit) / (1 - autocvar_g_balance_nex_charge_animlimit);
                                }
-                               else
-                                       self.weaponentity_glowmod = self.glowmod;
                        }
+                       else
+                               self.weaponentity_glowmod = colormapPaletteColor(self.clientcolors & 0x0F, TRUE) * 2;
+
                        player_powerups();
                }
 
@@ -3015,7 +2798,10 @@ void PlayerPreThink (void)
 
                if(g_nexball)
                        nexball_setstatus();
-
+               
+               // secret status
+               secrets_setstatus();
+               
                self.dmg_team = max(0, self.dmg_team - autocvar_g_teamdamage_resetspeed * frametime);
 
                //self.angles_y=self.v_angle_y + 90;   // temp
@@ -3243,7 +3029,7 @@ void PlayerPostThink (void)
                        self.stored_netname = strzone(uid2name(self.crypto_idfp));
                if(self.stored_netname != self.netname)
                {
-                       db_put(ServerProgsDB, strcat("uid2name", self.crypto_idfp), self.netname);
+                       db_put(ServerProgsDB, strcat("/uid2name/", self.crypto_idfp), self.netname);
                        strunzone(self.stored_netname);
                        self.stored_netname = strzone(self.netname);
                }
@@ -3253,4 +3039,6 @@ void PlayerPostThink (void)
        if(g_race)
                dprint(sprintf("%f %.6f\n", time, race_GetFractionalLapCount(self)));
        */
+
+       CSQCMODEL_AUTOUPDATE();
 }