set waypoint_benchmark 0 "quit after waypoint loading to benchmark bot navigation code"
set g_debug_bot_commands 0 "print scripted bot commands before executing"
set g_debug_defaultsounds 0 "always use default sounds"
+set sv_use_csqc_players 0 "set to 1 to network players to CSQC"
// debug cvars for keyhunt attaching
set _angles "0 0 0"
-erebus,erebus
-erebus.001,shadowhead
+erebus,textures/common/nodraw
+erebus.001,textures/common/nodraw
case ENT_CLIENT_ACCURACY: Ent_ReadAccuracy(); break;
case ENT_CLIENT_AUXILIARYXHAIR: Net_AuXair2(bIsNewEntity); break;
case ENT_CLIENT_TURRET: ent_turret(); break;
+ case ENT_CLIENT_MODEL: CSQCModel_Read(); break;
default:
//error(strcat(_("unknown entity type in CSQC_Ent_Update: %d\n"), self.enttype));
error(sprintf(_("Unknown entity type in CSQC_Ent_Update (enttype: %d, edict: %d, classname: %s)\n"), self.enttype, num_for_edict(self), self.classname));
WaypointSprite_Load();
+ CSQCPlayer_SetCamera();
+
if(spectatee_status)
myteam = GetPlayerColor(spectatee_status - 1);
else
var float autocvar_cl_eventchase_speed = 1.3;
float autocvar_cl_lerpexcess;
string autocvar__togglezoom;
+float autocvar_cl_playerdetailreduction;
+float autocvar_cl_loddistance1 = 1024;
+float autocvar_cl_loddistance2 = 4096;
--- /dev/null
+.vector glowmod;
+.float lodmodelindex0;
+.float lodmodelindex1;
+.float lodmodelindex2;
+
+void CSQCModel_Hook_PreDraw()
+{
+ // auto glowmod from colormap
+ self.glowmod = colormapPaletteColor(((self.colormap >= 1024) ? (self.colormap & 0xFF) : stof(getplayerkeyvalue(self.entnum - 1, "colors"))), TRUE) * 2;
+
+ if(self.modelindex != 0)
+ {
+ // LOD
+ if(self.lodmodelindex0 != self.modelindex)
+ {
+ string modelname = self.model;
+ string s;
+
+ // set modelindex
+ self.lodmodelindex0 = self.modelindex;
+ self.lodmodelindex1 = self.modelindex;
+ self.lodmodelindex2 = self.modelindex;
+
+ // FIXME: this only supports 3-letter extensions
+ s = strcat(substring(modelname, 0, strlen(modelname)-4), "_lod1", substring(modelname, -4, 4));
+ if(fexists(s))
+ {
+ precache_model(s);
+ setmodel(self, s);
+ if(self.modelindex > 0)
+ self.lodmodelindex1 = self.modelindex;
+ }
+
+ s = strcat(substring(modelname, 0, strlen(modelname)-4), "_lod2", substring(modelname, -4, 4));
+ if(fexists(s))
+ {
+ precache_model(s);
+ setmodel(self, s);
+ if(self.modelindex > 0)
+ self.lodmodelindex2 = self.modelindex;
+ }
+
+ setmodel(self, modelname); // make everything normal again
+ }
+
+ if(autocvar_cl_playerdetailreduction <= 0)
+ {
+ if(autocvar_cl_playerdetailreduction <= -2)
+ self.modelindex = self.lodmodelindex2;
+ else if(autocvar_cl_playerdetailreduction <= -1)
+ self.modelindex = self.lodmodelindex1;
+ else
+ self.modelindex = self.lodmodelindex0;
+ }
+ else
+ {
+ float distance = vlen(self.origin - other.origin);
+ float f = (distance + 100.0) * autocvar_cl_playerdetailreduction;
+ if(f > autocvar_cl_loddistance2)
+ self.modelindex = self.lodmodelindex2;
+ else if(f > autocvar_cl_loddistance1)
+ self.modelindex = self.lodmodelindex1;
+ else
+ self.modelindex = self.lodmodelindex0;
+ }
+ }
+}
+
return;
if(self.touch == Gib_Touch) // don't do this for the "chunk" thingie...
+ // TODO somehow make it spray in a direction dependent on self.angles
trailparticles(self, particleeffectnum(strcat(species_prefix(self.cnt), "TR_SLIGHTBLOOD")), oldorg, self.origin);
else
trailparticles(self, particleeffectnum(strcat(species_prefix(self.cnt), "TR_BLOOD")), oldorg, self.origin);
+++ /dev/null
-// FIXME make this generic code, to be used for other entities too?
-.vector iorigin1, iorigin2;
-.vector ivelocity1, ivelocity2;
-.vector iforward1, iforward2;
-.vector iup1, iup2;
-.float itime1, itime2;
-void InterpolateOrigin_Reset()
-{
- self.iflags &~= IFLAG_INTERNALMASK;
- self.itime1 = self.itime2 = 0;
-}
-void InterpolateOrigin_Note()
-{
- float dt;
- float f0;
-
- dt = time - self.itime2;
-
- f0 = self.iflags;
- if(self.iflags & IFLAG_PREVALID)
- self.iflags |= IFLAG_VALID;
- else
- self.iflags |= IFLAG_PREVALID;
-
- self.iorigin1 = self.iorigin2;
- self.iorigin2 = self.origin;
-
- if(self.iflags & IFLAG_AUTOANGLES)
- if(self.iorigin2 != self.iorigin1)
- self.angles = vectoangles(self.iorigin2 - self.iorigin1);
-
- if(self.iflags & IFLAG_ANGLES)
- {
- fixedmakevectors(self.angles);
- if(f0 & IFLAG_VALID)
- {
- self.iforward1 = self.iforward2;
- self.iup1 = self.iup2;
- }
- else
- {
- self.iforward1 = v_forward;
- self.iup1 = v_up;
- }
- self.iforward2 = v_forward;
- self.iup2 = v_up;
- }
-
- if(self.iflags & IFLAG_VELOCITY)
- {
- self.ivelocity1 = self.ivelocity2;
- self.ivelocity2 = self.velocity;
- }
-
- if(vlen(self.iorigin2 - self.iorigin1) > 1000)
- {
- self.itime1 = self.itime2 = time; // don't lerp
- }
-
- if((self.iflags & IFLAG_VELOCITY) && (vlen(self.ivelocity2 - self.ivelocity1) > 1000))
- {
- self.itime1 = self.itime2 = time; // don't lerp
- }
-
- if(dt < 0.2)
- {
- self.itime1 = serverprevtime;
- self.itime2 = time;
- }
- else
- {
- // don't lerp
- self.itime1 = self.itime2 = time;
- }
-}
-void InterpolateOrigin_Do()
-{
- vector forward, up;
- if(self.itime1 && self.itime2 && self.itime1 != self.itime2)
- {
- float f;
- f = bound(0, (time - self.itime1) / (self.itime2 - self.itime1), 1 + autocvar_cl_lerpexcess);
- self.origin = (1 - f) * self.iorigin1 + f * self.iorigin2;
- if(self.iflags & IFLAG_ANGLES)
- {
- forward = (1 - f) * self.iforward1 + f * self.iforward2;
- up = (1 - f) * self.iup1 + f * self.iup2;
- self.angles = fixedvectoangles2(forward, up);
- }
- if(self.iflags & IFLAG_VELOCITY)
- self.velocity = (1 - f) * self.ivelocity1 + f * self.ivelocity2;
- }
-}
-void InterpolateOrigin_Undo()
-{
- self.origin = self.iorigin2;
- if(self.iflags & IFLAG_ANGLES)
- self.angles = fixedvectoangles2(self.iforward2, self.iup2);
- if(self.iflags & IFLAG_VELOCITY)
- self.velocity = self.ivelocity2;
-}
-
+++ /dev/null
-.float iflags;
-#define IFLAG_VELOCITY 1
-#define IFLAG_ANGLES 2
-#define IFLAG_AUTOANGLES 4
-#define IFLAG_VALID 8
-#define IFLAG_PREVALID 16
-#define IFLAG_INTERNALMASK (IFLAG_VALID | IFLAG_PREVALID)
-
-// call this BEFORE reading an entity update
-void InterpolateOrigin_Undo();
-
-// call this AFTER receiving an entity update
-void InterpolateOrigin_Note();
-
-// call this when the entity got teleported, before InterpolateOrigin_Note
-void InterpolateOrigin_Reset();
-
-// call this BEFORE drawing
-void InterpolateOrigin_Do();
string s;
entity e;
+ e = CSQCPlayer_GetPlayer(pl + 1);
+ if(e)
+ return e.origin;
+
+#ifndef NO_LEGACY_NETWORKING
s = getplayerkeyvalue(pl, "TEMPHACK_origin");
if(s != "")
return stov(s);
+#endif
e = entcs_receiver[pl];
if(e)
autocvars.qh
-interpolate.qh
+../csqcmodel/interpolate.qh
teamradar.qh
hud.qh
scoreboard.qh
../server/movelib.qc
main.qh
vehicles/vehicles.qh
+../csqcmodel/settings.qh
+../csqcmodel/cl_model.qh
+../csqcmodel/cl_player.qh
sortlist.qc
miscfunctions.qc
hud.qc
scoreboard.qc
mapvoting.qc
+csqcmodel_hooks.qc
rubble.qc
hook.qc
gibs.qc
damage.qc
casings.qc
+../csqcmodel/common.qc
+../csqcmodel/cl_model.qc
+../csqcmodel/cl_player.qc
effects.qc
wall.qc
modeleffects.qc
announcer.qc
Main.qc
View.qc
-interpolate.qc
+../csqcmodel/interpolate.qc
waypointsprites.qc
movetypes.qc
prandom.qc
const float ENT_CLIENT_ACCURACY = 30;
const float ENT_CLIENT_SHOWNAMES = 31;
const float ENT_CLIENT_WARPZONE_TELEPORTED = 32;
+const float ENT_CLIENT_MODEL = 33;
const float ENT_CLIENT_TURRET = 40;
const float ENT_CLIENT_AUXILIARYXHAIR = 50;
#define SERVERFLAG_ALLOW_FULLBRIGHT 1
#define SERVERFLAG_TEAMPLAY 2
#define SERVERFLAG_PLAYERSTATS 4
+
+var vector autocvar_sv_player_maxs = '16 16 45';
+var vector autocvar_sv_player_mins = '-16 -16 -24';
+var vector autocvar_sv_player_viewoffset = '0 0 20';
+var vector autocvar_sv_player_crouch_maxs = '16 16 25';
+var vector autocvar_sv_player_crouch_mins = '-16 -16 -24';
+var vector autocvar_sv_player_crouch_viewoffset = '0 0 20';
+noref var vector autocvar_sv_player_headsize = '24 24 12';
+
+#define PL_VIEW_OFS autocvar_sv_player_viewoffset
+#define PL_MIN autocvar_sv_player_mins
+#define PL_MAX autocvar_sv_player_maxs
+#define PL_CROUCH_VIEW_OFS autocvar_sv_player_crouch_viewoffset
+#define PL_CROUCH_MIN autocvar_sv_player_crouch_mins
+#define PL_CROUCH_MAX autocvar_sv_player_crouch_maxs
+#define PL_HEAD autocvar_sv_player_headsize
+
+// helpers
+#define PL_VIEW_OFS_z autocvar_sv_player_viewoffset_z
+#define PL_MIN_z autocvar_sv_player_mins_z
+#define PL_MAX_z autocvar_sv_player_maxs_z
+#define PL_CROUCH_VIEW_OFS_z autocvar_sv_player_crouch_viewoffset_z
+#define PL_CROUCH_MIN_z autocvar_sv_player_mins_z
+#define PL_HEAD_x autocvar_sv_player_headsize_x
+#define PL_HEAD_y autocvar_sv_player_headsize_y
+#define PL_HEAD_z autocvar_sv_player_headsize_z
--- /dev/null
+/*
+ * Copyright (c) 2011 Rudolf Polzer
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+var float autocvar_cl_lerpanim_maxdelta_framegroups = 0.1;
+var float autocvar_cl_nolerp = 0;
+
+.float csqcmodel_lerpfrac;
+.float csqcmodel_lerpfrac2;
+.float csqcmodel_lerpfractime;
+.float csqcmodel_lerpfrac2time;
+.float csqcmodel_teleported;
+
+void CSQCModel_InterpolateAnimation_PreNote(float sf)
+{
+#ifdef CSQCMODEL_HAVE_TWO_FRAMES
+ if(sf & CSQCMODEL_PROPERTY_FRAME)
+ {
+ self.frame3 = self.frame;
+ self.frame3time = self.frame1time;
+ }
+ if(sf & CSQCMODEL_PROPERTY_FRAME2)
+ {
+ self.frame4 = self.frame2;
+ self.frame4time = self.frame2time;
+ }
+ if(sf & CSQCMODEL_PROPERTY_LERPFRAC)
+ {
+ self.csqcmodel_lerpfrac2 = self.csqcmodel_lerpfrac;
+ self.csqcmodel_lerpfrac2time = self.csqcmodel_lerpfractime;
+ self.lerpfrac = self.csqcmodel_lerpfrac;
+ }
+#else
+ if(sf & CSQCMODEL_PROPERTY_FRAME)
+ {
+ self.frame2 = self.frame;
+ self.frame2time = self.frame1time;
+ }
+#endif
+}
+
+void CSQCModel_InterpolateAnimation_Note(float sf)
+{
+#ifdef CSQCMODEL_HAVE_TWO_FRAMES
+ if(sf & CSQCMODEL_PROPERTY_FRAME)
+ {
+ self.frame1time = time;
+ }
+ if(sf & CSQCMODEL_PROPERTY_FRAME2)
+ {
+ self.frame2time = time;
+ }
+ if(sf & CSQCMODEL_PROPERTY_LERPFRAC)
+ {
+ self.csqcmodel_lerpfrac = self.lerpfrac;
+ self.csqcmodel_lerpfractime = time;
+ }
+#else
+ if(sf & CSQCMODEL_PROPERTY_FRAME)
+ {
+ self.frame1time = time;
+ }
+#endif
+}
+
+void CSQCModel_InterpolateAnimation_Do()
+{
+#ifdef CSQCMODEL_HAVE_TWO_FRAMES
+ if(autocvar_cl_nolerp || (autocvar_cl_lerpanim_maxdelta_framegroups == 0))
+ {
+ self.lerpfrac = self.csqcmodel_lerpfrac;
+ self.lerpfrac3 = 0;
+ self.lerpfrac4 = 0;
+ }
+ else
+ {
+ float l13, l24, llf;
+ float l24_13;
+
+ if(self.frame3time == 0) // if frame1/3 were not previously displayed, only frame1 can make sense
+ l13 = 1;
+ else
+ l13 = bound(0, (time - self.frame1time) / autocvar_cl_lerpanim_maxdelta_framegroups, 1);
+
+ if(self.frame4time == 0) // if frame2/4 were not previously displayed, only frame2 can make sense
+ l24 = 1;
+ else
+ l24 = bound(0, (time - self.frame2time) / autocvar_cl_lerpanim_maxdelta_framegroups, 1);
+
+ if(self.csqcmodel_lerpfrac2time == 0) // if there is no old lerpfrac (newly displayed model), only lerpfrac makes sense
+ llf = 1;
+ else
+ llf = bound(0, (time - self.csqcmodel_lerpfractime) / autocvar_cl_lerpanim_maxdelta_framegroups, 1);
+
+ l24_13 = self.csqcmodel_lerpfrac * llf + self.csqcmodel_lerpfrac2 * (1 - llf);
+
+ self.lerpfrac = l24 * l24_13;
+ self.lerpfrac4 = (1 - l24) * l24_13;
+ self.lerpfrac3 = (1 - l13) * (1 - l24_13);
+
+ if(l24_13 == 0) // if frames 2/4 are not displayed, clear their frametime
+ {
+ self.frame2time = 0;
+ self.frame4time = 0;
+ }
+
+ if(l24_13 == 1) // if frames 1/3 are not displayed, clear their frametime
+ {
+ self.frame1time = 0;
+ self.frame3time = 0;
+ }
+ }
+#else
+ if(autocvar_cl_nolerp || (autocvar_cl_lerpanim_maxdelta_framegroups == 0))
+ {
+ self.lerpfrac = 0;
+ }
+ else
+ {
+ self.lerpfrac = 1 - bound(0, (time - self.frame1time) / autocvar_cl_lerpanim_maxdelta_framegroups, 1);
+ }
+#endif
+}
+
+void CSQCModel_Draw()
+{
+ // we don't do this for the local player as that one is already handled
+ // by CSQCPlayer_SetCamera()
+ if(!CSQCPlayer_IsLocalPlayer())
+ InterpolateOrigin_Do();
+
+ // TODO csqcplayers: run prediction here too
+ CSQCModel_InterpolateAnimation_Do();
+
+ { CSQCMODEL_HOOK_PREDRAW }
+}
+
+void CSQCModel_Read()
+{
+ float sf;
+ sf = ReadShort();
+
+ self.iflags |= IFLAG_ANGLES; // interpolate angles too
+
+ { CSQCMODEL_HOOK_PREUPDATE }
+
+ CSQCPlayer_PreUpdate();
+ InterpolateOrigin_Undo();
+ CSQCModel_InterpolateAnimation_PreNote(sf);
+
+#define CSQCMODEL_PROPERTY(flag,r,w,f) \
+ if(sf & flag) \
+ self.f = r();
+#define CSQCMODEL_PROPERTY_SCALED(flag,r,w,f,s,mi,ma) \
+ if(sf & flag) \
+ self.f = r() / s;
+ ALLPROPERTIES
+#undef CSQCMODEL_PROPERTY_SCALED
+#undef CSQCMODEL_PROPERTY
+
+ if(sf & CSQCMODEL_PROPERTY_MODELINDEX)
+ setmodelindex(self, self.modelindex); // this retrieves the .model key and sets mins/maxs/absmin/absmax
+
+ if(sf & CSQCMODEL_PROPERTY_TELEPORTED)
+ {
+ self.iflags |= IFLAG_TELEPORTED;
+ self.csqcmodel_teleported = 1;
+ }
+
+ CSQCModel_InterpolateAnimation_Note(sf);
+ InterpolateOrigin_Note();
+ CSQCPlayer_PostUpdate();
+
+ { CSQCMODEL_HOOK_POSTUPDATE }
+
+#ifdef CSQCMODEL_SUPPORT_GETTAGINFO_BEFORE_DRAW
+ InterpolateOrigin_Do();
+ CSQCModel_InterpolateAnimation_Do();
+#endif
+
+ // relink
+ setorigin(self, self.origin);
+
+ // draw it
+ self.drawmask = MASK_NORMAL;
+ self.predraw = CSQCModel_Draw;
+}
--- /dev/null
+/*
+ * Copyright (c) 2011 Rudolf Polzer
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+void CSQCModel_Read();
--- /dev/null
+/*
+ * Copyright (c) 2011 Rudolf Polzer
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+var float autocvar_cl_predictionerrorcompensation = 0;
+
+// engine stuff
+.float pmove_flags;
+float pmove_onground; // weird engine flag we shouldn't really use but have to for now
+#define PMF_DUCKED 4
+#define PMF_ONGROUND 8
+#define REFDEFFLAG_TELEPORTED 1
+#define REFDEFFLAG_JUMPING 2
+
+entity csqcplayer;
+vector csqcplayer_origin, csqcplayer_velocity;
+float csqcplayer_sequence, player_pmflags;
+float csqcplayer_moveframe;
+vector csqcplayer_predictionerror;
+float csqcplayer_predictionerrortime;
+
+vector CSQCPlayer_GetPredictionError()
+{
+ if(!autocvar_cl_predictionerrorcompensation)
+ return '0 0 0';
+ if(time < csqcplayer_predictionerrortime)
+ return csqcplayer_predictionerror * (csqcplayer_predictionerrortime - time) * autocvar_cl_predictionerrorcompensation;
+ return '0 0 0';
+}
+
+void CSQCPlayer_SetPredictionError(vector v)
+{
+ if(!autocvar_cl_predictionerrorcompensation)
+ return;
+ csqcplayer_predictionerror = (csqcplayer_predictionerrortime - time) * autocvar_cl_predictionerrorcompensation * csqcplayer_predictionerror + v;
+ csqcplayer_predictionerrortime = time + 1.0 / autocvar_cl_predictionerrorcompensation;
+}
+
+void CSQCPlayer_Unpredict()
+{
+ if(csqcplayer_status == CSQCPLAYERSTATUS_UNPREDICTED)
+ return;
+ if(csqcplayer_status != CSQCPLAYERSTATUS_PREDICTED)
+ error("Cannot unpredict in current status");
+ self.origin = csqcplayer_origin;
+ self.velocity = csqcplayer_velocity;
+ csqcplayer_moveframe = csqcplayer_sequence+1; //+1 because the recieved frame has the move already done (server side)
+ self.pmove_flags = player_pmflags;
+}
+
+void CSQCPlayer_SetMinsMaxs()
+{
+ if(self.pmove_flags & PMF_DUCKED)
+ {
+ self.mins = PL_CROUCH_MIN;
+ self.maxs = PL_CROUCH_MAX;
+ self.view_ofs = PL_CROUCH_VIEW_OFS;
+ }
+ else
+ {
+ self.mins = PL_MIN;
+ self.maxs = PL_MAX;
+ self.view_ofs = PL_VIEW_OFS;
+ }
+}
+
+void CSQCPlayer_SavePrediction()
+{
+ player_pmflags = self.pmove_flags;
+ csqcplayer_origin = self.origin;
+ csqcplayer_velocity = self.velocity;
+ csqcplayer_sequence = servercommandframe;
+ csqcplayer_status = CSQCPLAYERSTATUS_PREDICTED;
+}
+
+void CSQCPlayer_PredictTo(float endframe)
+{
+ CSQCPlayer_Unpredict();
+ CSQCPlayer_SetMinsMaxs();
+
+ csqcplayer_status = CSQCPLAYERSTATUS_PREDICTED;
+
+ if (getstatf(STAT_HEALTH) <= 0)
+ {
+ csqcplayer_moveframe = clientcommandframe;
+ getinputstate(csqcplayer_moveframe-1);
+ return;
+ }
+
+ while(csqcplayer_moveframe < endframe)
+ {
+ if (!getinputstate(csqcplayer_moveframe))
+ {
+ break;
+ }
+ runstandardplayerphysics(self);
+ CSQCPlayer_SetMinsMaxs();
+ csqcplayer_moveframe++;
+ }
+
+ //add in anything that was applied after (for low packet rate protocols)
+ input_angles = view_angles;
+}
+
+float CSQCPlayer_IsLocalPlayer()
+{
+ return (self == csqcplayer);
+}
+
+void(entity e, float fl) V_CalcRefdef = #640; // DP_CSQC_V_CALCREFDEF
+
+void CSQCPlayer_SetCamera()
+{
+ if(csqcplayer)
+ {
+ entity oldself;
+ oldself = self;
+ self = csqcplayer;
+
+ if(servercommandframe == 0)
+ {
+ InterpolateOrigin_Do();
+ self.view_ofs = '0 0 1' * getstati(STAT_VIEWHEIGHT);
+
+ // get crouch state from the server
+ if(getstati(STAT_VIEWHEIGHT) == PL_VIEW_OFS_z)
+ self.pmove_flags &~= PMF_DUCKED;
+ else if(getstati(STAT_VIEWHEIGHT) == PL_CROUCH_VIEW_OFS_z)
+ self.pmove_flags |= PMF_DUCKED;
+
+ // get onground state from the server
+ if(pmove_onground)
+ self.pmove_flags |= PMF_ONGROUND;
+ else
+ self.pmove_flags &~= PMF_ONGROUND;
+
+ CSQCPlayer_SetMinsMaxs();
+
+ // override it back just in case
+ self.view_ofs = '0 0 1' * getstati(STAT_VIEWHEIGHT);
+ }
+ else
+ {
+ if(csqcplayer_status == CSQCPLAYERSTATUS_FROMSERVER)
+ {
+ vector o, v;
+ o = self.origin;
+ v = pmove_vel; // TRICK: pmove_vel is set by the engine when we get here. No need to network velocity
+ csqcplayer_status = CSQCPLAYERSTATUS_PREDICTED;
+ CSQCPlayer_PredictTo(servercommandframe + 1);
+ CSQCPlayer_SetPredictionError(o - self.origin);
+ self.origin = o;
+ self.velocity = v;
+
+ // get crouch state from the server
+ if(getstati(STAT_VIEWHEIGHT) == PL_VIEW_OFS_z)
+ self.pmove_flags &~= PMF_DUCKED;
+ else if(getstati(STAT_VIEWHEIGHT) == PL_CROUCH_VIEW_OFS_z)
+ self.pmove_flags |= PMF_DUCKED;
+
+ // get onground state from the server
+ if(pmove_onground)
+ self.pmove_flags |= PMF_ONGROUND;
+ else
+ self.pmove_flags &~= PMF_ONGROUND;
+
+ CSQCPlayer_SavePrediction();
+ }
+ CSQCPlayer_PredictTo(clientcommandframe);
+
+ CSQCPlayer_SetMinsMaxs();
+ }
+
+ // relink
+ setorigin(self, self.origin);
+
+ // FIXME support svc_setview?
+
+ if(checkextension("DP_CSQC_V_CALCREFDEF") || checkextension("DP_CSQC_V_CALCREFDEF_WIP1"))
+ {
+ var float refdefflags = 0;
+
+ if(self.csqcmodel_teleported)
+ {
+ refdefflags |= REFDEFFLAG_TELEPORTED;
+ self.csqcmodel_teleported = 0;
+ }
+
+ if(input_buttons & 4)
+ refdefflags |= REFDEFFLAG_JUMPING;
+
+ V_CalcRefdef(self, refdefflags);
+ }
+ else
+ setproperty(VF_ORIGIN, self.origin + self.view_ofs);
+
+ { CSQCPLAYER_HOOK_POSTCAMERASETUP }
+
+ self = oldself;
+ }
+}
+
+void CSQCPlayer_Remove()
+{
+ if(self.entnum != player_localentnum)
+ return;
+ csqcplayer = world;
+ cvar_clientsettemp("cl_movement_replay", "1");
+}
+
+float CSQCPlayer_PreUpdate()
+{
+ if(self.entnum != player_localentnum)
+ return 0;
+ cvar_clientsettemp("cl_movement_replay", "0");
+ if(csqcplayer_status != CSQCPLAYERSTATUS_FROMSERVER)
+ CSQCPlayer_Unpredict();
+ return 1;
+}
+
+float CSQCPlayer_PostUpdate()
+{
+ /*
+ if(self.entnum == player_localentnum)
+ self.renderflags |= RF_EXTERNALMODEL;
+ else
+ self.renderflags &~= RF_EXTERNALMODEL;
+ */
+
+ if(self.entnum != player_localentnum)
+ return 0;
+ csqcplayer_status = CSQCPLAYERSTATUS_FROMSERVER;
+ csqcplayer = self;
+ self.entremove = CSQCPlayer_Remove;
+ return 1;
+}
+
+entity CSQCPlayer_GetPlayer(float pl)
+{
+ return findfloat(world, entnum, pl); // FIXME optimize this using an array
+}
--- /dev/null
+/*
+ * Copyright (c) 2011 Rudolf Polzer
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+float csqcplayer_status;
+#define CSQCPLAYERSTATUS_UNPREDICTED 0
+#define CSQCPLAYERSTATUS_FROMSERVER 1
+#define CSQCPLAYERSTATUS_PREDICTED 2
+
+void CSQCPlayer_SetCamera();
+float CSQCPlayer_PreUpdate();
+float CSQCPlayer_PostUpdate();
+float CSQCPlayer_IsLocalPlayer();
+entity CSQCPlayer_GetPlayer(float pl);
--- /dev/null
+/*
+ * Copyright (c) 2011 Rudolf Polzer
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+noref string csqcmodel_license = "\
+Copyright (c) 2011 Rudolf Polzer\
+\
+Permission is hereby granted, free of charge, to any person obtaining a copy\
+of this software and associated documentation files (the \"Software\"), to\
+deal in the Software without restriction, including without limitation the\
+rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\
+sell copies of the Software, and to permit persons to whom the Software is\
+furnished to do so, subject to the following conditions:\
+\
+The above copyright notice and this permission notice shall be included in\
+all copies or substantial portions of the Software.\
+\
+THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\
+IN THE SOFTWARE.\
+";
+
+.vector glowmod;
+.vector view_ofs;
+.float frame;
+.float frame1time;
+.float frame2;
+.float frame2time;
+.float lerpfrac;
+
+#define CSQCMODEL_PROPERTY_FRAME 32768
+#define CSQCMODEL_PROPERTY_FRAME2 16384
+#define CSQCMODEL_PROPERTY_LERPFRAC 8192
+#define CSQCMODEL_PROPERTY_TELEPORTED 4096 // the "teleport bit" cancelling interpolation
+#define CSQCMODEL_PROPERTY_MODELINDEX 2048
+#define CSQCMODEL_PROPERTY_ORIGIN 1024
+#define CSQCMODEL_PROPERTY_YAW 512
+#define CSQCMODEL_PROPERTY_PITCHROLL 256
+
+#define ALLPROPERTIES_COMMON \
+ CSQCMODEL_PROPERTY(CSQCMODEL_PROPERTY_FRAME, ReadByte, WriteByte, frame) \
+ CSQCMODEL_PROPERTY(CSQCMODEL_PROPERTY_MODELINDEX, ReadShort, WriteShort, modelindex) \
+ CSQCMODEL_PROPERTY(CSQCMODEL_PROPERTY_ORIGIN, ReadCoord, WriteCoord, origin_x) \
+ CSQCMODEL_PROPERTY(CSQCMODEL_PROPERTY_ORIGIN, ReadCoord, WriteCoord, origin_y) \
+ CSQCMODEL_PROPERTY(CSQCMODEL_PROPERTY_ORIGIN, ReadCoord, WriteCoord, origin_z) \
+ CSQCMODEL_PROPERTY(CSQCMODEL_PROPERTY_PITCHROLL, ReadAngle, WriteAngle, angles_x) \
+ CSQCMODEL_PROPERTY(CSQCMODEL_PROPERTY_YAW, ReadAngle, WriteAngle, angles_y) \
+ CSQCMODEL_PROPERTY(CSQCMODEL_PROPERTY_PITCHROLL, ReadAngle, WriteAngle, angles_z) \
+ CSQCMODEL_EXTRAPROPERTIES
+
+#ifdef CSQCMODEL_HAVE_TWO_FRAMES
+.float frame3;
+.float frame3time;
+.float lerpfrac3;
+.float frame4;
+.float frame4time;
+.float lerpfrac4;
+#define ALLPROPERTIES ALLPROPERTIES_COMMON \
+ CSQCMODEL_PROPERTY(CSQCMODEL_PROPERTY_FRAME2, ReadByte, WriteByte, frame2) \
+ CSQCMODEL_PROPERTY_SCALED(CSQCMODEL_PROPERTY_LERPFRAC, ReadByte, WriteByte, lerpfrac, 255, 0, 255)
+#else
+#define ALLPROPERTIES ALLPROPERTIES_COMMON
+#endif
+
--- /dev/null
+/*
+ * Copyright (c) 2011 Rudolf Polzer
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+.vector iorigin1, iorigin2;
+.vector ivelocity1, ivelocity2;
+.vector iforward1, iforward2;
+.vector iup1, iup2;
+.float itime1, itime2;
+void InterpolateOrigin_Reset()
+{
+ self.iflags &~= IFLAG_INTERNALMASK;
+ self.itime1 = self.itime2 = 0;
+}
+void InterpolateOrigin_Note()
+{
+ float dt;
+ float f0;
+
+ dt = time - self.itime2;
+
+ f0 = self.iflags;
+ if(self.iflags & IFLAG_PREVALID)
+ self.iflags |= IFLAG_VALID;
+ else
+ self.iflags |= IFLAG_PREVALID;
+
+ self.iorigin1 = self.iorigin2;
+ self.iorigin2 = self.origin;
+
+ if(self.iflags & IFLAG_AUTOANGLES)
+ if(self.iorigin2 != self.iorigin1)
+ self.angles = vectoangles(self.iorigin2 - self.iorigin1);
+
+ if(self.iflags & IFLAG_ANGLES)
+ {
+ fixedmakevectors(self.angles);
+ if(f0 & IFLAG_VALID)
+ {
+ self.iforward1 = self.iforward2;
+ self.iup1 = self.iup2;
+ }
+ else
+ {
+ self.iforward1 = v_forward;
+ self.iup1 = v_up;
+ }
+ self.iforward2 = v_forward;
+ self.iup2 = v_up;
+ }
+
+ if(self.iflags & IFLAG_VELOCITY)
+ {
+ self.ivelocity1 = self.ivelocity2;
+ self.ivelocity2 = self.velocity;
+ }
+
+ if(self.iflags & IFLAG_TELEPORTED)
+ {
+ self.iflags &~= IFLAG_TELEPORTED;
+ self.itime1 = self.itime2 = time; // don't lerp
+ }
+ else if(vlen(self.iorigin2 - self.iorigin1) > 1000)
+ {
+ self.itime1 = self.itime2 = time; // don't lerp
+ }
+ else if((self.iflags & IFLAG_VELOCITY) && (vlen(self.ivelocity2 - self.ivelocity1) > 1000))
+ {
+ self.itime1 = self.itime2 = time; // don't lerp
+ }
+ else if(dt >= 0.2)
+ {
+ self.itime1 = self.itime2 = time;
+ }
+ else
+ {
+ self.itime1 = serverprevtime;
+ self.itime2 = time;
+ }
+}
+void InterpolateOrigin_Do()
+{
+ vector forward, up;
+ if(self.itime1 && self.itime2 && self.itime1 != self.itime2)
+ {
+ float f;
+ f = bound(0, (time - self.itime1) / (self.itime2 - self.itime1), 1 + autocvar_cl_lerpexcess);
+ self.origin = (1 - f) * self.iorigin1 + f * self.iorigin2;
+ if(self.iflags & IFLAG_ANGLES)
+ {
+ forward = (1 - f) * self.iforward1 + f * self.iforward2;
+ up = (1 - f) * self.iup1 + f * self.iup2;
+ self.angles = fixedvectoangles2(forward, up);
+ }
+ if(self.iflags & IFLAG_VELOCITY)
+ self.velocity = (1 - f) * self.ivelocity1 + f * self.ivelocity2;
+ }
+}
+void InterpolateOrigin_Undo()
+{
+ self.origin = self.iorigin2;
+ if(self.iflags & IFLAG_ANGLES)
+ self.angles = fixedvectoangles2(self.iforward2, self.iup2);
+ if(self.iflags & IFLAG_VELOCITY)
+ self.velocity = self.ivelocity2;
+}
+
--- /dev/null
+/*
+ * Copyright (c) 2011 Rudolf Polzer
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+.float iflags;
+#define IFLAG_VELOCITY 1
+#define IFLAG_ANGLES 2
+#define IFLAG_AUTOANGLES 4
+#define IFLAG_VALID 8
+#define IFLAG_PREVALID 16
+#define IFLAG_TELEPORTED 32
+#define IFLAG_INTERNALMASK (IFLAG_VALID | IFLAG_PREVALID)
+
+// call this BEFORE reading an entity update
+void InterpolateOrigin_Undo();
+
+// call this AFTER receiving an entity update
+void InterpolateOrigin_Note();
+
+// call this when the entity got teleported, before InterpolateOrigin_Note
+void InterpolateOrigin_Reset();
+
+// call this BEFORE drawing
+void InterpolateOrigin_Do();
--- /dev/null
+// define this if svqc code wants to use .frame2 and .lerpfrac
+#define CSQCMODEL_HAVE_TWO_FRAMES
+
+// don't define this ever
+//#define CSQCMODEL_SUPPORT_GETTAGINFO_BEFORE_DRAW
+
+// add properties you want networked to CSQC here
+#define CSQCMODEL_EXTRAPROPERTIES \
+ CSQCMODEL_PROPERTY(1, ReadByte, WriteByte, skin) \
+ CSQCMODEL_PROPERTY(2, ReadShort, WriteShort, colormap) \
+ CSQCMODEL_PROPERTY(4, ReadInt24_t, WriteInt24_t, effects) \
+ CSQCMODEL_PROPERTY_SCALED(8, ReadByte, WriteByte, alpha, 255, 0, 255)
+
+// add hook function calls here
+#define CSQCMODEL_HOOK_PREUPDATE
+#define CSQCMODEL_HOOK_POSTUPDATE
+#define CSQCMODEL_HOOK_PREDRAW \
+ CSQCModel_Hook_PreDraw();
+#define CSQCPLAYER_HOOK_POSTCAMERASETUP
+
+// mod must define:
+//vector PL_MIN = ...;
+//vector PL_MAX = ...;
+//vector PL_VIEW_OFS = ...;
+//vector PL_CROUCH_MIN = ...;
+//vector PL_CROUCH_MAX = ...;
+//vector PL_CROUCH_VIEW_OFS = ...;
--- /dev/null
+/*
+ * Copyright (c) 2011 Rudolf Polzer
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+// generic CSQC model code
+
+#define CSQCMODEL_PROPERTY(flag,r,w,f) \
+ .float csqcmodel_##f;
+#define CSQCMODEL_PROPERTY_SCALED(flag,r,w,f,s,mi,ma) CSQCMODEL_PROPERTY(flag,r,w,f)
+ ALLPROPERTIES
+#undef CSQCMODEL_PROPERTY_SCALED
+#undef CSQCMODEL_PROPERTY
+
+float CSQCModel_Send(entity to, float sf)
+{
+ WriteByte(MSG_ENTITY, ENT_CLIENT_MODEL);
+ WriteShort(MSG_ENTITY, sf);
+
+#define CSQCMODEL_PROPERTY(flag,r,w,f) \
+ if(sf & flag) \
+ { \
+ w(MSG_ENTITY, self.csqcmodel_##f); \
+ }
+#define CSQCMODEL_PROPERTY_SCALED(flag,r,w,f,s,mi,ma) CSQCMODEL_PROPERTY(flag,r,w,f)
+ ALLPROPERTIES
+#undef CSQCMODEL_PROPERTY_SCALED
+#undef CSQCMODEL_PROPERTY
+
+ return TRUE;
+}
+
+void CSQCModel_CheckUpdate()
+{
+ float tmp;
+
+ if(self.effects & EF_RESTARTANIM_BIT)
+ {
+ self.SendFlags |= CSQCMODEL_PROPERTY_FRAME | CSQCMODEL_PROPERTY_FRAME2; // full anim resend please
+ self.effects &~= EF_RESTARTANIM_BIT;
+ }
+
+ if(self.effects & EF_TELEPORT_BIT)
+ {
+ self.SendFlags |= CSQCMODEL_PROPERTY_TELEPORTED; // no interpolation please
+ self.effects &~= EF_TELEPORT_BIT;
+ }
+
+#define CSQCMODEL_PROPERTY(flag,r,w,f) \
+ tmp = self.f; \
+ if(tmp != self.csqcmodel_##f) \
+ { \
+ self.csqcmodel_##f = tmp; \
+ self.SendFlags |= flag; \
+ }
+#define CSQCMODEL_PROPERTY_SCALED(flag,r,w,f,s,mi,ma) \
+ tmp = bound(mi, s * self.f, ma); \
+ if(tmp != self.csqcmodel_##f) \
+ { \
+ self.csqcmodel_##f = tmp; \
+ self.SendFlags |= flag; \
+ }
+ ALLPROPERTIES
+#undef CSQCMODEL_PROPERTY_SCALED
+#undef CSQCMODEL_PROPERTY
+}
+
+void CSQCModel_LinkEntity()
+{
+ self.SendEntity = CSQCModel_Send;
+ self.SendFlags = 0xFFFFFF;
+}
+
+void CSQCModel_UnlinkEntity()
+{
+ self.SendEntity = func_null;
+}
--- /dev/null
+/*
+ * Copyright (c) 2011 Rudolf Polzer
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+// generic CSQC model code
+
+void CSQCModel_CheckUpdate();
+void CSQCModel_LinkEntity();
+void CSQCModel_UnlinkEntity();
float autocvar_g_ctf_shield_min_negscore;
float autocvar_g_cts_finish_kill_delay;
float autocvar_g_cts_selfdamage;
-float autocvar_g_deathglow;
float autocvar_g_debug_bot_commands;
float autocvar_g_domination_default_teams;
float autocvar_g_domination_disable_frags;
float autocvar_sv_maxairstrafespeed;
float autocvar_sv_maxspeed;
string autocvar_sv_motd;
-string autocvar_sv_player_crouch_maxs;
-string autocvar_sv_player_crouch_mins;
-string autocvar_sv_player_crouch_viewoffset;
-string autocvar_sv_player_headsize;
-string autocvar_sv_player_maxs;
-string autocvar_sv_player_mins;
-string autocvar_sv_player_viewoffset;
float autocvar_sv_player_jumpanim_minfall;
float autocvar_sv_precacheplayermodels;
float autocvar_sv_precacheweapons;
float autocvar_g_trueaim_minrange;
float autocvar_g_debug_defaultsounds;
float autocvar_g_loituma;
+#ifndef NO_LEGACY_NETWORKING
+float autocvar_sv_use_csqc_players;
+#endif
IS_CHEAT(i, 0, 0);
FOR_EACH_PLAYER(e)
{
- get_model_parameters(e.playermodel, e.skinindex);
+ get_model_parameters(e.playermodel, e.skin);
if(get_model_parameters_sex == "Female")
{
makevectors(e.angles);
return plyermodel;
}
-/*
-=============
-Client_customizeentityforclient
-
-LOD reduction
-=============
-*/
-void Client_uncustomizeentityforclient()
+void setplayermodel(entity e, string modelname)
{
- 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)
-{
- 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();
}
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;
self.oldvelocity = self.velocity;
self.fire_endtime = -1;
- if(sv_loddistance1)
- SetCustomizer(self, Client_customizeentityforclient, Client_uncustomizeentityforclient);
-
if(g_arena)
{
if(self.version_mismatch)
{
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)
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)
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;
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();
}
return 0;
}
+#ifndef NO_LEGACY_NETWORKING
+.float iscsqcmodel;
+#endif
+
/*
=============
ClientConnect
if(!autocvar_g_campaign)
Send_CSQC_Centerprint_Generic(self, CPID_MOTD, getwelcomemessage(), autocvar_welcome_message_time, 0);
+
+#ifndef NO_LEGACY_NETWORKING
+ if(autocvar_sv_use_csqc_players)
+ {
+ CSQCModel_LinkEntity();
+ self.iscsqcmodel = 1;
+ }
+#else
+ CSQCModel_LinkEntity();
+#endif
}
/*
if(frametime)
{
- if(self.health <= 0 && autocvar_g_deathglow)
+ if(self.weapon == WEP_NEX && autocvar_g_balance_nex_charge)
{
- 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
- {
- // 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();
}
if(g_race)
dprint(sprintf("%f %.6f\n", time, race_GetFractionalLapCount(self)));
*/
+
+#ifndef NO_LEGACY_NETWORKING
+ if(autocvar_sv_use_csqc_players && !self.iscsqcmodel)
+ {
+ CSQCModel_LinkEntity();
+ self.iscsqcmodel = 1;
+ }
+
+ if(!autocvar_sv_use_csqc_players && self.iscsqcmodel)
+ {
+ CSQCModel_UnlinkEntity();
+ self.iscsqcmodel = 0;
+ }
+
+ if(self.iscsqcmodel)
+ CSQCModel_CheckUpdate();
+#else
+ CSQCModel_CheckUpdate();
+#endif
}
self.enemy = oldself;
self.lip = oldself.lip;
self.colormap = oldself.colormap;
- self.glowmod = oldself.glowmod;
self.iscreature = oldself.iscreature;
self.damagedbycontents = oldself.damagedbycontents;
self.angles = oldself.angles;
self.armortype = oldself.armortype;
self.model = oldself.model;
self.modelindex = oldself.modelindex;
- self.modelindex_lod0 = oldself.modelindex_lod0;
- self.modelindex_lod0_from_xonotic = oldself.modelindex_lod0_from_xonotic;
- self.modelindex_lod1 = oldself.modelindex_lod1;
- self.modelindex_lod2 = oldself.modelindex_lod2;
- self.skinindex = oldself.skinindex;
+ self.skin = oldself.skin;
self.species = oldself.species;
self.movetype = oldself.movetype;
self.nextthink = oldself.nextthink;
float player_getspecies()
{
float s;
- get_model_parameters(self.model, self.skinindex);
+ get_model_parameters(self.model, self.skin);
s = get_model_parameters_species;
get_model_parameters(string_null, 0);
if(s < 0)
}
.float modelindex_for_playersound;
-.float skinindex_for_playersound;
+.float skin_for_playersound;
void UpdatePlayerSounds()
{
if(self.modelindex == self.modelindex_for_playersound)
- if(self.skinindex == self.skinindex_for_playersound)
+ if(self.skin == self.skin_for_playersound)
return;
self.modelindex_for_playersound = self.modelindex;
- self.skinindex_for_playersound = self.skinindex;
+ self.skin_for_playersound = self.skin;
ClearPlayerSounds();
LoadPlayerSounds("sound/player/default.sounds", 1);
if(!autocvar_g_debug_defaultsounds)
- if(!LoadPlayerSounds(get_model_datafilename(self.model, self.skinindex, "sounds"), 0))
+ if(!LoadPlayerSounds(get_model_datafilename(self.model, self.skin, "sounds"), 0))
LoadPlayerSounds(get_model_datafilename(self.model, 0, "sounds"), 0);
}
float MSG_INIT = 3; // initialization
float MSG_ENTITY = 5; // csqc
-vector PL_VIEW_OFS = '0 0 35';
-vector PL_MIN = '-16 -16 -24';
-vector PL_MAX = '16 16 45';
-vector PL_CROUCH_VIEW_OFS = '0 0 15';
-vector PL_CROUCH_MIN = '-16 -16 -24';
-vector PL_CROUCH_MAX = '16 16 25';
-
// Sajt - added these, just as constants. Not sure how you want them actually put in the game, but I just
// did this so at least they worked
// NOTE: instagib IS NOT compatible with rocket-arena, so make sure to prevent selecting both in a menu
.float() customizeentityforclient;
.float cvar_cl_handicap;
-.float cvar_cl_playerdetailreduction;
.float cvar_cl_clippedspectating;
.float cvar_cl_autoscreenshot;
.float cvar_cl_movement_track_canjump;
.float cvar_cl_forceplayermodelsfromxonotic;
float sv_clforceplayermodels;
#endif
-float sv_loddistance1;
-float sv_loddistance2;
.float cvar_cl_gunalign;
.float cvar_cl_noantilag;
.float version_nagtime;
-.float modelindex_lod0;
-.float modelindex_lod0_from_xonotic;
-.float skinindex;
-.float modelindex_lod1;
-.float modelindex_lod2;
-
#define NUM_JUMPPADSUSED 3
.float jumppadcount;
.entity jumppadsused[NUM_JUMPPADSUSED];
}
}
-vector PL_VIEW_OFS;
-vector PL_MIN;
-vector PL_MAX;
-vector PL_HEAD;
-vector PL_CROUCH_VIEW_OFS;
-vector PL_CROUCH_MIN;
-vector PL_CROUCH_MAX;
-
float spawnpoint_nag;
void relocate_spawnpoint()
{
- PL_VIEW_OFS = stov(autocvar_sv_player_viewoffset);
- PL_MIN = stov(autocvar_sv_player_mins);
- PL_MAX = stov(autocvar_sv_player_maxs);
- PL_HEAD = stov(autocvar_sv_player_headsize);
- PL_CROUCH_VIEW_OFS = stov(autocvar_sv_player_crouch_viewoffset);
- PL_CROUCH_MIN = stov(autocvar_sv_player_crouch_mins);
- PL_CROUCH_MAX = stov(autocvar_sv_player_crouch_maxs);
-
// nudge off the floor
setorigin(self, self.origin + '0 0 1');
MUTATOR_CALLHOOK(GetCvars);
GetCvars_handleFloat(s, f, autoswitch, "cl_autoswitch");
GetCvars_handleFloat(s, f, cvar_cl_autoscreenshot, "cl_autoscreenshot");
- GetCvars_handleFloat(s, f, cvar_cl_playerdetailreduction, "cl_playerdetailreduction");
GetCvars_handleString(s, f, cvar_g_xonoticversion, "g_xonoticversion");
GetCvars_handleFloat(s, f, cvar_cl_handicap, "cl_handicap");
GetCvars_handleFloat(s, f, cvar_cl_clippedspectating, "cl_clippedspectating");
#ifdef ALLOW_FORCEMODELS
sv_clforceplayermodels = cvar("sv_clforceplayermodels");
#endif
- sv_loddistance1 = cvar("sv_loddistance1");
- sv_loddistance2 = cvar("sv_loddistance2");
-
- if(sv_loddistance2 <= sv_loddistance1)
- sv_loddistance2 = 1073741824; // enough to turn off LOD 2 reliably
sv_clones = cvar("sv_clones");
sv_gentle = cvar("sv_gentle");
if(substring(m, -9,5) == "_lod2")
return;
precache_model(m);
- if(sv_loddistance1)
- {
- f = strcat(substring(m, 0, -5), "_lod1", substring(m, -4, -1));
- if(fexists(f))
- precache_model(f);
- f = strcat(substring(m, 0, -5), "_lod2", substring(m, -4, -1));
- if(fexists(f))
- precache_model(f);
- }
+ f = strcat(substring(m, 0, -5), "_lod1", substring(m, -4, -1));
+ if(fexists(f))
+ precache_model(f);
+ f = strcat(substring(m, 0, -5), "_lod2", substring(m, -4, -1));
+ if(fexists(f))
+ precache_model(f);
globhandle = search_begin(strcat(m, "_*.sounds"), TRUE, FALSE);
if (globhandle < 0)
.vector portal_safe_origin;
.float portal_wants_to_vanish;
.float portal_activatetime;
+.float savemodelindex;
float PlayerEdgeDistance(entity p, vector v)
{
other = other.enemy;
if(other == self.aiment)
{
- self.modelindex = self.modelindex_lod0;
+ self.modelindex = self.savemodelindex;
}
else if(IS_INDEPENDENT_PLAYER(other) || IS_INDEPENDENT_PLAYER(self.aiment))
{
}
else
{
- self.modelindex = self.modelindex_lod0;
+ self.modelindex = self.savemodelindex;
}
return TRUE;
}
portal.fade_time = time + autocvar_g_balance_portal_lifetime;
portal.health = autocvar_g_balance_portal_health;
setmodel(portal, "models/portal.md3");
- portal.modelindex_lod0 = portal.modelindex;
+ portal.savemodelindex = portal.modelindex;
portal.customizeentityforclient = Portal_Customize;
if(!Portal_FindSafeOrigin(portal))
accuracy.qh
csqcprojectile.qh
+../csqcmodel/settings.qh
+../csqcmodel/sv_model.qh
csqceffects.qc
anticheat.qh
accuracy.qc
+../csqcmodel/common.qc
+../csqcmodel/sv_model.qc
csqcprojectile.qc
playerdemo.qc