#include "csqcmodel_hooks.qh"
-#include "autocvars.qh"
+
#include <client/mutators/_mod.qh>
-#include "player_skeleton.qh"
-#include "weapons/projectile.qh"
+#include <client/player_skeleton.qh>
+#include <client/weapons/projectile.qh>
#include <common/animdecide.qh>
+#include <common/effects/all.inc>
+#include <common/effects/all.qh>
#include <common/ent_cs.qh>
#include <common/gamemodes/_mod.qh>
#include <common/mapinfo.qh>
#include <common/physics/movetypes/movetypes.qh>
#include <common/physics/player.qh>
#include <common/viewloc.qh>
-#include <common/effects/all.qh>
-#include <common/effects/all.inc>
#include <lib/csqcmodel/cl_model.qh>
#include <lib/csqcmodel/cl_player.qh>
#include <lib/csqcmodel/interpolate.qh>
.int lodmodelindex0;
.int lodmodelindex1;
.int lodmodelindex2;
-void CSQCPlayer_LOD_Apply(entity this)
+.float loddistance1;
+.float loddistance2;
+void CSQCModel_LOD_Apply(entity this, bool isplayer)
{
+ int detailreduction = ((isplayer) ? autocvar_cl_playerdetailreduction : autocvar_cl_modeldetailreduction);
+
// LOD model loading
- if(this.lodmodelindex0 != this.modelindex)
+ if(this.lodmodelindex0 != this.modelindex && this.lodmodelindex1 != this.modelindex && this.lodmodelindex2 != this.modelindex)
{
string modelname = this.model;
string s;
precache_model(s);
_setmodel(this, s);
if(this.modelindex)
- this.lodmodelindex1 = this.modelindex;
+ this.lodmodelindex2 = this.lodmodelindex1 = this.modelindex;
}
s = strcat(substring(modelname, 0, strlen(modelname)-4), "_lod2", substring(modelname, -4, 4));
}
// apply LOD
- if(autocvar_cl_playerdetailreduction <= 0)
+ if(detailreduction <= 0)
{
- if(autocvar_cl_playerdetailreduction <= -2)
+ if(detailreduction <= -2)
this.modelindex = this.lodmodelindex2;
- else if(autocvar_cl_playerdetailreduction <= -1)
+ else if(detailreduction <= -1)
this.modelindex = this.lodmodelindex1;
else
this.modelindex = this.lodmodelindex0;
}
else
{
- float distance = vlen(this.origin - view_origin);
- float f = (distance * current_viewzoom + 100.0) * autocvar_cl_playerdetailreduction;
+ float distance = vlen(((isplayer) ? this.origin : NearestPointOnBox(this, view_origin)) - view_origin); // TODO: perhaps it should just use NearestPointOnBox all the time, player hitbox can potentially be huge
+ float dist1 = (this.loddistance1 > 0) ? this.loddistance1 : autocvar_cl_loddistance1;
+ float dist2 = (this.loddistance2 > 0) ? this.loddistance2 : autocvar_cl_loddistance2;
+ float f = (distance * current_viewzoom + 100.0) * detailreduction;
f *= 1.0 / bound(0.01, view_quality, 1);
- if(f > autocvar_cl_loddistance2)
+ if(f > dist2)
this.modelindex = this.lodmodelindex2;
- else if(f > autocvar_cl_loddistance1)
+ else if(f > dist1)
this.modelindex = this.lodmodelindex1;
else
this.modelindex = this.lodmodelindex0;
if(MUTATOR_CALLHOOK(ForcePlayercolors_Skip, this, islocalplayer))
goto skipforcecolors;
+ bool forceplayercolors_enabled = false;
+ #define fpc autocvar_cl_forceplayercolors
+ if (gametype.m_1v1)
+ {
+ if ((myteam != NUM_SPECTATOR) && (fpc == 1 || fpc == 2 || fpc == 3 || fpc == 5))
+ forceplayercolors_enabled = true;
+ }
+ else if (teamplay)
+ {
+ if ((team_count == 2) && (myteam != NUM_SPECTATOR) && (fpc == 2 || fpc == 4 || fpc == 5))
+ forceplayercolors_enabled = true;
+ }
+ else
+ {
+ if (fpc == 1 || fpc == 2)
+ forceplayercolors_enabled = true;
+ }
+
// forceplayercolors too
if(teamplay)
{
// own team's color is never forced
- int forcecolor_friend = 0;
- int forcecolor_enemy = 0;
+ int forcecolor_friend = 0, forcecolor_enemy = 0;
entity tm;
if(autocvar_cl_forcemyplayercolors)
forcecolor_friend = 1024 + autocvar_cl_forcemyplayercolors;
- if((autocvar_cl_forceplayercolors == 2 && team_count == 2)
- || (autocvar_cl_forceplayercolors == 3 && IS_GAMETYPE(DUEL)))
+
+ if(forceplayercolors_enabled)
forcecolor_enemy = 1024 + autocvar__cl_color;
if(forcecolor_enemy && !forcecolor_friend)
this.colormap = forcecolor_enemy;
}
}
- else
+ else // if(!teamplay)
{
if(autocvar_cl_forcemyplayercolors && islocalplayer)
this.colormap = 1024 + autocvar_cl_forcemyplayercolors;
- else if(autocvar_cl_forceplayercolors)
+ else if (autocvar_cl_forceuniqueplayercolors && !islocalplayer && !gametype.m_1v1)
+ {
+ // Assign each enemy unique colors
+ // pick colors from 0 to 14 since 15 is the rainbow color
+ // pl01 0 1, pl02 1 2, ..., pl14 13 14, pl15 14 0
+ // pl16 0 2, pl17 1 3, ..., pl29 13 0, pl30 14 1
+ int num = this.entnum - 1;
+ int c1 = num % 15;
+ int q = floor(num / 15);
+ int c2 = (c1 + 1 + q) % 15;
+ this.colormap = 1024 + (c1 << 4) + c2;
+ }
+ else if(forceplayercolors_enabled)
this.colormap = player_localnum + 1;
}
// GLOWMOD AND DEATH FADING
if(this.colormap > 0)
- this.glowmod = colormapPaletteColor(((this.colormap >= 1024) ? this.colormap : entcs_GetClientColors(this.colormap - 1)) & 0x0F, true) * 2;
+ this.glowmod = colormapPaletteColor(((this.colormap >= 1024) ? this.colormap : entcs_GetClientColors(this.colormap - 1)) & 0x0F, true);
else
this.glowmod = '1 1 1';
}
}
+ // don't let the engine increase player's glowmod
+ if (autocvar_r_hdr_glowintensity > 1)
+ this.glowmod /= autocvar_r_hdr_glowintensity;
+
//printf("CSQCPlayer_ModelAppearance_Apply(): state = %s, colormap = %f, glowmod = %s\n", (this.csqcmodel_isdead ? "DEAD" : "ALIVE"), this.colormap, vtos(this.glowmod));
}
}
// recursive predraw call to fix issues with forcemodels and LOD if bone indexes mismatch
- if(this.tag_entity.classname == "csqcmodel")
+ if(this.tag_entity.classname == "ENT_CLIENT_MODEL")
{
CSQCModel_Hook_PreDraw(this.tag_entity, (this.tag_entity.isplayermodel & ISPLAYER_CLIENT));
}
return;
this.csqcmodel_predraw_run = framecount;
- if(!this.modelindex || this.model == "null" || this.alpha < 0)
+ if(!this.modelindex || this.model == "null")
{
this.drawmask = 0;
if(this.snd_looping > 0)
if((this.isplayermodel & ISPLAYER_MODEL) && this.drawmask) // this checks if it's a player MODEL!
{
CSQCPlayer_ModelAppearance_Apply(this, (this.isplayermodel & ISPLAYER_LOCAL));
- CSQCPlayer_LOD_Apply(this);
+ CSQCModel_LOD_Apply(this, true);
if(!isplayer)
{
tracebox(this.origin + '0 0 1', this.mins, this.maxs, this.origin - '0 0 4', MOVE_NORMAL, this);
if(trace_startsolid || trace_fraction < 1)
onground = 1;
+ // predicted clients handle smoothing in the prediction code
+ this.origin = CSQCModel_ApplyStairSmoothing(this, onground, this.origin);
}
animdecide_load_if_needed(this);
animdecide_setimplicitstate(this, onground);
}
}
}
+ else
+ CSQCModel_LOD_Apply(this, false);
CSQCModel_AutoTagIndex_Apply(this);
bool is_playermodel = (substring(this.model, 0, 14) == "models/player/" || substring(this.model, 0, 17) == "models/ok_player/" ||
(substring(this.model, 0, 16) == "models/monsters/" && (this.isplayermodel & BIT(1))));
this.isplayermodel = BITSET(this.isplayermodel, ISPLAYER_MODEL, is_playermodel);
+ this.csqcmodel_isdead = false; // workaround for dead players who become a spectator
// save values set by server
if((this.isplayermodel & ISPLAYER_MODEL))