$(RM) qccversion.*
echo This file intentionally left blank. > $@
-FILES_CSPROGS = $(shell find client lib common warpzonelib csqcmodellib -type f -not -name fteqcc.log -not -name qc.asm)
+FILES_CSPROGS = $(shell find lib common client -type f)
../csprogs.dat: $(FILES_CSPROGS) $(QCCVERSIONFILE)
@echo make[1]: Entering directory \`$(PWD)/client\'
cd client && $(QCC) $(QCCFLAGS) -DCSQC
-FILES_PROGS = $(shell find server lib common warpzonelib csqcmodellib -type f -not -name fteqcc.log -not -name qc.asm)
+FILES_PROGS = $(shell find lib common server -type f)
../progs.dat: $(FILES_PROGS) $(QCCVERSIONFILE)
@echo make[1]: Entering directory \`$(PWD)/server\'
cd server && $(QCC) $(QCCFLAGS) -DSVQC
-FILES_MENU = $(shell find menu lib common warpzonelib -type f -not -name fteqcc.log -not -name qc.asm)
+FILES_MENU = $(shell find lib common menu -type f)
../menu.dat: $(FILES_MENU) $(QCCVERSIONFILE)
@echo make[1]: Entering directory \`$(PWD)/menu\'
cd menu && $(QCC) $(QCCFLAGS) -DMENUQC
#include "../common/animdecide.qh"
#include "../common/movetypes/movetypes.qh"
#include "../common/viewloc.qh"
-#include "../csqcmodellib/cl_model.qh"
-#include "../csqcmodellib/cl_player.qh"
-#include "../csqcmodellib/interpolate.qh"
+#include "../lib/csqcmodel/cl_model.qh"
+#include "../lib/csqcmodel/cl_player.qh"
+#include "../lib/csqcmodel/interpolate.qh"
.float death_time;
.int modelflags;
#include "hook.qh"
-#include "../csqcmodellib/interpolate.qh"
-#include "../warpzonelib/common.qh"
+#include "../lib/csqcmodel/interpolate.qh"
+#include "../lib/warpzone/common.qh"
entityclass(Hook);
class(Hook) .float HookType; // ENT_CLIENT_*
#include "../common/mutators/mutator/waypoints/all.qh"
#include "../common/nades/all.qh"
#include "../common/stats.qh"
-#include "../csqcmodellib/cl_player.qh"
+#include "../lib/csqcmodel/cl_player.qh"
#include "../server/mutators/gamemode_ctf.qh"
#include "laser.qh"
-#include "../csqcmodellib/interpolate.qh"
+#include "../lib/csqcmodel/interpolate.qh"
// a laser goes from origin in direction angles
// it has color 'colormod'
#include "../common/triggers/include.qh"
#include "../common/turrets/cl_turrets.qh"
#include "../common/vehicles/all.qh"
-#include "../csqcmodellib/cl_model.qh"
-#include "../csqcmodellib/interpolate.qh"
-#include "../warpzonelib/client.qh"
+#include "../lib/csqcmodel/cl_model.qh"
+#include "../lib/csqcmodel/interpolate.qh"
+#include "../lib/warpzone/client.qh"
// --------------------------------------------------------------------------
// BEGIN REQUIRED CSQC FUNCTIONS
#include "../common/teams.qh"
-#include "../csqcmodellib/cl_model.qh"
+#include "../lib/csqcmodel/cl_model.qh"
void AuditLists()
#include "../common/stats.qh"
-#include "../warpzonelib/common.qh"
+#include "../lib/warpzone/common.qh"
void Net_ReadVortexBeamParticle()
{
#include "player_skeleton.qh"
#include "mutators/events.qh"
-#include "../csqcmodellib/cl_player.qh"
-#include "../warpzonelib/anglestransform.qh"
+#include "../lib/csqcmodel/cl_player.qh"
+#include "../lib/warpzone/anglestransform.qh"
.vector v_angle;
#include "../common/triggers/include.qc"
-#include "../csqcmodellib/cl_model.qc"
-#include "../csqcmodellib/cl_player.qc"
-#include "../csqcmodellib/interpolate.qc"
+#include "../lib/csqcmodel/cl_model.qc"
+#include "../lib/csqcmodel/cl_player.qc"
+#include "../lib/csqcmodel/interpolate.qc"
#include "../server/mutators/mutator_multijump.qc"
-#include "../warpzonelib/anglestransform.qc"
-#include "../warpzonelib/client.qc"
-#include "../warpzonelib/common.qc"
-#include "../warpzonelib/util_server.qc"
+#include "../lib/warpzone/anglestransform.qc"
+#include "../lib/warpzone/client.qc"
+#include "../lib/warpzone/common.qc"
+#include "../lib/warpzone/util_server.qc"
#if BUILD_MOD
#include "../../mod/client/progs.inc"
#include "../common/mapinfo.qh"
#include "../common/teams.qh"
-#include "../csqcmodellib/cl_model.qh"
+#include "../lib/csqcmodel/cl_model.qh"
// self.isactive = player is in range and coordinates/status (health and armor) are up to date
// self.origin = player origin TODO: should maybe move this so it's the origin of the shownames tag already in SSQC for culling?
#include "../common/buffs/all.qh"
#include "../common/movetypes/movetypes.qh"
#include "../common/weapons/all.qh"
-#include "../csqcmodellib/cl_model.qh"
-#include "../csqcmodellib/common.qh"
+#include "../lib/csqcmodel/cl_model.qh"
+#include "../lib/csqcmodel/common.qh"
#include "../server/t_items.qc"
#include "../common/mutators/mutator/waypoints/all.qh"
-#include "../csqcmodellib/interpolate.qh"
+#include "../lib/csqcmodel/interpolate.qh"
vector teamradar_3dcoord_to_texcoord(vector in)
{
#include "../common/minigames/cl_minigames.qh"
#include "../common/minigames/cl_minigames_hud.qh"
-#include "../csqcmodellib/cl_player.qh"
+#include "../lib/csqcmodel/cl_player.qh"
-#include "../warpzonelib/client.qh"
-#include "../warpzonelib/common.qh"
+#include "../lib/warpzone/client.qh"
+#include "../lib/warpzone/common.qh"
entity porto;
vector polyline[16];
#include "bgmscript.qh"
-#include "../csqcmodellib/interpolate.qh"
+#include "../lib/csqcmodel/interpolate.qh"
.float alpha;
.float scale;
#include "../../common/nades/all.qh"
#include "../../common/movetypes/movetypes.qh"
-#include "../../csqcmodellib/interpolate.qh"
+#include "../../lib/csqcmodel/interpolate.qh"
-#include "../../warpzonelib/anglestransform.qh"
+#include "../../lib/warpzone/anglestransform.qh"
.float alpha;
.float scale;
#include "../../server/weapons/tracing.qh"
#include "../../server/weapons/weaponsystem.qh"
#include "../mutators/mutator/waypoints/waypointsprites.qh"
-#include "../../warpzonelib/server.qh"
+#include "../../lib/warpzone/server.qh"
#endif
// special spawn flags
#if defined(CSQC)
#elif defined(MENUQC)
#elif defined(SVQC)
- #include "../../warpzonelib/common.qh"
+ #include "../../lib/warpzone/common.qh"
#include "../constants.qh"
#include "../teams.qh"
#include "../util.qh"
#include "../../server/command/common.qh"
#include "../../server/command/cmd.qh"
#include "../triggers/triggers.qh"
- #include "../../csqcmodellib/sv_model.qh"
+ #include "../../lib/csqcmodel/sv_model.qh"
#include "../../server/round_handler.qh"
#endif
#include "../stats.qh"
#include "../util.qh"
#include "movetypes.qh"
- #include "../../csqcmodellib/common.qh"
+ #include "../../lib/csqcmodel/common.qh"
#include "../../server/t_items.qh"
#elif defined(MENUQC)
#elif defined(SVQC)
#include "../buffs/all.qh"
#include "../movetypes/movetypes.qh"
#include "../../client/main.qh"
- #include "../../csqcmodellib/cl_model.qh"
+ #include "../../lib/csqcmodel/cl_model.qh"
#elif defined(MENUQC)
#elif defined(SVQC)
#include "../constants.qh"
#include "../../../server/g_damage.qh"
#include "../../../server/bot/bot.qh"
#include "../../../common/csqcmodel_settings.qh"
-#include "../../../csqcmodellib/sv_model.qh"
+#include "../../../lib/csqcmodel/sv_model.qh"
#include "../../../server/weapons/common.qh"
.entity sprite;
#if defined(CSQC)
#include "../../buffs/all.qh"
- #include "../../../csqcmodellib/interpolate.qh"
+ #include "../../../lib/csqcmodel/interpolate.qh"
#include "../../../client/main.qh"
- #include "../../../csqcmodellib/cl_model.qh"
+ #include "../../../lib/csqcmodel/cl_model.qh"
#elif defined(MENUQC)
#elif defined(SVQC)
#endif
#if defined(CSQC)
#elif defined(MENUQC)
#elif defined(SVQC)
- #include "../../warpzonelib/common.qh"
- #include "../../warpzonelib/util_server.qh"
- #include "../../warpzonelib/server.qh"
+ #include "../../lib/warpzone/common.qh"
+ #include "../../lib/warpzone/util_server.qh"
+ #include "../../lib/warpzone/server.qh"
#include "../constants.qh"
#include "../triggers/subs.qh"
#include "../util.qh"
#if defined(CSQC)
#elif defined(MENUQC)
#elif defined(SVQC)
- #include "../../../warpzonelib/util_server.qh"
+ #include "../../../lib/warpzone/util_server.qh"
#include "../../weapons/all.qh"
#include "../../../server/defs.qh"
#include "../../deathtypes.qh"
#if defined(CSQC)
#elif defined(MENUQC)
#elif defined(SVQC)
- #include "../../../warpzonelib/util_server.qh"
+ #include "../../../lib/warpzone/util_server.qh"
#include "../../../server/defs.qh"
#endif
#include "../../client/defs.qh"
#include "../constants.qh"
#include "../stats.qh"
- #include "../../warpzonelib/anglestransform.qh"
- #include "../../warpzonelib/common.qh"
- #include "../../warpzonelib/client.qh"
+ #include "../../lib/warpzone/anglestransform.qh"
+ #include "../../lib/warpzone/common.qh"
+ #include "../../lib/warpzone/client.qh"
#include "../util.qh"
#include "../buffs/all.qh"
#include "../../client/autocvars.qh"
#include "../deathtypes.qh"
- #include "../../csqcmodellib/interpolate.qh"
+ #include "../../lib/csqcmodel/interpolate.qh"
#include "../movetypes/movetypes.qh"
#include "../../client/main.qh"
- #include "../../csqcmodellib/cl_model.qh"
+ #include "../../lib/csqcmodel/cl_model.qh"
#elif defined(MENUQC)
#elif defined(SVQC)
- #include "../../warpzonelib/anglestransform.qh"
- #include "../../warpzonelib/common.qh"
- #include "../../warpzonelib/util_server.qh"
- #include "../../warpzonelib/server.qh"
+ #include "../../lib/warpzone/anglestransform.qh"
+ #include "../../lib/warpzone/common.qh"
+ #include "../../lib/warpzone/util_server.qh"
+ #include "../../lib/warpzone/server.qh"
#include "../constants.qh"
#include "../stats.qh"
#include "../teams.qh"
#include "../../server/mutators/mutators_include.qh"
#include "../mapinfo.qh"
#include "../../server/command/common.qh"
- #include "../../csqcmodellib/sv_model.qh"
+ #include "../../lib/csqcmodel/sv_model.qh"
#include "../../server/portals.qh"
#include "../../server/g_hook.qh"
#endif
+++ /dev/null
-/*
- * Copyright (c) 2011 Rudolf PolzerCSQCModel_InterpolateAnimation_2To4_PreNote
- *
- * 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.
- */
- #if defined(CSQC)
- #include "../client/defs.qh"
- #include "../common/util.qh"
- #include "../common/animdecide.qh"
- #include "interpolate.qh"
- #include "../common/csqcmodel_settings.qh"
- #include "common.qh"
- #include "cl_model.qh"
- #include "cl_player.qh"
- #elif defined(MENUQC)
- #elif defined(SVQC)
- #endif
-
-float autocvar_cl_lerpanim_maxdelta_framegroups = 0.1;
-float autocvar_cl_nolerp = 0;
-
-.float csqcmodel_lerpfrac;
-.float csqcmodel_lerpfrac2;
-.float csqcmodel_lerpfractime;
-.float csqcmodel_lerpfrac2time;
-
-void CSQCModel_InterpolateAnimation_2To4_PreNote(int sf)
-{SELFPARAM();
- 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;
- }
-}
-void CSQCModel_InterpolateAnimation_1To2_PreNote(int sf)
-{SELFPARAM();
- if(sf & CSQCMODEL_PROPERTY_FRAME)
- {
- self.frame2 = self.frame;
- self.frame2time = self.frame1time;
- }
-}
-void CSQCModel_InterpolateAnimation_PreNote(int sf)
-{
-#ifdef CSQCMODEL_HAVE_TWO_FRAMES
- CSQCModel_InterpolateAnimation_2To4_PreNote(sf);
-#else
- CSQCModel_InterpolateAnimation_1To2_PreNote(sf);
-#endif
-}
-
-void CSQCModel_InterpolateAnimation_2To4_Note(int sf, float set_times)
-{SELFPARAM();
- if(sf & CSQCMODEL_PROPERTY_FRAME)
- {
- if(set_times)
- self.frame1time = time;
- }
- if(sf & CSQCMODEL_PROPERTY_FRAME2)
- {
- if(set_times)
- self.frame2time = time;
- }
- if(sf & CSQCMODEL_PROPERTY_LERPFRAC)
- {
- self.csqcmodel_lerpfrac = self.lerpfrac;
- if(set_times)
- self.csqcmodel_lerpfractime = time;
- }
-}
-void CSQCModel_InterpolateAnimation_1To2_Note(int sf, float set_times)
-{SELFPARAM();
- if(sf & CSQCMODEL_PROPERTY_FRAME)
- {
- if(set_times)
- self.frame1time = time;
- }
-}
-void CSQCModel_InterpolateAnimation_Note(int sf)
-{
-#ifdef CSQCMODEL_HAVE_TWO_FRAMES
- CSQCModel_InterpolateAnimation_2To4_Note(sf, true);
-#else
- CSQCModel_InterpolateAnimation_1To2_Note(sf, true);
-#endif
-}
-
-void CSQCModel_InterpolateAnimation_2To4_Do()
-{SELFPARAM();
- 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;
- }
- }
-}
-void CSQCModel_InterpolateAnimation_1To2_Do()
-{SELFPARAM();
- if(autocvar_cl_nolerp || (autocvar_cl_lerpanim_maxdelta_framegroups == 0))
- {
- self.lerpfrac = 0;
- }
- else
- {
- if(self.frame2time == 0) // if frame2 was not previously displayed, only frame1 can make sense
- self.lerpfrac = 0;
- else
- self.lerpfrac = 1 - bound(0, (time - self.frame1time) / autocvar_cl_lerpanim_maxdelta_framegroups, 1);
- }
-}
-void CSQCModel_InterpolateAnimation_Do()
-{
-#ifdef CSQCMODEL_HAVE_TWO_FRAMES
- CSQCModel_InterpolateAnimation_2To4_Do();
-#else
- CSQCModel_InterpolateAnimation_1To2_Do();
-#endif
-}
-
-void CSQCModel_Draw()
-{SELFPARAM();
- // some nice flags for CSQCMODEL_IF and the hooks
- bool isplayer = (self.entnum >= 1 && self.entnum <= maxclients);
- noref bool islocalplayer = (self.entnum == player_localnum + 1);
- noref bool isnolocalplayer = (isplayer && (self.entnum != player_localnum + 1));
-
- // we don't do this for the local player as that one is already handled
- // by CSQCPlayer_SetCamera()
- if(!CSQCPlayer_IsLocalPlayer())
- InterpolateOrigin_Do();
-
- CSQCModel_InterpolateAnimation_Do();
-
- { CSQCMODEL_HOOK_PREDRAW }
-
- // inherit draw flags easily
- entity root = self;
- while(root.tag_entity)
- root = root.tag_entity;
- if(self != root)
- {
- self.renderflags &= ~(RF_EXTERNALMODEL | RF_VIEWMODEL);
- self.renderflags |= (root.renderflags & (RF_EXTERNALMODEL | RF_VIEWMODEL));
- }
-
- // we're drawn, now teleporting is over
- self.csqcmodel_teleported = 0;
-}
-
-void CSQCModel_Read(bool isnew)
-{SELFPARAM();
- int sf = ReadInt24_t();
-
- // some nice flags for CSQCMODEL_IF and the hooks
- bool isplayer = (self.entnum >= 1 && self.entnum <= maxclients);
- bool islocalplayer = (self.entnum == player_localnum + 1);
- noref bool isnolocalplayer = (isplayer && (self.entnum != player_localnum + 1));
-
- self.classname = "csqcmodel";
- self.iflags |= IFLAG_ORIGIN; // interpolate origin too
- self.iflags |= IFLAG_ANGLES; // interpolate angles too
- self.iflags |= IFLAG_VELOCITY | IFLAG_AUTOVELOCITY; // let's calculate velocity automatically
-
- { CSQCMODEL_HOOK_PREUPDATE }
-
- CSQCPlayer_PreUpdate();
- InterpolateOrigin_Undo();
- CSQCModel_InterpolateAnimation_PreNote(sf);
-
-#define CSQCMODEL_IF(cond) if(cond) {
-#define CSQCMODEL_ENDIF }
-#define CSQCMODEL_PROPERTY(flag,t,r,w,f) \
- if(sf & flag) \
- self.f = r();
-#define CSQCMODEL_PROPERTY_SCALED(flag,t,r,w,f,s,mi,ma) \
- if(sf & flag) \
- self.f = (r() + mi) / s;
- ALLPROPERTIES
-#undef CSQCMODEL_PROPERTY_SCALED
-#undef CSQCMODEL_PROPERTY
-#undef CSQCMODEL_ENDIF
-#undef CSQCMODEL_IF
-
- if(sf & CSQCMODEL_PROPERTY_MODELINDEX)
- {
- vector pmin = self.mins, pmax = self.maxs;
- setmodelindex(self, self.modelindex); // this retrieves the .model key and sets mins/maxs/absmin/absmax
- setsize(self, pmin, pmax);
- }
-
- 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);
-
- // set obvious render flags
- if(self.entnum == player_localentnum)
- self.renderflags |= RF_EXTERNALMODEL;
- else
- self.renderflags &= ~RF_EXTERNALMODEL;
-
- // draw it
- self.drawmask = MASK_NORMAL;
- self.predraw = CSQCModel_Draw;
-}
-
-entity CSQCModel_server2csqc(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.
- */
-#ifndef CL_MODEL_H
-#define CL_MODEL_H
-
-#include "common.qh"
-
-void CSQCModel_Read(bool isnew);
-
-#define CSQCMODEL_IF(cond)
-#define CSQCMODEL_ENDIF
-#define CSQCMODEL_PROPERTY(flag,t,r,w,f) \
- .t f;
-#define CSQCMODEL_PROPERTY_SCALED(flag,t,r,w,f,s,mi,ma) CSQCMODEL_PROPERTY(flag,t,r,w,f)
- ALLPROPERTIES
-#undef CSQCMODEL_PROPERTY_SCALED
-#undef CSQCMODEL_PROPERTY
-#undef CSQCMODEL_ENDIF
-#undef CSQCMODEL_IF
-
-entity CSQCModel_server2csqc(float pl);
-.float csqcmodel_teleported;
-
-// this is exported for custom frame animation code. Use with care.
-// to update frames, first call this:
-void CSQCModel_InterpolateAnimation_2To4_PreNote(int sf);
-void CSQCModel_InterpolateAnimation_1To2_PreNote(int sf);
-// then update frame, frame1time (and possibly frame2, frame2time, lerpfrac)
-// if set_times is not set, caller is responsible for frame1time, frame2time, csqcmodel_lerpfractime!
-void CSQCModel_InterpolateAnimation_2To4_Note(int sf, float set_times);
-void CSQCModel_InterpolateAnimation_1To2_Note(int sf, float set_times);
-// to retrieve animation state, call this
-void CSQCModel_InterpolateAnimation_2To4_Do();
-void CSQCModel_InterpolateAnimation_1To2_Do();
-// will overwrite lerpfrac, lerpfrac3, lerpfrac4, and possibly clear frame*time if they are undisplayed according to lerpfracs
-#endif
+++ /dev/null
-/*
- * Copyright (c) 2011 Rudolf Polzer
- * Copyright (c) 2015 Micah Talkiewicz
- *
- * 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.
- */
-#if defined(CSQC)
- #include "../client/defs.qh"
- #include "../common/constants.qh"
- #include "../common/stats.qh"
- #include "../common/util.qh"
- #include "interpolate.qh"
- #include "../client/main.qh"
- #include "common.qh"
- #include "cl_model.qh"
- #include "cl_player.qh"
- #include "../common/triggers/trigger/viewloc.qh"
- #include "../common/viewloc.qh"
-#elif defined(MENUQC)
-#elif defined(SVQC)
-#endif
-
-float autocvar_cl_movement_errorcompensation = 0;
-int autocvar_cl_movement = 1;
-
-// engine stuff
-float pmove_onground; // weird engine flag we shouldn't really use but have to for now
-
-vector csqcplayer_origin, csqcplayer_velocity;
-float csqcplayer_sequence;
-int player_pmflags;
-float csqcplayer_moveframe;
-vector csqcplayer_predictionerroro;
-vector csqcplayer_predictionerrorv;
-float csqcplayer_predictionerrortime;
-float csqcplayer_predictionerrorfactor;
-
-vector CSQCPlayer_GetPredictionErrorO()
-{
- if(time >= csqcplayer_predictionerrortime)
- return '0 0 0';
- return csqcplayer_predictionerroro * (csqcplayer_predictionerrortime - time) * csqcplayer_predictionerrorfactor;
-}
-
-vector CSQCPlayer_GetPredictionErrorV()
-{
- if(time >= csqcplayer_predictionerrortime)
- return '0 0 0';
- return csqcplayer_predictionerrorv * (csqcplayer_predictionerrortime - time) * csqcplayer_predictionerrorfactor;
-}
-
-void CSQCPlayer_SetPredictionError(vector o, vector v, float onground_diff)
-{
- // error too big to compensate, we LIKELY hit a teleport or a
- // jumppad, or it's a jump time disagreement that'll get fixed
- // next frame
-
- // FIXME we sometimes have disagreement in order of jump velocity. Do not act on them!
- /*
- // commented out as this one did not help
- if(onground_diff)
- {
- printf("ONGROUND MISMATCH: %d x=%v v=%v\n", onground_diff, o, v);
- return;
- }
- */
- if(vlen(o) > 32 || vlen(v) > 192)
- {
- //printf("TOO BIG: x=%v v=%v\n", o, v);
- return;
- }
-
- if(!autocvar_cl_movement_errorcompensation)
- {
- csqcplayer_predictionerrorfactor = 0;
- return;
- }
-
- csqcplayer_predictionerroro = CSQCPlayer_GetPredictionErrorO() + o;
- csqcplayer_predictionerrorv = CSQCPlayer_GetPredictionErrorV() + v;
- csqcplayer_predictionerrorfactor = autocvar_cl_movement_errorcompensation / ticrate;
- csqcplayer_predictionerrortime = time + 1.0 / csqcplayer_predictionerrorfactor;
-}
-
-void CSQCPlayer_Unpredict()
-{SELFPARAM();
- 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.flags = player_pmflags;
-}
-
-void CSQCPlayer_SetMinsMaxs()
-{SELFPARAM();
- if(self.flags & FL_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()
-{SELFPARAM();
- player_pmflags = self.flags;
- csqcplayer_origin = self.origin;
- csqcplayer_velocity = self.velocity;
- csqcplayer_sequence = servercommandframe;
- csqcplayer_status = CSQCPLAYERSTATUS_PREDICTED;
-}
-
-void CSQC_ClientMovement_PlayerMove_Frame();
-
-void PM_Movement_Move()
-{SELFPARAM();
- runstandardplayerphysics(self);
-#ifdef CSQC
- self.flags =
- ((self.pmove_flags & PMF_DUCKED) ? FL_DUCKED : 0) |
- (!(self.pmove_flags & PMF_JUMP_HELD) ? FL_JUMPRELEASED : 0) |
- ((self.pmove_flags & PMF_ONGROUND) ? FL_ONGROUND : 0);
-#endif
-}
-
-void CSQCPlayer_Physics(void)
-{
- switch(autocvar_cl_movement)
- {
- case 1: CSQC_ClientMovement_PlayerMove_Frame(); break;
- case 2: PM_Movement_Move(); break;
- }
-}
-
-void CSQCPlayer_PredictTo(float endframe, float apply_error)
-{SELFPARAM();
- CSQCPlayer_Unpredict();
- if(apply_error)
- {
- self.origin += CSQCPlayer_GetPredictionErrorO();
- self.velocity += CSQCPlayer_GetPredictionErrorV();
- }
- CSQCPlayer_SetMinsMaxs();
-
- csqcplayer_status = CSQCPLAYERSTATUS_PREDICTED;
-
-#if 0
- // we don't need this
- // darkplaces makes servercommandframe == 0 in these cases anyway
- if (getstatf(STAT_HEALTH) <= 0)
- {
- csqcplayer_moveframe = clientcommandframe;
- getinputstate(csqcplayer_moveframe-1);
- LOG_INFO("the Weird code path got hit\n");
- return;
- }
-#endif
-
- if(csqcplayer_moveframe >= endframe)
- {
- getinputstate(csqcplayer_moveframe - 1);
- }
- else
- {
- do
- {
- if (!getinputstate(csqcplayer_moveframe))
- break;
- CSQCPlayer_Physics();
- CSQCPlayer_SetMinsMaxs();
- csqcplayer_moveframe++;
- }
- while(csqcplayer_moveframe < endframe);
- }
-
- //add in anything that was applied after (for low packet rate protocols)
- input_angles = view_angles;
-}
-
-bool CSQCPlayer_IsLocalPlayer()
-{SELFPARAM();
- return (self == csqcplayer);
-}
-
-void CSQCPlayer_SetViewLocation()
-{
- viewloc_SetViewLocation();
-}
-
-void CSQCPlayer_SetCamera()
-{SELFPARAM();
- vector v0;
- v0 = pmove_vel; // TRICK: pmove_vel is set by the engine when we get here. No need to network velocity
-
- if(csqcplayer)
- {
- setself(csqcplayer);
-
- if(servercommandframe == 0 || clientcommandframe == 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.flags &= ~FL_DUCKED;
- else if(getstati(STAT_VIEWHEIGHT) == PL_CROUCH_VIEW_OFS.z)
- self.flags |= FL_DUCKED;
-
- // get onground state from the server
- if(pmove_onground)
- self.flags |= FL_ONGROUND;
- else
- self.flags &= ~FL_ONGROUND;
-
- CSQCPlayer_SetMinsMaxs();
-
- // override it back just in case
- self.view_ofs = '0 0 1' * getstati(STAT_VIEWHEIGHT);
-
- // set velocity
- self.velocity = v0;
- }
- else
- {
- float flg = self.iflags;
- self.iflags &= ~(IFLAG_ORIGIN | IFLAG_ANGLES);
- InterpolateOrigin_Do();
- self.iflags = flg;
-
- if(csqcplayer_status == CSQCPLAYERSTATUS_FROMSERVER)
- {
- vector o, v;
- o = self.origin;
- v = v0;
- csqcplayer_status = CSQCPLAYERSTATUS_PREDICTED;
- CSQCPlayer_PredictTo(servercommandframe + 1, false);
- CSQCPlayer_SetPredictionError(self.origin - o, self.velocity - v, pmove_onground - !!(self.flags & FL_ONGROUND));
- self.origin = o;
- self.velocity = v;
-
- // get crouch state from the server
- if(getstati(STAT_VIEWHEIGHT) == PL_VIEW_OFS.z)
- self.flags &= ~FL_DUCKED;
- else if(getstati(STAT_VIEWHEIGHT) == PL_CROUCH_VIEW_OFS.z)
- self.flags |= FL_DUCKED;
-
- // get onground state from the server
- if(pmove_onground)
- self.flags |= FL_ONGROUND;
- else
- self.flags &= ~FL_ONGROUND;
-
- CSQCPlayer_SavePrediction();
- }
- CSQCPlayer_PredictTo(clientcommandframe + 1, true);
-
-#ifdef CSQCMODEL_SERVERSIDE_CROUCH
- // get crouch state from the server (LAG)
- if(getstati(STAT_VIEWHEIGHT) == PL_VIEW_OFS.z)
- self.flags &= ~FL_DUCKED;
- else if(getstati(STAT_VIEWHEIGHT) == PL_CROUCH_VIEW_OFS.z)
- self.flags |= FL_DUCKED;
-#endif
-
- CSQCPlayer_SetMinsMaxs();
-
- self.angles_y = input_angles.y;
- }
-
- // relink
- setorigin(self, self.origin);
-
- setself(this);
- }
-
- entity view = CSQCModel_server2csqc(player_localentnum);
-
- if(view && view != csqcplayer)
- {
- WITH(entity, self, view, InterpolateOrigin_Do());
- view.view_ofs = '0 0 1' * getstati(STAT_VIEWHEIGHT);
- }
-
- if(view)
- {
- int refdefflags = 0;
-
- if(view.csqcmodel_teleported)
- refdefflags |= REFDEFFLAG_TELEPORTED;
-
- if(input_buttons & 4)
- refdefflags |= REFDEFFLAG_JUMPING;
-
- // note: these two only work in WIP2, but are harmless in WIP1
- if(getstati(STAT_HEALTH) <= 0)
- refdefflags |= REFDEFFLAG_DEAD;
-
- if(intermission)
- refdefflags |= REFDEFFLAG_INTERMISSION;
-
- V_CalcRefdef(view, refdefflags);
- }
- else
- {
- // FIXME by CSQC spec we have to do this:
- // but it breaks chase cam
- /*
- setproperty(VF_ORIGIN, pmove_org + '0 0 1' * getstati(STAT_VIEWHEIGHT));
- setproperty(VF_ANGLES, view_angles);
- */
- }
-
- { CSQCPLAYER_HOOK_POSTCAMERASETUP }
-}
-
-void CSQCPlayer_Remove()
-{
- csqcplayer = world;
- cvar_settemp("cl_movement_replay", "1");
-}
-
-float CSQCPlayer_PreUpdate()
-{SELFPARAM();
- if(self != csqcplayer)
- return 0;
- if(csqcplayer_status != CSQCPLAYERSTATUS_FROMSERVER)
- CSQCPlayer_Unpredict();
- return 1;
-}
-
-float CSQCPlayer_PostUpdate()
-{SELFPARAM();
- if(self.entnum != player_localnum + 1)
- return 0;
- csqcplayer = self;
- csqcplayer_status = CSQCPLAYERSTATUS_FROMSERVER;
- cvar_settemp("cl_movement_replay", "0");
- self.entremove = CSQCPlayer_Remove;
- return 1;
-}
+++ /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.
- */
-#ifndef CL_PLAYER_H
-#define CL_PLAYER_H
-
-entity csqcplayer;
-float csqcplayer_status;
-const int CSQCPLAYERSTATUS_UNPREDICTED = 0;
-const int CSQCPLAYERSTATUS_FROMSERVER = 1;
-const int CSQCPLAYERSTATUS_PREDICTED = 2;
-
-// only ever READ these!
-.int pmove_flags;
-const int PMF_JUMP_HELD = 1;
-//const int PMF_DUCKED = 4;
-//const int PMF_ONGROUND = 8;
-
-const int FL_DUCKED = 524288;
-
-void CSQCPlayer_SetCamera();
-float CSQCPlayer_PreUpdate();
-float CSQCPlayer_PostUpdate();
-float CSQCPlayer_IsLocalPlayer();
-#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.
- */
-#ifndef CSQCMODELLIB_COMMON_H
-#define CSQCMODELLIB_COMMON_H
-
-#include "../common/csqcmodel_settings.qh"
-
-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;
-.int frame;
-.float frame1time;
-.int frame2;
-.float frame2time;
-.float lerpfrac;
-
-const int CSQCMODEL_PROPERTY_FRAME = 8388608;
-const int CSQCMODEL_PROPERTY_TELEPORTED = 4194304; // the "teleport bit" cancelling interpolation
-const int CSQCMODEL_PROPERTY_MODELINDEX = 2097152;
-const int CSQCMODEL_PROPERTY_ORIGIN = 1048576;
-const int CSQCMODEL_PROPERTY_YAW = 524288;
-const int CSQCMODEL_PROPERTY_PITCHROLL = 262144;
-const int CSQCMODEL_PROPERTY_FRAME2 = 131072;
-const int CSQCMODEL_PROPERTY_LERPFRAC = 65536;
-const int CSQCMODEL_PROPERTY_SIZE = 32768;
-
-#define ALLPROPERTIES_COMMON \
- CSQCMODEL_PROPERTY(CSQCMODEL_PROPERTY_FRAME, int, ReadByte, WriteByte, frame) \
- CSQCMODEL_PROPERTY(CSQCMODEL_PROPERTY_MODELINDEX, int, ReadShort, WriteShort, modelindex) \
- CSQCMODEL_PROPERTY(CSQCMODEL_PROPERTY_ORIGIN, float, ReadCoord, WriteCoord, origin_x) \
- CSQCMODEL_PROPERTY(CSQCMODEL_PROPERTY_ORIGIN, float, ReadCoord, WriteCoord, origin_y) \
- CSQCMODEL_PROPERTY(CSQCMODEL_PROPERTY_ORIGIN, float, ReadCoord, WriteCoord, origin_z) \
- CSQCMODEL_PROPERTY(CSQCMODEL_PROPERTY_SIZE, float, ReadShort, WriteShort, mins_x) \
- CSQCMODEL_PROPERTY(CSQCMODEL_PROPERTY_SIZE, float, ReadShort, WriteShort, mins_y) \
- CSQCMODEL_PROPERTY(CSQCMODEL_PROPERTY_SIZE, float, ReadShort, WriteShort, mins_z) \
- CSQCMODEL_PROPERTY(CSQCMODEL_PROPERTY_SIZE, float, ReadShort, WriteShort, maxs_x) \
- CSQCMODEL_PROPERTY(CSQCMODEL_PROPERTY_SIZE, float, ReadShort, WriteShort, maxs_y) \
- CSQCMODEL_PROPERTY(CSQCMODEL_PROPERTY_SIZE, float, ReadShort, WriteShort, maxs_z) \
- CSQCMODEL_PROPERTY(CSQCMODEL_PROPERTY_PITCHROLL, float, ReadAngle, WriteAngle, angles_x) \
- CSQCMODEL_PROPERTY(CSQCMODEL_PROPERTY_YAW, float, ReadAngle, WriteAngle, angles_y) \
- CSQCMODEL_PROPERTY(CSQCMODEL_PROPERTY_PITCHROLL, float, 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, int, ReadByte, WriteByte, frame2) \
- CSQCMODEL_PROPERTY_SCALED(CSQCMODEL_PROPERTY_LERPFRAC, float, ReadByte, WriteByte, lerpfrac, 255, 0, 255)
-#else
-#define ALLPROPERTIES ALLPROPERTIES_COMMON
-#endif
-#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.
- */
-#if defined(CSQC)
- #include "../client/defs.qh"
- #include "../warpzonelib/anglestransform.qh"
- #include "../client/autocvars.qh"
- #include "interpolate.qh"
- #include "cl_model.qh"
-#elif defined(MENUQC)
-#elif defined(SVQC)
-#endif
-
-.vector iorigin1, iorigin2;
-.vector ivelocity1, ivelocity2;
-.vector iforward1, iforward2;
-.vector iup1, iup2;
-.vector ivforward1, ivforward2;
-.vector ivup1, ivup2;
-.float itime1, itime2;
-void InterpolateOrigin_Reset()
-{SELFPARAM();
- self.iflags &= ~IFLAG_INTERNALMASK;
- self.itime1 = self.itime2 = 0;
-}
-void InterpolateOrigin_Note()
-{SELFPARAM();
- float dt;
- int f0;
-
- dt = time - self.itime2;
-
- f0 = self.iflags;
- if(self.iflags & IFLAG_PREVALID)
- self.iflags |= IFLAG_VALID;
- else
- self.iflags |= IFLAG_PREVALID;
-
- if(self.iflags & IFLAG_ORIGIN)
- {
- 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_AUTOVELOCITY)
- if(self.itime2 != self.itime1)
- self.velocity = (self.iorigin2 - self.iorigin1) * (1.0 / (self.itime2 - self.itime1));
-
- 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_V_ANGLE)
- {
- fixedmakevectors(self.v_angle);
- if(f0 & IFLAG_VALID)
- {
- self.ivforward1 = self.ivforward2;
- self.ivup1 = self.ivup2;
- }
- else
- {
- self.ivforward1 = v_forward;
- self.ivup1 = v_up;
- }
- self.ivforward2 = v_forward;
- self.ivup2 = v_up;
- }
- else if(self.iflags & IFLAG_V_ANGLE_X)
- {
- self.ivforward1_x = self.ivforward2_x;
- self.ivforward2_x = self.v_angle.x;
- }
-
- 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()
-{SELFPARAM();
- 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);
- if(self.iflags & IFLAG_ORIGIN)
- setorigin(self, (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_V_ANGLE)
- {
- forward = (1 - f) * self.ivforward1 + f * self.ivforward2;
- up = (1 - f) * self.ivup1 + f * self.ivup2;
- self.v_angle = fixedvectoangles2(forward, up);
- }
- else if(self.iflags & IFLAG_V_ANGLE_X)
- self.v_angle_x = (1 - f) * self.ivforward1_x + f * self.ivforward2_x;
- if(self.iflags & IFLAG_VELOCITY)
- self.velocity = (1 - f) * self.ivelocity1 + f * self.ivelocity2;
- }
-}
-void InterpolateOrigin_Undo()
-{SELFPARAM();
- if(self.iflags & IFLAG_ORIGIN)
- setorigin(self, self.iorigin2);
- if(self.iflags & IFLAG_ANGLES)
- self.angles = fixedvectoangles2(self.iforward2, self.iup2);
- if(self.iflags & IFLAG_V_ANGLE)
- self.v_angle = fixedvectoangles2(self.ivforward2, self.ivup2);
- else if(self.iflags & IFLAG_V_ANGLE_X)
- self.v_angle_x = self.ivforward2_x;
- 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.
- */
-#ifndef INTERPOLATE_H
-#define INTERPOLATE_H
-
-.int iflags;
-const int IFLAG_VELOCITY = 1;
-const int IFLAG_ANGLES = 2;
-const int IFLAG_AUTOANGLES = 4;
-const int IFLAG_VALID = 8;
-const int IFLAG_PREVALID = 16;
-const int IFLAG_TELEPORTED = 32;
-const int IFLAG_AUTOVELOCITY = 64;
-const int IFLAG_V_ANGLE = 128;
-const int IFLAG_V_ANGLE_X = 256;
-const int IFLAG_ORIGIN = 512;
-#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();
-
-// in case we interpolate that:
-.vector v_angle;
-#endif
+++ /dev/null
-#ifndef CSQCMODELLIB_SETTINGS_H
-#define CSQCMODELLIB_SETTINGS_H
-// 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, float, ReadShort, WriteShort, colormap) */ \
- /* CSQCMODEL_PROPERTY(2, float, ReadInt24_t, WriteInt24_t, effects) */
-
-// add hook function calls here
-#define CSQCMODEL_HOOK_PREUPDATE
-#define CSQCMODEL_HOOK_POSTUPDATE
-#define CSQCMODEL_HOOK_PREDRAW
-#define CSQCPLAYER_HOOK_POSTCAMERASETUP
-
-// force updates of player entities that often even if unchanged
-#define CSQCPLAYER_FORCE_UPDATES 0.25
-
-// 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 = ...;
-#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.
- */
-
-#if defined(CSQC)
-#elif defined(MENUQC)
-#elif defined(SVQC)
- #include "../common/constants.qh"
- #include "../common/util.qh"
- #include "../common/animdecide.qh"
- #include "../server/constants.qh"
- #include "../server/defs.qh"
- #include "common.qh"
- #include "sv_model.qh"
-#endif
-
-// generic CSQC model code
-
-bool CSQCModel_Send(entity to, int sf)
-{SELFPARAM();
- // some nice flags for CSQCMODEL_IF
- float isplayer = (IS_CLIENT(self));
- float islocalplayer = (self == to);
- float isnolocalplayer = (isplayer && (self != to));
-
- unused_float = isplayer;
- unused_float = islocalplayer;
- unused_float = isnolocalplayer;
-
- WriteByte(MSG_ENTITY, ENT_CLIENT_MODEL);
- WriteInt24_t(MSG_ENTITY, sf);
-
-#define CSQCMODEL_IF(cond) if(cond) {
-#define CSQCMODEL_ENDIF }
-#define CSQCMODEL_PROPERTY(flag,t,r,w,f) \
- if(sf & flag) \
- { \
- w(MSG_ENTITY, self.csqcmodel_##f); \
- }
-#define CSQCMODEL_PROPERTY_SCALED(flag,t,r,w,f,s,mi,ma) CSQCMODEL_PROPERTY(flag,t,r,w,f)
- ALLPROPERTIES
-#undef CSQCMODEL_PROPERTY_SCALED
-#undef CSQCMODEL_PROPERTY
-#undef CSQCMODEL_ENDIF
-#undef CSQCMODEL_IF
-
- return true;
-}
-
-#ifdef CSQCPLAYER_FORCE_UPDATES
-.float csqcmodel_nextforcedupdate;
-#endif
-void CSQCModel_CheckUpdate(entity e)
-{
- // some nice flags for CSQCMODEL_IF
- float isplayer = (IS_CLIENT(e));
- float islocalplayer = isplayer; // we set BOTH to 1 here as we need the sendflags
- float isnolocalplayer = isplayer; // we set BOTH to 1 here as we need the sendflags
-
- unused_float = isplayer;
- unused_float = islocalplayer;
- unused_float = isnolocalplayer;
-
-#ifdef CSQCPLAYER_FORCE_UPDATES
- if(isplayer && time > e.csqcmodel_nextforcedupdate)
- {
- e.SendFlags |= CSQCMODEL_PROPERTY_ORIGIN;
- e.csqcmodel_nextforcedupdate = time + CSQCPLAYER_FORCE_UPDATES * (0.5 + random()); // ensure about 4 origin sends per sec
- }
-#endif
-
- if(e.effects & EF_RESTARTANIM_BIT)
- {
- e.SendFlags |= CSQCMODEL_PROPERTY_FRAME | CSQCMODEL_PROPERTY_FRAME2; // full anim resend please
- e.effects &= ~EF_RESTARTANIM_BIT;
- }
-
- if(e.effects & EF_TELEPORT_BIT)
- {
- e.SendFlags |= CSQCMODEL_PROPERTY_TELEPORTED; // no interpolation please
- e.effects &= ~EF_TELEPORT_BIT;
- }
-
-#define CSQCMODEL_IF(cond) if(cond) {
-#define CSQCMODEL_ENDIF }
-#define CSQCMODEL_PROPERTY(flag,t,r,w,f) \
- { \
- t tmp = e.f; \
- if(tmp != e.csqcmodel_##f) \
- { \
- e.csqcmodel_##f = tmp; \
- e.SendFlags |= flag; \
- } \
- }
-#define CSQCMODEL_PROPERTY_SCALED(flag,t,r,w,f,s,mi,ma) \
- { \
- t tmp = rint(bound(mi, s * e.f, ma) - mi); \
- if(tmp != e.csqcmodel_##f) \
- { \
- e.csqcmodel_##f = tmp; \
- e.SendFlags |= flag; \
- } \
- }
- ALLPROPERTIES
-#undef CSQCMODEL_PROPERTY_SCALED
-#undef CSQCMODEL_PROPERTY
-#undef CSQCMODEL_ENDIF
-#undef CSQCMODEL_IF
-}
-
-void CSQCModel_LinkEntity(entity e)
-{
- e.SendEntity = CSQCModel_Send;
- e.SendFlags = 0xFFFFFF;
- CSQCModel_CheckUpdate(e);
-}
-
-void CSQCModel_UnlinkEntity(entity e)
-{
- e.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.
- */
- #ifndef SV_MODEL_H
- #define SV_MODEL_H
-
- #include "common.qh"
-
-// generic CSQC model code
-
-void CSQCModel_CheckUpdate(entity e);
-void CSQCModel_LinkEntity(entity e);
-void CSQCModel_UnlinkEntity(entity e);
-
-#define CSQCMODEL_IF(cond)
-#define CSQCMODEL_ENDIF
-#define CSQCMODEL_PROPERTY(flag,t,r,w,f) \
- .t f; \
- .t csqcmodel_##f;
-#define CSQCMODEL_PROPERTY_SCALED(flag,t,r,w,f,s,mi,ma) CSQCMODEL_PROPERTY(flag,t,r,w,f)
- ALLPROPERTIES
-#undef CSQCMODEL_PROPERTY_SCALED
-#undef CSQCMODEL_PROPERTY
-#undef CSQCMODEL_ENDIF
-#undef CSQCMODEL_IF
-#endif
#include "../dpdefs/keycodes.qh"
#endif
-#include "../warpzonelib/mathlib.qc"
+#include "warpzone/mathlib.qc"
#include "accumulate.qh"
#include "bits.qh"
--- /dev/null
+/*
+ * Copyright (c) 2011 Rudolf PolzerCSQCModel_InterpolateAnimation_2To4_PreNote
+ *
+ * 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.
+ */
+#include "cl_model.qh"
+
+#include "cl_player.qh"
+#include "common.qh"
+#include "interpolate.qh"
+#include "../../client/defs.qh"
+#include "../../common/animdecide.qh"
+#include "../../common/csqcmodel_settings.qh"
+#include "../../common/util.qh"
+
+float autocvar_cl_lerpanim_maxdelta_framegroups = 0.1;
+float autocvar_cl_nolerp = 0;
+
+.float csqcmodel_lerpfrac;
+.float csqcmodel_lerpfrac2;
+.float csqcmodel_lerpfractime;
+.float csqcmodel_lerpfrac2time;
+
+void CSQCModel_InterpolateAnimation_2To4_PreNote(int sf)
+{SELFPARAM();
+ 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;
+ }
+}
+void CSQCModel_InterpolateAnimation_1To2_PreNote(int sf)
+{SELFPARAM();
+ if(sf & CSQCMODEL_PROPERTY_FRAME)
+ {
+ self.frame2 = self.frame;
+ self.frame2time = self.frame1time;
+ }
+}
+void CSQCModel_InterpolateAnimation_PreNote(int sf)
+{
+#ifdef CSQCMODEL_HAVE_TWO_FRAMES
+ CSQCModel_InterpolateAnimation_2To4_PreNote(sf);
+#else
+ CSQCModel_InterpolateAnimation_1To2_PreNote(sf);
+#endif
+}
+
+void CSQCModel_InterpolateAnimation_2To4_Note(int sf, float set_times)
+{SELFPARAM();
+ if(sf & CSQCMODEL_PROPERTY_FRAME)
+ {
+ if(set_times)
+ self.frame1time = time;
+ }
+ if(sf & CSQCMODEL_PROPERTY_FRAME2)
+ {
+ if(set_times)
+ self.frame2time = time;
+ }
+ if(sf & CSQCMODEL_PROPERTY_LERPFRAC)
+ {
+ self.csqcmodel_lerpfrac = self.lerpfrac;
+ if(set_times)
+ self.csqcmodel_lerpfractime = time;
+ }
+}
+void CSQCModel_InterpolateAnimation_1To2_Note(int sf, float set_times)
+{SELFPARAM();
+ if(sf & CSQCMODEL_PROPERTY_FRAME)
+ {
+ if(set_times)
+ self.frame1time = time;
+ }
+}
+void CSQCModel_InterpolateAnimation_Note(int sf)
+{
+#ifdef CSQCMODEL_HAVE_TWO_FRAMES
+ CSQCModel_InterpolateAnimation_2To4_Note(sf, true);
+#else
+ CSQCModel_InterpolateAnimation_1To2_Note(sf, true);
+#endif
+}
+
+void CSQCModel_InterpolateAnimation_2To4_Do()
+{SELFPARAM();
+ 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;
+ }
+ }
+}
+void CSQCModel_InterpolateAnimation_1To2_Do()
+{SELFPARAM();
+ if(autocvar_cl_nolerp || (autocvar_cl_lerpanim_maxdelta_framegroups == 0))
+ {
+ self.lerpfrac = 0;
+ }
+ else
+ {
+ if(self.frame2time == 0) // if frame2 was not previously displayed, only frame1 can make sense
+ self.lerpfrac = 0;
+ else
+ self.lerpfrac = 1 - bound(0, (time - self.frame1time) / autocvar_cl_lerpanim_maxdelta_framegroups, 1);
+ }
+}
+void CSQCModel_InterpolateAnimation_Do()
+{
+#ifdef CSQCMODEL_HAVE_TWO_FRAMES
+ CSQCModel_InterpolateAnimation_2To4_Do();
+#else
+ CSQCModel_InterpolateAnimation_1To2_Do();
+#endif
+}
+
+void CSQCModel_Draw()
+{SELFPARAM();
+ // some nice flags for CSQCMODEL_IF and the hooks
+ bool isplayer = (self.entnum >= 1 && self.entnum <= maxclients);
+ noref bool islocalplayer = (self.entnum == player_localnum + 1);
+ noref bool isnolocalplayer = (isplayer && (self.entnum != player_localnum + 1));
+
+ // we don't do this for the local player as that one is already handled
+ // by CSQCPlayer_SetCamera()
+ if(!CSQCPlayer_IsLocalPlayer())
+ InterpolateOrigin_Do();
+
+ CSQCModel_InterpolateAnimation_Do();
+
+ { CSQCMODEL_HOOK_PREDRAW }
+
+ // inherit draw flags easily
+ entity root = self;
+ while(root.tag_entity)
+ root = root.tag_entity;
+ if(self != root)
+ {
+ self.renderflags &= ~(RF_EXTERNALMODEL | RF_VIEWMODEL);
+ self.renderflags |= (root.renderflags & (RF_EXTERNALMODEL | RF_VIEWMODEL));
+ }
+
+ // we're drawn, now teleporting is over
+ self.csqcmodel_teleported = 0;
+}
+
+void CSQCModel_Read(bool isnew)
+{SELFPARAM();
+ int sf = ReadInt24_t();
+
+ // some nice flags for CSQCMODEL_IF and the hooks
+ bool isplayer = (self.entnum >= 1 && self.entnum <= maxclients);
+ bool islocalplayer = (self.entnum == player_localnum + 1);
+ noref bool isnolocalplayer = (isplayer && (self.entnum != player_localnum + 1));
+
+ self.classname = "csqcmodel";
+ self.iflags |= IFLAG_ORIGIN; // interpolate origin too
+ self.iflags |= IFLAG_ANGLES; // interpolate angles too
+ self.iflags |= IFLAG_VELOCITY | IFLAG_AUTOVELOCITY; // let's calculate velocity automatically
+
+ { CSQCMODEL_HOOK_PREUPDATE }
+
+ CSQCPlayer_PreUpdate();
+ InterpolateOrigin_Undo();
+ CSQCModel_InterpolateAnimation_PreNote(sf);
+
+#define CSQCMODEL_IF(cond) if(cond) {
+#define CSQCMODEL_ENDIF }
+#define CSQCMODEL_PROPERTY(flag,t,r,w,f) \
+ if(sf & flag) \
+ self.f = r();
+#define CSQCMODEL_PROPERTY_SCALED(flag,t,r,w,f,s,mi,ma) \
+ if(sf & flag) \
+ self.f = (r() + mi) / s;
+ ALLPROPERTIES
+#undef CSQCMODEL_PROPERTY_SCALED
+#undef CSQCMODEL_PROPERTY
+#undef CSQCMODEL_ENDIF
+#undef CSQCMODEL_IF
+
+ if(sf & CSQCMODEL_PROPERTY_MODELINDEX)
+ {
+ vector pmin = self.mins, pmax = self.maxs;
+ setmodelindex(self, self.modelindex); // this retrieves the .model key and sets mins/maxs/absmin/absmax
+ setsize(self, pmin, pmax);
+ }
+
+ 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);
+
+ // set obvious render flags
+ if(self.entnum == player_localentnum)
+ self.renderflags |= RF_EXTERNALMODEL;
+ else
+ self.renderflags &= ~RF_EXTERNALMODEL;
+
+ // draw it
+ self.drawmask = MASK_NORMAL;
+ self.predraw = CSQCModel_Draw;
+}
+
+entity CSQCModel_server2csqc(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.
+ */
+#ifndef LIB_CSQCMODEL_CL_MODEL_H
+#define LIB_CSQCMODEL_CL_MODEL_H
+
+#include "common.qh"
+
+void CSQCModel_Read(bool isnew);
+
+#define CSQCMODEL_IF(cond)
+#define CSQCMODEL_ENDIF
+#define CSQCMODEL_PROPERTY(flag,t,r,w,f) \
+ .t f;
+#define CSQCMODEL_PROPERTY_SCALED(flag,t,r,w,f,s,mi,ma) CSQCMODEL_PROPERTY(flag,t,r,w,f)
+ ALLPROPERTIES
+#undef CSQCMODEL_PROPERTY_SCALED
+#undef CSQCMODEL_PROPERTY
+#undef CSQCMODEL_ENDIF
+#undef CSQCMODEL_IF
+
+entity CSQCModel_server2csqc(float pl);
+.float csqcmodel_teleported;
+
+// this is exported for custom frame animation code. Use with care.
+// to update frames, first call this:
+void CSQCModel_InterpolateAnimation_2To4_PreNote(int sf);
+void CSQCModel_InterpolateAnimation_1To2_PreNote(int sf);
+// then update frame, frame1time (and possibly frame2, frame2time, lerpfrac)
+// if set_times is not set, caller is responsible for frame1time, frame2time, csqcmodel_lerpfractime!
+void CSQCModel_InterpolateAnimation_2To4_Note(int sf, float set_times);
+void CSQCModel_InterpolateAnimation_1To2_Note(int sf, float set_times);
+// to retrieve animation state, call this
+void CSQCModel_InterpolateAnimation_2To4_Do();
+void CSQCModel_InterpolateAnimation_1To2_Do();
+// will overwrite lerpfrac, lerpfrac3, lerpfrac4, and possibly clear frame*time if they are undisplayed according to lerpfracs
+#endif
--- /dev/null
+/*
+ * Copyright (c) 2011 Rudolf Polzer
+ * Copyright (c) 2015 Micah Talkiewicz
+ *
+ * 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.
+ */
+#include "cl_player.qh"
+
+#include "cl_model.qh"
+#include "common.qh"
+#include "interpolate.qh"
+#include "../../client/defs.qh"
+#include "../../client/main.qh"
+#include "../../common/constants.qh"
+#include "../../common/stats.qh"
+#include "../../common/triggers/trigger/viewloc.qh"
+#include "../../common/util.qh"
+#include "../../common/viewloc.qh"
+
+float autocvar_cl_movement_errorcompensation = 0;
+int autocvar_cl_movement = 1;
+
+// engine stuff
+float pmove_onground; // weird engine flag we shouldn't really use but have to for now
+
+vector csqcplayer_origin, csqcplayer_velocity;
+float csqcplayer_sequence;
+int player_pmflags;
+float csqcplayer_moveframe;
+vector csqcplayer_predictionerroro;
+vector csqcplayer_predictionerrorv;
+float csqcplayer_predictionerrortime;
+float csqcplayer_predictionerrorfactor;
+
+vector CSQCPlayer_GetPredictionErrorO()
+{
+ if(time >= csqcplayer_predictionerrortime)
+ return '0 0 0';
+ return csqcplayer_predictionerroro * (csqcplayer_predictionerrortime - time) * csqcplayer_predictionerrorfactor;
+}
+
+vector CSQCPlayer_GetPredictionErrorV()
+{
+ if(time >= csqcplayer_predictionerrortime)
+ return '0 0 0';
+ return csqcplayer_predictionerrorv * (csqcplayer_predictionerrortime - time) * csqcplayer_predictionerrorfactor;
+}
+
+void CSQCPlayer_SetPredictionError(vector o, vector v, float onground_diff)
+{
+ // error too big to compensate, we LIKELY hit a teleport or a
+ // jumppad, or it's a jump time disagreement that'll get fixed
+ // next frame
+
+ // FIXME we sometimes have disagreement in order of jump velocity. Do not act on them!
+ /*
+ // commented out as this one did not help
+ if(onground_diff)
+ {
+ printf("ONGROUND MISMATCH: %d x=%v v=%v\n", onground_diff, o, v);
+ return;
+ }
+ */
+ if(vlen(o) > 32 || vlen(v) > 192)
+ {
+ //printf("TOO BIG: x=%v v=%v\n", o, v);
+ return;
+ }
+
+ if(!autocvar_cl_movement_errorcompensation)
+ {
+ csqcplayer_predictionerrorfactor = 0;
+ return;
+ }
+
+ csqcplayer_predictionerroro = CSQCPlayer_GetPredictionErrorO() + o;
+ csqcplayer_predictionerrorv = CSQCPlayer_GetPredictionErrorV() + v;
+ csqcplayer_predictionerrorfactor = autocvar_cl_movement_errorcompensation / ticrate;
+ csqcplayer_predictionerrortime = time + 1.0 / csqcplayer_predictionerrorfactor;
+}
+
+void CSQCPlayer_Unpredict()
+{SELFPARAM();
+ 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.flags = player_pmflags;
+}
+
+void CSQCPlayer_SetMinsMaxs()
+{SELFPARAM();
+ if(self.flags & FL_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()
+{SELFPARAM();
+ player_pmflags = self.flags;
+ csqcplayer_origin = self.origin;
+ csqcplayer_velocity = self.velocity;
+ csqcplayer_sequence = servercommandframe;
+ csqcplayer_status = CSQCPLAYERSTATUS_PREDICTED;
+}
+
+void CSQC_ClientMovement_PlayerMove_Frame();
+
+void PM_Movement_Move()
+{SELFPARAM();
+ runstandardplayerphysics(self);
+#ifdef CSQC
+ self.flags =
+ ((self.pmove_flags & PMF_DUCKED) ? FL_DUCKED : 0) |
+ (!(self.pmove_flags & PMF_JUMP_HELD) ? FL_JUMPRELEASED : 0) |
+ ((self.pmove_flags & PMF_ONGROUND) ? FL_ONGROUND : 0);
+#endif
+}
+
+void CSQCPlayer_Physics(void)
+{
+ switch(autocvar_cl_movement)
+ {
+ case 1: CSQC_ClientMovement_PlayerMove_Frame(); break;
+ case 2: PM_Movement_Move(); break;
+ }
+}
+
+void CSQCPlayer_PredictTo(float endframe, float apply_error)
+{SELFPARAM();
+ CSQCPlayer_Unpredict();
+ if(apply_error)
+ {
+ self.origin += CSQCPlayer_GetPredictionErrorO();
+ self.velocity += CSQCPlayer_GetPredictionErrorV();
+ }
+ CSQCPlayer_SetMinsMaxs();
+
+ csqcplayer_status = CSQCPLAYERSTATUS_PREDICTED;
+
+#if 0
+ // we don't need this
+ // darkplaces makes servercommandframe == 0 in these cases anyway
+ if (getstatf(STAT_HEALTH) <= 0)
+ {
+ csqcplayer_moveframe = clientcommandframe;
+ getinputstate(csqcplayer_moveframe-1);
+ LOG_INFO("the Weird code path got hit\n");
+ return;
+ }
+#endif
+
+ if(csqcplayer_moveframe >= endframe)
+ {
+ getinputstate(csqcplayer_moveframe - 1);
+ }
+ else
+ {
+ do
+ {
+ if (!getinputstate(csqcplayer_moveframe))
+ break;
+ CSQCPlayer_Physics();
+ CSQCPlayer_SetMinsMaxs();
+ csqcplayer_moveframe++;
+ }
+ while(csqcplayer_moveframe < endframe);
+ }
+
+ //add in anything that was applied after (for low packet rate protocols)
+ input_angles = view_angles;
+}
+
+bool CSQCPlayer_IsLocalPlayer()
+{SELFPARAM();
+ return (self == csqcplayer);
+}
+
+void CSQCPlayer_SetViewLocation()
+{
+ viewloc_SetViewLocation();
+}
+
+void CSQCPlayer_SetCamera()
+{SELFPARAM();
+ vector v0;
+ v0 = pmove_vel; // TRICK: pmove_vel is set by the engine when we get here. No need to network velocity
+
+ if(csqcplayer)
+ {
+ setself(csqcplayer);
+
+ if(servercommandframe == 0 || clientcommandframe == 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.flags &= ~FL_DUCKED;
+ else if(getstati(STAT_VIEWHEIGHT) == PL_CROUCH_VIEW_OFS.z)
+ self.flags |= FL_DUCKED;
+
+ // get onground state from the server
+ if(pmove_onground)
+ self.flags |= FL_ONGROUND;
+ else
+ self.flags &= ~FL_ONGROUND;
+
+ CSQCPlayer_SetMinsMaxs();
+
+ // override it back just in case
+ self.view_ofs = '0 0 1' * getstati(STAT_VIEWHEIGHT);
+
+ // set velocity
+ self.velocity = v0;
+ }
+ else
+ {
+ float flg = self.iflags;
+ self.iflags &= ~(IFLAG_ORIGIN | IFLAG_ANGLES);
+ InterpolateOrigin_Do();
+ self.iflags = flg;
+
+ if(csqcplayer_status == CSQCPLAYERSTATUS_FROMSERVER)
+ {
+ vector o, v;
+ o = self.origin;
+ v = v0;
+ csqcplayer_status = CSQCPLAYERSTATUS_PREDICTED;
+ CSQCPlayer_PredictTo(servercommandframe + 1, false);
+ CSQCPlayer_SetPredictionError(self.origin - o, self.velocity - v, pmove_onground - !!(self.flags & FL_ONGROUND));
+ self.origin = o;
+ self.velocity = v;
+
+ // get crouch state from the server
+ if(getstati(STAT_VIEWHEIGHT) == PL_VIEW_OFS.z)
+ self.flags &= ~FL_DUCKED;
+ else if(getstati(STAT_VIEWHEIGHT) == PL_CROUCH_VIEW_OFS.z)
+ self.flags |= FL_DUCKED;
+
+ // get onground state from the server
+ if(pmove_onground)
+ self.flags |= FL_ONGROUND;
+ else
+ self.flags &= ~FL_ONGROUND;
+
+ CSQCPlayer_SavePrediction();
+ }
+ CSQCPlayer_PredictTo(clientcommandframe + 1, true);
+
+#ifdef CSQCMODEL_SERVERSIDE_CROUCH
+ // get crouch state from the server (LAG)
+ if(getstati(STAT_VIEWHEIGHT) == PL_VIEW_OFS.z)
+ self.flags &= ~FL_DUCKED;
+ else if(getstati(STAT_VIEWHEIGHT) == PL_CROUCH_VIEW_OFS.z)
+ self.flags |= FL_DUCKED;
+#endif
+
+ CSQCPlayer_SetMinsMaxs();
+
+ self.angles_y = input_angles.y;
+ }
+
+ // relink
+ setorigin(self, self.origin);
+
+ setself(this);
+ }
+
+ entity view = CSQCModel_server2csqc(player_localentnum);
+
+ if(view && view != csqcplayer)
+ {
+ WITH(entity, self, view, InterpolateOrigin_Do());
+ view.view_ofs = '0 0 1' * getstati(STAT_VIEWHEIGHT);
+ }
+
+ if(view)
+ {
+ int refdefflags = 0;
+
+ if(view.csqcmodel_teleported)
+ refdefflags |= REFDEFFLAG_TELEPORTED;
+
+ if(input_buttons & 4)
+ refdefflags |= REFDEFFLAG_JUMPING;
+
+ // note: these two only work in WIP2, but are harmless in WIP1
+ if(getstati(STAT_HEALTH) <= 0)
+ refdefflags |= REFDEFFLAG_DEAD;
+
+ if(intermission)
+ refdefflags |= REFDEFFLAG_INTERMISSION;
+
+ V_CalcRefdef(view, refdefflags);
+ }
+ else
+ {
+ // FIXME by CSQC spec we have to do this:
+ // but it breaks chase cam
+ /*
+ setproperty(VF_ORIGIN, pmove_org + '0 0 1' * getstati(STAT_VIEWHEIGHT));
+ setproperty(VF_ANGLES, view_angles);
+ */
+ }
+
+ { CSQCPLAYER_HOOK_POSTCAMERASETUP }
+}
+
+void CSQCPlayer_Remove()
+{
+ csqcplayer = world;
+ cvar_settemp("cl_movement_replay", "1");
+}
+
+float CSQCPlayer_PreUpdate()
+{SELFPARAM();
+ if(self != csqcplayer)
+ return 0;
+ if(csqcplayer_status != CSQCPLAYERSTATUS_FROMSERVER)
+ CSQCPlayer_Unpredict();
+ return 1;
+}
+
+float CSQCPlayer_PostUpdate()
+{SELFPARAM();
+ if(self.entnum != player_localnum + 1)
+ return 0;
+ csqcplayer = self;
+ csqcplayer_status = CSQCPLAYERSTATUS_FROMSERVER;
+ cvar_settemp("cl_movement_replay", "0");
+ self.entremove = CSQCPlayer_Remove;
+ return 1;
+}
--- /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.
+ */
+#ifndef LIB_CSQCMODEL_CL_PLAYER_H
+#define LIB_CSQCMODEL_CL_PLAYER_H
+
+entity csqcplayer;
+float csqcplayer_status;
+const int CSQCPLAYERSTATUS_UNPREDICTED = 0;
+const int CSQCPLAYERSTATUS_FROMSERVER = 1;
+const int CSQCPLAYERSTATUS_PREDICTED = 2;
+
+// only ever READ these!
+.int pmove_flags;
+const int PMF_JUMP_HELD = 1;
+//const int PMF_DUCKED = 4;
+//const int PMF_ONGROUND = 8;
+
+const int FL_DUCKED = 524288;
+
+void CSQCPlayer_SetCamera();
+float CSQCPlayer_PreUpdate();
+float CSQCPlayer_PostUpdate();
+float CSQCPlayer_IsLocalPlayer();
+#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.
+ */
+#ifndef LIB_CSQCMODEL_COMMON_H
+#define LIB_CSQCMODEL_COMMON_H
+
+#include "../../common/csqcmodel_settings.qh"
+
+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;
+.int frame;
+.float frame1time;
+.int frame2;
+.float frame2time;
+.float lerpfrac;
+
+const int CSQCMODEL_PROPERTY_FRAME = 8388608;
+const int CSQCMODEL_PROPERTY_TELEPORTED = 4194304; // the "teleport bit" cancelling interpolation
+const int CSQCMODEL_PROPERTY_MODELINDEX = 2097152;
+const int CSQCMODEL_PROPERTY_ORIGIN = 1048576;
+const int CSQCMODEL_PROPERTY_YAW = 524288;
+const int CSQCMODEL_PROPERTY_PITCHROLL = 262144;
+const int CSQCMODEL_PROPERTY_FRAME2 = 131072;
+const int CSQCMODEL_PROPERTY_LERPFRAC = 65536;
+const int CSQCMODEL_PROPERTY_SIZE = 32768;
+
+#define ALLPROPERTIES_COMMON \
+ CSQCMODEL_PROPERTY(CSQCMODEL_PROPERTY_FRAME, int, ReadByte, WriteByte, frame) \
+ CSQCMODEL_PROPERTY(CSQCMODEL_PROPERTY_MODELINDEX, int, ReadShort, WriteShort, modelindex) \
+ CSQCMODEL_PROPERTY(CSQCMODEL_PROPERTY_ORIGIN, float, ReadCoord, WriteCoord, origin_x) \
+ CSQCMODEL_PROPERTY(CSQCMODEL_PROPERTY_ORIGIN, float, ReadCoord, WriteCoord, origin_y) \
+ CSQCMODEL_PROPERTY(CSQCMODEL_PROPERTY_ORIGIN, float, ReadCoord, WriteCoord, origin_z) \
+ CSQCMODEL_PROPERTY(CSQCMODEL_PROPERTY_SIZE, float, ReadShort, WriteShort, mins_x) \
+ CSQCMODEL_PROPERTY(CSQCMODEL_PROPERTY_SIZE, float, ReadShort, WriteShort, mins_y) \
+ CSQCMODEL_PROPERTY(CSQCMODEL_PROPERTY_SIZE, float, ReadShort, WriteShort, mins_z) \
+ CSQCMODEL_PROPERTY(CSQCMODEL_PROPERTY_SIZE, float, ReadShort, WriteShort, maxs_x) \
+ CSQCMODEL_PROPERTY(CSQCMODEL_PROPERTY_SIZE, float, ReadShort, WriteShort, maxs_y) \
+ CSQCMODEL_PROPERTY(CSQCMODEL_PROPERTY_SIZE, float, ReadShort, WriteShort, maxs_z) \
+ CSQCMODEL_PROPERTY(CSQCMODEL_PROPERTY_PITCHROLL, float, ReadAngle, WriteAngle, angles_x) \
+ CSQCMODEL_PROPERTY(CSQCMODEL_PROPERTY_YAW, float, ReadAngle, WriteAngle, angles_y) \
+ CSQCMODEL_PROPERTY(CSQCMODEL_PROPERTY_PITCHROLL, float, 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, int, ReadByte, WriteByte, frame2) \
+ CSQCMODEL_PROPERTY_SCALED(CSQCMODEL_PROPERTY_LERPFRAC, float, ReadByte, WriteByte, lerpfrac, 255, 0, 255)
+#else
+#define ALLPROPERTIES ALLPROPERTIES_COMMON
+#endif
+#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.
+ */
+#include "interpolate.qh"
+
+#if defined(CSQC)
+// #include "../../client/defs.qh"
+// #include "../warpzone/anglestransform.qh"
+// #include "../../client/autocvars.qh"
+// #include "cl_model.qh"
+#elif defined(MENUQC)
+#elif defined(SVQC)
+#endif
+
+.vector iorigin1, iorigin2;
+.vector ivelocity1, ivelocity2;
+.vector iforward1, iforward2;
+.vector iup1, iup2;
+.vector ivforward1, ivforward2;
+.vector ivup1, ivup2;
+.float itime1, itime2;
+void InterpolateOrigin_Reset()
+{SELFPARAM();
+ self.iflags &= ~IFLAG_INTERNALMASK;
+ self.itime1 = self.itime2 = 0;
+}
+void InterpolateOrigin_Note()
+{SELFPARAM();
+ float dt;
+ int f0;
+
+ dt = time - self.itime2;
+
+ f0 = self.iflags;
+ if(self.iflags & IFLAG_PREVALID)
+ self.iflags |= IFLAG_VALID;
+ else
+ self.iflags |= IFLAG_PREVALID;
+
+ if(self.iflags & IFLAG_ORIGIN)
+ {
+ 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_AUTOVELOCITY)
+ if(self.itime2 != self.itime1)
+ self.velocity = (self.iorigin2 - self.iorigin1) * (1.0 / (self.itime2 - self.itime1));
+
+ 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_V_ANGLE)
+ {
+ fixedmakevectors(self.v_angle);
+ if(f0 & IFLAG_VALID)
+ {
+ self.ivforward1 = self.ivforward2;
+ self.ivup1 = self.ivup2;
+ }
+ else
+ {
+ self.ivforward1 = v_forward;
+ self.ivup1 = v_up;
+ }
+ self.ivforward2 = v_forward;
+ self.ivup2 = v_up;
+ }
+ else if(self.iflags & IFLAG_V_ANGLE_X)
+ {
+ self.ivforward1_x = self.ivforward2_x;
+ self.ivforward2_x = self.v_angle.x;
+ }
+
+ 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()
+{SELFPARAM();
+ 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);
+ if(self.iflags & IFLAG_ORIGIN)
+ setorigin(self, (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_V_ANGLE)
+ {
+ forward = (1 - f) * self.ivforward1 + f * self.ivforward2;
+ up = (1 - f) * self.ivup1 + f * self.ivup2;
+ self.v_angle = fixedvectoangles2(forward, up);
+ }
+ else if(self.iflags & IFLAG_V_ANGLE_X)
+ self.v_angle_x = (1 - f) * self.ivforward1_x + f * self.ivforward2_x;
+ if(self.iflags & IFLAG_VELOCITY)
+ self.velocity = (1 - f) * self.ivelocity1 + f * self.ivelocity2;
+ }
+}
+void InterpolateOrigin_Undo()
+{SELFPARAM();
+ if(self.iflags & IFLAG_ORIGIN)
+ setorigin(self, self.iorigin2);
+ if(self.iflags & IFLAG_ANGLES)
+ self.angles = fixedvectoangles2(self.iforward2, self.iup2);
+ if(self.iflags & IFLAG_V_ANGLE)
+ self.v_angle = fixedvectoangles2(self.ivforward2, self.ivup2);
+ else if(self.iflags & IFLAG_V_ANGLE_X)
+ self.v_angle_x = self.ivforward2_x;
+ 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.
+ */
+#ifndef LIB_CSQCMODEL_INTERPOLATE_H
+#define LIB_CSQCMODEL_INTERPOLATE_H
+
+.int iflags;
+const int IFLAG_VELOCITY = 1;
+const int IFLAG_ANGLES = 2;
+const int IFLAG_AUTOANGLES = 4;
+const int IFLAG_VALID = 8;
+const int IFLAG_PREVALID = 16;
+const int IFLAG_TELEPORTED = 32;
+const int IFLAG_AUTOVELOCITY = 64;
+const int IFLAG_V_ANGLE = 128;
+const int IFLAG_V_ANGLE_X = 256;
+const int IFLAG_ORIGIN = 512;
+#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();
+
+// in case we interpolate that:
+.vector v_angle;
+#endif
--- /dev/null
+#ifndef LIB_CSQCMODEL_SETTINGS_H
+#define LIB_CSQCMODEL_SETTINGS_H
+// 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, float, ReadShort, WriteShort, colormap) */ \
+ /* CSQCMODEL_PROPERTY(2, float, ReadInt24_t, WriteInt24_t, effects) */
+
+// add hook function calls here
+#define CSQCMODEL_HOOK_PREUPDATE
+#define CSQCMODEL_HOOK_POSTUPDATE
+#define CSQCMODEL_HOOK_PREDRAW
+#define CSQCPLAYER_HOOK_POSTCAMERASETUP
+
+// force updates of player entities that often even if unchanged
+#define CSQCPLAYER_FORCE_UPDATES 0.25
+
+// 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 = ...;
+#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.
+ */
+#include "sv_model.qh"
+
+#include "common.qh"
+#include "../../common/animdecide.qh"
+#include "../../common/constants.qh"
+#include "../../common/util.qh"
+#include "../../server/constants.qh"
+#include "../../server/defs.qh"
+
+// generic CSQC model code
+
+bool CSQCModel_Send(entity to, int sf)
+{SELFPARAM();
+ // some nice flags for CSQCMODEL_IF
+ float isplayer = (IS_CLIENT(self));
+ float islocalplayer = (self == to);
+ float isnolocalplayer = (isplayer && (self != to));
+
+ unused_float = isplayer;
+ unused_float = islocalplayer;
+ unused_float = isnolocalplayer;
+
+ WriteByte(MSG_ENTITY, ENT_CLIENT_MODEL);
+ WriteInt24_t(MSG_ENTITY, sf);
+
+#define CSQCMODEL_IF(cond) if(cond) {
+#define CSQCMODEL_ENDIF }
+#define CSQCMODEL_PROPERTY(flag,t,r,w,f) \
+ if(sf & flag) \
+ { \
+ w(MSG_ENTITY, self.csqcmodel_##f); \
+ }
+#define CSQCMODEL_PROPERTY_SCALED(flag,t,r,w,f,s,mi,ma) CSQCMODEL_PROPERTY(flag,t,r,w,f)
+ ALLPROPERTIES
+#undef CSQCMODEL_PROPERTY_SCALED
+#undef CSQCMODEL_PROPERTY
+#undef CSQCMODEL_ENDIF
+#undef CSQCMODEL_IF
+
+ return true;
+}
+
+#ifdef CSQCPLAYER_FORCE_UPDATES
+.float csqcmodel_nextforcedupdate;
+#endif
+void CSQCModel_CheckUpdate(entity e)
+{
+ // some nice flags for CSQCMODEL_IF
+ float isplayer = (IS_CLIENT(e));
+ float islocalplayer = isplayer; // we set BOTH to 1 here as we need the sendflags
+ float isnolocalplayer = isplayer; // we set BOTH to 1 here as we need the sendflags
+
+ unused_float = isplayer;
+ unused_float = islocalplayer;
+ unused_float = isnolocalplayer;
+
+#ifdef CSQCPLAYER_FORCE_UPDATES
+ if(isplayer && time > e.csqcmodel_nextforcedupdate)
+ {
+ e.SendFlags |= CSQCMODEL_PROPERTY_ORIGIN;
+ e.csqcmodel_nextforcedupdate = time + CSQCPLAYER_FORCE_UPDATES * (0.5 + random()); // ensure about 4 origin sends per sec
+ }
+#endif
+
+ if(e.effects & EF_RESTARTANIM_BIT)
+ {
+ e.SendFlags |= CSQCMODEL_PROPERTY_FRAME | CSQCMODEL_PROPERTY_FRAME2; // full anim resend please
+ e.effects &= ~EF_RESTARTANIM_BIT;
+ }
+
+ if(e.effects & EF_TELEPORT_BIT)
+ {
+ e.SendFlags |= CSQCMODEL_PROPERTY_TELEPORTED; // no interpolation please
+ e.effects &= ~EF_TELEPORT_BIT;
+ }
+
+#define CSQCMODEL_IF(cond) if(cond) {
+#define CSQCMODEL_ENDIF }
+#define CSQCMODEL_PROPERTY(flag,t,r,w,f) \
+ { \
+ t tmp = e.f; \
+ if(tmp != e.csqcmodel_##f) \
+ { \
+ e.csqcmodel_##f = tmp; \
+ e.SendFlags |= flag; \
+ } \
+ }
+#define CSQCMODEL_PROPERTY_SCALED(flag,t,r,w,f,s,mi,ma) \
+ { \
+ t tmp = rint(bound(mi, s * e.f, ma) - mi); \
+ if(tmp != e.csqcmodel_##f) \
+ { \
+ e.csqcmodel_##f = tmp; \
+ e.SendFlags |= flag; \
+ } \
+ }
+ ALLPROPERTIES
+#undef CSQCMODEL_PROPERTY_SCALED
+#undef CSQCMODEL_PROPERTY
+#undef CSQCMODEL_ENDIF
+#undef CSQCMODEL_IF
+}
+
+void CSQCModel_LinkEntity(entity e)
+{
+ e.SendEntity = CSQCModel_Send;
+ e.SendFlags = 0xFFFFFF;
+ CSQCModel_CheckUpdate(e);
+}
+
+void CSQCModel_UnlinkEntity(entity e)
+{
+ e.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.
+ */
+#ifndef LIB_CSQCMODEL_SV_MODEL_H
+#define LIB_CSQCMODEL_SV_MODEL_H
+
+#include "common.qh"
+
+// generic CSQC model code
+
+void CSQCModel_CheckUpdate(entity e);
+void CSQCModel_LinkEntity(entity e);
+void CSQCModel_UnlinkEntity(entity e);
+
+#define CSQCMODEL_IF(cond)
+#define CSQCMODEL_ENDIF
+#define CSQCMODEL_PROPERTY(flag,t,r,w,f) \
+ .t f; \
+ .t csqcmodel_##f;
+#define CSQCMODEL_PROPERTY_SCALED(flag,t,r,w,f,s,mi,ma) CSQCMODEL_PROPERTY(flag,t,r,w,f)
+ ALLPROPERTIES
+#undef CSQCMODEL_PROPERTY_SCALED
+#undef CSQCMODEL_PROPERTY
+#undef CSQCMODEL_ENDIF
+#undef CSQCMODEL_IF
+#endif
--- /dev/null
+The code in this directory is dual-licensed MIT and GPLv2 "or any later version".
+
+
+
+MIT license:
+
+Copyright (c) 2010 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.
+
+
+
+GPL v2:
+
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
--- /dev/null
+Open issues:
+- grep for TODO and FIXME
+- when shot origin is inside warpzone, vortex shot fails (and is even drawn in totally wrong direction). WHY? Possibly v_forward got lost?
+
+Weapon support:
+
+- blaster: YES
+- shotgun: YES
+- machinegun: YES
+- mortar: YES
+- electro: YES
+- crylink: YES
+- vortex: YES
+- hagar: YES
+- devastator: YES (except for trail bug)
+- porto: YES (bwahahahaha)
+- hlac: YES
+- vaporizer: YES
+- rifle: YES
+- fireball: YES (BFG effect cannot work through warpzones by design, so it's not available through warpzones)
+- hook: YES
+
+- shockwave: NO (does not support warpzones currently)
+- tuba: NO (sound)
--- /dev/null
+#include "anglestransform.qh"
+
+#ifdef POSITIVE_PITCH_IS_DOWN
+vector fixedvectoangles(vector a)
+{
+ vector ang;
+ ang = vectoangles(a);
+ ang.x = -ang.x;
+ return ang;
+}
+vector fixedvectoangles2(vector a, vector b)
+{
+ vector ang;
+ ang = vectoangles2(a, b);
+ ang.x = -ang.x;
+ return ang;
+}
+#else
+void fixedmakevectors(vector a)
+{
+ // a makevectors that actually inverts vectoangles
+ a.x = -a.x;
+ makevectors(a);
+}
+#endif
+
+// angles transforms
+// angles in fixedmakevectors/fixedvectoangles space
+vector AnglesTransform_Apply(vector transform, vector v)
+{
+ fixedmakevectors(transform);
+ return v_forward * v.x
+ + v_right * (-v.y)
+ + v_up * v.z;
+}
+
+vector AnglesTransform_Multiply(vector t1, vector t2)
+{
+ vector m_forward, m_up;
+ fixedmakevectors(t2); m_forward = v_forward; m_up = v_up;
+ m_forward = AnglesTransform_Apply(t1, m_forward); m_up = AnglesTransform_Apply(t1, m_up);
+ return fixedvectoangles2(m_forward, m_up);
+}
+
+vector AnglesTransform_Invert(vector transform)
+{
+ vector i_forward, i_up;
+ fixedmakevectors(transform);
+ // we want angles that turn v_forward into '1 0 0', v_right into '0 1 0' and v_up into '0 0 1'
+ // but these are orthogonal unit vectors!
+ // so to invert, we can simply fixedvectoangles the TRANSPOSED matrix
+ // TODO is this always -transform?
+ i_forward.x = v_forward.x;
+ i_forward.y = -v_right.x;
+ i_forward.z = v_up.x;
+ i_up.x = v_forward.z;
+ i_up.y = -v_right.z;
+ i_up.z = v_up.z;
+ return fixedvectoangles2(i_forward, i_up);
+}
+
+vector AnglesTransform_TurnDirectionFR(vector transform)
+{
+ // turn 180 degrees around v_up
+ // changes in-direction to out-direction
+ //fixedmakevectors(transform);
+ //return fixedvectoangles2(-1 * v_forward, 1 * v_up);
+ transform.x = -transform.x;
+ transform.y = 180 + transform.y;
+ transform.z = -transform.z;
+ // pitch: -s +c
+ // yaw: -s -c
+ // roll: -s +c
+ return transform;
+}
+
+vector AnglesTransform_TurnDirectionFU(vector transform)
+{
+ // turn 180 degrees around v_up
+ // changes in-direction to out-direction
+ //fixedmakevectors(transform);
+ //return fixedvectoangles2(-1 * v_forward, 1 * v_up);
+ transform.x = -transform.x;
+ transform.y = 180 + transform.y;
+ transform.z = 180 - transform.z;
+ return transform;
+}
+
+vector AnglesTransform_RightDivide(vector to_transform, vector from_transform)
+{
+ return AnglesTransform_Multiply(to_transform, AnglesTransform_Invert(from_transform));
+}
+
+vector AnglesTransform_LeftDivide(vector from_transform, vector to_transform)
+{
+ return AnglesTransform_Multiply(AnglesTransform_Invert(from_transform), to_transform);
+}
+
+vector AnglesTransform_Normalize(vector t, float minimize_roll)
+{
+ float need_flip;
+ // first, bring all angles in their range...
+ t.x = t.x - 360 * rint(t.x / 360);
+ t.y = t.y - 360 * rint(t.y / 360);
+ t.z = t.z - 360 * rint(t.z / 360);
+ if(minimize_roll)
+ need_flip = (t.z > 90 || t.z <= -90);
+ else
+ need_flip = (t.x > 90 || t.x < -90); // for pitch we prefer to allow exactly -90 degrees for looking straight down
+ if(need_flip)
+ {
+ if(t.x >= 0) t.x = 180 - t.x; else t.x = -180 - t.x;
+ if(t.y > 0) t.y -= 180; else t.y += 180;
+ if(t.z > 0) t.z -= 180; else t.z += 180;
+ }
+ return t;
+}
+
+vector AnglesTransform_CancelRoll(vector t)
+{
+ const float epsilon = 30;
+ float f;
+
+ // constraints:
+ // forward vector (NOT SO important)
+ // right vector, up vector: screen rotation (MORE important)
+ // choose best match among all pitch-yaw only rotations
+
+ // FIXME find a better method
+
+ f = fabs(t.x - (-90)) / epsilon;
+ if(f < 1)
+ {
+ //t_x = -90;
+ t.y += t.z;
+ t.z = 0;
+ }
+ else
+ {
+ f = fabs(t.x - 90) / epsilon;
+ if(f < 1)
+ {
+ //t_x = 90;
+ t.y -= t.z;
+ t.z = 0;
+ }
+ }
+ return t;
+}
+
+#ifdef POSITIVE_PITCH_IS_DOWN
+vector AnglesTransform_ApplyToAngles(vector transform, vector v)
+{
+ v.x = -v.x;
+ v = AnglesTransform_Multiply(transform, v);
+ v.x = -v.x;
+ return v;
+}
+vector AnglesTransform_ApplyToVAngles(vector transform, vector v)
+{
+ v = AnglesTransform_Multiply(transform, v);
+ return v;
+}
+vector AnglesTransform_FromAngles(vector v)
+{
+ v.x = -v.x;
+ return v;
+}
+vector AnglesTransform_ToAngles(vector v)
+{
+ v.x = -v.x;
+ return v;
+}
+vector AnglesTransform_FromVAngles(vector v)
+{
+ return v;
+}
+vector AnglesTransform_ToVAngles(vector v)
+{
+ return v;
+}
+#else
+vector AnglesTransform_ApplyToAngles(vector transform, vector v)
+{
+ v = AnglesTransform_Multiply(transform, v);
+ return v;
+}
+vector AnglesTransform_ApplyToVAngles(vector transform, vector v)
+{
+ v.x = -v.x;
+ v = AnglesTransform_Multiply(transform, v);
+ v.x = -v.x;
+ return v;
+}
+vector AnglesTransform_FromAngles(vector v)
+{
+ return v;
+}
+vector AnglesTransform_ToAngles(vector v)
+{
+ return v;
+}
+vector AnglesTransform_FromVAngles(vector v)
+{
+ v.x = -v.x;
+ return v;
+}
+vector AnglesTransform_ToVAngles(vector v)
+{
+ v.x = -v.x;
+ return v;
+}
+#endif
+
+vector AnglesTransform_Multiply_GetPostShift(vector t0, vector st0, vector t1, vector st1)
+{
+ // we want the result of:
+ // t0 * (t1 * p + st1) + st0
+ // t0 * t1 * p + t0 * st1 + st0
+ return st0 + AnglesTransform_Apply(t0, st1);
+}
+vector AnglesTransform_PrePostShift_GetPostShift(vector sf, vector t, vector st)
+{
+ return st - AnglesTransform_Apply(t, sf);
+}
--- /dev/null
+#ifndef LIB_WARPZONE_ANGLETRANSFORM_H
+#define LIB_WARPZONE_ANGLETRANSFORM_H
+
+#define POSITIVE_PITCH_IS_DOWN
+
+#ifdef POSITIVE_PITCH_IS_DOWN
+#define fixedmakevectors makevectors
+vector fixedvectoangles(vector a);
+vector fixedvectoangles2(vector a, vector b);
+#else
+void fixedmakevectors(vector a);
+#define fixedvectoangles2 vectoangles2
+#define fixedvectoangles vectoangles
+#endif
+
+vector AnglesTransform_Apply(vector transform, vector v);
+vector AnglesTransform_Multiply(vector t1, vector t2); // A B
+vector AnglesTransform_Invert(vector transform);
+vector AnglesTransform_TurnDirectionFU(vector transform);
+vector AnglesTransform_TurnDirectionFR(vector transform);
+vector AnglesTransform_RightDivide(vector to_transform, vector from_transform); // A B^-1
+vector AnglesTransform_LeftDivide(vector from_transform, vector to_transform); // A^-1 B
+
+vector AnglesTransform_Normalize(vector t, float minimize_roll); // makes sure all angles are in their range: yaw in -180..180, pitch in -90..90, roll in -180..180 (or if minimize_roll is set, pitch in -180..180, roll in -90..90)
+
+vector AnglesTransform_ApplyToAngles(vector transform, vector v);
+vector AnglesTransform_ApplyToVAngles(vector transform, vector v);
+vector AnglesTransform_FromAngles(vector v);
+vector AnglesTransform_ToAngles(vector v);
+vector AnglesTransform_FromVAngles(vector v);
+vector AnglesTransform_ToVAngles(vector v);
+
+// transformed = original * transform + postshift
+vector AnglesTransform_Multiply_GetPostShift(vector sf0, vector st0, vector t1, vector st1);
+vector AnglesTransform_PrePostShift_GetPostShift(vector sf, vector t, vector st);
+#endif
--- /dev/null
+#include "client.qh"
+#include "common.qh"
+
+#if defined(CSQC)
+ #include "../../client/autocvars.qh"
+ #include "../csqcmodel/cl_model.qh"
+#elif defined(MENUQC)
+#elif defined(SVQC)
+#endif
+
+void WarpZone_Fade_PreDraw()
+{SELFPARAM();
+ vector org;
+ org = getpropertyvec(VF_ORIGIN);
+ if(!checkpvs(org, self)) // this makes sense as long as we don't support recursive warpzones
+ self.alpha = 0;
+ else if(self.warpzone_fadestart)
+ self.alpha = bound(0, (self.warpzone_fadeend - vlen(org - self.origin - 0.5 * (self.mins + self.maxs))) / (self.warpzone_fadeend - self.warpzone_fadestart), 1);
+ else
+ self.alpha = 1;
+ //printf("%v <-> %v\n", view_origin, self.origin + 0.5 * (self.mins + self.maxs));
+ if(self.alpha <= 0)
+ self.drawmask = 0;
+ else
+ self.drawmask = MASK_NORMAL;
+}
+
+void WarpZone_Read(float isnew)
+{SELFPARAM();
+ warpzone_warpzones_exist = 1;
+ if (!self.enemy)
+ {
+ self.enemy = spawn();
+ self.enemy.classname = "warpzone_from";
+ }
+ self.classname = "trigger_warpzone";
+
+ int f = ReadByte();
+ self.warpzone_isboxy = (f & 1);
+ if(f & 4)
+ {
+ self.origin_x = ReadCoord();
+ self.origin_y = ReadCoord();
+ self.origin_z = ReadCoord();
+ }
+ else
+ self.origin = '0 0 0';
+ self.modelindex = ReadShort();
+ self.mins_x = ReadCoord();
+ self.mins_y = ReadCoord();
+ self.mins_z = ReadCoord();
+ self.maxs_x = ReadCoord();
+ self.maxs_y = ReadCoord();
+ self.maxs_z = ReadCoord();
+ self.scale = ReadByte() / 16;
+ self.enemy.oldorigin_x = ReadCoord();
+ self.enemy.oldorigin_y = ReadCoord();
+ self.enemy.oldorigin_z = ReadCoord();
+ self.enemy.avelocity_x = ReadCoord();
+ self.enemy.avelocity_y = ReadCoord();
+ self.enemy.avelocity_z = ReadCoord();
+ self.oldorigin_x = ReadCoord();
+ self.oldorigin_y = ReadCoord();
+ self.oldorigin_z = ReadCoord();
+ self.avelocity_x = ReadCoord();
+ self.avelocity_y = ReadCoord();
+ self.avelocity_z = ReadCoord();
+
+ if(f & 2)
+ {
+ self.warpzone_fadestart = ReadShort();
+ self.warpzone_fadeend = max(self.warpzone_fadestart + 1, ReadShort());
+ }
+ else
+ {
+ self.warpzone_fadestart = 0;
+ self.warpzone_fadeend = 0;
+ }
+
+ // common stuff
+ WarpZone_SetUp(self, self.enemy.oldorigin, self.enemy.avelocity, self.oldorigin, self.avelocity);
+
+ // link me
+ //setmodel(self, self.model);
+ setorigin(self, self.origin);
+ setsize(self, self.mins, self.maxs);
+
+ // how to draw
+ // engine currently wants this
+ self.predraw = WarpZone_Fade_PreDraw;
+}
+
+void WarpZone_Camera_Read(float isnew)
+{SELFPARAM();
+ warpzone_cameras_exist = 1;
+ self.classname = "func_warpzone_camera";
+
+ int f = ReadByte();
+ if(f & 4)
+ {
+ self.origin_x = ReadCoord();
+ self.origin_y = ReadCoord();
+ self.origin_z = ReadCoord();
+ }
+ else
+ self.origin = '0 0 0';
+ self.modelindex = ReadShort();
+ self.mins_x = ReadCoord();
+ self.mins_y = ReadCoord();
+ self.mins_z = ReadCoord();
+ self.maxs_x = ReadCoord();
+ self.maxs_y = ReadCoord();
+ self.maxs_z = ReadCoord();
+ self.scale = ReadByte() / 16;
+ self.oldorigin_x = ReadCoord();
+ self.oldorigin_y = ReadCoord();
+ self.oldorigin_z = ReadCoord();
+ self.avelocity_x = ReadCoord();
+ self.avelocity_y = ReadCoord();
+ self.avelocity_z = ReadCoord();
+
+ if(f & 2)
+ {
+ self.warpzone_fadestart = ReadShort();
+ self.warpzone_fadeend = max(self.warpzone_fadestart + 1, ReadShort());
+ }
+ else
+ {
+ self.warpzone_fadestart = 0;
+ self.warpzone_fadeend = 0;
+ }
+
+ // common stuff
+ WarpZone_Camera_SetUp(self, self.oldorigin, self.avelocity);
+
+ // engine currently wants this
+ self.drawmask = MASK_NORMAL;
+
+ // link me
+ //setmodel(self, self.model);
+ setorigin(self, self.origin);
+ setsize(self, self.mins, self.maxs);
+
+ // how to draw
+ // engine currently wants this
+ self.predraw = WarpZone_Fade_PreDraw;
+}
+
+void CL_RotateMoves(vector ang) = #638;
+void WarpZone_Teleported_Read(float isnew)
+{SELFPARAM();
+ vector v;
+ self.classname = "warpzone_teleported";
+ v.x = ReadCoord();
+ v.y = ReadCoord();
+ v.z = ReadCoord();
+ if(!isnew)
+ return;
+ self.warpzone_transform = v;
+ setproperty(VF_CL_VIEWANGLES, WarpZone_TransformVAngles(self, getpropertyvec(VF_CL_VIEWANGLES)));
+ if(checkextension("DP_CSQC_ROTATEMOVES"))
+ CL_RotateMoves(v);
+ //CL_RotateMoves('0 90 0');
+}
+
+float warpzone_fixingview;
+float warpzone_fixingview_drawexteriormodel;
+
+void WarpZone_View_Outside()
+{
+ if(!warpzone_fixingview)
+ return;
+ warpzone_fixingview = 0;
+ cvar_set("r_drawexteriormodel", ftos(warpzone_fixingview_drawexteriormodel));
+}
+
+void WarpZone_View_Inside()
+{
+ if(autocvar_chase_active)
+ {
+ WarpZone_View_Outside();
+ return;
+ }
+ if(warpzone_fixingview)
+ return;
+ warpzone_fixingview = 1;
+ warpzone_fixingview_drawexteriormodel = cvar("r_drawexteriormodel");
+ cvar_set("r_drawexteriormodel", "0");
+}
+
+vector WarpZone_FixNearClip(vector o, vector c0, vector c1, vector c2, vector c3)
+{
+ vector mi, ma;
+ entity e;
+ float pd;
+
+ mi.x = min(o.x, c0_x, c1_x, c2_x, c3_x);
+ ma.x = max(o.x, c0_x, c1_x, c2_x, c3_x);
+ mi.y = min(o.y, c0_y, c1_y, c2_y, c3_y);
+ ma.y = max(o.y, c0_y, c1_y, c2_y, c3_y);
+ mi.z = min(o.z, c0_z, c1_z, c2_z, c3_z);
+ ma.z = max(o.z, c0_z, c1_z, c2_z, c3_z);
+
+ e = WarpZone_Find(mi, ma);
+ if(e)
+ {
+ if(WarpZone_PlaneDist(e, o) < 0)
+ return '0 0 0';
+ // can't really be, though, but if it is, this is not my warpzone, but a random different one in the same mins/maxs
+ pd = min(
+ WarpZone_PlaneDist(e, c0),
+ WarpZone_PlaneDist(e, c1),
+ WarpZone_PlaneDist(e, c2),
+ WarpZone_PlaneDist(e, c3)
+ );
+ if(pd < 0)
+ return e.warpzone_forward * -pd;
+ }
+
+ return '0 0 0';
+}
+
+void WarpZone_FixPMove()
+{
+ entity e;
+ e = WarpZone_Find(pmove_org, pmove_org);
+ if(e)
+ {
+ pmove_org = WarpZone_TransformOrigin(e, pmove_org);
+ input_angles = WarpZone_TransformVAngles(e, input_angles);
+ }
+}
+
+#ifndef KEEP_ROLL
+float autocvar_cl_rollkillspeed = 10;
+#endif
+void WarpZone_FixView()
+{
+ entity e;
+ vector org, ang, nearclip, corner0, corner1, corner2, corner3, o;
+ float f;
+
+ warpzone_save_view_origin = org = getpropertyvec(VF_ORIGIN);
+ warpzone_save_view_angles = ang = getpropertyvec(VF_ANGLES);
+
+ e = WarpZone_Find(org, org);
+ if(e)
+ {
+ org = WarpZone_TransformOrigin(e, org);
+ ang = WarpZone_TransformVAngles(e, ang);
+ WarpZone_View_Inside();
+ }
+ else
+ WarpZone_View_Outside();
+
+#ifndef KEEP_ROLL
+ float rick;
+ if(autocvar_cl_rollkillspeed)
+ f = max(0, (1 - frametime * autocvar_cl_rollkillspeed));
+ else
+ f = 0;
+
+ rick = getproperty(VF_CL_VIEWANGLES_Z);
+ rick *= f;
+ setproperty(VF_CL_VIEWANGLES_Z, rick);
+ ang.z *= f;
+#endif
+
+ setproperty(VF_ORIGIN, org);
+ setproperty(VF_ANGLES, ang);
+
+ nearclip = '0 0 1' * (cvar("r_nearclip") * 1.125);
+ corner0 = cs_unproject('0 0 0' + nearclip);
+ corner1 = cs_unproject('1 0 0' * cvar("vid_conwidth") + nearclip);
+ corner2 = cs_unproject('0 1 0' * cvar("vid_conheight") + nearclip);
+ corner3 = cs_unproject('1 0 0' * cvar("vid_conwidth") + '0 1 0' * cvar("vid_conheight") + nearclip);
+ o = WarpZone_FixNearClip(org, corner0, corner1, corner2, corner3);
+ if(o != '0 0 0')
+ setproperty(VF_ORIGIN, org + o);
+}
+
+void WarpZone_Init()
+{
+}
+
+void WarpZone_Shutdown()
+{
+ WarpZone_View_Outside();
+}
--- /dev/null
+#ifndef LIB_WARPZONE_CLIENT_H
+#define LIB_WARPZONE_CLIENT_H
+
+void WarpZone_Read(float bIsNewEntity);
+void WarpZone_Camera_Read(float bIsNewEntity);
+void WarpZone_Teleported_Read(float bIsNewEntity);
+
+void WarpZone_FixPMove();
+void WarpZone_FixView();
+
+void WarpZone_Init();
+void WarpZone_Shutdown();
+
+vector warpzone_save_view_origin;
+vector warpzone_save_view_angles;
+#endif
--- /dev/null
+#include "common.qh"
+
+#if defined(CSQC)
+ #include "../../server/t_items.qh"
+#elif defined(MENUQC)
+#elif defined(SVQC)
+ #include "../../common/weapons/all.qh"
+#endif
+
+void WarpZone_Accumulator_Clear(entity acc)
+{
+ acc.warpzone_transform = '0 0 0';
+ acc.warpzone_shift = '0 0 0';
+}
+void WarpZone_Accumulator_AddTransform(entity acc, vector t, vector s)
+{
+ vector tr, st;
+ tr = AnglesTransform_Multiply(t, acc.warpzone_transform);
+ st = AnglesTransform_Multiply_GetPostShift(t, s, acc.warpzone_transform, acc.warpzone_shift);
+ acc.warpzone_transform = tr;
+ acc.warpzone_shift = st;
+}
+void WarpZone_Accumulator_Add(entity acc, entity wz)
+{
+ WarpZone_Accumulator_AddTransform(acc, wz.warpzone_transform, wz.warpzone_shift);
+}
+void WarpZone_Accumulator_AddInverseTransform(entity acc, vector t, vector s)
+{
+ vector tt, ss;
+ tt = AnglesTransform_Invert(t);
+ ss = AnglesTransform_PrePostShift_GetPostShift(s, tt, '0 0 0');
+ WarpZone_Accumulator_AddTransform(acc, tt, ss);
+ // yes, this probably can be done simpler... but this way is "obvious" :)
+}
+void WarpZone_Accumulator_AddInverse(entity acc, entity wz)
+{
+ WarpZone_Accumulator_AddInverseTransform(acc, wz.warpzone_transform, wz.warpzone_shift);
+}
+
+.vector(vector, vector) camera_transform;
+float autocvar_cl_warpzone_usetrace = 1;
+vector WarpZone_camera_transform(vector org, vector ang)
+{SELFPARAM();
+ vector vf, vr, vu;
+ if(self.warpzone_fadestart)
+ if(vlen(org - self.origin - 0.5 * (self.mins + self.maxs)) > self.warpzone_fadeend + 400)
+ return org;
+ // don't transform if zone faded out (plus 400qu safety margin for typical speeds and latencies)
+ // unneeded on client, on server this helps a lot
+ vf = v_forward;
+ vr = v_right;
+ vu = v_up;
+ org = WarpZone_TransformOrigin(self, org);
+ vf = WarpZone_TransformVelocity(self, vf);
+ vr = WarpZone_TransformVelocity(self, vr);
+ vu = WarpZone_TransformVelocity(self, vu);
+ if(autocvar_cl_warpzone_usetrace)
+ traceline(self.warpzone_targetorigin, org, MOVE_NOMONSTERS, world);
+ else
+ trace_endpos = self.warpzone_targetorigin;
+ v_forward = vf;
+ v_right = vr;
+ v_up = vu;
+ return org;
+}
+
+void WarpZone_SetUp(entity e, vector my_org, vector my_ang, vector other_org, vector other_ang)
+{
+ e.warpzone_transform = AnglesTransform_RightDivide(other_ang, AnglesTransform_TurnDirectionFR(my_ang));
+ e.warpzone_shift = AnglesTransform_PrePostShift_GetPostShift(my_org, e.warpzone_transform, other_org);
+ e.warpzone_origin = my_org;
+ e.warpzone_targetorigin = other_org;
+ e.warpzone_angles = my_ang;
+ e.warpzone_targetangles = other_ang;
+ fixedmakevectors(my_ang); e.warpzone_forward = v_forward;
+ fixedmakevectors(other_ang); e.warpzone_targetforward = v_forward;
+ e.camera_transform = WarpZone_camera_transform;
+}
+
+vector WarpZone_Camera_camera_transform(vector org, vector ang)
+{SELFPARAM();
+ // a fixed camera view
+ if(self.warpzone_fadestart)
+ if(vlen(org - self.origin - 0.5 * (self.mins + self.maxs)) > self.warpzone_fadeend + 400)
+ return org;
+ // don't transform if zone faded out (plus 400qu safety margin for typical speeds and latencies)
+ // unneeded on client, on server this helps a lot
+ trace_endpos = self.warpzone_origin;
+ makevectors(self.warpzone_angles);
+ return self.warpzone_origin;
+}
+
+void WarpZone_Camera_SetUp(entity e, vector my_org, vector my_ang) // we assume that e.oldorigin and e.avelocity point to view origin and direction
+{
+ e.warpzone_origin = my_org;
+ e.warpzone_angles = my_ang;
+ e.camera_transform = WarpZone_Camera_camera_transform;
+}
+
+.entity enemy;
+
+vector WarpZoneLib_BoxTouchesBrush_mins;
+vector WarpZoneLib_BoxTouchesBrush_maxs;
+entity WarpZoneLib_BoxTouchesBrush_ent;
+entity WarpZoneLib_BoxTouchesBrush_ignore;
+float WarpZoneLib_BoxTouchesBrush_Recurse()
+{
+ float s;
+ entity se;
+ float f;
+
+ tracebox('0 0 0', WarpZoneLib_BoxTouchesBrush_mins, WarpZoneLib_BoxTouchesBrush_maxs, '0 0 0', MOVE_NOMONSTERS, WarpZoneLib_BoxTouchesBrush_ignore);
+#ifdef CSQC
+ if (trace_networkentity)
+ {
+ LOG_TRACE("hit a network ent, cannot continue WarpZoneLib_BoxTouchesBrush\n");
+ // we cannot continue, as a player blocks us...
+ // so, abort
+ return 0;
+ }
+#endif
+ if (!trace_ent)
+ return 0;
+ if (trace_ent == WarpZoneLib_BoxTouchesBrush_ent)
+ return 1;
+
+ se = trace_ent;
+ s = se.solid;
+ se.solid = SOLID_NOT;
+ f = WarpZoneLib_BoxTouchesBrush_Recurse();
+ se.solid = s;
+
+ return f;
+}
+
+float WarpZoneLib_BoxTouchesBrush(vector mi, vector ma, entity e, entity ig)
+{
+ float f, s;
+
+ if(!e.modelindex || e.warpzone_isboxy)
+ return 1;
+
+ s = e.solid;
+ e.solid = SOLID_BSP;
+ WarpZoneLib_BoxTouchesBrush_mins = mi;
+ WarpZoneLib_BoxTouchesBrush_maxs = ma;
+ WarpZoneLib_BoxTouchesBrush_ent = e;
+ WarpZoneLib_BoxTouchesBrush_ignore = ig;
+ f = WarpZoneLib_BoxTouchesBrush_Recurse();
+ e.solid = s;
+
+ return f;
+}
+
+entity WarpZone_Find(vector mi, vector ma)
+{
+ // if we are near any warpzone planes - MOVE AWAY (work around nearclip)
+ entity e;
+ if(!warpzone_warpzones_exist)
+ return world;
+ for(e = world; (e = find(e, classname, "trigger_warpzone")); )
+ if(WarpZoneLib_BoxTouchesBrush(mi, ma, e, world))
+ return e;
+ return world;
+}
+
+void WarpZone_MakeAllSolid()
+{
+ entity e;
+ if(!warpzone_warpzones_exist)
+ return;
+ for(e = world; (e = find(e, classname, "trigger_warpzone")); )
+ e.solid = SOLID_BSP;
+}
+
+void WarpZone_MakeAllOther()
+{
+ entity e;
+ if(!warpzone_warpzones_exist)
+ return;
+ for(e = world; (e = find(e, classname, "trigger_warpzone")); )
+ e.solid = SOLID_TRIGGER;
+}
+
+void WarpZone_Trace_InitTransform()
+{
+ if(!WarpZone_trace_transform)
+ {
+ WarpZone_trace_transform = spawn();
+ WarpZone_trace_transform.classname = "warpzone_trace_transform";
+ }
+ WarpZone_Accumulator_Clear(WarpZone_trace_transform);
+}
+void WarpZone_Trace_AddTransform(entity wz)
+{
+ WarpZone_Accumulator_Add(WarpZone_trace_transform, wz);
+}
+
+void WarpZone_TraceBox_ThroughZone(vector org, vector mi, vector ma, vector end, float nomonsters, entity forent, entity zone, WarpZone_trace_callback_t cb)
+{
+ float nomonsters_adjusted;
+ float frac, sol, i;
+ float contentshack;
+ vector o0, e0;
+ entity wz;
+ vector vf, vr, vu;
+
+ WarpZone_trace_forent = forent;
+ WarpZone_trace_firstzone = world;
+ WarpZone_trace_lastzone = world;
+ WarpZone_Trace_InitTransform();
+ if(!warpzone_warpzones_exist)
+ {
+ if(nomonsters == MOVE_NOTHING)
+ {
+ trace_endpos = end;
+ trace_fraction = 1;
+ if(cb)
+ cb(org, trace_endpos, end);
+ return;
+ }
+ else
+ {
+ tracebox(org, mi, ma, end, nomonsters, WarpZone_trace_forent);
+ if(cb)
+ cb(org, trace_endpos, end);
+ return;
+ }
+ }
+
+ vf = v_forward;
+ vr = v_right;
+ vu = v_up;
+ o0 = org;
+ e0 = end;
+
+ switch(nomonsters)
+ {
+ case MOVE_WORLDONLY:
+ case MOVE_NOTHING:
+ nomonsters_adjusted = MOVE_NOMONSTERS;
+ break;
+ default:
+ nomonsters_adjusted = nomonsters;
+ break;
+ }
+ if((contentshack = (WarpZone_trace_forent.dphitcontentsmask && !(WarpZone_trace_forent.dphitcontentsmask & DPCONTENTS_SOLID))))
+ BITSET_ASSIGN(WarpZone_trace_forent.dphitcontentsmask, DPCONTENTS_SOLID);
+
+ // if starting in warpzone, first transform
+ wz = WarpZone_Find(org + mi, org + ma);
+ if(wz)
+ {
+ WarpZone_trace_firstzone = wz;
+ WarpZone_trace_lastzone = wz;
+ if(zone && wz != zone)
+ {
+ // we are in ANOTHER warpzone. This is bad. Make a zero length trace and return.
+ sol = 1;
+ trace_fraction = 0;
+ trace_endpos = org;
+ goto fail;
+ }
+ WarpZone_Trace_AddTransform(wz);
+ org = WarpZone_TransformOrigin(wz, org);
+ end = WarpZone_TransformOrigin(wz, end);
+ }
+ WarpZone_MakeAllSolid();
+ sol = -1;
+ frac = 0;
+ i = 16;
+ for (;;)
+ {
+ if(--i < 1)
+ {
+ LOG_TRACE("Too many warpzones in sequence, aborting trace.\n");
+ trace_ent = world;
+ break;
+ }
+ tracebox(org, mi, ma, end, nomonsters_adjusted, WarpZone_trace_forent);
+ if(cb)
+ cb(org, trace_endpos, end);
+ if(sol < 0)
+ sol = trace_startsolid;
+
+ frac = trace_fraction = frac + (1 - frac) * trace_fraction;
+ if(trace_fraction >= 1)
+ break;
+ if(trace_ent.classname != "trigger_warpzone")
+ {
+ if((nomonsters == MOVE_NOTHING) || ((nomonsters == MOVE_WORLDONLY) && trace_ent) || (contentshack && (trace_dphitcontents & WarpZone_trace_forent.dphitcontentsmask) == DPCONTENTS_SOLID))
+ {
+ // continue the trace, ignoring this hit (we only care for warpzones)
+ org = trace_endpos + normalize(end - org);
+ continue;
+ // we cannot do an inverted trace here, as we do care for further warpzones inside that "solid" to be found
+ // otherwise, players could block entrances that way
+ }
+ break;
+ }
+ if(trace_ent == wz)
+ {
+ // FIXME can this check be removed? Do we really need it?
+ LOG_TRACE("I transformed into the same zone again, wtf, aborting the trace\n");
+ trace_ent = world;
+ break;
+ }
+ wz = trace_ent;
+ if(!WarpZone_trace_firstzone)
+ WarpZone_trace_firstzone = wz;
+ WarpZone_trace_lastzone = wz;
+ if(zone && wz != zone)
+ break;
+ WarpZone_Trace_AddTransform(wz);
+ // we hit a warpzone... so, let's perform the trace after the warp again
+ org = WarpZone_TransformOrigin(wz, trace_endpos);
+ end = WarpZone_TransformOrigin(wz, end);
+
+ // we got warped, so let's step back a bit
+ tracebox(org, mi, ma, org + normalize(org - end) * 32, nomonsters_adjusted, WarpZone_trace_forent);
+ org = trace_endpos;
+ }
+ WarpZone_MakeAllOther();
+:fail
+ if(contentshack)
+ BITCLR_ASSIGN(WarpZone_trace_forent.dphitcontentsmask, DPCONTENTS_SOLID);
+ trace_startsolid = sol;
+ v_forward = vf;
+ v_right = vr;
+ v_up = vu;
+}
+
+void WarpZone_TraceBox(vector org, vector mi, vector ma, vector end, float nomonsters, entity forent)
+{
+ WarpZone_TraceBox_ThroughZone(org, mi, ma, end, nomonsters, forent, world, WarpZone_trace_callback_t_null);
+}
+
+void WarpZone_TraceLine(vector org, vector end, float nomonsters, entity forent)
+{
+ WarpZone_TraceBox(org, '0 0 0', '0 0 0', end, nomonsters, forent);
+}
+
+void WarpZone_TraceToss_ThroughZone(entity e, entity forent, entity zone, WarpZone_trace_callback_t cb)
+{
+ float g, dt, i;
+ vector vf, vr, vu, v0, o0;
+ entity wz;
+
+ o0 = e.origin;
+ v0 = e.velocity;
+ g = cvar("sv_gravity") * e.gravity;
+
+ WarpZone_trace_forent = forent;
+ WarpZone_trace_firstzone = world;
+ WarpZone_trace_lastzone = world;
+ WarpZone_Trace_InitTransform();
+ WarpZone_tracetoss_time = 0;
+ if(!warpzone_warpzones_exist)
+ {
+ tracetoss(e, WarpZone_trace_forent);
+ if(cb)
+ cb(e.origin, trace_endpos, trace_endpos);
+ dt = vlen(e.origin - o0) / vlen(e.velocity);
+ WarpZone_tracetoss_time += dt;
+ e.velocity_z -= dt * g;
+ WarpZone_tracetoss_velocity = e.velocity;
+ e.velocity = v0;
+ return;
+ }
+
+ vf = v_forward;
+ vr = v_right;
+ vu = v_up;
+
+ // if starting in warpzone, first transform
+ wz = WarpZone_Find(e.origin + e.mins, e.origin + e.maxs);
+ if(wz)
+ {
+ WarpZone_trace_firstzone = wz;
+ WarpZone_trace_lastzone = wz;
+ if(zone && wz != zone)
+ {
+ // we are in ANOTHER warpzone. This is bad. Make a zero length trace and return.
+
+ WarpZone_tracetoss_time = 0;
+ trace_endpos = o0;
+ goto fail;
+ }
+ WarpZone_Trace_AddTransform(wz);
+ setorigin(e, WarpZone_TransformOrigin(wz, e.origin));
+ e.velocity = WarpZone_TransformVelocity(wz, e.velocity);
+ }
+ WarpZone_MakeAllSolid();
+ i = 16;
+ for (;;)
+ {
+ if(--i < 1)
+ {
+ LOG_TRACE("Too many warpzones in sequence, aborting trace.\n");
+ trace_ent = world;
+ break;
+ }
+ tracetoss(e, WarpZone_trace_forent);
+ if(cb)
+ cb(e.origin, trace_endpos, trace_endpos);
+ dt = vlen(trace_endpos - e.origin) / vlen(e.velocity);
+ WarpZone_tracetoss_time += dt;
+ e.origin = trace_endpos;
+ e.velocity_z -= dt * g;
+ if(trace_fraction >= 1)
+ break;
+ if(trace_ent.classname != "trigger_warpzone")
+ break;
+ if(trace_ent == wz)
+ {
+ // FIXME can this check be removed? Do we really need it?
+ LOG_TRACE("I transformed into the same zone again, wtf, aborting the trace\n");
+ trace_ent = world;
+ break;
+ }
+ wz = trace_ent;
+ if(!WarpZone_trace_firstzone)
+ WarpZone_trace_firstzone = wz;
+ WarpZone_trace_lastzone = wz;
+ if(zone && wz != zone)
+ break;
+ WarpZone_Trace_AddTransform(wz);
+ // we hit a warpzone... so, let's perform the trace after the warp again
+ e.origin = WarpZone_TransformOrigin(wz, e.origin);
+ e.velocity = WarpZone_TransformVelocity(wz, e.velocity);
+
+ // we got warped, so let's step back a bit
+ e.velocity = -e.velocity;
+ tracetoss(e, WarpZone_trace_forent);
+ dt = vlen(trace_endpos - e.origin) / vlen(e.velocity);
+ WarpZone_tracetoss_time -= dt;
+ e.origin = trace_endpos;
+ e.velocity = -e.velocity;
+ }
+ WarpZone_MakeAllOther();
+:fail
+ WarpZone_tracetoss_velocity = e.velocity;
+ v_forward = vf;
+ v_right = vr;
+ v_up = vu;
+ // restore old entity data (caller just uses trace_endpos, WarpZone_tracetoss_velocity and the transform)
+ e.velocity = v0;
+ e.origin = o0;
+}
+
+void WarpZone_TraceToss(entity e, entity forent)
+{
+ WarpZone_TraceToss_ThroughZone(e, forent, world, WarpZone_trace_callback_t_null);
+}
+
+entity WarpZone_TrailParticles_trace_callback_own;
+float WarpZone_TrailParticles_trace_callback_eff;
+void WarpZone_TrailParticles_trace_callback(vector from, vector endpos, vector to)
+{
+ trailparticles(WarpZone_TrailParticles_trace_callback_own, WarpZone_TrailParticles_trace_callback_eff, from, endpos);
+}
+
+void WarpZone_TrailParticles(entity own, float eff, vector org, vector end)
+{
+ WarpZone_TrailParticles_trace_callback_own = own;
+ WarpZone_TrailParticles_trace_callback_eff = eff;
+ WarpZone_TraceBox_ThroughZone(org, '0 0 0', '0 0 0', end, MOVE_NOMONSTERS, world, world, WarpZone_TrailParticles_trace_callback);
+}
+
+#ifdef CSQC
+float WarpZone_TrailParticles_trace_callback_f;
+float WarpZone_TrailParticles_trace_callback_flags;
+void WarpZone_TrailParticles_WithMultiplier_trace_callback(vector from, vector endpos, vector to)
+{
+ boxparticles(WarpZone_TrailParticles_trace_callback_eff, WarpZone_TrailParticles_trace_callback_own, from, endpos, WarpZone_TrailParticles_trace_callback_own.velocity, WarpZone_TrailParticles_trace_callback_own.velocity, WarpZone_TrailParticles_trace_callback_f, WarpZone_TrailParticles_trace_callback_flags);
+}
+
+void WarpZone_TrailParticles_WithMultiplier(entity own, float eff, vector org, vector end, float f, int boxflags)
+{
+ WarpZone_TrailParticles_trace_callback_own = own;
+ WarpZone_TrailParticles_trace_callback_eff = eff;
+ WarpZone_TrailParticles_trace_callback_f = f;
+ WarpZone_TrailParticles_trace_callback_flags = boxflags | PARTICLES_DRAWASTRAIL;
+ WarpZone_TraceBox_ThroughZone(org, '0 0 0', '0 0 0', end, MOVE_NOMONSTERS, world, world, WarpZone_TrailParticles_WithMultiplier_trace_callback);
+}
+#endif
+
+float WarpZone_PlaneDist(entity wz, vector v)
+{
+ return (v - wz.warpzone_origin) * wz.warpzone_forward;
+}
+
+float WarpZone_TargetPlaneDist(entity wz, vector v)
+{
+ return (v - wz.warpzone_targetorigin) * wz.warpzone_targetforward;
+}
+
+vector WarpZone_TransformOrigin(entity wz, vector v)
+{
+ return wz.warpzone_shift + AnglesTransform_Apply(wz.warpzone_transform, v);
+}
+
+vector WarpZone_TransformVelocity(entity wz, vector v)
+{
+ return AnglesTransform_Apply(wz.warpzone_transform, v);
+}
+
+vector WarpZone_TransformAngles(entity wz, vector v)
+{
+ return AnglesTransform_ApplyToAngles(wz.warpzone_transform, v);
+}
+
+vector WarpZone_TransformVAngles(entity wz, vector ang)
+{
+#ifdef KEEP_ROLL
+ float roll;
+ roll = ang.z;
+ ang.z = 0;
+#endif
+
+ ang = AnglesTransform_ApplyToVAngles(wz.warpzone_transform, ang);
+
+#ifdef KEEP_ROLL
+ ang = AnglesTransform_Normalize(ang, true);
+ ang = AnglesTransform_CancelRoll(ang);
+ ang.z = roll;
+#else
+ ang = AnglesTransform_Normalize(ang, false);
+#endif
+
+ return ang;
+}
+
+vector WarpZone_UnTransformOrigin(entity wz, vector v)
+{
+ return AnglesTransform_Apply(AnglesTransform_Invert(wz.warpzone_transform), v - wz.warpzone_shift);
+}
+
+vector WarpZone_UnTransformVelocity(entity wz, vector v)
+{
+ return AnglesTransform_Apply(AnglesTransform_Invert(wz.warpzone_transform), v);
+}
+
+vector WarpZone_UnTransformAngles(entity wz, vector v)
+{
+ return AnglesTransform_ApplyToAngles(AnglesTransform_Invert(wz.warpzone_transform), v);
+}
+
+vector WarpZone_UnTransformVAngles(entity wz, vector ang)
+{
+ float roll;
+
+ roll = ang.z;
+ ang.z = 0;
+
+ ang = AnglesTransform_ApplyToVAngles(AnglesTransform_Invert(wz.warpzone_transform), ang);
+ ang = AnglesTransform_Normalize(ang, true);
+ ang = AnglesTransform_CancelRoll(ang);
+
+ ang.z = roll;
+ return ang;
+}
+
+vector WarpZoneLib_NearestPointOnBox(vector mi, vector ma, vector org)
+{
+ vector nearest;
+ nearest.x = bound(mi.x, org.x, ma.x);
+ nearest.y = bound(mi.y, org.y, ma.y);
+ nearest.z = bound(mi.z, org.z, ma.z);
+ return nearest;
+}
+
+bool WarpZoneLib_BadEntity(entity e)
+{
+ string myclassname = e.classname;
+ if (e.instanceOfObject) return true;
+ switch(myclassname)
+ {
+ case "deathtype":
+ case "weaponentity":
+ case "exteriorweaponentity":
+ case "csqc_score_team":
+ case "pingplreport":
+ case "ent_client_scoreinfo":
+ case "saved_cvar_value":
+ case "accuracy":
+ case "entcs_sender":
+ case "entcs_receiver":
+ case "clientinit":
+ case "sprite_waypoint":
+ case "waypoint":
+ case "gibsplash":
+ //case "net_linked": // actually some real entities are linked without classname, fail
+ case "":
+ return true;
+ }
+
+ if(startsWith(myclassname, "msg_"))
+ return true;
+
+ if(startsWith(myclassname, "target_"))
+ return true;
+
+ if(startsWith(myclassname, "info_"))
+ return true;
+
+ return false;
+}
+
+.float WarpZone_findradius_hit;
+.entity WarpZone_findradius_next;
+void WarpZone_FindRadius_Recurse(vector org, float rad, vector org0, vector transform, vector shift, float needlineofsight)
+// blast origin of current search original blast origin how to untransform (victim to blast system)
+{
+ vector org_new;
+ vector org0_new;
+ vector shift_new, transform_new;
+ vector p;
+ entity e, e0;
+ entity wz;
+ if(rad <= 0)
+ return;
+ e0 = findradius(org, rad);
+ wz = world;
+
+ for(e = e0; e; e = e.chain)
+ {
+ if(WarpZoneLib_BadEntity(e))
+ continue;
+ p = WarpZoneLib_NearestPointOnBox(e.origin + e.mins, e.origin + e.maxs, org0);
+ if(needlineofsight)
+ {
+ traceline(org, p, MOVE_NOMONSTERS, e);
+ if(trace_fraction < 1)
+ continue;
+ }
+ if(!e.WarpZone_findradius_hit || vlen(e.WarpZone_findradius_dist) > vlen(org0 - p))
+ {
+ e.WarpZone_findradius_nearest = p;
+ e.WarpZone_findradius_dist = org0 - p;
+ e.WarpZone_findradius_findorigin = org;
+ e.WarpZone_findradius_findradius = rad;
+ if(e.classname == "warpzone_refsys")
+ {
+ // ignore, especially: do not overwrite the refsys parameters
+ }
+ else if(e.classname == "trigger_warpzone")
+ {
+ e.WarpZone_findradius_next = wz;
+ wz = e;
+ e.WarpZone_findradius_hit = 1;
+ e.enemy.WarpZone_findradius_dist = '0 0 0'; // we don't want to go through this zone ever again
+ e.enemy.WarpZone_findradius_hit = 1;
+ }
+ else
+ {
+ e.warpzone_transform = transform;
+ e.warpzone_shift = shift;
+ e.WarpZone_findradius_hit = 1;
+ }
+ }
+ }
+ for(e = wz; e; e = e.WarpZone_findradius_next)
+ {
+ if(WarpZoneLib_BadEntity(e))
+ continue;
+
+ org0_new = WarpZone_TransformOrigin(e, org);
+ traceline(e.warpzone_targetorigin, org0_new, MOVE_NOMONSTERS, e);
+ org_new = trace_endpos;
+
+ transform_new = AnglesTransform_Multiply(e.warpzone_transform, transform);
+ shift_new = AnglesTransform_Multiply_GetPostShift(e.warpzone_transform, e.warpzone_shift, transform, shift);
+ WarpZone_FindRadius_Recurse(
+ org_new,
+ bound(0, rad - vlen(org_new - org0_new), rad - 8),
+ org0_new,
+ transform_new, shift_new,
+ needlineofsight);
+ e.WarpZone_findradius_hit = 0;
+ e.enemy.WarpZone_findradius_hit = 0;
+ }
+}
+entity WarpZone_FindRadius(vector org, float rad, float needlineofsight)
+{
+ entity e0, e;
+ WarpZone_FindRadius_Recurse(org, rad, org, '0 0 0', '0 0 0', needlineofsight);
+ e0 = findchainfloat(WarpZone_findradius_hit, 1);
+ for(e = e0; e; e = e.chain)
+ e.WarpZone_findradius_hit = 0;
+ return e0;
+}
+
+.entity WarpZone_refsys;
+void WarpZone_RefSys_GC()
+{SELFPARAM();
+ // garbage collect unused reference systems
+ self.nextthink = time + 1;
+ if(self.owner.WarpZone_refsys != self)
+ remove(self);
+}
+void WarpZone_RefSys_CheckCreate(entity me)
+{
+ if(me.WarpZone_refsys.owner != me)
+ {
+ me.WarpZone_refsys = spawn();
+ me.WarpZone_refsys.classname = "warpzone_refsys";
+ me.WarpZone_refsys.owner = me;
+ me.WarpZone_refsys.think = WarpZone_RefSys_GC;
+ me.WarpZone_refsys.nextthink = time + 1;
+ WarpZone_Accumulator_Clear(me.WarpZone_refsys);
+ }
+}
+void WarpZone_RefSys_Clear(entity me)
+{
+ if(me.WarpZone_refsys)
+ {
+ remove(me.WarpZone_refsys);
+ me.WarpZone_refsys = world;
+ }
+}
+void WarpZone_RefSys_AddTransform(entity me, vector t, vector s)
+{
+ if(t != '0 0 0' || s != '0 0 0')
+ {
+ WarpZone_RefSys_CheckCreate(me);
+ WarpZone_Accumulator_AddTransform(me.WarpZone_refsys, t, s);
+ }
+}
+void WarpZone_RefSys_Add(entity me, entity wz)
+{
+ WarpZone_RefSys_AddTransform(me, wz.warpzone_transform, wz.warpzone_shift);
+}
+void WarpZone_RefSys_AddInverseTransform(entity me, vector t, vector s)
+{
+ if(t != '0 0 0' || s != '0 0 0')
+ {
+ WarpZone_RefSys_CheckCreate(me);
+ WarpZone_Accumulator_AddInverseTransform(me.WarpZone_refsys, t, s);
+ }
+}
+void WarpZone_RefSys_AddInverse(entity me, entity wz)
+{
+ WarpZone_RefSys_AddInverseTransform(me, wz.warpzone_transform, wz.warpzone_shift);
+}
+.vector WarpZone_refsys_incremental_shift;
+.vector WarpZone_refsys_incremental_transform;
+void WarpZone_RefSys_AddIncrementally(entity me, entity ref)
+{
+ //vector t, s;
+ if(me.WarpZone_refsys_incremental_transform == ref.WarpZone_refsys.warpzone_transform)
+ if(me.WarpZone_refsys_incremental_shift == ref.WarpZone_refsys.warpzone_shift)
+ return;
+ WarpZone_Accumulator_AddInverseTransform(me.WarpZone_refsys, me.WarpZone_refsys_incremental_transform, me.WarpZone_refsys_incremental_shift);
+ WarpZone_Accumulator_Add(me.WarpZone_refsys, ref.WarpZone_refsys);
+ me.WarpZone_refsys_incremental_shift = ref.WarpZone_refsys.warpzone_shift;
+ me.WarpZone_refsys_incremental_transform = ref.WarpZone_refsys.warpzone_transform;
+}
+void WarpZone_RefSys_BeginAddingIncrementally(entity me, entity ref)
+{
+ me.WarpZone_refsys_incremental_shift = ref.WarpZone_refsys.warpzone_shift;
+ me.WarpZone_refsys_incremental_transform = ref.WarpZone_refsys.warpzone_transform;
+}
+vector WarpZone_RefSys_TransformOrigin(entity from, entity to, vector org)
+{
+ if(from.WarpZone_refsys)
+ org = WarpZone_UnTransformOrigin(from.WarpZone_refsys, org);
+ if(to.WarpZone_refsys)
+ org = WarpZone_TransformOrigin(to.WarpZone_refsys, org);
+ return org;
+}
+vector WarpZone_RefSys_TransformVelocity(entity from, entity to, vector vel)
+{
+ if(from.WarpZone_refsys)
+ vel = WarpZone_UnTransformVelocity(from.WarpZone_refsys, vel);
+ if(to.WarpZone_refsys)
+ vel = WarpZone_TransformVelocity(to.WarpZone_refsys, vel);
+ return vel;
+}
+vector WarpZone_RefSys_TransformAngles(entity from, entity to, vector ang)
+{
+ if(from.WarpZone_refsys)
+ ang = WarpZone_UnTransformAngles(from.WarpZone_refsys, ang);
+ if(to.WarpZone_refsys)
+ ang = WarpZone_TransformAngles(to.WarpZone_refsys, ang);
+ return ang;
+}
+vector WarpZone_RefSys_TransformVAngles(entity from, entity to, vector ang)
+{
+ if(from.WarpZone_refsys)
+ ang = WarpZone_UnTransformVAngles(from.WarpZone_refsys, ang);
+ if(to.WarpZone_refsys)
+ ang = WarpZone_TransformVAngles(to.WarpZone_refsys, ang);
+ return ang;
+}
+void WarpZone_RefSys_Copy(entity me, entity from)
+{
+ if(from.WarpZone_refsys)
+ {
+ WarpZone_RefSys_CheckCreate(me);
+ me.WarpZone_refsys.warpzone_shift = from.WarpZone_refsys.warpzone_shift;
+ me.WarpZone_refsys.warpzone_transform = from.WarpZone_refsys.warpzone_transform;
+ }
+ else
+ WarpZone_RefSys_Clear(me);
+}
+entity WarpZone_RefSys_SpawnSameRefSys(entity me)
+{
+ entity e;
+ e = spawn();
+ WarpZone_RefSys_Copy(e, me);
+ return e;
+}
+
+float WarpZoneLib_ExactTrigger_Touch()
+{SELFPARAM();
+ return !WarpZoneLib_BoxTouchesBrush(other.absmin, other.absmax, self, other);
+}
+
+
+void WarpZoneLib_MoveOutOfSolid_Expand(entity e, vector by)
+{
+ float eps = 0.0625;
+ tracebox(e.origin, e.mins - '1 1 1' * eps, e.maxs + '1 1 1' * eps, e.origin + by, MOVE_WORLDONLY, e);
+ if (trace_startsolid)
+ return;
+ if (trace_fraction < 1)
+ {
+ // hit something
+ // adjust origin in the other direction...
+ setorigin(e,e.origin - by * (1 - trace_fraction));
+ }
+}
+
+float WarpZoneLib_MoveOutOfSolid(entity e)
+{
+ vector o, m0, m1;
+
+ o = e.origin;
+ traceline(o, o, MOVE_WORLDONLY, e);
+ if (trace_startsolid)
+ return false;
+
+ tracebox(o, e.mins, e.maxs, o, MOVE_WORLDONLY, e);
+ if (!trace_startsolid)
+ return true;
+
+ m0 = e.mins;
+ m1 = e.maxs;
+ e.mins = '0 0 0';
+ e.maxs = '0 0 0';
+ WarpZoneLib_MoveOutOfSolid_Expand(e, '1 0 0' * m0_x);
+ e.mins_x = m0_x;
+ WarpZoneLib_MoveOutOfSolid_Expand(e, '1 0 0' * m1_x);
+ e.maxs_x = m1_x;
+ WarpZoneLib_MoveOutOfSolid_Expand(e, '0 1 0' * m0_y);
+ e.mins_y = m0_y;
+ WarpZoneLib_MoveOutOfSolid_Expand(e, '0 1 0' * m1_y);
+ e.maxs_y = m1_y;
+ WarpZoneLib_MoveOutOfSolid_Expand(e, '0 0 1' * m0_z);
+ e.mins_z = m0_z;
+ WarpZoneLib_MoveOutOfSolid_Expand(e, '0 0 1' * m1_z);
+ e.maxs_z = m1_z;
+ setorigin(e, e.origin);
+
+ tracebox(e.origin, e.mins, e.maxs, e.origin, MOVE_WORLDONLY, e);
+ if (trace_startsolid)
+ {
+ setorigin(e, o);
+ return false;
+ }
+
+ return true;
+}
--- /dev/null
+#ifndef LIB_WARPZONE_COMMON_H
+#define LIB_WARPZONE_COMMON_H
+
+// uncomment this if your mod uses the roll angle in fixangle
+// #define KEEP_ROLL
+
+float warpzone_warpzones_exist;
+float warpzone_cameras_exist;
+
+.float warpzone_isboxy;
+.vector warpzone_shift;
+.vector warpzone_origin;
+.vector warpzone_angles;
+.vector warpzone_forward;
+.vector warpzone_targetorigin;
+.vector warpzone_targetangles;
+.vector warpzone_targetforward;
+.vector warpzone_transform;
+.float warpzone_fadestart;
+.float warpzone_fadeend;
+void WarpZone_SetUp(entity e, vector my_org, vector my_ang, vector other_org, vector other_ang);
+void WarpZone_Camera_SetUp(entity e, vector my_org, vector my_ang);
+
+float WarpZoneLib_BoxTouchesBrush(vector mi, vector ma, entity e, entity ig);
+vector WarpZoneLib_NearestPointOnBox(vector mi, vector ma, vector org);
+
+entity WarpZone_Find(vector mi, vector ma);
+void WarpZone_MakeAllSolid();
+void WarpZone_MakeAllOther();
+
+#define MOVE_NOTHING -1
+entity WarpZone_trace_forent; // temp, callback is allowed to change it
+typedef void(vector start, vector hit, vector end) WarpZone_trace_callback_t; // called on every elementary trace
+var WarpZone_trace_callback_t WarpZone_trace_callback_t_null;
+entity WarpZone_trace_transform; // transform accumulator during a trace
+entity WarpZone_trace_firstzone; // first warpzone hit by a trace (can differ from the requested zone in case of _ThroughZone, the trace is aborted then)
+entity WarpZone_trace_lastzone; // first warpzone hit by a trace (can differ from the requested zone in case of _ThroughZone, the trace is aborted then)
+vector WarpZone_tracetoss_velocity; // ending velocity of a tracetoss (post-transform)
+float WarpZone_tracetoss_time; // duration of toss (approximate)
+void WarpZone_TraceBox(vector org, vector min, vector max, vector end, float nomonsters, entity forent);
+void WarpZone_TraceBox_ThroughZone(vector org, vector min, vector max, vector end, float nomonsters, entity forent, entity zone, WarpZone_trace_callback_t cb);
+void WarpZone_TraceLine(vector org, vector end, float nomonsters, entity forent);
+void WarpZone_TraceToss(entity e, entity forent);
+void WarpZone_TraceToss_ThroughZone(entity e, entity forent, entity zone, WarpZone_trace_callback_t cb);
+void WarpZone_TrailParticles(entity own, float eff, vector org, vector end);
+#ifdef CSQC
+void WarpZone_TrailParticles_WithMultiplier(entity own, float eff, vector org, vector end, float f, float boxflags);
+#endif
+
+.vector WarpZone_findradius_dist;
+.vector WarpZone_findradius_nearest;
+// also set: warpzone parameters, so WarpZone_TransformOrigin can transform vectors from blast's to victim's system
+.vector WarpZone_findradius_findorigin;
+.float WarpZone_findradius_findradius;
+entity WarpZone_FindRadius(vector org, float radius, float needlineofsight);
+
+float WarpZone_PlaneDist(entity wz, vector v);
+float WarpZone_TargetPlaneDist(entity wz, vector v);
+vector WarpZone_TransformOrigin(entity wz, vector v);
+vector WarpZone_TransformVelocity(entity wz, vector v);
+vector WarpZone_TransformAngles(entity wz, vector v);
+vector WarpZone_TransformVAngles(entity wz, vector v);
+vector WarpZone_UnTransformOrigin(entity wz, vector v);
+vector WarpZone_UnTransformVelocity(entity wz, vector v);
+vector WarpZone_UnTransformAngles(entity wz, vector v);
+vector WarpZone_UnTransformVAngles(entity wz, vector v);
+
+// reference systems (chained warpzone transforms)
+void WarpZone_RefSys_Clear(entity me); // R := id
+void WarpZone_RefSys_Add(entity me, entity wz); // me.R := wz me.R
+void WarpZone_RefSys_AddInverse(entity me, entity wz); // me.R := wz^-1 me.R
+void WarpZone_RefSys_AddTransform(entity me, vector t, vector s); // me.R := [t s] me.R
+void WarpZone_RefSys_AddInverseTransform(entity me, vector t, vector s); // me.R := [t s]^-1 me.R
+
+// makes this reference system track ref's changes
+// NOTE: this is ONLY sensible if WarpZone_RefSys_Add is no longer called on "me" while doing this
+// To achieve this, make sure no touch events on warpzone are raised by this entity
+// or set a movetype that causes no warpzoning (e.g. MOVETYPE_NONE, MOVETYPE_FOLLOW)
+void WarpZone_RefSys_AddIncrementally(entity me, entity ref); // me.R := ref.R me.Rref^-1 me.R; me.Rref := ref.R
+void WarpZone_RefSys_BeginAddingIncrementally(entity me, entity ref); // me.Rref := ref.R
+
+vector WarpZone_RefSys_TransformOrigin(entity from, entity to, vector org); // return to.R from.R^-1 org
+vector WarpZone_RefSys_TransformVelocity(entity from, entity to, vector vel); // return to.R from.R^-1 vel
+vector WarpZone_RefSys_TransformAngles(entity from, entity to, vector ang); // return to.R from.R^-1 ang
+vector WarpZone_RefSys_TransformVAngles(entity from, entity to, vector ang); // return to.R from.R^-1 ang
+void WarpZone_RefSys_Copy(entity me, entity from); // to.R := from.R
+entity WarpZone_RefSys_SpawnSameRefSys(entity me); // spawn().R = me.R
+
+#ifndef BITCLR
+# define BITCLR(a,b) ((a) - ((a) & (b)))
+#endif
+#ifndef BITSET
+# define BITSET(a,b) ((a) | (b))
+#endif
+#ifndef BITXOR
+# define BITXOR(a,b) (((a) | (b)) - ((a) & (b)))
+#endif
+#ifndef BITCLR_ASSIGN
+# define BITCLR_ASSIGN(a,b) ((a) = (a) - ((a) & (b)))
+#endif
+#ifndef BITSET_ASSIGN
+# define BITSET_ASSIGN(a,b) ((a) |= (b))
+#endif
+#ifndef BITXOR_ASSIGN
+# define BITXOR_ASSIGN(a,b) ((a) = ((a) | (b)) - ((a) & (b)))
+#endif
+float WarpZoneLib_MoveOutOfSolid(entity e);
+#define move_out_of_solid(e) WarpZoneLib_MoveOutOfSolid(e)
+
+float WarpZoneLib_ExactTrigger_Touch();
+void WarpZoneLib_ExactTrigger_Init();
+
+// WARNING: this kills the trace globals
+#define EXACTTRIGGER_TOUCH if(WarpZoneLib_ExactTrigger_Touch()) return
+#define EXACTTRIGGER_INIT WarpZoneLib_ExactTrigger_Init()
+#endif
--- /dev/null
+#include "mathlib.qh"
+#if defined(CSQC)
+#elif defined(MENUQC)
+#elif defined(SVQC)
+#endif
+
+int fpclassify(float x)
+{
+ if(isnan(x))
+ return FP_NAN;
+ if(isinf(x))
+ return FP_INFINITE;
+ if(x == 0)
+ return FP_ZERO;
+ return FP_NORMAL;
+}
+bool isfinite(float x)
+{
+ return !(isnan(x) || isinf(x));
+}
+bool isinf(float x)
+{
+ return (x != 0) && (x + x == x);
+}
+bool isnan(float x)
+{
+ float y;
+ y = x;
+ return (x != y);
+}
+bool isnormal(float x)
+{
+ return isfinite(x);
+}
+bool signbit(float x)
+{
+ return (x < 0);
+}
+
+float acosh(float x)
+{
+ return log(x + sqrt(x*x - 1));
+}
+float asinh(float x)
+{
+ return log(x + sqrt(x*x + 1));
+}
+float atanh(float x)
+{
+ return 0.5 * log((1+x) / (1-x));
+}
+float cosh(float x)
+{
+ return 0.5 * (exp(x) + exp(-x));
+}
+float sinh(float x)
+{
+ return 0.5 * (exp(x) - exp(-x));
+}
+float tanh(float x)
+{
+ return sinh(x) / cosh(x);
+}
+
+float exp(float x)
+{
+ return pow(M_E, x);
+}
+float exp2(float x)
+{
+ return pow(2, x);
+}
+float expm1(float x)
+{
+ return exp(x) - 1;
+}
+
+vector frexp(float x)
+{
+ vector v;
+ v.z = 0;
+ v.y = ilogb(x) + 1;
+ v.x = x / exp2(v.y);
+ return v;
+}
+int ilogb(float x)
+{
+ return floor(log2(fabs(x)));
+}
+float ldexp(float x, int e)
+{
+ return x * pow(2, e);
+}
+float logn(float x, float base)
+{
+ return log(x) / log(base);
+}
+float log10(float x)
+{
+ return log(x) * M_LOG10E;
+}
+float log1p(float x)
+{
+ return log(x + 1);
+}
+float log2(float x)
+{
+ return log(x) * M_LOG2E;
+}
+float logb(float x)
+{
+ return floor(log2(fabs(x)));
+}
+vector modf(float f)
+{
+ return '1 0 0' * (f - trunc(f)) + '0 1 0' * trunc(f);
+}
+
+float scalbn(float x, int n)
+{
+ return x * pow(2, n);
+}
+
+float cbrt(float x)
+{
+ return copysign(pow(fabs(x), 1.0/3.0), x);
+}
+float hypot(float x, float y)
+{
+ return sqrt(x*x + y*y);
+}
+
+float erf(float x)
+{
+ // approximation taken from wikipedia
+ float y;
+ y = x*x;
+ return copysign(sqrt(1 - exp(-y * (1.273239544735163 + 0.14001228868667 * y) / (1 + 0.14001228868667 * y))), x);
+}
+float erfc(float x)
+{
+ return 1.0 - erf(x);
+}
+vector lgamma(float x)
+{
+ // TODO improve accuracy
+ if(!isfinite(x))
+ return fabs(x) * '1 0 0' + copysign(1, x) * '0 1 0';
+ if(x < 1 && x == floor(x))
+ return nan("gamma") * '1 1 1';
+ if(x < 0.1)
+ {
+ vector v;
+ v = lgamma(1.0 - x);
+ // reflection formula:
+ // gamma(1-z) * gamma(z) = pi / sin(pi*z)
+ // lgamma(1-z) + lgamma(z) = log(pi) - log(sin(pi*z))
+ // sign of gamma(1-z) = sign of gamma(z) * sign of sin(pi*z)
+ v.z = sin(M_PI * x);
+ v.x = log(M_PI) - log(fabs(v.z)) - v.x;
+ if(v.z < 0)
+ v.y = -v.y;
+ v.z = 0;
+ return v;
+ }
+ if(x < 1.1)
+ return lgamma(x + 1) - log(x) * '1 0 0';
+ x -= 1;
+ return (0.5 * log(2 * M_PI * x) + x * (log(x) - 1)) * '1 0 0' + '0 1 0';
+}
+float tgamma(float x)
+{
+ vector v;
+ v = lgamma(x);
+ return exp(v.x) * v.y;
+}
+
+/**
+ * Pythonic mod:
+ * TODO: %% operator?
+ *
+ * 1 % 2 == 1
+ * -1 % 2 == 1
+ * 1 % -2 == -1
+ * -1 % -2 == -1
+ */
+float pymod(float x, float y)
+{
+ return x - y * floor(x / y);
+}
+
+float nearbyint(float x)
+{
+ return rint(x);
+}
+float trunc(float x)
+{
+ return (x>=0) ? floor(x) : ceil(x);
+}
+
+float fmod(float x, float y)
+{
+ return x - y * trunc(x / y);
+}
+float remainder(float x, float y)
+{
+ return x - y * rint(x / y);
+}
+vector remquo(float x, float y)
+{
+ vector v;
+ v.z = 0;
+ v.y = rint(x / y);
+ v.x = x - y * v.y;
+ return v;
+}
+
+float copysign(float x, float y)
+{
+ return fabs(x) * ((y>0) ? 1 : -1);
+}
+float nan(string tag)
+{
+ return sqrt(-1);
+}
+float nextafter(float x, float y)
+{
+ // TODO very crude
+ if(x == y)
+ return nan("nextafter");
+ if(x > y)
+ return -nextafter(-x, -y);
+ // now we know that x < y
+ // so we need the next number > x
+ float d, a, b;
+ d = max(fabs(x), 0.00000000000000000000001);
+ a = x + d;
+ do
+ {
+ d *= 0.5;
+ b = a;
+ a = x + d;
+ }
+ while(a != x);
+ return b;
+}
+float nexttoward(float x, float y)
+{
+ return nextafter(x, y);
+}
+
+float fdim(float x, float y)
+{
+ return max(x-y, 0);
+}
+float fmax(float x, float y)
+{
+ return max(x, y);
+}
+float fmin(float x, float y)
+{
+ return min(x, y);
+}
+float fma(float x, float y, float z)
+{
+ return x * y + z;
+}
+
+int isgreater(float x, float y)
+{
+ return x > y;
+}
+int isgreaterequal(float x, float y)
+{
+ return x >= y;
+}
+int isless(float x, float y)
+{
+ return x < y;
+}
+int islessequal(float x, float y)
+{
+ return x <= y;
+}
+int islessgreater(float x, float y)
+{
+ return x < y || x > y;
+}
+int isunordered(float x, float y)
+{
+ return !(x < y || x == y || x > y);
+}
+
+vector cross(vector a, vector b)
+{
+ return
+ '1 0 0' * (a.y * b.z - a.z * b.y)
+ + '0 1 0' * (a.z * b.x - a.x * b.z)
+ + '0 0 1' * (a.x * b.y - a.y * b.x);
+}
--- /dev/null
+#ifndef LIB_WARPZONE_MATHLIB_H
+#define LIB_WARPZONE_MATHLIB_H
+
+// <math.h>
+
+// The commented-out functions need no implementation because DarkPlaces offers
+// them as builtins. They are listed here anyway for completeness sake.
+
+const int FP_NAN = 0;
+const int FP_INFINITE = 1;
+const int FP_ZERO = 2;
+const int FP_SUBNORMAL = 3;
+const int FP_NORMAL = 4;
+int fpclassify(float x);
+bool isfinite(float x);
+bool isinf(float x);
+bool isnan(float x);
+bool isnormal(float x);
+bool signbit(float x);
+
+//float acos(float x);
+//float asin(float x);
+//float atan(float x);
+//float atan2(float y, float x);
+//float cos(float x);
+//float sin(float x);
+//float tan(float x);
+
+float acosh(float x);
+float asinh(float x);
+float atanh(float x);
+float cosh(float x);
+float sinh(float x);
+float tanh(float x);
+
+float exp(float x);
+float exp2(float x);
+float expm1(float x);
+
+vector frexp(float x); // returns mantissa as _x, exponent as _y
+int ilogb(float x);
+float ldexp(float x, int e);
+//float log(float x);
+float logn(float x, float base);
+float log10(float x);
+float log1p(float x);
+float log2(float x);
+float logb(float x);
+vector modf(float f); // fraction as _x, integer as _y
+
+float scalbn(float x, int n);
+
+float cbrt(float x);
+//float fabs(float x);
+float hypot(float x, float y);
+//float pow(float x, float y);
+//float sqrt(float x, float y);
+
+float erf(float x);
+float erfc(float x);
+vector lgamma(float x); // value in _x, sign in _y
+float tgamma(float x);
+
+/**
+ * Pythonic mod:
+ * TODO: %% operator?
+ *
+ * 1 % 2 == 1
+ * -1 % 2 == 1
+ * 1 % -2 == -1
+ * -1 % -2 == -1
+ */
+float pymod(float x, float y);
+
+//float ceil(float x);
+//float floor(float x);
+float nearbyint(float x);
+//float rint(float x);
+//float round(float x);
+float trunc(float x);
+
+float fmod(float x, float y);
+float remainder(float x, float y);
+vector remquo(float x, float y);
+
+float copysign(float x, float y);
+float nan(string tag);
+float nextafter(float x, float y);
+float nexttoward(float x, float y);
+
+float fdim(float x, float y);
+float fmax(float x, float y);
+float fmin(float x, float y);
+float fma(float x, float y, float z);
+
+int isgreater(float x, float y);
+int isgreaterequal(float x, float y);
+int isless(float x, float y);
+int islessequal(float x, float y);
+int islessgreater(float x, float y);
+int isunordered(float x, float y);
+
+const float M_E = 2.7182818284590452354; /* e */
+const float M_LOG2E = 1.4426950408889634074; /* log_2 e */
+const float M_LOG10E = 0.43429448190325182765; /* log_10 e */
+const float M_LN2 = 0.69314718055994530942; /* log_e 2 */
+const float M_LN10 = 2.30258509299404568402; /* log_e 10 */
+// -Wdouble-declaration
+#define M_PI 3.14159265358979323846 /* pi */
+const float M_PI_2 = 1.57079632679489661923; /* pi/2 */
+const float M_PI_4 = 0.78539816339744830962; /* pi/4 */
+const float M_1_PI = 0.31830988618379067154; /* 1/pi */
+const float M_2_PI = 0.63661977236758134308; /* 2/pi */
+const float M_2_SQRTPI = 1.12837916709551257390; /* 2/sqrt(pi) */
+const float M_SQRT2 = 1.41421356237309504880; /* sqrt(2) */
+const float M_SQRT1_2 = 0.70710678118654752440; /* 1/sqrt(2) */
+
+// Non-<math.h> stuff follows here.
+vector cross(vector a, vector b);
+
+#endif
--- /dev/null
+#include "server.qh"
+
+#include "common.qh"
+#if defined(CSQC)
+#elif defined(MENUQC)
+#elif defined(SVQC)
+ #include "../../common/constants.qh"
+ #include "../../common/triggers/subs.qh"
+ #include "../../common/util.qh"
+ #include "../../server/command/common.qh"
+ #include "../../server/constants.qh"
+ #include "../../server/defs.qh"
+#endif
+
+#ifdef WARPZONELIB_KEEPDEBUG
+#define WARPZONELIB_REMOVEHACK
+#endif
+
+// for think function
+.vector warpzone_save_origin;
+.vector warpzone_save_angles;
+.vector warpzone_save_eorigin;
+.vector warpzone_save_eangles;
+
+// for all entities
+.vector warpzone_oldorigin, warpzone_oldvelocity, warpzone_oldangles;
+.float warpzone_teleport_time;
+.float warpzone_teleport_finishtime;
+.entity warpzone_teleport_zone;
+
+void WarpZone_StoreProjectileData(entity e)
+{
+ e.warpzone_oldorigin = e.origin;
+ e.warpzone_oldvelocity = e.velocity;
+ e.warpzone_oldangles = e.angles;
+}
+
+void WarpZone_TeleportPlayer(entity teleporter, entity player, vector to, vector to_angles, vector to_velocity)
+{
+ setorigin (player, to); // NOTE: this also aborts the move, when this is called by touch
+ player.oldorigin = to; // for DP's unsticking
+ player.angles = to_angles;
+ player.fixangle = true;
+ player.velocity = to_velocity;
+
+ BITXOR_ASSIGN(player.effects, EF_TELEPORT_BIT);
+
+ if(IS_PLAYER(player))
+ BITCLR_ASSIGN(player.flags, FL_ONGROUND);
+
+ WarpZone_PostTeleportPlayer_Callback(player);
+}
+
+bool WarpZone_Teleported_Send(entity to, int sf)
+{SELFPARAM();
+ WriteByte(MSG_ENTITY, ENT_CLIENT_WARPZONE_TELEPORTED);
+ WriteCoord(MSG_ENTITY, self.angles.x);
+ WriteCoord(MSG_ENTITY, self.angles.y);
+ WriteCoord(MSG_ENTITY, self.angles.z);
+ return true;
+}
+
+float WarpZone_Teleport(entity wz, entity player, float f0, float f1)
+{
+ vector o0, a0, v0, o1, a1, v1, o10;
+
+ o0 = player.origin + player.view_ofs;
+ v0 = player.velocity;
+ a0 = player.angles;
+
+ o10 = o1 = WarpZone_TransformOrigin(wz, o0);
+ v1 = WarpZone_TransformVelocity(wz, v0);
+ if (!IS_NOT_A_CLIENT(player))
+ a1 = WarpZone_TransformVAngles(wz, player.v_angle);
+ else
+ a1 = WarpZone_TransformAngles(wz, a0);
+
+ if(f0 != 0 || f1 != 0)
+ {
+ // retry last move but behind the warpzone!
+ // we must first go back as far as we can, then forward again, to not cause double touch events!
+
+ tracebox(o1 - player.view_ofs + v1 * frametime * f1, player.mins, player.maxs, o1 - player.view_ofs + v1 * frametime * f0, MOVE_WORLDONLY, player);
+ {
+ entity own;
+ own = player.owner;
+ player.owner = world;
+ tracebox(trace_endpos, player.mins, player.maxs, o1 - player.view_ofs + v1 * frametime * f1, MOVE_NORMAL, player); // this should get us through the warpzone
+ player.owner = own;
+ }
+ o1 = trace_endpos + player.view_ofs;
+
+ float d, dv, md;
+ md = max(vlen(player.mins), vlen(player.maxs));
+ d = WarpZone_TargetPlaneDist(wz, o1);
+ dv = WarpZone_TargetPlaneDist(wz, v1);
+ if(d < 0)
+ o1 = o1 - v1 * (d / dv);
+ }
+
+ // put him out of solid
+ tracebox(o1 - player.view_ofs, player.mins, player.maxs, o1 - player.view_ofs, MOVE_NOMONSTERS, player);
+ if(trace_startsolid)
+ {
+ setorigin(player, o1 - player.view_ofs);
+ if(WarpZoneLib_MoveOutOfSolid(player))
+ {
+ o1 = player.origin + player.view_ofs;
+ setorigin(player, o0 - player.view_ofs);
+ }
+ else
+ {
+ LOG_INFO("would have to put player in solid, won't do that\n");
+ setorigin(player, o0 - player.view_ofs);
+ return 0;
+ }
+ }
+
+ // do the teleport
+ WarpZone_RefSys_Add(player, wz);
+ WarpZone_TeleportPlayer(wz, player, o1 - player.view_ofs, a1, v1);
+ WarpZone_StoreProjectileData(player);
+ player.warpzone_teleport_time = time;
+ player.warpzone_teleport_finishtime = time;
+ player.warpzone_teleport_zone = wz;
+
+ // prevent further teleports back
+ float dt = (o1 - o10) * v1 * (1 / (v1 * v1));
+ if(dt < sys_frametime)
+ player.warpzone_teleport_finishtime += sys_frametime - dt;
+
+#ifndef WARPZONE_USE_FIXANGLE
+ if(IS_VEHICLE(player) && player.owner)
+ player = player.owner; // hax
+ if(IS_PLAYER(player))
+ {
+ // instead of fixangle, send the transform to the client for smoother operation
+ player.fixangle = false;
+
+ entity ts = spawn();
+ setmodel(ts, MDL_Null);
+ ts.SendEntity = WarpZone_Teleported_Send;
+ ts.SendFlags = 0xFFFFFF;
+ ts.drawonlytoclient = player;
+ ts.think = SUB_Remove;
+ ts.nextthink = time + 1;
+ ts.owner = player;
+ ts.enemy = wz;
+ ts.effects = EF_NODEPTHTEST;
+ ts.classname = "warpzone_teleported";
+ ts.angles = wz.warpzone_transform;
+ }
+#endif
+
+ return 1;
+}
+
+void WarpZone_Touch (void)
+{SELFPARAM();
+ if(other.classname == "trigger_warpzone")
+ return;
+
+ if(time <= other.warpzone_teleport_finishtime) // already teleported this frame
+ return;
+
+ // FIXME needs a better check to know what is safe to teleport and what not
+ if(other.movetype == MOVETYPE_NONE || other.movetype == MOVETYPE_FOLLOW || other.tag_entity)
+ return;
+
+ if(WarpZoneLib_ExactTrigger_Touch())
+ return;
+
+ if(WarpZone_PlaneDist(self, other.origin + other.view_ofs) >= 0) // wrong side of the trigger_warpzone (don't teleport yet)
+ return;
+
+ float f;
+ // number of frames we need to go back:
+ // dist = 16*sqrt(2) qu
+ // dist ~ 24 qu
+ // 24 qu = v*t
+ // 24 qu = v*frametime*n
+ // n = 24 qu/(v*frametime)
+ // for clients go only one frame though, may be too irritating otherwise
+ // but max 0.25 sec = 0.25/frametime frames
+ // 24/(0.25/frametime)
+ // 96*frametime
+ float d;
+ d = 24 + max(vlen(other.mins), vlen(other.maxs));
+ if(IS_NOT_A_CLIENT(other))
+ f = -d / bound(frametime * d * 1, frametime * vlen(other.velocity), d);
+ else
+ f = -1;
+ if(WarpZone_Teleport(self, other, f, 0))
+ {
+ string save1, save2;
+ activator = other;
+
+ save1 = self.target; self.target = string_null;
+ save2 = self.target3; self.target3 = string_null;
+ SUB_UseTargets();
+ if (!self.target) self.target = save1;
+ if (!self.target3) self.target3 = save2;
+
+ setself(self.enemy);
+ save1 = self.target; self.target = string_null;
+ save2 = self.target2; self.target2 = string_null;
+ SUB_UseTargets();
+ if (!self.target) self.target = save1;
+ if (!self.target2) self.target2 = save2;
+ setself(this);
+ }
+ else
+ {
+ LOG_TRACE("WARPZONE FAIL AHAHAHAHAH))\n");
+ }
+}
+
+bool WarpZone_Send(entity to, int sendflags)
+{SELFPARAM();
+ WriteByte(MSG_ENTITY, ENT_CLIENT_WARPZONE);
+
+ // we must send this flag for clientside to match properly too
+ int f = 0;
+ if(self.warpzone_isboxy)
+ BITSET_ASSIGN(f, 1);
+ if(self.warpzone_fadestart)
+ BITSET_ASSIGN(f, 2);
+ if(self.origin != '0 0 0')
+ BITSET_ASSIGN(f, 4);
+ WriteByte(MSG_ENTITY, f);
+
+ // we need THESE to render the warpzone (and cull properly)...
+ if(f & 4)
+ {
+ WriteCoord(MSG_ENTITY, self.origin.x);
+ WriteCoord(MSG_ENTITY, self.origin.y);
+ WriteCoord(MSG_ENTITY, self.origin.z);
+ }
+
+ WriteShort(MSG_ENTITY, self.modelindex);
+ WriteCoord(MSG_ENTITY, self.mins.x);
+ WriteCoord(MSG_ENTITY, self.mins.y);
+ WriteCoord(MSG_ENTITY, self.mins.z);
+ WriteCoord(MSG_ENTITY, self.maxs.x);
+ WriteCoord(MSG_ENTITY, self.maxs.y);
+ WriteCoord(MSG_ENTITY, self.maxs.z);
+ WriteByte(MSG_ENTITY, bound(1, self.scale * 16, 255));
+
+ // we need THESE to calculate the proper transform
+ WriteCoord(MSG_ENTITY, self.warpzone_origin.x);
+ WriteCoord(MSG_ENTITY, self.warpzone_origin.y);
+ WriteCoord(MSG_ENTITY, self.warpzone_origin.z);
+ WriteCoord(MSG_ENTITY, self.warpzone_angles.x);
+ WriteCoord(MSG_ENTITY, self.warpzone_angles.y);
+ WriteCoord(MSG_ENTITY, self.warpzone_angles.z);
+ WriteCoord(MSG_ENTITY, self.warpzone_targetorigin.x);
+ WriteCoord(MSG_ENTITY, self.warpzone_targetorigin.y);
+ WriteCoord(MSG_ENTITY, self.warpzone_targetorigin.z);
+ WriteCoord(MSG_ENTITY, self.warpzone_targetangles.x);
+ WriteCoord(MSG_ENTITY, self.warpzone_targetangles.y);
+ WriteCoord(MSG_ENTITY, self.warpzone_targetangles.z);
+
+ if(f & 2)
+ {
+ WriteShort(MSG_ENTITY, self.warpzone_fadestart);
+ WriteShort(MSG_ENTITY, self.warpzone_fadeend);
+ }
+
+ return true;
+}
+
+bool WarpZone_Camera_Send(entity to, int sendflags)
+{SELFPARAM();
+ int f = 0;
+ WriteByte(MSG_ENTITY, ENT_CLIENT_WARPZONE_CAMERA);
+
+ if(self.warpzone_fadestart)
+ BITSET_ASSIGN(f, 2);
+ if(self.origin != '0 0 0')
+ BITSET_ASSIGN(f, 4);
+ WriteByte(MSG_ENTITY, f);
+
+ // we need THESE to render the warpzone (and cull properly)...
+ if(f & 4)
+ {
+ WriteCoord(MSG_ENTITY, self.origin.x);
+ WriteCoord(MSG_ENTITY, self.origin.y);
+ WriteCoord(MSG_ENTITY, self.origin.z);
+ }
+
+ WriteShort(MSG_ENTITY, self.modelindex);
+ WriteCoord(MSG_ENTITY, self.mins.x);
+ WriteCoord(MSG_ENTITY, self.mins.y);
+ WriteCoord(MSG_ENTITY, self.mins.z);
+ WriteCoord(MSG_ENTITY, self.maxs.x);
+ WriteCoord(MSG_ENTITY, self.maxs.y);
+ WriteCoord(MSG_ENTITY, self.maxs.z);
+ WriteByte(MSG_ENTITY, bound(1, self.scale * 16, 255));
+
+ // we need THESE to calculate the proper transform
+ WriteCoord(MSG_ENTITY, self.enemy.origin.x);
+ WriteCoord(MSG_ENTITY, self.enemy.origin.y);
+ WriteCoord(MSG_ENTITY, self.enemy.origin.z);
+ WriteCoord(MSG_ENTITY, self.enemy.angles.x);
+ WriteCoord(MSG_ENTITY, self.enemy.angles.y);
+ WriteCoord(MSG_ENTITY, self.enemy.angles.z);
+
+ if(f & 2)
+ {
+ WriteShort(MSG_ENTITY, self.warpzone_fadestart);
+ WriteShort(MSG_ENTITY, self.warpzone_fadeend);
+ }
+
+ return true;
+}
+
+#ifdef WARPZONELIB_KEEPDEBUG
+float WarpZone_CheckProjectileImpact(entity player)
+{SELFPARAM();
+ vector o0, v0;
+
+ o0 = player.origin + player.view_ofs;
+ v0 = player.velocity;
+
+ // if we teleported shortly before, abort
+ if(time <= player.warpzone_teleport_finishtime + 0.1)
+ return 0;
+
+ // if player hit a warpzone, abort
+ entity wz;
+ wz = WarpZone_Find(o0 + player.mins, o0 + player.maxs);
+ if(!wz)
+ return 0;
+
+#ifdef WARPZONELIB_REMOVEHACK
+ LOG_INFO("impactfilter found something - and it no longer gets handled correctly - please tell divVerent whether anything behaves broken now\n");
+#else
+ LOG_INFO("impactfilter found something - and it even gets handled correctly - please tell divVerent that this code apparently gets triggered again\n");
+#endif
+ LOG_INFO("Entity type: ", player.classname, "\n");
+ LOG_INFO("Origin: ", vtos(player.origin), "\n");
+ LOG_INFO("Velocity: ", vtos(player.velocity), "\n");
+
+#ifdef WARPZONELIB_REMOVEHACK
+ return 0;
+#else
+ // retry previous move
+ setorigin(player, player.warpzone_oldorigin);
+ player.velocity = player.warpzone_oldvelocity;
+ if(WarpZone_Teleport(wz, player, 0, 1))
+ {
+ entity oldself;
+ string save1, save2;
+
+ oldself = self;
+ self = wz;
+ other = player;
+ activator = player;
+
+ save1 = self.target; self.target = string_null;
+ save2 = self.target3; self.target3 = string_null;
+ SUB_UseTargets();
+ if (!self.target) self.target = save1;
+ if (!self.target3) self.target3 = save2;
+
+ self = self.enemy;
+ save1 = self.target; self.target = string_null;
+ save2 = self.target2; self.target2 = string_null;
+ SUB_UseTargets();
+ if (!self.target) self.target = save1;
+ if (!self.target2) self.target2 = save2;
+ self = oldself;
+ }
+ else
+ {
+ setorigin(player, o0 - player.view_ofs);
+ player.velocity = v0;
+ }
+
+ return +1;
+#endif
+}
+#endif
+
+float WarpZone_Projectile_Touch()
+{SELFPARAM();
+ if(other.classname == "trigger_warpzone")
+ return true;
+
+ // no further impacts if we teleported this frame!
+ // this is because even if we did teleport, the engine still may raise
+ // touch events for the previous location
+ // engine now aborts moves on teleport, so this SHOULD not happen any more
+ // but if this is called from TouchAreaGrid of the projectile moving,
+ // then this won't do
+ if(time == self.warpzone_teleport_time)
+ return true;
+
+#ifdef WARPZONELIB_KEEPDEBUG
+ // this SEEMS to not happen at the moment, but if it did, it would be more reliable
+ {
+ float save_dpstartcontents;
+ float save_dphitcontents;
+ float save_dphitq3surfaceflags;
+ string save_dphittexturename;
+ float save_allsolid;
+ float save_startsolid;
+ float save_fraction;
+ vector save_endpos;
+ vector save_plane_normal;
+ float save_plane_dist;
+ entity save_ent;
+ float save_inopen;
+ float save_inwater;
+ save_dpstartcontents = trace_dpstartcontents;
+ save_dphitcontents = trace_dphitcontents;
+ save_dphitq3surfaceflags = trace_dphitq3surfaceflags;
+ save_dphittexturename = trace_dphittexturename;
+ save_allsolid = trace_allsolid;
+ save_startsolid = trace_startsolid;
+ save_fraction = trace_fraction;
+ save_endpos = trace_endpos;
+ save_plane_normal = trace_plane_normal;
+ save_plane_dist = trace_plane_dist;
+ save_ent = trace_ent;
+ save_inopen = trace_inopen;
+ save_inwater = trace_inwater;
+ float f;
+ if((f = WarpZone_CheckProjectileImpact(self)) != 0)
+ return (f > 0);
+ trace_dpstartcontents = save_dpstartcontents;
+ trace_dphitcontents = save_dphitcontents;
+ trace_dphitq3surfaceflags = save_dphitq3surfaceflags;
+ trace_dphittexturename = save_dphittexturename;
+ trace_allsolid = save_allsolid;
+ trace_startsolid = save_startsolid;
+ trace_fraction = save_fraction;
+ trace_endpos = save_endpos;
+ trace_plane_normal = save_plane_normal;
+ trace_plane_dist = save_plane_dist;
+ trace_ent = save_ent;
+ trace_inopen = save_inopen;
+ trace_inwater = save_inwater;
+ }
+#endif
+
+ if(WarpZone_Projectile_Touch_ImpactFilter_Callback())
+ return true;
+
+ return false;
+}
+
+void WarpZone_InitStep_FindOriginTarget()
+{SELFPARAM();
+ if(self.killtarget != "")
+ {
+ self.aiment = find(world, targetname, self.killtarget);
+ if(self.aiment == world)
+ {
+ error("Warp zone with nonexisting killtarget");
+ return;
+ }
+ self.killtarget = string_null;
+ }
+}
+
+void WarpZonePosition_InitStep_FindTarget()
+{SELFPARAM();
+ if(self.target == "")
+ {
+ error("Warp zone position with no target");
+ return;
+ }
+ self.enemy = find(world, targetname, self.target);
+ if(self.enemy == world)
+ {
+ error("Warp zone position with nonexisting target");
+ return;
+ }
+ if(self.enemy.aiment)
+ {
+ // already is positioned
+ error("Warp zone position targeting already oriented warpzone");
+ return;
+ }
+ self.enemy.aiment = self;
+}
+
+void WarpZoneCamera_Think(void)
+{SELFPARAM();
+ if(self.warpzone_save_origin != self.origin
+ || self.warpzone_save_angles != self.angles
+ || self.warpzone_save_eorigin != self.enemy.origin
+ || self.warpzone_save_eangles != self.enemy.angles)
+ {
+ WarpZone_Camera_SetUp(self, self.enemy.origin, self.enemy.angles);
+ self.warpzone_save_origin = self.origin;
+ self.warpzone_save_angles = self.angles;
+ self.warpzone_save_eorigin = self.enemy.origin;
+ self.warpzone_save_eangles = self.enemy.angles;
+ }
+ self.nextthink = time;
+}
+
+void WarpZoneCamera_InitStep_FindTarget()
+{SELFPARAM();
+ entity e;
+ float i;
+ if(self.target == "")
+ {
+ error("Camera with no target");
+ return;
+ }
+ self.enemy = world;
+ for(e = world, i = 0; (e = find(e, targetname, self.target)); )
+ if(random() * ++i < 1)
+ self.enemy = e;
+ if(self.enemy == world)
+ {
+ error("Camera with nonexisting target");
+ return;
+ }
+ warpzone_cameras_exist = 1;
+ WarpZone_Camera_SetUp(self, self.enemy.origin, self.enemy.angles);
+ self.SendFlags = 0xFFFFFF;
+ if(self.spawnflags & 1)
+ {
+ self.think = WarpZoneCamera_Think;
+ self.nextthink = time;
+ }
+ else
+ self.nextthink = 0;
+}
+
+void WarpZone_InitStep_UpdateTransform()
+{SELFPARAM();
+ vector org, ang, norm, point;
+ float area;
+ vector tri, a, b, c, n;
+ float i_s, i_t, n_t;
+ string tex;
+
+ org = self.origin;
+ if(org == '0 0 0')
+ org = 0.5 * (self.mins + self.maxs);
+
+ norm = point = '0 0 0';
+ area = 0;
+ for(i_s = 0; ; ++i_s)
+ {
+ tex = getsurfacetexture(self, i_s);
+ if (!tex)
+ break; // this is beyond the last one
+ if(tex == "textures/common/trigger" || tex == "trigger")
+ continue;
+ n_t = getsurfacenumtriangles(self, i_s);
+ for(i_t = 0; i_t < n_t; ++i_t)
+ {
+ tri = getsurfacetriangle(self, i_s, i_t);
+ a = getsurfacepoint(self, i_s, tri.x);
+ b = getsurfacepoint(self, i_s, tri.y);
+ c = getsurfacepoint(self, i_s, tri.z);
+ n = cross(c - a, b - a);
+ area = area + vlen(n);
+ norm = norm + n;
+ point = point + vlen(n) * (a + b + c);
+ }
+ }
+ if(area > 0)
+ {
+ norm = norm * (1 / area);
+ point = point * (1 / (3 * area));
+ if(vlen(norm) < 0.99)
+ {
+ LOG_INFO("trigger_warpzone near ", vtos(self.aiment.origin), " is nonplanar. BEWARE.\n");
+ area = 0; // no autofixing in this case
+ }
+ norm = normalize(norm);
+ }
+
+ ang = '0 0 0';
+ if(self.aiment)
+ {
+ org = self.aiment.origin;
+ ang = self.aiment.angles;
+ if(area > 0)
+ {
+ org = org - ((org - point) * norm) * norm; // project to plane
+ makevectors(ang);
+ if(norm * v_forward < 0)
+ {
+ LOG_INFO("Position target of trigger_warpzone near ", vtos(self.aiment.origin), " points into trigger_warpzone. BEWARE.\n");
+ norm = -1 * norm;
+ }
+ ang = vectoangles2(norm, v_up); // keep rotation, but turn exactly against plane
+ ang.x = -ang.x;
+ if(norm * v_forward < 0.99)
+ LOG_INFO("trigger_warpzone near ", vtos(self.aiment.origin), " has been turned to match plane orientation (", vtos(self.aiment.angles), " -> ", vtos(ang), "\n");
+ if(vlen(org - self.aiment.origin) > 0.5)
+ LOG_INFO("trigger_warpzone near ", vtos(self.aiment.origin), " has been moved to match the plane (", vtos(self.aiment.origin), " -> ", vtos(org), ").\n");
+ }
+ }
+ else if(area > 0)
+ {
+ org = point;
+ ang = vectoangles(norm);
+ ang.x = -ang.x;
+ }
+ else
+ error("cannot infer origin/angles for this warpzone, please use a killtarget or a trigger_warpzone_position");
+
+ self.warpzone_origin = org;
+ self.warpzone_angles = ang;
+}
+
+void WarpZone_InitStep_ClearTarget()
+{SELFPARAM();
+ if(self.enemy)
+ self.enemy.enemy = world;
+ self.enemy = world;
+}
+
+entity warpzone_first; .entity warpzone_next;
+void WarpZone_InitStep_FindTarget()
+{SELFPARAM();
+ float i;
+ entity e, e2;
+
+ if(self.enemy)
+ return;
+
+ // this way only one of the two ents needs to target
+ if(self.target != "")
+ {
+ self.enemy = self; // so the if(!e.enemy) check also skips self, saves one IF
+
+ e2 = world;
+ for(e = world, i = 0; (e = find(e, targetname, self.target)); )
+ if(!e.enemy)
+ if(e.classname == self.classname) // possibly non-warpzones may use the same targetname!
+ if(random() * ++i < 1)
+ e2 = e;
+ if(!e2)
+ {
+ self.enemy = world;
+ error("Warpzone with non-existing target");
+ return;
+ }
+ self.enemy = e2;
+ e2.enemy = self;
+ }
+}
+
+void WarpZone_Think();
+void WarpZone_InitStep_FinalizeTransform()
+{SELFPARAM();
+ if(!self.enemy || self.enemy.enemy != self)
+ {
+ error("Invalid warp zone detected. Killed.");
+ return;
+ }
+
+ warpzone_warpzones_exist = 1;
+ WarpZone_SetUp(self, self.warpzone_origin, self.warpzone_angles, self.enemy.warpzone_origin, self.enemy.warpzone_angles);
+ self.touch = WarpZone_Touch;
+ self.SendFlags = 0xFFFFFF;
+ if(self.spawnflags & 1)
+ {
+ self.think = WarpZone_Think;
+ self.nextthink = time;
+ }
+ else
+ self.nextthink = 0;
+}
+
+float warpzone_initialized;
+//entity warpzone_first;
+entity warpzone_position_first;
+entity warpzone_camera_first;
+.entity warpzone_next;
+spawnfunc(misc_warpzone_position)
+{
+ // "target", "angles", "origin"
+ self.warpzone_next = warpzone_position_first;
+ warpzone_position_first = self;
+}
+spawnfunc(trigger_warpzone_position)
+{
+ spawnfunc_misc_warpzone_position(this);
+}
+spawnfunc(trigger_warpzone)
+{
+ // warp zone entities must have:
+ // "killtarget" pointing to a target_position with a direction arrow
+ // that points AWAY from the warp zone, and that is inside
+ // the warp zone trigger
+ // "target" pointing to an identical warp zone at another place in
+ // the map, with another killtarget to designate its
+ // orientation
+
+ if(!self.scale)
+ self.scale = self.modelscale;
+ if(!self.scale)
+ self.scale = 1;
+ string m;
+ m = self.model;
+ WarpZoneLib_ExactTrigger_Init();
+ if(m != "")
+ {
+ precache_model(m);
+ _setmodel(self, m); // no precision needed
+ }
+ setorigin(self, self.origin);
+ if(self.scale)
+ setsize(self, self.mins * self.scale, self.maxs * self.scale);
+ else
+ setsize(self, self.mins, self.maxs);
+ self.SendEntity = WarpZone_Send;
+ self.SendFlags = 0xFFFFFF;
+ BITSET_ASSIGN(self.effects, EF_NODEPTHTEST);
+ self.warpzone_next = warpzone_first;
+ warpzone_first = self;
+}
+spawnfunc(func_camera)
+{
+ if(!self.scale)
+ self.scale = self.modelscale;
+ if(!self.scale)
+ self.scale = 1;
+ if(self.model != "")
+ {
+ precache_model(self.model);
+ _setmodel(self, self.model); // no precision needed
+ }
+ setorigin(self, self.origin);
+ if(self.scale)
+ setsize(self, self.mins * self.scale, self.maxs * self.scale);
+ else
+ setsize(self, self.mins, self.maxs);
+ if(!self.solid)
+ self.solid = SOLID_BSP;
+ else if(self.solid < 0)
+ self.solid = SOLID_NOT;
+ self.SendEntity = WarpZone_Camera_Send;
+ self.SendFlags = 0xFFFFFF;
+ self.warpzone_next = warpzone_camera_first;
+ warpzone_camera_first = self;
+}
+void WarpZones_Reconnect()
+{SELFPARAM();
+ for(setself(warpzone_first); self; setself(self.warpzone_next))
+ WarpZone_InitStep_ClearTarget();
+ for(setself(warpzone_first); self; setself(self.warpzone_next))
+ WarpZone_InitStep_FindTarget();
+ for(setself(warpzone_camera_first); self; setself(self.warpzone_next))
+ WarpZoneCamera_InitStep_FindTarget();
+ for(setself(warpzone_first); self; setself(self.warpzone_next))
+ WarpZone_InitStep_FinalizeTransform();
+ setself(this);
+}
+
+void WarpZone_Think()
+{SELFPARAM();
+ if(self.warpzone_save_origin != self.origin
+ || self.warpzone_save_angles != self.angles
+ || self.warpzone_save_eorigin != self.enemy.origin
+ || self.warpzone_save_eangles != self.enemy.angles)
+ {
+ WarpZone_InitStep_UpdateTransform();
+ setself(self.enemy);
+ WarpZone_InitStep_UpdateTransform();
+ setself(this);
+ WarpZone_InitStep_FinalizeTransform();
+ setself(self.enemy);
+ WarpZone_InitStep_FinalizeTransform();
+ setself(this);
+ self.warpzone_save_origin = self.origin;
+ self.warpzone_save_angles = self.angles;
+ self.warpzone_save_eorigin = self.enemy.origin;
+ self.warpzone_save_eangles = self.enemy.angles;
+ }
+ self.nextthink = time;
+}
+
+void WarpZone_StartFrame()
+{SELFPARAM();
+ entity e;
+ if(warpzone_initialized == 0)
+ {
+ warpzone_initialized = 1;
+ for(setself(warpzone_first); self; setself(self.warpzone_next))
+ WarpZone_InitStep_FindOriginTarget();
+ for(setself(warpzone_position_first); self; setself(self.warpzone_next))
+ WarpZonePosition_InitStep_FindTarget();
+ for(setself(warpzone_first); self; setself(self.warpzone_next))
+ WarpZone_InitStep_UpdateTransform();
+ setself(this);
+ WarpZones_Reconnect();
+ WarpZone_PostInitialize_Callback();
+ }
+
+ entity oldother;
+ oldother = other;
+ for(e = world; (e = nextent(e)); )
+ {
+ if(warpzone_warpzones_exist) { WarpZone_StoreProjectileData(e); }
+
+ if(IS_REAL_CLIENT(e))
+ {
+ if(e.solid == SOLID_NOT) // not spectating?
+ if(e.movetype == MOVETYPE_NOCLIP || e.movetype == MOVETYPE_FLY || e.movetype == MOVETYPE_FLY_WORLDONLY) // not spectating? (this is to catch observers)
+ {
+ other = e; // player
+
+ // warpzones
+ if(warpzone_warpzones_exist) {
+ setself(WarpZone_Find(e.origin + e.mins, e.origin + e.maxs));
+ if(self)
+ if(!WarpZoneLib_ExactTrigger_Touch())
+ if(WarpZone_PlaneDist(self, e.origin + e.view_ofs) <= 0)
+ WarpZone_Teleport(self, e, -1, 0); } // NOT triggering targets by this!
+
+ // teleporters
+ setself(Teleport_Find(e.origin + e.mins, e.origin + e.maxs));
+ if(self)
+ if(!WarpZoneLib_ExactTrigger_Touch())
+ Simple_TeleportPlayer(self, other); // NOT triggering targets by this!
+ }
+ }
+
+ if(IS_NOT_A_CLIENT(e))
+ {
+ if(warpzone_warpzones_exist)
+ for (; (e = nextent(e)); )
+ WarpZone_StoreProjectileData(e);
+ break;
+ }
+ }
+ setself(this);
+ other = oldother;
+}
+
+.float warpzone_reconnecting;
+float visible_to_some_client(entity ent)
+{
+ entity e;
+ for(e = nextent(world); !IS_NOT_A_CLIENT(e); e = nextent(e))
+ if(IS_PLAYER(e) && IS_REAL_CLIENT(e))
+ if(checkpvs(e.origin + e.view_ofs, ent))
+ return 1;
+ return 0;
+}
+void trigger_warpzone_reconnect_use()
+{SELFPARAM();
+ entity e;
+ e = self;
+ // NOTE: this matches for target, not targetname, but of course
+ // targetname must be set too on the other entities
+ for(setself(warpzone_first); self; setself(self.warpzone_next))
+ self.warpzone_reconnecting = ((e.target == "" || self.target == e.target) && !((e.spawnflags & 1) && (visible_to_some_client(self) || visible_to_some_client(self.enemy))));
+ for(setself(warpzone_camera_first); self; setself(self.warpzone_next))
+ self.warpzone_reconnecting = ((e.target == "" || self.target == e.target) && !((e.spawnflags & 1) && visible_to_some_client(self)));
+ for(setself(warpzone_first); self; setself(self.warpzone_next))
+ if(self.warpzone_reconnecting)
+ WarpZone_InitStep_ClearTarget();
+ for(setself(warpzone_first); self; setself(self.warpzone_next))
+ if(self.warpzone_reconnecting)
+ WarpZone_InitStep_FindTarget();
+ for(setself(warpzone_camera_first); self; setself(self.warpzone_next))
+ if(self.warpzone_reconnecting)
+ WarpZoneCamera_InitStep_FindTarget();
+ for(setself(warpzone_first); self; setself(self.warpzone_next))
+ if(self.warpzone_reconnecting || self.enemy.warpzone_reconnecting)
+ WarpZone_InitStep_FinalizeTransform();
+ setself(e);
+}
+
+spawnfunc(trigger_warpzone_reconnect)
+{
+ self.use = trigger_warpzone_reconnect_use;
+}
+
+spawnfunc(target_warpzone_reconnect)
+{
+ spawnfunc_trigger_warpzone_reconnect(this); // both names make sense here :(
+}
+
+void WarpZone_PlayerPhysics_FixVAngle(void)
+{SELFPARAM();
+#ifndef WARPZONE_DONT_FIX_VANGLE
+ if(IS_REAL_CLIENT(self))
+ if(self.v_angle.z <= 360) // if not already adjusted
+ if(time - self.ping * 0.001 < self.warpzone_teleport_time)
+ {
+ self.v_angle = WarpZone_TransformVAngles(self.warpzone_teleport_zone, self.v_angle);
+ self.v_angle_z += 720; // mark as adjusted
+ }
+#endif
+}
--- /dev/null
+#ifndef LIB_WARPZONE_SERVER_H
+#define LIB_WARPZONE_SERVER_H
+
+void WarpZone_StartFrame();
+float WarpZone_Projectile_Touch();
+
+// THESE must be defined by calling QC code:
+void WarpZone_PostTeleportPlayer_Callback(entity pl);
+float WarpZone_Projectile_Touch_ImpactFilter_Callback();
+
+// server must also define a float called ENT_CLIENT_WARPZONE for the initial byte of WarpZone entities
+//const float ENT_CLIENT_WARPZONE;
+//const float ENT_CLIENT_WARPZONE_CAMERA;
+
+void WarpZone_PlayerPhysics_FixVAngle(void);
+
+void WarpZone_PostInitialize_Callback(void);
+#endif
--- /dev/null
+#include "util_server.qh"
+
+#include "common.qh"
+
+#if defined(CSQC)
+#elif defined(MENUQC)
+#elif defined(SVQC)
+ #include "../csqcmodel/sv_model.qh"
+#endif
+#include "common.qh"
+
+void WarpZoneLib_ExactTrigger_Init()
+{SELFPARAM();
+ vector mi, ma;
+ if (self.movedir == '0 0 0')
+ if (self.angles != '0 0 0')
+ {
+ makevectors (self.angles);
+ self.movedir = v_forward;
+ }
+ if(self.model == "")
+ {
+ // It's a box! No need to match with exacttriggers.
+ self.warpzone_isboxy = 1;
+ }
+ else
+ {
+ mi = self.mins;
+ ma = self.maxs;
+ precache_model(self.model);
+ _setmodel(self, self.model);
+ // let mapper-set mins/maxs override the model's bounds if set
+ if(mi != '0 0 0' || ma != '0 0 0')
+ {
+ // It's a box! No need to match with exacttriggers.
+ self.mins = mi;
+ self.maxs = ma;
+ self.warpzone_isboxy = 1;
+ }
+ }
+ setorigin(self, self.origin);
+ if(self.scale)
+ setsize(self, self.mins * self.scale, self.maxs * self.scale);
+ else
+ setsize(self, self.mins, self.maxs);
+ self.movetype = MOVETYPE_NONE;
+ self.solid = SOLID_TRIGGER;
+ self.model = "";
+}
--- /dev/null
+#ifndef LIB_WARPZONE_UTIL_SERVER_H
+#define LIB_WARPZONE_UTIL_SERVER_H
+
+float WarpZoneLib_MoveOutOfSolid(entity e);
+float WarpZoneLib_ExactTrigger_Touch();
+#ifdef SVQC
+void WarpZoneLib_ExactTrigger_Init();
+#endif
+#endif
#include "../../common/weapons/all.qh"
-#include "../../csqcmodellib/sv_model.qh"
+#include "../../lib/csqcmodel/sv_model.qh"
-#include "../../warpzonelib/common.qh"
-#include "../../warpzonelib/util_server.qh"
+#include "../../lib/warpzone/common.qh"
+#include "../../lib/warpzone/util_server.qh"
entity bot_spawn()
{SELFPARAM();
#include "../../../common/triggers/trigger/jumppads.qh"
-#include "../../../warpzonelib/common.qh"
+#include "../../../lib/warpzone/common.qh"
.float speed;
#include "../../common/constants.qh"
-#include "../../warpzonelib/common.qh"
-#include "../../warpzonelib/util_server.qh"
+#include "../../lib/warpzone/common.qh"
+#include "../../lib/warpzone/util_server.qh"
// create a new spawnfunc_waypoint and automatically link it to other waypoints, and link
// them back to it as well
#include "../common/triggers/func/breakable.qh"
-#include "../csqcmodellib/sv_model.qh"
+#include "../lib/csqcmodel/sv_model.qh"
-#include "../warpzonelib/anglestransform.qh"
-#include "../warpzonelib/util_server.qh"
+#include "../lib/warpzone/anglestransform.qh"
+#include "../lib/warpzone/util_server.qh"
void CopyBody(float keepvelocity);
#include "../common/monsters/sv_monsters.qh"
-#include "../warpzonelib/server.qh"
+#include "../lib/warpzone/server.qh"
void send_CSQC_teamnagger() {
#include "../common/deathtypes.qh"
#include "../common/triggers/subs.qh"
#include "../common/playerstats.qh"
-#include "../csqcmodellib/sv_model.qh"
+#include "../lib/csqcmodel/sv_model.qh"
#include "../common/minigames/sv_minigames.qh"
#include "../../common/monsters/spawn.qh"
#include "../../common/monsters/sv_monsters.qh"
-#include "../../warpzonelib/common.qh"
+#include "../../lib/warpzone/common.qh"
void ClientKill_TeamChange (float targetteam); // 0 = don't change, -1 = auto, -2 = spec
#include "../../common/monsters/all.qh"
#include "../../common/notifications.qh"
-#include "../../warpzonelib/common.qh"
+#include "../../lib/warpzone/common.qh"
// ====================================================
#include "../../common/util.qh"
-#include "../../csqcmodellib/sv_model.qh"
+#include "../../lib/csqcmodel/sv_model.qh"
// ===============================================
// Generates radar map images for use in the HUD
#include "../common/teams.qh"
#include "../common/util.qh"
#include "../common/weapons/all.qh"
-#include "../csqcmodellib/sv_model.qh"
-#include "../warpzonelib/common.qh"
+#include "../lib/csqcmodel/sv_model.qh"
+#include "../lib/warpzone/common.qh"
bool Damage_DamageInfo_SendEntity(entity this, entity to, int sf)
{
#if defined(CSQC)
#elif defined(MENUQC)
#elif defined(SVQC)
- #include "../warpzonelib/common.qh"
+ #include "../lib/warpzone/common.qh"
#include "../common/constants.qh"
#include "../common/teams.qh"
#include "../common/util.qh"
#include "mutators/mutators_include.qh"
#include "../common/turrets/sv_turrets.qh"
#include "../common/vehicles/all.qh"
- #include "../csqcmodellib/sv_model.qh"
+ #include "../lib/csqcmodel/sv_model.qh"
#include "../common/playerstats.qh"
#include "g_hook.qh"
#include "scores.qh"
#include "../common/constants.qh"
#include "../common/util.qh"
#include "../common/weapons/all.qh"
-#include "../warpzonelib/common.qh"
-#include "../warpzonelib/server.qh"
+#include "../lib/warpzone/common.qh"
+#include "../lib/warpzone/server.qh"
.int state;
class(BGMScript) .float bgmscriptrelease;
#include "../common/constants.qh"
-#include "../csqcmodellib/sv_model.qh"
+#include "../lib/csqcmodel/sv_model.qh"
.float modelscale;
#include "antilag.qh"
#include "command/common.qh"
-#include "../warpzonelib/common.qh"
+#include "../lib/warpzone/common.qh"
#include "../common/triggers/subs.qh"
spawnfunc(info_null)
#include "../common/monsters/all.qh"
#include "../common/notifications.qh"
#include "../common/util.qh"
-#include "../warpzonelib/util_server.qh"
+#include "../lib/warpzone/util_server.qh"
/*
TODO:
#include "../common/vehicles/sv_vehicles.qh"
#include "../common/vehicles/vehicle.qh"
#include "../common/items/all.qc"
-#include "../csqcmodellib/sv_model.qh"
-#include "../warpzonelib/anglestransform.qh"
-#include "../warpzonelib/server.qh"
+#include "../lib/csqcmodel/sv_model.qh"
+#include "../lib/warpzone/anglestransform.qh"
+#include "../lib/warpzone/server.qh"
void crosshair_trace(entity pl)
{
#include "../../common/stats.qh"
#include "../../common/teams.qh"
-#include "../../warpzonelib/server.qh"
-#include "../../warpzonelib/util_server.qh"
+#include "../../lib/warpzone/server.qh"
+#include "../../lib/warpzone/util_server.qh"
.float lastground;
float total_players;
#include "../teamplay.qh"
#endif
-#include "../../warpzonelib/common.qh"
+#include "../../lib/warpzone/common.qh"
void ctf_FakeTimeLimit(entity e, float t)
{
#include "../../common/monsters/all.qh"
-#include "../../warpzonelib/anglestransform.qh"
-#include "../../warpzonelib/server.qh"
-#include "../../warpzonelib/util_server.qh"
+#include "../../lib/warpzone/anglestransform.qh"
+#include "../../lib/warpzone/server.qh"
+#include "../../lib/warpzone/util_server.qh"
#endif
#if defined(CSQC)
#elif defined(MENUQC)
#elif defined(SVQC)
- #include "../../warpzonelib/anglestransform.qh"
- #include "../../warpzonelib/common.qh"
- #include "../../warpzonelib/util_server.qh"
- #include "../../warpzonelib/server.qh"
+ #include "../../lib/warpzone/anglestransform.qh"
+ #include "../../lib/warpzone/common.qh"
+ #include "../../lib/warpzone/util_server.qh"
+ #include "../../lib/warpzone/server.qh"
#include "../../common/constants.qh"
#include "../../common/stats.qh"
#include "../../common/teams.qh"
#include "../command/cmd.qh"
#include "../command/sv_cmd.qh"
#include "../../common/csqcmodel_settings.qh"
- #include "../../csqcmodellib/common.qh"
- #include "../../csqcmodellib/sv_model.qh"
+ #include "../../lib/csqcmodel/common.qh"
+ #include "../../lib/csqcmodel/sv_model.qh"
#include "../anticheat.qh"
#include "../cheats.qh"
#include "../../common/playerstats.qh"
#include "../common/triggers/subs.qh"
#include "../common/util.qh"
#include "../common/weapons/all.qh"
-#include "../csqcmodellib/sv_model.qh"
-#include "../warpzonelib/anglestransform.qh"
-#include "../warpzonelib/util_server.qh"
-#include "../warpzonelib/common.qh"
+#include "../lib/csqcmodel/sv_model.qh"
+#include "../lib/warpzone/anglestransform.qh"
+#include "../lib/warpzone/util_server.qh"
+#include "../lib/warpzone/common.qh"
#include "../common/vehicles/vehicle.qh"
#include "../common/vehicles/sv_vehicles.qh"
#include "../common/turrets/targettrigger.qc"
#include "../common/weapons/config.qc"
-#include "../csqcmodellib/sv_model.qc"
+#include "../lib/csqcmodel/sv_model.qc"
-#include "../warpzonelib/anglestransform.qc"
-#include "../warpzonelib/common.qc"
-#include "../warpzonelib/server.qc"
-#include "../warpzonelib/util_server.qc"
+#include "../lib/warpzone/anglestransform.qc"
+#include "../lib/warpzone/common.qc"
+#include "../lib/warpzone/server.qc"
+#include "../lib/warpzone/util_server.qc"
#if BUILD_MOD
#include "../../mod/server/progs.inc"
#include "../common/notifications.qh"
#include "../common/mapinfo.qh"
#include "../common/triggers/subs.qh"
-#include "../warpzonelib/util_server.qh"
-#include "../warpzonelib/common.qh"
+#include "../lib/warpzone/util_server.qh"
+#include "../lib/warpzone/common.qh"
#include "../common/mutators/mutator/waypoints/waypointsprites.qh"
void W_Porto_Fail(float failhard);
#include "../common/teams.qh"
#include "../common/triggers/subs.qh"
#include "../common/util.qh"
-#include "../warpzonelib/common.qh"
-#include "../warpzonelib/util_server.qh"
+#include "../lib/warpzone/common.qh"
+#include "../lib/warpzone/util_server.qh"
bool SpawnPoint_Send(entity this, entity to, int sf)
{
#include "../common/vehicles/all.qh"
#include "../common/weapons/all.qh"
-#include "../csqcmodellib/sv_model.qh"
+#include "../lib/csqcmodel/sv_model.qh"
-#include "../warpzonelib/common.qh"
-#include "../warpzonelib/server.qh"
+#include "../lib/warpzone/common.qh"
+#include "../lib/warpzone/server.qh"
.float lastground;
.int state;
#include "../common/weapons/all.qh"
- #include "../warpzonelib/util_server.qh"
+ #include "../lib/warpzone/util_server.qh"
#endif
#ifdef CSQC
#include "../../common/weapons/all.qh"
-#include "../../warpzonelib/common.qh"
+#include "../../lib/warpzone/common.qh"
// this function calculates w_shotorg and w_shotdir based on the weapon model
// offset, trueaim and antilag, and won't put w_shotorg inside a wall.
#include "../../common/notifications.qh"
#include "../../common/util.qh"
#include "../../common/weapons/all.qh"
-#include "../../csqcmodellib/sv_model.qh"
+#include "../../lib/csqcmodel/sv_model.qh"
/*
===========================================================================
+++ /dev/null
-The code in this directory is dual-licensed MIT and GPLv2 "or any later version".
-
-
-
-MIT license:
-
-Copyright (c) 2010 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.
-
-
-
-GPL v2:
-
- GNU GENERAL PUBLIC LICENSE
- Version 2, June 1991
-
- Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- Everyone is permitted to copy and distribute verbatim copies
- of this license document, but changing it is not allowed.
-
- Preamble
-
- The licenses for most software are designed to take away your
-freedom to share and change it. By contrast, the GNU General Public
-License is intended to guarantee your freedom to share and change free
-software--to make sure the software is free for all its users. This
-General Public License applies to most of the Free Software
-Foundation's software and to any other program whose authors commit to
-using it. (Some other Free Software Foundation software is covered by
-the GNU Lesser General Public License instead.) You can apply it to
-your programs, too.
-
- When we speak of free software, we are referring to freedom, not
-price. Our General Public Licenses are designed to make sure that you
-have the freedom to distribute copies of free software (and charge for
-this service if you wish), that you receive source code or can get it
-if you want it, that you can change the software or use pieces of it
-in new free programs; and that you know you can do these things.
-
- To protect your rights, we need to make restrictions that forbid
-anyone to deny you these rights or to ask you to surrender the rights.
-These restrictions translate to certain responsibilities for you if you
-distribute copies of the software, or if you modify it.
-
- For example, if you distribute copies of such a program, whether
-gratis or for a fee, you must give the recipients all the rights that
-you have. You must make sure that they, too, receive or can get the
-source code. And you must show them these terms so they know their
-rights.
-
- We protect your rights with two steps: (1) copyright the software, and
-(2) offer you this license which gives you legal permission to copy,
-distribute and/or modify the software.
-
- Also, for each author's protection and ours, we want to make certain
-that everyone understands that there is no warranty for this free
-software. If the software is modified by someone else and passed on, we
-want its recipients to know that what they have is not the original, so
-that any problems introduced by others will not reflect on the original
-authors' reputations.
-
- Finally, any free program is threatened constantly by software
-patents. We wish to avoid the danger that redistributors of a free
-program will individually obtain patent licenses, in effect making the
-program proprietary. To prevent this, we have made it clear that any
-patent must be licensed for everyone's free use or not licensed at all.
-
- The precise terms and conditions for copying, distribution and
-modification follow.
-
- GNU GENERAL PUBLIC LICENSE
- TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
-
- 0. This License applies to any program or other work which contains
-a notice placed by the copyright holder saying it may be distributed
-under the terms of this General Public License. The "Program", below,
-refers to any such program or work, and a "work based on the Program"
-means either the Program or any derivative work under copyright law:
-that is to say, a work containing the Program or a portion of it,
-either verbatim or with modifications and/or translated into another
-language. (Hereinafter, translation is included without limitation in
-the term "modification".) Each licensee is addressed as "you".
-
-Activities other than copying, distribution and modification are not
-covered by this License; they are outside its scope. The act of
-running the Program is not restricted, and the output from the Program
-is covered only if its contents constitute a work based on the
-Program (independent of having been made by running the Program).
-Whether that is true depends on what the Program does.
-
- 1. You may copy and distribute verbatim copies of the Program's
-source code as you receive it, in any medium, provided that you
-conspicuously and appropriately publish on each copy an appropriate
-copyright notice and disclaimer of warranty; keep intact all the
-notices that refer to this License and to the absence of any warranty;
-and give any other recipients of the Program a copy of this License
-along with the Program.
-
-You may charge a fee for the physical act of transferring a copy, and
-you may at your option offer warranty protection in exchange for a fee.
-
- 2. You may modify your copy or copies of the Program or any portion
-of it, thus forming a work based on the Program, and copy and
-distribute such modifications or work under the terms of Section 1
-above, provided that you also meet all of these conditions:
-
- a) You must cause the modified files to carry prominent notices
- stating that you changed the files and the date of any change.
-
- b) You must cause any work that you distribute or publish, that in
- whole or in part contains or is derived from the Program or any
- part thereof, to be licensed as a whole at no charge to all third
- parties under the terms of this License.
-
- c) If the modified program normally reads commands interactively
- when run, you must cause it, when started running for such
- interactive use in the most ordinary way, to print or display an
- announcement including an appropriate copyright notice and a
- notice that there is no warranty (or else, saying that you provide
- a warranty) and that users may redistribute the program under
- these conditions, and telling the user how to view a copy of this
- License. (Exception: if the Program itself is interactive but
- does not normally print such an announcement, your work based on
- the Program is not required to print an announcement.)
-
-These requirements apply to the modified work as a whole. If
-identifiable sections of that work are not derived from the Program,
-and can be reasonably considered independent and separate works in
-themselves, then this License, and its terms, do not apply to those
-sections when you distribute them as separate works. But when you
-distribute the same sections as part of a whole which is a work based
-on the Program, the distribution of the whole must be on the terms of
-this License, whose permissions for other licensees extend to the
-entire whole, and thus to each and every part regardless of who wrote it.
-
-Thus, it is not the intent of this section to claim rights or contest
-your rights to work written entirely by you; rather, the intent is to
-exercise the right to control the distribution of derivative or
-collective works based on the Program.
-
-In addition, mere aggregation of another work not based on the Program
-with the Program (or with a work based on the Program) on a volume of
-a storage or distribution medium does not bring the other work under
-the scope of this License.
-
- 3. You may copy and distribute the Program (or a work based on it,
-under Section 2) in object code or executable form under the terms of
-Sections 1 and 2 above provided that you also do one of the following:
-
- a) Accompany it with the complete corresponding machine-readable
- source code, which must be distributed under the terms of Sections
- 1 and 2 above on a medium customarily used for software interchange; or,
-
- b) Accompany it with a written offer, valid for at least three
- years, to give any third party, for a charge no more than your
- cost of physically performing source distribution, a complete
- machine-readable copy of the corresponding source code, to be
- distributed under the terms of Sections 1 and 2 above on a medium
- customarily used for software interchange; or,
-
- c) Accompany it with the information you received as to the offer
- to distribute corresponding source code. (This alternative is
- allowed only for noncommercial distribution and only if you
- received the program in object code or executable form with such
- an offer, in accord with Subsection b above.)
-
-The source code for a work means the preferred form of the work for
-making modifications to it. For an executable work, complete source
-code means all the source code for all modules it contains, plus any
-associated interface definition files, plus the scripts used to
-control compilation and installation of the executable. However, as a
-special exception, the source code distributed need not include
-anything that is normally distributed (in either source or binary
-form) with the major components (compiler, kernel, and so on) of the
-operating system on which the executable runs, unless that component
-itself accompanies the executable.
-
-If distribution of executable or object code is made by offering
-access to copy from a designated place, then offering equivalent
-access to copy the source code from the same place counts as
-distribution of the source code, even though third parties are not
-compelled to copy the source along with the object code.
-
- 4. You may not copy, modify, sublicense, or distribute the Program
-except as expressly provided under this License. Any attempt
-otherwise to copy, modify, sublicense or distribute the Program is
-void, and will automatically terminate your rights under this License.
-However, parties who have received copies, or rights, from you under
-this License will not have their licenses terminated so long as such
-parties remain in full compliance.
-
- 5. You are not required to accept this License, since you have not
-signed it. However, nothing else grants you permission to modify or
-distribute the Program or its derivative works. These actions are
-prohibited by law if you do not accept this License. Therefore, by
-modifying or distributing the Program (or any work based on the
-Program), you indicate your acceptance of this License to do so, and
-all its terms and conditions for copying, distributing or modifying
-the Program or works based on it.
-
- 6. Each time you redistribute the Program (or any work based on the
-Program), the recipient automatically receives a license from the
-original licensor to copy, distribute or modify the Program subject to
-these terms and conditions. You may not impose any further
-restrictions on the recipients' exercise of the rights granted herein.
-You are not responsible for enforcing compliance by third parties to
-this License.
-
- 7. If, as a consequence of a court judgment or allegation of patent
-infringement or for any other reason (not limited to patent issues),
-conditions are imposed on you (whether by court order, agreement or
-otherwise) that contradict the conditions of this License, they do not
-excuse you from the conditions of this License. If you cannot
-distribute so as to satisfy simultaneously your obligations under this
-License and any other pertinent obligations, then as a consequence you
-may not distribute the Program at all. For example, if a patent
-license would not permit royalty-free redistribution of the Program by
-all those who receive copies directly or indirectly through you, then
-the only way you could satisfy both it and this License would be to
-refrain entirely from distribution of the Program.
-
-If any portion of this section is held invalid or unenforceable under
-any particular circumstance, the balance of the section is intended to
-apply and the section as a whole is intended to apply in other
-circumstances.
-
-It is not the purpose of this section to induce you to infringe any
-patents or other property right claims or to contest validity of any
-such claims; this section has the sole purpose of protecting the
-integrity of the free software distribution system, which is
-implemented by public license practices. Many people have made
-generous contributions to the wide range of software distributed
-through that system in reliance on consistent application of that
-system; it is up to the author/donor to decide if he or she is willing
-to distribute software through any other system and a licensee cannot
-impose that choice.
-
-This section is intended to make thoroughly clear what is believed to
-be a consequence of the rest of this License.
-
- 8. If the distribution and/or use of the Program is restricted in
-certain countries either by patents or by copyrighted interfaces, the
-original copyright holder who places the Program under this License
-may add an explicit geographical distribution limitation excluding
-those countries, so that distribution is permitted only in or among
-countries not thus excluded. In such case, this License incorporates
-the limitation as if written in the body of this License.
-
- 9. The Free Software Foundation may publish revised and/or new versions
-of the General Public License from time to time. Such new versions will
-be similar in spirit to the present version, but may differ in detail to
-address new problems or concerns.
-
-Each version is given a distinguishing version number. If the Program
-specifies a version number of this License which applies to it and "any
-later version", you have the option of following the terms and conditions
-either of that version or of any later version published by the Free
-Software Foundation. If the Program does not specify a version number of
-this License, you may choose any version ever published by the Free Software
-Foundation.
-
- 10. If you wish to incorporate parts of the Program into other free
-programs whose distribution conditions are different, write to the author
-to ask for permission. For software which is copyrighted by the Free
-Software Foundation, write to the Free Software Foundation; we sometimes
-make exceptions for this. Our decision will be guided by the two goals
-of preserving the free status of all derivatives of our free software and
-of promoting the sharing and reuse of software generally.
-
- NO WARRANTY
-
- 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
-FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
-OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
-PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
-OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
-MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
-TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
-PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
-REPAIR OR CORRECTION.
-
- 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
-WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
-REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
-INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
-OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
-TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
-YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
-PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGES.
-
- END OF TERMS AND CONDITIONS
-
- How to Apply These Terms to Your New Programs
-
- If you develop a new program, and you want it to be of the greatest
-possible use to the public, the best way to achieve this is to make it
-free software which everyone can redistribute and change under these terms.
-
- To do so, attach the following notices to the program. It is safest
-to attach them to the start of each source file to most effectively
-convey the exclusion of warranty; and each file should have at least
-the "copyright" line and a pointer to where the full notice is found.
-
- <one line to give the program's name and a brief idea of what it does.>
- Copyright (C) <year> <name of author>
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along
- with this program; if not, write to the Free Software Foundation, Inc.,
- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-
-Also add information on how to contact you by electronic and paper mail.
-
-If the program is interactive, make it output a short notice like this
-when it starts in an interactive mode:
-
- Gnomovision version 69, Copyright (C) year name of author
- Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
- This is free software, and you are welcome to redistribute it
- under certain conditions; type `show c' for details.
-
-The hypothetical commands `show w' and `show c' should show the appropriate
-parts of the General Public License. Of course, the commands you use may
-be called something other than `show w' and `show c'; they could even be
-mouse-clicks or menu items--whatever suits your program.
-
-You should also get your employer (if you work as a programmer) or your
-school, if any, to sign a "copyright disclaimer" for the program, if
-necessary. Here is a sample; alter the names:
-
- Yoyodyne, Inc., hereby disclaims all copyright interest in the program
- `Gnomovision' (which makes passes at compilers) written by James Hacker.
-
- <signature of Ty Coon>, 1 April 1989
- Ty Coon, President of Vice
-
-This General Public License does not permit incorporating your program into
-proprietary programs. If your program is a subroutine library, you may
-consider it more useful to permit linking proprietary applications with the
-library. If this is what you want to do, use the GNU Lesser General
-Public License instead of this License.
+++ /dev/null
-Open issues:
-- grep for TODO and FIXME
-- when shot origin is inside warpzone, vortex shot fails (and is even drawn in totally wrong direction). WHY? Possibly v_forward got lost?
-
-Weapon support:
-
-- blaster: YES
-- shotgun: YES
-- machinegun: YES
-- mortar: YES
-- electro: YES
-- crylink: YES
-- vortex: YES
-- hagar: YES
-- devastator: YES (except for trail bug)
-- porto: YES (bwahahahaha)
-- hlac: YES
-- vaporizer: YES
-- rifle: YES
-- fireball: YES (BFG effect cannot work through warpzones by design, so it's not available through warpzones)
-- hook: YES
-
-- shockwave: NO (does not support warpzones currently)
-- tuba: NO (sound)
+++ /dev/null
-#include "anglestransform.qh"
-
-#if defined(CSQC)
-#elif defined(MENUQC)
-#elif defined(SVQC)
-#endif
-
-#ifdef POSITIVE_PITCH_IS_DOWN
-vector fixedvectoangles(vector a)
-{
- vector ang;
- ang = vectoangles(a);
- ang.x = -ang.x;
- return ang;
-}
-vector fixedvectoangles2(vector a, vector b)
-{
- vector ang;
- ang = vectoangles2(a, b);
- ang.x = -ang.x;
- return ang;
-}
-#else
-void fixedmakevectors(vector a)
-{
- // a makevectors that actually inverts vectoangles
- a.x = -a.x;
- makevectors(a);
-}
-#endif
-
-// angles transforms
-// angles in fixedmakevectors/fixedvectoangles space
-vector AnglesTransform_Apply(vector transform, vector v)
-{
- fixedmakevectors(transform);
- return v_forward * v.x
- + v_right * (-v.y)
- + v_up * v.z;
-}
-
-vector AnglesTransform_Multiply(vector t1, vector t2)
-{
- vector m_forward, m_up;
- fixedmakevectors(t2); m_forward = v_forward; m_up = v_up;
- m_forward = AnglesTransform_Apply(t1, m_forward); m_up = AnglesTransform_Apply(t1, m_up);
- return fixedvectoangles2(m_forward, m_up);
-}
-
-vector AnglesTransform_Invert(vector transform)
-{
- vector i_forward, i_up;
- fixedmakevectors(transform);
- // we want angles that turn v_forward into '1 0 0', v_right into '0 1 0' and v_up into '0 0 1'
- // but these are orthogonal unit vectors!
- // so to invert, we can simply fixedvectoangles the TRANSPOSED matrix
- // TODO is this always -transform?
- i_forward.x = v_forward.x;
- i_forward.y = -v_right.x;
- i_forward.z = v_up.x;
- i_up.x = v_forward.z;
- i_up.y = -v_right.z;
- i_up.z = v_up.z;
- return fixedvectoangles2(i_forward, i_up);
-}
-
-vector AnglesTransform_TurnDirectionFR(vector transform)
-{
- // turn 180 degrees around v_up
- // changes in-direction to out-direction
- //fixedmakevectors(transform);
- //return fixedvectoangles2(-1 * v_forward, 1 * v_up);
- transform.x = -transform.x;
- transform.y = 180 + transform.y;
- transform.z = -transform.z;
- // pitch: -s +c
- // yaw: -s -c
- // roll: -s +c
- return transform;
-}
-
-vector AnglesTransform_TurnDirectionFU(vector transform)
-{
- // turn 180 degrees around v_up
- // changes in-direction to out-direction
- //fixedmakevectors(transform);
- //return fixedvectoangles2(-1 * v_forward, 1 * v_up);
- transform.x = -transform.x;
- transform.y = 180 + transform.y;
- transform.z = 180 - transform.z;
- return transform;
-}
-
-vector AnglesTransform_RightDivide(vector to_transform, vector from_transform)
-{
- return AnglesTransform_Multiply(to_transform, AnglesTransform_Invert(from_transform));
-}
-
-vector AnglesTransform_LeftDivide(vector from_transform, vector to_transform)
-{
- return AnglesTransform_Multiply(AnglesTransform_Invert(from_transform), to_transform);
-}
-
-vector AnglesTransform_Normalize(vector t, float minimize_roll)
-{
- float need_flip;
- // first, bring all angles in their range...
- t.x = t.x - 360 * rint(t.x / 360);
- t.y = t.y - 360 * rint(t.y / 360);
- t.z = t.z - 360 * rint(t.z / 360);
- if(minimize_roll)
- need_flip = (t.z > 90 || t.z <= -90);
- else
- need_flip = (t.x > 90 || t.x < -90); // for pitch we prefer to allow exactly -90 degrees for looking straight down
- if(need_flip)
- {
- if(t.x >= 0) t.x = 180 - t.x; else t.x = -180 - t.x;
- if(t.y > 0) t.y -= 180; else t.y += 180;
- if(t.z > 0) t.z -= 180; else t.z += 180;
- }
- return t;
-}
-
-vector AnglesTransform_CancelRoll(vector t)
-{
- const float epsilon = 30;
- float f;
-
- // constraints:
- // forward vector (NOT SO important)
- // right vector, up vector: screen rotation (MORE important)
- // choose best match among all pitch-yaw only rotations
-
- // FIXME find a better method
-
- f = fabs(t.x - (-90)) / epsilon;
- if(f < 1)
- {
- //t_x = -90;
- t.y += t.z;
- t.z = 0;
- }
- else
- {
- f = fabs(t.x - 90) / epsilon;
- if(f < 1)
- {
- //t_x = 90;
- t.y -= t.z;
- t.z = 0;
- }
- }
- return t;
-}
-
-#ifdef POSITIVE_PITCH_IS_DOWN
-vector AnglesTransform_ApplyToAngles(vector transform, vector v)
-{
- v.x = -v.x;
- v = AnglesTransform_Multiply(transform, v);
- v.x = -v.x;
- return v;
-}
-vector AnglesTransform_ApplyToVAngles(vector transform, vector v)
-{
- v = AnglesTransform_Multiply(transform, v);
- return v;
-}
-vector AnglesTransform_FromAngles(vector v)
-{
- v.x = -v.x;
- return v;
-}
-vector AnglesTransform_ToAngles(vector v)
-{
- v.x = -v.x;
- return v;
-}
-vector AnglesTransform_FromVAngles(vector v)
-{
- return v;
-}
-vector AnglesTransform_ToVAngles(vector v)
-{
- return v;
-}
-#else
-vector AnglesTransform_ApplyToAngles(vector transform, vector v)
-{
- v = AnglesTransform_Multiply(transform, v);
- return v;
-}
-vector AnglesTransform_ApplyToVAngles(vector transform, vector v)
-{
- v.x = -v.x;
- v = AnglesTransform_Multiply(transform, v);
- v.x = -v.x;
- return v;
-}
-vector AnglesTransform_FromAngles(vector v)
-{
- return v;
-}
-vector AnglesTransform_ToAngles(vector v)
-{
- return v;
-}
-vector AnglesTransform_FromVAngles(vector v)
-{
- v.x = -v.x;
- return v;
-}
-vector AnglesTransform_ToVAngles(vector v)
-{
- v.x = -v.x;
- return v;
-}
-#endif
-
-vector AnglesTransform_Multiply_GetPostShift(vector t0, vector st0, vector t1, vector st1)
-{
- // we want the result of:
- // t0 * (t1 * p + st1) + st0
- // t0 * t1 * p + t0 * st1 + st0
- return st0 + AnglesTransform_Apply(t0, st1);
-}
-vector AnglesTransform_PrePostShift_GetPostShift(vector sf, vector t, vector st)
-{
- return st - AnglesTransform_Apply(t, sf);
-}
+++ /dev/null
-#ifndef ANGLETRANSFORM_H
-#define ANGLETRANSFORM_H
-
-#define POSITIVE_PITCH_IS_DOWN
-
-#ifdef POSITIVE_PITCH_IS_DOWN
-#define fixedmakevectors makevectors
-vector fixedvectoangles(vector a);
-vector fixedvectoangles2(vector a, vector b);
-#else
-void fixedmakevectors(vector a);
-#define fixedvectoangles2 vectoangles2
-#define fixedvectoangles vectoangles
-#endif
-
-vector AnglesTransform_Apply(vector transform, vector v);
-vector AnglesTransform_Multiply(vector t1, vector t2); // A B
-vector AnglesTransform_Invert(vector transform);
-vector AnglesTransform_TurnDirectionFU(vector transform);
-vector AnglesTransform_TurnDirectionFR(vector transform);
-vector AnglesTransform_RightDivide(vector to_transform, vector from_transform); // A B^-1
-vector AnglesTransform_LeftDivide(vector from_transform, vector to_transform); // A^-1 B
-
-vector AnglesTransform_Normalize(vector t, float minimize_roll); // makes sure all angles are in their range: yaw in -180..180, pitch in -90..90, roll in -180..180 (or if minimize_roll is set, pitch in -180..180, roll in -90..90)
-
-vector AnglesTransform_ApplyToAngles(vector transform, vector v);
-vector AnglesTransform_ApplyToVAngles(vector transform, vector v);
-vector AnglesTransform_FromAngles(vector v);
-vector AnglesTransform_ToAngles(vector v);
-vector AnglesTransform_FromVAngles(vector v);
-vector AnglesTransform_ToVAngles(vector v);
-
-// transformed = original * transform + postshift
-vector AnglesTransform_Multiply_GetPostShift(vector sf0, vector st0, vector t1, vector st1);
-vector AnglesTransform_PrePostShift_GetPostShift(vector sf, vector t, vector st);
-#endif
+++ /dev/null
-#include "client.qh"
-#include "common.qh"
-
-#if defined(CSQC)
- #include "../client/autocvars.qh"
- #include "../csqcmodellib/cl_model.qh"
-#elif defined(MENUQC)
-#elif defined(SVQC)
-#endif
-
-void WarpZone_Fade_PreDraw()
-{SELFPARAM();
- vector org;
- org = getpropertyvec(VF_ORIGIN);
- if(!checkpvs(org, self)) // this makes sense as long as we don't support recursive warpzones
- self.alpha = 0;
- else if(self.warpzone_fadestart)
- self.alpha = bound(0, (self.warpzone_fadeend - vlen(org - self.origin - 0.5 * (self.mins + self.maxs))) / (self.warpzone_fadeend - self.warpzone_fadestart), 1);
- else
- self.alpha = 1;
- //printf("%v <-> %v\n", view_origin, self.origin + 0.5 * (self.mins + self.maxs));
- if(self.alpha <= 0)
- self.drawmask = 0;
- else
- self.drawmask = MASK_NORMAL;
-}
-
-void WarpZone_Read(float isnew)
-{SELFPARAM();
- warpzone_warpzones_exist = 1;
- if (!self.enemy)
- {
- self.enemy = spawn();
- self.enemy.classname = "warpzone_from";
- }
- self.classname = "trigger_warpzone";
-
- int f = ReadByte();
- self.warpzone_isboxy = (f & 1);
- if(f & 4)
- {
- self.origin_x = ReadCoord();
- self.origin_y = ReadCoord();
- self.origin_z = ReadCoord();
- }
- else
- self.origin = '0 0 0';
- self.modelindex = ReadShort();
- self.mins_x = ReadCoord();
- self.mins_y = ReadCoord();
- self.mins_z = ReadCoord();
- self.maxs_x = ReadCoord();
- self.maxs_y = ReadCoord();
- self.maxs_z = ReadCoord();
- self.scale = ReadByte() / 16;
- self.enemy.oldorigin_x = ReadCoord();
- self.enemy.oldorigin_y = ReadCoord();
- self.enemy.oldorigin_z = ReadCoord();
- self.enemy.avelocity_x = ReadCoord();
- self.enemy.avelocity_y = ReadCoord();
- self.enemy.avelocity_z = ReadCoord();
- self.oldorigin_x = ReadCoord();
- self.oldorigin_y = ReadCoord();
- self.oldorigin_z = ReadCoord();
- self.avelocity_x = ReadCoord();
- self.avelocity_y = ReadCoord();
- self.avelocity_z = ReadCoord();
-
- if(f & 2)
- {
- self.warpzone_fadestart = ReadShort();
- self.warpzone_fadeend = max(self.warpzone_fadestart + 1, ReadShort());
- }
- else
- {
- self.warpzone_fadestart = 0;
- self.warpzone_fadeend = 0;
- }
-
- // common stuff
- WarpZone_SetUp(self, self.enemy.oldorigin, self.enemy.avelocity, self.oldorigin, self.avelocity);
-
- // link me
- //setmodel(self, self.model);
- setorigin(self, self.origin);
- setsize(self, self.mins, self.maxs);
-
- // how to draw
- // engine currently wants this
- self.predraw = WarpZone_Fade_PreDraw;
-}
-
-void WarpZone_Camera_Read(float isnew)
-{SELFPARAM();
- warpzone_cameras_exist = 1;
- self.classname = "func_warpzone_camera";
-
- int f = ReadByte();
- if(f & 4)
- {
- self.origin_x = ReadCoord();
- self.origin_y = ReadCoord();
- self.origin_z = ReadCoord();
- }
- else
- self.origin = '0 0 0';
- self.modelindex = ReadShort();
- self.mins_x = ReadCoord();
- self.mins_y = ReadCoord();
- self.mins_z = ReadCoord();
- self.maxs_x = ReadCoord();
- self.maxs_y = ReadCoord();
- self.maxs_z = ReadCoord();
- self.scale = ReadByte() / 16;
- self.oldorigin_x = ReadCoord();
- self.oldorigin_y = ReadCoord();
- self.oldorigin_z = ReadCoord();
- self.avelocity_x = ReadCoord();
- self.avelocity_y = ReadCoord();
- self.avelocity_z = ReadCoord();
-
- if(f & 2)
- {
- self.warpzone_fadestart = ReadShort();
- self.warpzone_fadeend = max(self.warpzone_fadestart + 1, ReadShort());
- }
- else
- {
- self.warpzone_fadestart = 0;
- self.warpzone_fadeend = 0;
- }
-
- // common stuff
- WarpZone_Camera_SetUp(self, self.oldorigin, self.avelocity);
-
- // engine currently wants this
- self.drawmask = MASK_NORMAL;
-
- // link me
- //setmodel(self, self.model);
- setorigin(self, self.origin);
- setsize(self, self.mins, self.maxs);
-
- // how to draw
- // engine currently wants this
- self.predraw = WarpZone_Fade_PreDraw;
-}
-
-void CL_RotateMoves(vector ang) = #638;
-void WarpZone_Teleported_Read(float isnew)
-{SELFPARAM();
- vector v;
- self.classname = "warpzone_teleported";
- v.x = ReadCoord();
- v.y = ReadCoord();
- v.z = ReadCoord();
- if(!isnew)
- return;
- self.warpzone_transform = v;
- setproperty(VF_CL_VIEWANGLES, WarpZone_TransformVAngles(self, getpropertyvec(VF_CL_VIEWANGLES)));
- if(checkextension("DP_CSQC_ROTATEMOVES"))
- CL_RotateMoves(v);
- //CL_RotateMoves('0 90 0');
-}
-
-float warpzone_fixingview;
-float warpzone_fixingview_drawexteriormodel;
-
-void WarpZone_View_Outside()
-{
- if(!warpzone_fixingview)
- return;
- warpzone_fixingview = 0;
- cvar_set("r_drawexteriormodel", ftos(warpzone_fixingview_drawexteriormodel));
-}
-
-void WarpZone_View_Inside()
-{
- if(autocvar_chase_active)
- {
- WarpZone_View_Outside();
- return;
- }
- if(warpzone_fixingview)
- return;
- warpzone_fixingview = 1;
- warpzone_fixingview_drawexteriormodel = cvar("r_drawexteriormodel");
- cvar_set("r_drawexteriormodel", "0");
-}
-
-vector WarpZone_FixNearClip(vector o, vector c0, vector c1, vector c2, vector c3)
-{
- vector mi, ma;
- entity e;
- float pd;
-
- mi.x = min(o.x, c0_x, c1_x, c2_x, c3_x);
- ma.x = max(o.x, c0_x, c1_x, c2_x, c3_x);
- mi.y = min(o.y, c0_y, c1_y, c2_y, c3_y);
- ma.y = max(o.y, c0_y, c1_y, c2_y, c3_y);
- mi.z = min(o.z, c0_z, c1_z, c2_z, c3_z);
- ma.z = max(o.z, c0_z, c1_z, c2_z, c3_z);
-
- e = WarpZone_Find(mi, ma);
- if(e)
- {
- if(WarpZone_PlaneDist(e, o) < 0)
- return '0 0 0';
- // can't really be, though, but if it is, this is not my warpzone, but a random different one in the same mins/maxs
- pd = min(
- WarpZone_PlaneDist(e, c0),
- WarpZone_PlaneDist(e, c1),
- WarpZone_PlaneDist(e, c2),
- WarpZone_PlaneDist(e, c3)
- );
- if(pd < 0)
- return e.warpzone_forward * -pd;
- }
-
- return '0 0 0';
-}
-
-void WarpZone_FixPMove()
-{
- entity e;
- e = WarpZone_Find(pmove_org, pmove_org);
- if(e)
- {
- pmove_org = WarpZone_TransformOrigin(e, pmove_org);
- input_angles = WarpZone_TransformVAngles(e, input_angles);
- }
-}
-
-#ifndef KEEP_ROLL
-float autocvar_cl_rollkillspeed = 10;
-#endif
-void WarpZone_FixView()
-{
- entity e;
- vector org, ang, nearclip, corner0, corner1, corner2, corner3, o;
- float f;
-
- warpzone_save_view_origin = org = getpropertyvec(VF_ORIGIN);
- warpzone_save_view_angles = ang = getpropertyvec(VF_ANGLES);
-
- e = WarpZone_Find(org, org);
- if(e)
- {
- org = WarpZone_TransformOrigin(e, org);
- ang = WarpZone_TransformVAngles(e, ang);
- WarpZone_View_Inside();
- }
- else
- WarpZone_View_Outside();
-
-#ifndef KEEP_ROLL
- float rick;
- if(autocvar_cl_rollkillspeed)
- f = max(0, (1 - frametime * autocvar_cl_rollkillspeed));
- else
- f = 0;
-
- rick = getproperty(VF_CL_VIEWANGLES_Z);
- rick *= f;
- setproperty(VF_CL_VIEWANGLES_Z, rick);
- ang.z *= f;
-#endif
-
- setproperty(VF_ORIGIN, org);
- setproperty(VF_ANGLES, ang);
-
- nearclip = '0 0 1' * (cvar("r_nearclip") * 1.125);
- corner0 = cs_unproject('0 0 0' + nearclip);
- corner1 = cs_unproject('1 0 0' * cvar("vid_conwidth") + nearclip);
- corner2 = cs_unproject('0 1 0' * cvar("vid_conheight") + nearclip);
- corner3 = cs_unproject('1 0 0' * cvar("vid_conwidth") + '0 1 0' * cvar("vid_conheight") + nearclip);
- o = WarpZone_FixNearClip(org, corner0, corner1, corner2, corner3);
- if(o != '0 0 0')
- setproperty(VF_ORIGIN, org + o);
-}
-
-void WarpZone_Init()
-{
-}
-
-void WarpZone_Shutdown()
-{
- WarpZone_View_Outside();
-}
+++ /dev/null
-#ifndef CLIENT_H
-#define CLIENT_H
-
-void WarpZone_Read(float bIsNewEntity);
-void WarpZone_Camera_Read(float bIsNewEntity);
-void WarpZone_Teleported_Read(float bIsNewEntity);
-
-void WarpZone_FixPMove();
-void WarpZone_FixView();
-
-void WarpZone_Init();
-void WarpZone_Shutdown();
-
-vector warpzone_save_view_origin;
-vector warpzone_save_view_angles;
-#endif
+++ /dev/null
-#include "common.qh"
-
-#if defined(CSQC)
- #include "../server/t_items.qh"
-#elif defined(MENUQC)
-#elif defined(SVQC)
- #include "../common/weapons/all.qh"
-#endif
-
-void WarpZone_Accumulator_Clear(entity acc)
-{
- acc.warpzone_transform = '0 0 0';
- acc.warpzone_shift = '0 0 0';
-}
-void WarpZone_Accumulator_AddTransform(entity acc, vector t, vector s)
-{
- vector tr, st;
- tr = AnglesTransform_Multiply(t, acc.warpzone_transform);
- st = AnglesTransform_Multiply_GetPostShift(t, s, acc.warpzone_transform, acc.warpzone_shift);
- acc.warpzone_transform = tr;
- acc.warpzone_shift = st;
-}
-void WarpZone_Accumulator_Add(entity acc, entity wz)
-{
- WarpZone_Accumulator_AddTransform(acc, wz.warpzone_transform, wz.warpzone_shift);
-}
-void WarpZone_Accumulator_AddInverseTransform(entity acc, vector t, vector s)
-{
- vector tt, ss;
- tt = AnglesTransform_Invert(t);
- ss = AnglesTransform_PrePostShift_GetPostShift(s, tt, '0 0 0');
- WarpZone_Accumulator_AddTransform(acc, tt, ss);
- // yes, this probably can be done simpler... but this way is "obvious" :)
-}
-void WarpZone_Accumulator_AddInverse(entity acc, entity wz)
-{
- WarpZone_Accumulator_AddInverseTransform(acc, wz.warpzone_transform, wz.warpzone_shift);
-}
-
-.vector(vector, vector) camera_transform;
-float autocvar_cl_warpzone_usetrace = 1;
-vector WarpZone_camera_transform(vector org, vector ang)
-{SELFPARAM();
- vector vf, vr, vu;
- if(self.warpzone_fadestart)
- if(vlen(org - self.origin - 0.5 * (self.mins + self.maxs)) > self.warpzone_fadeend + 400)
- return org;
- // don't transform if zone faded out (plus 400qu safety margin for typical speeds and latencies)
- // unneeded on client, on server this helps a lot
- vf = v_forward;
- vr = v_right;
- vu = v_up;
- org = WarpZone_TransformOrigin(self, org);
- vf = WarpZone_TransformVelocity(self, vf);
- vr = WarpZone_TransformVelocity(self, vr);
- vu = WarpZone_TransformVelocity(self, vu);
- if(autocvar_cl_warpzone_usetrace)
- traceline(self.warpzone_targetorigin, org, MOVE_NOMONSTERS, world);
- else
- trace_endpos = self.warpzone_targetorigin;
- v_forward = vf;
- v_right = vr;
- v_up = vu;
- return org;
-}
-
-void WarpZone_SetUp(entity e, vector my_org, vector my_ang, vector other_org, vector other_ang)
-{
- e.warpzone_transform = AnglesTransform_RightDivide(other_ang, AnglesTransform_TurnDirectionFR(my_ang));
- e.warpzone_shift = AnglesTransform_PrePostShift_GetPostShift(my_org, e.warpzone_transform, other_org);
- e.warpzone_origin = my_org;
- e.warpzone_targetorigin = other_org;
- e.warpzone_angles = my_ang;
- e.warpzone_targetangles = other_ang;
- fixedmakevectors(my_ang); e.warpzone_forward = v_forward;
- fixedmakevectors(other_ang); e.warpzone_targetforward = v_forward;
- e.camera_transform = WarpZone_camera_transform;
-}
-
-vector WarpZone_Camera_camera_transform(vector org, vector ang)
-{SELFPARAM();
- // a fixed camera view
- if(self.warpzone_fadestart)
- if(vlen(org - self.origin - 0.5 * (self.mins + self.maxs)) > self.warpzone_fadeend + 400)
- return org;
- // don't transform if zone faded out (plus 400qu safety margin for typical speeds and latencies)
- // unneeded on client, on server this helps a lot
- trace_endpos = self.warpzone_origin;
- makevectors(self.warpzone_angles);
- return self.warpzone_origin;
-}
-
-void WarpZone_Camera_SetUp(entity e, vector my_org, vector my_ang) // we assume that e.oldorigin and e.avelocity point to view origin and direction
-{
- e.warpzone_origin = my_org;
- e.warpzone_angles = my_ang;
- e.camera_transform = WarpZone_Camera_camera_transform;
-}
-
-.entity enemy;
-
-vector WarpZoneLib_BoxTouchesBrush_mins;
-vector WarpZoneLib_BoxTouchesBrush_maxs;
-entity WarpZoneLib_BoxTouchesBrush_ent;
-entity WarpZoneLib_BoxTouchesBrush_ignore;
-float WarpZoneLib_BoxTouchesBrush_Recurse()
-{
- float s;
- entity se;
- float f;
-
- tracebox('0 0 0', WarpZoneLib_BoxTouchesBrush_mins, WarpZoneLib_BoxTouchesBrush_maxs, '0 0 0', MOVE_NOMONSTERS, WarpZoneLib_BoxTouchesBrush_ignore);
-#ifdef CSQC
- if (trace_networkentity)
- {
- LOG_TRACE("hit a network ent, cannot continue WarpZoneLib_BoxTouchesBrush\n");
- // we cannot continue, as a player blocks us...
- // so, abort
- return 0;
- }
-#endif
- if (!trace_ent)
- return 0;
- if (trace_ent == WarpZoneLib_BoxTouchesBrush_ent)
- return 1;
-
- se = trace_ent;
- s = se.solid;
- se.solid = SOLID_NOT;
- f = WarpZoneLib_BoxTouchesBrush_Recurse();
- se.solid = s;
-
- return f;
-}
-
-float WarpZoneLib_BoxTouchesBrush(vector mi, vector ma, entity e, entity ig)
-{
- float f, s;
-
- if(!e.modelindex || e.warpzone_isboxy)
- return 1;
-
- s = e.solid;
- e.solid = SOLID_BSP;
- WarpZoneLib_BoxTouchesBrush_mins = mi;
- WarpZoneLib_BoxTouchesBrush_maxs = ma;
- WarpZoneLib_BoxTouchesBrush_ent = e;
- WarpZoneLib_BoxTouchesBrush_ignore = ig;
- f = WarpZoneLib_BoxTouchesBrush_Recurse();
- e.solid = s;
-
- return f;
-}
-
-entity WarpZone_Find(vector mi, vector ma)
-{
- // if we are near any warpzone planes - MOVE AWAY (work around nearclip)
- entity e;
- if(!warpzone_warpzones_exist)
- return world;
- for(e = world; (e = find(e, classname, "trigger_warpzone")); )
- if(WarpZoneLib_BoxTouchesBrush(mi, ma, e, world))
- return e;
- return world;
-}
-
-void WarpZone_MakeAllSolid()
-{
- entity e;
- if(!warpzone_warpzones_exist)
- return;
- for(e = world; (e = find(e, classname, "trigger_warpzone")); )
- e.solid = SOLID_BSP;
-}
-
-void WarpZone_MakeAllOther()
-{
- entity e;
- if(!warpzone_warpzones_exist)
- return;
- for(e = world; (e = find(e, classname, "trigger_warpzone")); )
- e.solid = SOLID_TRIGGER;
-}
-
-void WarpZone_Trace_InitTransform()
-{
- if(!WarpZone_trace_transform)
- {
- WarpZone_trace_transform = spawn();
- WarpZone_trace_transform.classname = "warpzone_trace_transform";
- }
- WarpZone_Accumulator_Clear(WarpZone_trace_transform);
-}
-void WarpZone_Trace_AddTransform(entity wz)
-{
- WarpZone_Accumulator_Add(WarpZone_trace_transform, wz);
-}
-
-void WarpZone_TraceBox_ThroughZone(vector org, vector mi, vector ma, vector end, float nomonsters, entity forent, entity zone, WarpZone_trace_callback_t cb)
-{
- float nomonsters_adjusted;
- float frac, sol, i;
- float contentshack;
- vector o0, e0;
- entity wz;
- vector vf, vr, vu;
-
- WarpZone_trace_forent = forent;
- WarpZone_trace_firstzone = world;
- WarpZone_trace_lastzone = world;
- WarpZone_Trace_InitTransform();
- if(!warpzone_warpzones_exist)
- {
- if(nomonsters == MOVE_NOTHING)
- {
- trace_endpos = end;
- trace_fraction = 1;
- if(cb)
- cb(org, trace_endpos, end);
- return;
- }
- else
- {
- tracebox(org, mi, ma, end, nomonsters, WarpZone_trace_forent);
- if(cb)
- cb(org, trace_endpos, end);
- return;
- }
- }
-
- vf = v_forward;
- vr = v_right;
- vu = v_up;
- o0 = org;
- e0 = end;
-
- switch(nomonsters)
- {
- case MOVE_WORLDONLY:
- case MOVE_NOTHING:
- nomonsters_adjusted = MOVE_NOMONSTERS;
- break;
- default:
- nomonsters_adjusted = nomonsters;
- break;
- }
- if((contentshack = (WarpZone_trace_forent.dphitcontentsmask && !(WarpZone_trace_forent.dphitcontentsmask & DPCONTENTS_SOLID))))
- BITSET_ASSIGN(WarpZone_trace_forent.dphitcontentsmask, DPCONTENTS_SOLID);
-
- // if starting in warpzone, first transform
- wz = WarpZone_Find(org + mi, org + ma);
- if(wz)
- {
- WarpZone_trace_firstzone = wz;
- WarpZone_trace_lastzone = wz;
- if(zone && wz != zone)
- {
- // we are in ANOTHER warpzone. This is bad. Make a zero length trace and return.
- sol = 1;
- trace_fraction = 0;
- trace_endpos = org;
- goto fail;
- }
- WarpZone_Trace_AddTransform(wz);
- org = WarpZone_TransformOrigin(wz, org);
- end = WarpZone_TransformOrigin(wz, end);
- }
- WarpZone_MakeAllSolid();
- sol = -1;
- frac = 0;
- i = 16;
- for (;;)
- {
- if(--i < 1)
- {
- LOG_TRACE("Too many warpzones in sequence, aborting trace.\n");
- trace_ent = world;
- break;
- }
- tracebox(org, mi, ma, end, nomonsters_adjusted, WarpZone_trace_forent);
- if(cb)
- cb(org, trace_endpos, end);
- if(sol < 0)
- sol = trace_startsolid;
-
- frac = trace_fraction = frac + (1 - frac) * trace_fraction;
- if(trace_fraction >= 1)
- break;
- if(trace_ent.classname != "trigger_warpzone")
- {
- if((nomonsters == MOVE_NOTHING) || ((nomonsters == MOVE_WORLDONLY) && trace_ent) || (contentshack && (trace_dphitcontents & WarpZone_trace_forent.dphitcontentsmask) == DPCONTENTS_SOLID))
- {
- // continue the trace, ignoring this hit (we only care for warpzones)
- org = trace_endpos + normalize(end - org);
- continue;
- // we cannot do an inverted trace here, as we do care for further warpzones inside that "solid" to be found
- // otherwise, players could block entrances that way
- }
- break;
- }
- if(trace_ent == wz)
- {
- // FIXME can this check be removed? Do we really need it?
- LOG_TRACE("I transformed into the same zone again, wtf, aborting the trace\n");
- trace_ent = world;
- break;
- }
- wz = trace_ent;
- if(!WarpZone_trace_firstzone)
- WarpZone_trace_firstzone = wz;
- WarpZone_trace_lastzone = wz;
- if(zone && wz != zone)
- break;
- WarpZone_Trace_AddTransform(wz);
- // we hit a warpzone... so, let's perform the trace after the warp again
- org = WarpZone_TransformOrigin(wz, trace_endpos);
- end = WarpZone_TransformOrigin(wz, end);
-
- // we got warped, so let's step back a bit
- tracebox(org, mi, ma, org + normalize(org - end) * 32, nomonsters_adjusted, WarpZone_trace_forent);
- org = trace_endpos;
- }
- WarpZone_MakeAllOther();
-:fail
- if(contentshack)
- BITCLR_ASSIGN(WarpZone_trace_forent.dphitcontentsmask, DPCONTENTS_SOLID);
- trace_startsolid = sol;
- v_forward = vf;
- v_right = vr;
- v_up = vu;
-}
-
-void WarpZone_TraceBox(vector org, vector mi, vector ma, vector end, float nomonsters, entity forent)
-{
- WarpZone_TraceBox_ThroughZone(org, mi, ma, end, nomonsters, forent, world, WarpZone_trace_callback_t_null);
-}
-
-void WarpZone_TraceLine(vector org, vector end, float nomonsters, entity forent)
-{
- WarpZone_TraceBox(org, '0 0 0', '0 0 0', end, nomonsters, forent);
-}
-
-void WarpZone_TraceToss_ThroughZone(entity e, entity forent, entity zone, WarpZone_trace_callback_t cb)
-{
- float g, dt, i;
- vector vf, vr, vu, v0, o0;
- entity wz;
-
- o0 = e.origin;
- v0 = e.velocity;
- g = cvar("sv_gravity") * e.gravity;
-
- WarpZone_trace_forent = forent;
- WarpZone_trace_firstzone = world;
- WarpZone_trace_lastzone = world;
- WarpZone_Trace_InitTransform();
- WarpZone_tracetoss_time = 0;
- if(!warpzone_warpzones_exist)
- {
- tracetoss(e, WarpZone_trace_forent);
- if(cb)
- cb(e.origin, trace_endpos, trace_endpos);
- dt = vlen(e.origin - o0) / vlen(e.velocity);
- WarpZone_tracetoss_time += dt;
- e.velocity_z -= dt * g;
- WarpZone_tracetoss_velocity = e.velocity;
- e.velocity = v0;
- return;
- }
-
- vf = v_forward;
- vr = v_right;
- vu = v_up;
-
- // if starting in warpzone, first transform
- wz = WarpZone_Find(e.origin + e.mins, e.origin + e.maxs);
- if(wz)
- {
- WarpZone_trace_firstzone = wz;
- WarpZone_trace_lastzone = wz;
- if(zone && wz != zone)
- {
- // we are in ANOTHER warpzone. This is bad. Make a zero length trace and return.
-
- WarpZone_tracetoss_time = 0;
- trace_endpos = o0;
- goto fail;
- }
- WarpZone_Trace_AddTransform(wz);
- setorigin(e, WarpZone_TransformOrigin(wz, e.origin));
- e.velocity = WarpZone_TransformVelocity(wz, e.velocity);
- }
- WarpZone_MakeAllSolid();
- i = 16;
- for (;;)
- {
- if(--i < 1)
- {
- LOG_TRACE("Too many warpzones in sequence, aborting trace.\n");
- trace_ent = world;
- break;
- }
- tracetoss(e, WarpZone_trace_forent);
- if(cb)
- cb(e.origin, trace_endpos, trace_endpos);
- dt = vlen(trace_endpos - e.origin) / vlen(e.velocity);
- WarpZone_tracetoss_time += dt;
- e.origin = trace_endpos;
- e.velocity_z -= dt * g;
- if(trace_fraction >= 1)
- break;
- if(trace_ent.classname != "trigger_warpzone")
- break;
- if(trace_ent == wz)
- {
- // FIXME can this check be removed? Do we really need it?
- LOG_TRACE("I transformed into the same zone again, wtf, aborting the trace\n");
- trace_ent = world;
- break;
- }
- wz = trace_ent;
- if(!WarpZone_trace_firstzone)
- WarpZone_trace_firstzone = wz;
- WarpZone_trace_lastzone = wz;
- if(zone && wz != zone)
- break;
- WarpZone_Trace_AddTransform(wz);
- // we hit a warpzone... so, let's perform the trace after the warp again
- e.origin = WarpZone_TransformOrigin(wz, e.origin);
- e.velocity = WarpZone_TransformVelocity(wz, e.velocity);
-
- // we got warped, so let's step back a bit
- e.velocity = -e.velocity;
- tracetoss(e, WarpZone_trace_forent);
- dt = vlen(trace_endpos - e.origin) / vlen(e.velocity);
- WarpZone_tracetoss_time -= dt;
- e.origin = trace_endpos;
- e.velocity = -e.velocity;
- }
- WarpZone_MakeAllOther();
-:fail
- WarpZone_tracetoss_velocity = e.velocity;
- v_forward = vf;
- v_right = vr;
- v_up = vu;
- // restore old entity data (caller just uses trace_endpos, WarpZone_tracetoss_velocity and the transform)
- e.velocity = v0;
- e.origin = o0;
-}
-
-void WarpZone_TraceToss(entity e, entity forent)
-{
- WarpZone_TraceToss_ThroughZone(e, forent, world, WarpZone_trace_callback_t_null);
-}
-
-entity WarpZone_TrailParticles_trace_callback_own;
-float WarpZone_TrailParticles_trace_callback_eff;
-void WarpZone_TrailParticles_trace_callback(vector from, vector endpos, vector to)
-{
- trailparticles(WarpZone_TrailParticles_trace_callback_own, WarpZone_TrailParticles_trace_callback_eff, from, endpos);
-}
-
-void WarpZone_TrailParticles(entity own, float eff, vector org, vector end)
-{
- WarpZone_TrailParticles_trace_callback_own = own;
- WarpZone_TrailParticles_trace_callback_eff = eff;
- WarpZone_TraceBox_ThroughZone(org, '0 0 0', '0 0 0', end, MOVE_NOMONSTERS, world, world, WarpZone_TrailParticles_trace_callback);
-}
-
-#ifdef CSQC
-float WarpZone_TrailParticles_trace_callback_f;
-float WarpZone_TrailParticles_trace_callback_flags;
-void WarpZone_TrailParticles_WithMultiplier_trace_callback(vector from, vector endpos, vector to)
-{
- boxparticles(WarpZone_TrailParticles_trace_callback_eff, WarpZone_TrailParticles_trace_callback_own, from, endpos, WarpZone_TrailParticles_trace_callback_own.velocity, WarpZone_TrailParticles_trace_callback_own.velocity, WarpZone_TrailParticles_trace_callback_f, WarpZone_TrailParticles_trace_callback_flags);
-}
-
-void WarpZone_TrailParticles_WithMultiplier(entity own, float eff, vector org, vector end, float f, int boxflags)
-{
- WarpZone_TrailParticles_trace_callback_own = own;
- WarpZone_TrailParticles_trace_callback_eff = eff;
- WarpZone_TrailParticles_trace_callback_f = f;
- WarpZone_TrailParticles_trace_callback_flags = boxflags | PARTICLES_DRAWASTRAIL;
- WarpZone_TraceBox_ThroughZone(org, '0 0 0', '0 0 0', end, MOVE_NOMONSTERS, world, world, WarpZone_TrailParticles_WithMultiplier_trace_callback);
-}
-#endif
-
-float WarpZone_PlaneDist(entity wz, vector v)
-{
- return (v - wz.warpzone_origin) * wz.warpzone_forward;
-}
-
-float WarpZone_TargetPlaneDist(entity wz, vector v)
-{
- return (v - wz.warpzone_targetorigin) * wz.warpzone_targetforward;
-}
-
-vector WarpZone_TransformOrigin(entity wz, vector v)
-{
- return wz.warpzone_shift + AnglesTransform_Apply(wz.warpzone_transform, v);
-}
-
-vector WarpZone_TransformVelocity(entity wz, vector v)
-{
- return AnglesTransform_Apply(wz.warpzone_transform, v);
-}
-
-vector WarpZone_TransformAngles(entity wz, vector v)
-{
- return AnglesTransform_ApplyToAngles(wz.warpzone_transform, v);
-}
-
-vector WarpZone_TransformVAngles(entity wz, vector ang)
-{
-#ifdef KEEP_ROLL
- float roll;
- roll = ang.z;
- ang.z = 0;
-#endif
-
- ang = AnglesTransform_ApplyToVAngles(wz.warpzone_transform, ang);
-
-#ifdef KEEP_ROLL
- ang = AnglesTransform_Normalize(ang, true);
- ang = AnglesTransform_CancelRoll(ang);
- ang.z = roll;
-#else
- ang = AnglesTransform_Normalize(ang, false);
-#endif
-
- return ang;
-}
-
-vector WarpZone_UnTransformOrigin(entity wz, vector v)
-{
- return AnglesTransform_Apply(AnglesTransform_Invert(wz.warpzone_transform), v - wz.warpzone_shift);
-}
-
-vector WarpZone_UnTransformVelocity(entity wz, vector v)
-{
- return AnglesTransform_Apply(AnglesTransform_Invert(wz.warpzone_transform), v);
-}
-
-vector WarpZone_UnTransformAngles(entity wz, vector v)
-{
- return AnglesTransform_ApplyToAngles(AnglesTransform_Invert(wz.warpzone_transform), v);
-}
-
-vector WarpZone_UnTransformVAngles(entity wz, vector ang)
-{
- float roll;
-
- roll = ang.z;
- ang.z = 0;
-
- ang = AnglesTransform_ApplyToVAngles(AnglesTransform_Invert(wz.warpzone_transform), ang);
- ang = AnglesTransform_Normalize(ang, true);
- ang = AnglesTransform_CancelRoll(ang);
-
- ang.z = roll;
- return ang;
-}
-
-vector WarpZoneLib_NearestPointOnBox(vector mi, vector ma, vector org)
-{
- vector nearest;
- nearest.x = bound(mi.x, org.x, ma.x);
- nearest.y = bound(mi.y, org.y, ma.y);
- nearest.z = bound(mi.z, org.z, ma.z);
- return nearest;
-}
-
-bool WarpZoneLib_BadEntity(entity e)
-{
- string myclassname = e.classname;
- if (e.instanceOfObject) return true;
- switch(myclassname)
- {
- case "deathtype":
- case "weaponentity":
- case "exteriorweaponentity":
- case "csqc_score_team":
- case "pingplreport":
- case "ent_client_scoreinfo":
- case "saved_cvar_value":
- case "accuracy":
- case "entcs_sender":
- case "entcs_receiver":
- case "clientinit":
- case "sprite_waypoint":
- case "waypoint":
- case "gibsplash":
- //case "net_linked": // actually some real entities are linked without classname, fail
- case "":
- return true;
- }
-
- if(startsWith(myclassname, "msg_"))
- return true;
-
- if(startsWith(myclassname, "target_"))
- return true;
-
- if(startsWith(myclassname, "info_"))
- return true;
-
- return false;
-}
-
-.float WarpZone_findradius_hit;
-.entity WarpZone_findradius_next;
-void WarpZone_FindRadius_Recurse(vector org, float rad, vector org0, vector transform, vector shift, float needlineofsight)
-// blast origin of current search original blast origin how to untransform (victim to blast system)
-{
- vector org_new;
- vector org0_new;
- vector shift_new, transform_new;
- vector p;
- entity e, e0;
- entity wz;
- if(rad <= 0)
- return;
- e0 = findradius(org, rad);
- wz = world;
-
- for(e = e0; e; e = e.chain)
- {
- if(WarpZoneLib_BadEntity(e))
- continue;
- p = WarpZoneLib_NearestPointOnBox(e.origin + e.mins, e.origin + e.maxs, org0);
- if(needlineofsight)
- {
- traceline(org, p, MOVE_NOMONSTERS, e);
- if(trace_fraction < 1)
- continue;
- }
- if(!e.WarpZone_findradius_hit || vlen(e.WarpZone_findradius_dist) > vlen(org0 - p))
- {
- e.WarpZone_findradius_nearest = p;
- e.WarpZone_findradius_dist = org0 - p;
- e.WarpZone_findradius_findorigin = org;
- e.WarpZone_findradius_findradius = rad;
- if(e.classname == "warpzone_refsys")
- {
- // ignore, especially: do not overwrite the refsys parameters
- }
- else if(e.classname == "trigger_warpzone")
- {
- e.WarpZone_findradius_next = wz;
- wz = e;
- e.WarpZone_findradius_hit = 1;
- e.enemy.WarpZone_findradius_dist = '0 0 0'; // we don't want to go through this zone ever again
- e.enemy.WarpZone_findradius_hit = 1;
- }
- else
- {
- e.warpzone_transform = transform;
- e.warpzone_shift = shift;
- e.WarpZone_findradius_hit = 1;
- }
- }
- }
- for(e = wz; e; e = e.WarpZone_findradius_next)
- {
- if(WarpZoneLib_BadEntity(e))
- continue;
-
- org0_new = WarpZone_TransformOrigin(e, org);
- traceline(e.warpzone_targetorigin, org0_new, MOVE_NOMONSTERS, e);
- org_new = trace_endpos;
-
- transform_new = AnglesTransform_Multiply(e.warpzone_transform, transform);
- shift_new = AnglesTransform_Multiply_GetPostShift(e.warpzone_transform, e.warpzone_shift, transform, shift);
- WarpZone_FindRadius_Recurse(
- org_new,
- bound(0, rad - vlen(org_new - org0_new), rad - 8),
- org0_new,
- transform_new, shift_new,
- needlineofsight);
- e.WarpZone_findradius_hit = 0;
- e.enemy.WarpZone_findradius_hit = 0;
- }
-}
-entity WarpZone_FindRadius(vector org, float rad, float needlineofsight)
-{
- entity e0, e;
- WarpZone_FindRadius_Recurse(org, rad, org, '0 0 0', '0 0 0', needlineofsight);
- e0 = findchainfloat(WarpZone_findradius_hit, 1);
- for(e = e0; e; e = e.chain)
- e.WarpZone_findradius_hit = 0;
- return e0;
-}
-
-.entity WarpZone_refsys;
-void WarpZone_RefSys_GC()
-{SELFPARAM();
- // garbage collect unused reference systems
- self.nextthink = time + 1;
- if(self.owner.WarpZone_refsys != self)
- remove(self);
-}
-void WarpZone_RefSys_CheckCreate(entity me)
-{
- if(me.WarpZone_refsys.owner != me)
- {
- me.WarpZone_refsys = spawn();
- me.WarpZone_refsys.classname = "warpzone_refsys";
- me.WarpZone_refsys.owner = me;
- me.WarpZone_refsys.think = WarpZone_RefSys_GC;
- me.WarpZone_refsys.nextthink = time + 1;
- WarpZone_Accumulator_Clear(me.WarpZone_refsys);
- }
-}
-void WarpZone_RefSys_Clear(entity me)
-{
- if(me.WarpZone_refsys)
- {
- remove(me.WarpZone_refsys);
- me.WarpZone_refsys = world;
- }
-}
-void WarpZone_RefSys_AddTransform(entity me, vector t, vector s)
-{
- if(t != '0 0 0' || s != '0 0 0')
- {
- WarpZone_RefSys_CheckCreate(me);
- WarpZone_Accumulator_AddTransform(me.WarpZone_refsys, t, s);
- }
-}
-void WarpZone_RefSys_Add(entity me, entity wz)
-{
- WarpZone_RefSys_AddTransform(me, wz.warpzone_transform, wz.warpzone_shift);
-}
-void WarpZone_RefSys_AddInverseTransform(entity me, vector t, vector s)
-{
- if(t != '0 0 0' || s != '0 0 0')
- {
- WarpZone_RefSys_CheckCreate(me);
- WarpZone_Accumulator_AddInverseTransform(me.WarpZone_refsys, t, s);
- }
-}
-void WarpZone_RefSys_AddInverse(entity me, entity wz)
-{
- WarpZone_RefSys_AddInverseTransform(me, wz.warpzone_transform, wz.warpzone_shift);
-}
-.vector WarpZone_refsys_incremental_shift;
-.vector WarpZone_refsys_incremental_transform;
-void WarpZone_RefSys_AddIncrementally(entity me, entity ref)
-{
- //vector t, s;
- if(me.WarpZone_refsys_incremental_transform == ref.WarpZone_refsys.warpzone_transform)
- if(me.WarpZone_refsys_incremental_shift == ref.WarpZone_refsys.warpzone_shift)
- return;
- WarpZone_Accumulator_AddInverseTransform(me.WarpZone_refsys, me.WarpZone_refsys_incremental_transform, me.WarpZone_refsys_incremental_shift);
- WarpZone_Accumulator_Add(me.WarpZone_refsys, ref.WarpZone_refsys);
- me.WarpZone_refsys_incremental_shift = ref.WarpZone_refsys.warpzone_shift;
- me.WarpZone_refsys_incremental_transform = ref.WarpZone_refsys.warpzone_transform;
-}
-void WarpZone_RefSys_BeginAddingIncrementally(entity me, entity ref)
-{
- me.WarpZone_refsys_incremental_shift = ref.WarpZone_refsys.warpzone_shift;
- me.WarpZone_refsys_incremental_transform = ref.WarpZone_refsys.warpzone_transform;
-}
-vector WarpZone_RefSys_TransformOrigin(entity from, entity to, vector org)
-{
- if(from.WarpZone_refsys)
- org = WarpZone_UnTransformOrigin(from.WarpZone_refsys, org);
- if(to.WarpZone_refsys)
- org = WarpZone_TransformOrigin(to.WarpZone_refsys, org);
- return org;
-}
-vector WarpZone_RefSys_TransformVelocity(entity from, entity to, vector vel)
-{
- if(from.WarpZone_refsys)
- vel = WarpZone_UnTransformVelocity(from.WarpZone_refsys, vel);
- if(to.WarpZone_refsys)
- vel = WarpZone_TransformVelocity(to.WarpZone_refsys, vel);
- return vel;
-}
-vector WarpZone_RefSys_TransformAngles(entity from, entity to, vector ang)
-{
- if(from.WarpZone_refsys)
- ang = WarpZone_UnTransformAngles(from.WarpZone_refsys, ang);
- if(to.WarpZone_refsys)
- ang = WarpZone_TransformAngles(to.WarpZone_refsys, ang);
- return ang;
-}
-vector WarpZone_RefSys_TransformVAngles(entity from, entity to, vector ang)
-{
- if(from.WarpZone_refsys)
- ang = WarpZone_UnTransformVAngles(from.WarpZone_refsys, ang);
- if(to.WarpZone_refsys)
- ang = WarpZone_TransformVAngles(to.WarpZone_refsys, ang);
- return ang;
-}
-void WarpZone_RefSys_Copy(entity me, entity from)
-{
- if(from.WarpZone_refsys)
- {
- WarpZone_RefSys_CheckCreate(me);
- me.WarpZone_refsys.warpzone_shift = from.WarpZone_refsys.warpzone_shift;
- me.WarpZone_refsys.warpzone_transform = from.WarpZone_refsys.warpzone_transform;
- }
- else
- WarpZone_RefSys_Clear(me);
-}
-entity WarpZone_RefSys_SpawnSameRefSys(entity me)
-{
- entity e;
- e = spawn();
- WarpZone_RefSys_Copy(e, me);
- return e;
-}
-
-float WarpZoneLib_ExactTrigger_Touch()
-{SELFPARAM();
- return !WarpZoneLib_BoxTouchesBrush(other.absmin, other.absmax, self, other);
-}
-
-
-void WarpZoneLib_MoveOutOfSolid_Expand(entity e, vector by)
-{
- float eps = 0.0625;
- tracebox(e.origin, e.mins - '1 1 1' * eps, e.maxs + '1 1 1' * eps, e.origin + by, MOVE_WORLDONLY, e);
- if (trace_startsolid)
- return;
- if (trace_fraction < 1)
- {
- // hit something
- // adjust origin in the other direction...
- setorigin(e,e.origin - by * (1 - trace_fraction));
- }
-}
-
-float WarpZoneLib_MoveOutOfSolid(entity e)
-{
- vector o, m0, m1;
-
- o = e.origin;
- traceline(o, o, MOVE_WORLDONLY, e);
- if (trace_startsolid)
- return false;
-
- tracebox(o, e.mins, e.maxs, o, MOVE_WORLDONLY, e);
- if (!trace_startsolid)
- return true;
-
- m0 = e.mins;
- m1 = e.maxs;
- e.mins = '0 0 0';
- e.maxs = '0 0 0';
- WarpZoneLib_MoveOutOfSolid_Expand(e, '1 0 0' * m0_x);
- e.mins_x = m0_x;
- WarpZoneLib_MoveOutOfSolid_Expand(e, '1 0 0' * m1_x);
- e.maxs_x = m1_x;
- WarpZoneLib_MoveOutOfSolid_Expand(e, '0 1 0' * m0_y);
- e.mins_y = m0_y;
- WarpZoneLib_MoveOutOfSolid_Expand(e, '0 1 0' * m1_y);
- e.maxs_y = m1_y;
- WarpZoneLib_MoveOutOfSolid_Expand(e, '0 0 1' * m0_z);
- e.mins_z = m0_z;
- WarpZoneLib_MoveOutOfSolid_Expand(e, '0 0 1' * m1_z);
- e.maxs_z = m1_z;
- setorigin(e, e.origin);
-
- tracebox(e.origin, e.mins, e.maxs, e.origin, MOVE_WORLDONLY, e);
- if (trace_startsolid)
- {
- setorigin(e, o);
- return false;
- }
-
- return true;
-}
+++ /dev/null
-#ifndef WARPZONELIB_COMMON_H
-#define WARPZONELIB_COMMON_H
-
-// uncomment this if your mod uses the roll angle in fixangle
-// #define KEEP_ROLL
-
-float warpzone_warpzones_exist;
-float warpzone_cameras_exist;
-
-.float warpzone_isboxy;
-.vector warpzone_shift;
-.vector warpzone_origin;
-.vector warpzone_angles;
-.vector warpzone_forward;
-.vector warpzone_targetorigin;
-.vector warpzone_targetangles;
-.vector warpzone_targetforward;
-.vector warpzone_transform;
-.float warpzone_fadestart;
-.float warpzone_fadeend;
-void WarpZone_SetUp(entity e, vector my_org, vector my_ang, vector other_org, vector other_ang);
-void WarpZone_Camera_SetUp(entity e, vector my_org, vector my_ang);
-
-float WarpZoneLib_BoxTouchesBrush(vector mi, vector ma, entity e, entity ig);
-vector WarpZoneLib_NearestPointOnBox(vector mi, vector ma, vector org);
-
-entity WarpZone_Find(vector mi, vector ma);
-void WarpZone_MakeAllSolid();
-void WarpZone_MakeAllOther();
-
-#define MOVE_NOTHING -1
-entity WarpZone_trace_forent; // temp, callback is allowed to change it
-typedef void(vector start, vector hit, vector end) WarpZone_trace_callback_t; // called on every elementary trace
-var WarpZone_trace_callback_t WarpZone_trace_callback_t_null;
-entity WarpZone_trace_transform; // transform accumulator during a trace
-entity WarpZone_trace_firstzone; // first warpzone hit by a trace (can differ from the requested zone in case of _ThroughZone, the trace is aborted then)
-entity WarpZone_trace_lastzone; // first warpzone hit by a trace (can differ from the requested zone in case of _ThroughZone, the trace is aborted then)
-vector WarpZone_tracetoss_velocity; // ending velocity of a tracetoss (post-transform)
-float WarpZone_tracetoss_time; // duration of toss (approximate)
-void WarpZone_TraceBox(vector org, vector min, vector max, vector end, float nomonsters, entity forent);
-void WarpZone_TraceBox_ThroughZone(vector org, vector min, vector max, vector end, float nomonsters, entity forent, entity zone, WarpZone_trace_callback_t cb);
-void WarpZone_TraceLine(vector org, vector end, float nomonsters, entity forent);
-void WarpZone_TraceToss(entity e, entity forent);
-void WarpZone_TraceToss_ThroughZone(entity e, entity forent, entity zone, WarpZone_trace_callback_t cb);
-void WarpZone_TrailParticles(entity own, float eff, vector org, vector end);
-#ifdef CSQC
-void WarpZone_TrailParticles_WithMultiplier(entity own, float eff, vector org, vector end, float f, float boxflags);
-#endif
-
-.vector WarpZone_findradius_dist;
-.vector WarpZone_findradius_nearest;
-// also set: warpzone parameters, so WarpZone_TransformOrigin can transform vectors from blast's to victim's system
-.vector WarpZone_findradius_findorigin;
-.float WarpZone_findradius_findradius;
-entity WarpZone_FindRadius(vector org, float radius, float needlineofsight);
-
-float WarpZone_PlaneDist(entity wz, vector v);
-float WarpZone_TargetPlaneDist(entity wz, vector v);
-vector WarpZone_TransformOrigin(entity wz, vector v);
-vector WarpZone_TransformVelocity(entity wz, vector v);
-vector WarpZone_TransformAngles(entity wz, vector v);
-vector WarpZone_TransformVAngles(entity wz, vector v);
-vector WarpZone_UnTransformOrigin(entity wz, vector v);
-vector WarpZone_UnTransformVelocity(entity wz, vector v);
-vector WarpZone_UnTransformAngles(entity wz, vector v);
-vector WarpZone_UnTransformVAngles(entity wz, vector v);
-
-// reference systems (chained warpzone transforms)
-void WarpZone_RefSys_Clear(entity me); // R := id
-void WarpZone_RefSys_Add(entity me, entity wz); // me.R := wz me.R
-void WarpZone_RefSys_AddInverse(entity me, entity wz); // me.R := wz^-1 me.R
-void WarpZone_RefSys_AddTransform(entity me, vector t, vector s); // me.R := [t s] me.R
-void WarpZone_RefSys_AddInverseTransform(entity me, vector t, vector s); // me.R := [t s]^-1 me.R
-
-// makes this reference system track ref's changes
-// NOTE: this is ONLY sensible if WarpZone_RefSys_Add is no longer called on "me" while doing this
-// To achieve this, make sure no touch events on warpzone are raised by this entity
-// or set a movetype that causes no warpzoning (e.g. MOVETYPE_NONE, MOVETYPE_FOLLOW)
-void WarpZone_RefSys_AddIncrementally(entity me, entity ref); // me.R := ref.R me.Rref^-1 me.R; me.Rref := ref.R
-void WarpZone_RefSys_BeginAddingIncrementally(entity me, entity ref); // me.Rref := ref.R
-
-vector WarpZone_RefSys_TransformOrigin(entity from, entity to, vector org); // return to.R from.R^-1 org
-vector WarpZone_RefSys_TransformVelocity(entity from, entity to, vector vel); // return to.R from.R^-1 vel
-vector WarpZone_RefSys_TransformAngles(entity from, entity to, vector ang); // return to.R from.R^-1 ang
-vector WarpZone_RefSys_TransformVAngles(entity from, entity to, vector ang); // return to.R from.R^-1 ang
-void WarpZone_RefSys_Copy(entity me, entity from); // to.R := from.R
-entity WarpZone_RefSys_SpawnSameRefSys(entity me); // spawn().R = me.R
-
-#ifndef BITCLR
-# define BITCLR(a,b) ((a) - ((a) & (b)))
-#endif
-#ifndef BITSET
-# define BITSET(a,b) ((a) | (b))
-#endif
-#ifndef BITXOR
-# define BITXOR(a,b) (((a) | (b)) - ((a) & (b)))
-#endif
-#ifndef BITCLR_ASSIGN
-# define BITCLR_ASSIGN(a,b) ((a) = (a) - ((a) & (b)))
-#endif
-#ifndef BITSET_ASSIGN
-# define BITSET_ASSIGN(a,b) ((a) |= (b))
-#endif
-#ifndef BITXOR_ASSIGN
-# define BITXOR_ASSIGN(a,b) ((a) = ((a) | (b)) - ((a) & (b)))
-#endif
-float WarpZoneLib_MoveOutOfSolid(entity e);
-#define move_out_of_solid(e) WarpZoneLib_MoveOutOfSolid(e)
-
-float WarpZoneLib_ExactTrigger_Touch();
-void WarpZoneLib_ExactTrigger_Init();
-
-// WARNING: this kills the trace globals
-#define EXACTTRIGGER_TOUCH if(WarpZoneLib_ExactTrigger_Touch()) return
-#define EXACTTRIGGER_INIT WarpZoneLib_ExactTrigger_Init()
-#endif
+++ /dev/null
-#include "mathlib.qh"
-#if defined(CSQC)
-#elif defined(MENUQC)
-#elif defined(SVQC)
-#endif
-
-int fpclassify(float x)
-{
- if(isnan(x))
- return FP_NAN;
- if(isinf(x))
- return FP_INFINITE;
- if(x == 0)
- return FP_ZERO;
- return FP_NORMAL;
-}
-bool isfinite(float x)
-{
- return !(isnan(x) || isinf(x));
-}
-bool isinf(float x)
-{
- return (x != 0) && (x + x == x);
-}
-bool isnan(float x)
-{
- float y;
- y = x;
- return (x != y);
-}
-bool isnormal(float x)
-{
- return isfinite(x);
-}
-bool signbit(float x)
-{
- return (x < 0);
-}
-
-float acosh(float x)
-{
- return log(x + sqrt(x*x - 1));
-}
-float asinh(float x)
-{
- return log(x + sqrt(x*x + 1));
-}
-float atanh(float x)
-{
- return 0.5 * log((1+x) / (1-x));
-}
-float cosh(float x)
-{
- return 0.5 * (exp(x) + exp(-x));
-}
-float sinh(float x)
-{
- return 0.5 * (exp(x) - exp(-x));
-}
-float tanh(float x)
-{
- return sinh(x) / cosh(x);
-}
-
-float exp(float x)
-{
- return pow(M_E, x);
-}
-float exp2(float x)
-{
- return pow(2, x);
-}
-float expm1(float x)
-{
- return exp(x) - 1;
-}
-
-vector frexp(float x)
-{
- vector v;
- v.z = 0;
- v.y = ilogb(x) + 1;
- v.x = x / exp2(v.y);
- return v;
-}
-int ilogb(float x)
-{
- return floor(log2(fabs(x)));
-}
-float ldexp(float x, int e)
-{
- return x * pow(2, e);
-}
-float logn(float x, float base)
-{
- return log(x) / log(base);
-}
-float log10(float x)
-{
- return log(x) * M_LOG10E;
-}
-float log1p(float x)
-{
- return log(x + 1);
-}
-float log2(float x)
-{
- return log(x) * M_LOG2E;
-}
-float logb(float x)
-{
- return floor(log2(fabs(x)));
-}
-vector modf(float f)
-{
- return '1 0 0' * (f - trunc(f)) + '0 1 0' * trunc(f);
-}
-
-float scalbn(float x, int n)
-{
- return x * pow(2, n);
-}
-
-float cbrt(float x)
-{
- return copysign(pow(fabs(x), 1.0/3.0), x);
-}
-float hypot(float x, float y)
-{
- return sqrt(x*x + y*y);
-}
-
-float erf(float x)
-{
- // approximation taken from wikipedia
- float y;
- y = x*x;
- return copysign(sqrt(1 - exp(-y * (1.273239544735163 + 0.14001228868667 * y) / (1 + 0.14001228868667 * y))), x);
-}
-float erfc(float x)
-{
- return 1.0 - erf(x);
-}
-vector lgamma(float x)
-{
- // TODO improve accuracy
- if(!isfinite(x))
- return fabs(x) * '1 0 0' + copysign(1, x) * '0 1 0';
- if(x < 1 && x == floor(x))
- return nan("gamma") * '1 1 1';
- if(x < 0.1)
- {
- vector v;
- v = lgamma(1.0 - x);
- // reflection formula:
- // gamma(1-z) * gamma(z) = pi / sin(pi*z)
- // lgamma(1-z) + lgamma(z) = log(pi) - log(sin(pi*z))
- // sign of gamma(1-z) = sign of gamma(z) * sign of sin(pi*z)
- v.z = sin(M_PI * x);
- v.x = log(M_PI) - log(fabs(v.z)) - v.x;
- if(v.z < 0)
- v.y = -v.y;
- v.z = 0;
- return v;
- }
- if(x < 1.1)
- return lgamma(x + 1) - log(x) * '1 0 0';
- x -= 1;
- return (0.5 * log(2 * M_PI * x) + x * (log(x) - 1)) * '1 0 0' + '0 1 0';
-}
-float tgamma(float x)
-{
- vector v;
- v = lgamma(x);
- return exp(v.x) * v.y;
-}
-
-/**
- * Pythonic mod:
- * TODO: %% operator?
- *
- * 1 % 2 == 1
- * -1 % 2 == 1
- * 1 % -2 == -1
- * -1 % -2 == -1
- */
-float pymod(float x, float y)
-{
- return x - y * floor(x / y);
-}
-
-float nearbyint(float x)
-{
- return rint(x);
-}
-float trunc(float x)
-{
- return (x>=0) ? floor(x) : ceil(x);
-}
-
-float fmod(float x, float y)
-{
- return x - y * trunc(x / y);
-}
-float remainder(float x, float y)
-{
- return x - y * rint(x / y);
-}
-vector remquo(float x, float y)
-{
- vector v;
- v.z = 0;
- v.y = rint(x / y);
- v.x = x - y * v.y;
- return v;
-}
-
-float copysign(float x, float y)
-{
- return fabs(x) * ((y>0) ? 1 : -1);
-}
-float nan(string tag)
-{
- return sqrt(-1);
-}
-float nextafter(float x, float y)
-{
- // TODO very crude
- if(x == y)
- return nan("nextafter");
- if(x > y)
- return -nextafter(-x, -y);
- // now we know that x < y
- // so we need the next number > x
- float d, a, b;
- d = max(fabs(x), 0.00000000000000000000001);
- a = x + d;
- do
- {
- d *= 0.5;
- b = a;
- a = x + d;
- }
- while(a != x);
- return b;
-}
-float nexttoward(float x, float y)
-{
- return nextafter(x, y);
-}
-
-float fdim(float x, float y)
-{
- return max(x-y, 0);
-}
-float fmax(float x, float y)
-{
- return max(x, y);
-}
-float fmin(float x, float y)
-{
- return min(x, y);
-}
-float fma(float x, float y, float z)
-{
- return x * y + z;
-}
-
-int isgreater(float x, float y)
-{
- return x > y;
-}
-int isgreaterequal(float x, float y)
-{
- return x >= y;
-}
-int isless(float x, float y)
-{
- return x < y;
-}
-int islessequal(float x, float y)
-{
- return x <= y;
-}
-int islessgreater(float x, float y)
-{
- return x < y || x > y;
-}
-int isunordered(float x, float y)
-{
- return !(x < y || x == y || x > y);
-}
-
-vector cross(vector a, vector b)
-{
- return
- '1 0 0' * (a.y * b.z - a.z * b.y)
- + '0 1 0' * (a.z * b.x - a.x * b.z)
- + '0 0 1' * (a.x * b.y - a.y * b.x);
-}
+++ /dev/null
-#ifndef MATHLIB_H
-#define MATHLIB_H
-
-// <math.h>
-
-// The commented-out functions need no implementation because DarkPlaces offers
-// them as builtins. They are listed here anyway for completeness sake.
-
-const int FP_NAN = 0;
-const int FP_INFINITE = 1;
-const int FP_ZERO = 2;
-const int FP_SUBNORMAL = 3;
-const int FP_NORMAL = 4;
-int fpclassify(float x);
-bool isfinite(float x);
-bool isinf(float x);
-bool isnan(float x);
-bool isnormal(float x);
-bool signbit(float x);
-
-//float acos(float x);
-//float asin(float x);
-//float atan(float x);
-//float atan2(float y, float x);
-//float cos(float x);
-//float sin(float x);
-//float tan(float x);
-
-float acosh(float x);
-float asinh(float x);
-float atanh(float x);
-float cosh(float x);
-float sinh(float x);
-float tanh(float x);
-
-float exp(float x);
-float exp2(float x);
-float expm1(float x);
-
-vector frexp(float x); // returns mantissa as _x, exponent as _y
-int ilogb(float x);
-float ldexp(float x, int e);
-//float log(float x);
-float logn(float x, float base);
-float log10(float x);
-float log1p(float x);
-float log2(float x);
-float logb(float x);
-vector modf(float f); // fraction as _x, integer as _y
-
-float scalbn(float x, int n);
-
-float cbrt(float x);
-//float fabs(float x);
-float hypot(float x, float y);
-//float pow(float x, float y);
-//float sqrt(float x, float y);
-
-float erf(float x);
-float erfc(float x);
-vector lgamma(float x); // value in _x, sign in _y
-float tgamma(float x);
-
-/**
- * Pythonic mod:
- * TODO: %% operator?
- *
- * 1 % 2 == 1
- * -1 % 2 == 1
- * 1 % -2 == -1
- * -1 % -2 == -1
- */
-float pymod(float x, float y);
-
-//float ceil(float x);
-//float floor(float x);
-float nearbyint(float x);
-//float rint(float x);
-//float round(float x);
-float trunc(float x);
-
-float fmod(float x, float y);
-float remainder(float x, float y);
-vector remquo(float x, float y);
-
-float copysign(float x, float y);
-float nan(string tag);
-float nextafter(float x, float y);
-float nexttoward(float x, float y);
-
-float fdim(float x, float y);
-float fmax(float x, float y);
-float fmin(float x, float y);
-float fma(float x, float y, float z);
-
-int isgreater(float x, float y);
-int isgreaterequal(float x, float y);
-int isless(float x, float y);
-int islessequal(float x, float y);
-int islessgreater(float x, float y);
-int isunordered(float x, float y);
-
-const float M_E = 2.7182818284590452354; /* e */
-const float M_LOG2E = 1.4426950408889634074; /* log_2 e */
-const float M_LOG10E = 0.43429448190325182765; /* log_10 e */
-const float M_LN2 = 0.69314718055994530942; /* log_e 2 */
-const float M_LN10 = 2.30258509299404568402; /* log_e 10 */
-// -Wdouble-declaration
-#define M_PI 3.14159265358979323846 /* pi */
-const float M_PI_2 = 1.57079632679489661923; /* pi/2 */
-const float M_PI_4 = 0.78539816339744830962; /* pi/4 */
-const float M_1_PI = 0.31830988618379067154; /* 1/pi */
-const float M_2_PI = 0.63661977236758134308; /* 2/pi */
-const float M_2_SQRTPI = 1.12837916709551257390; /* 2/sqrt(pi) */
-const float M_SQRT2 = 1.41421356237309504880; /* sqrt(2) */
-const float M_SQRT1_2 = 0.70710678118654752440; /* 1/sqrt(2) */
-
-// Non-<math.h> stuff follows here.
-vector cross(vector a, vector b);
-
-#endif
+++ /dev/null
-#include "server.qh"
-
-#include "common.qh"
-#if defined(CSQC)
-#elif defined(MENUQC)
-#elif defined(SVQC)
- #include "../common/constants.qh"
- #include "../common/triggers/subs.qh"
- #include "../common/util.qh"
- #include "../server/command/common.qh"
- #include "../server/constants.qh"
- #include "../server/defs.qh"
-#endif
-
-#ifdef WARPZONELIB_KEEPDEBUG
-#define WARPZONELIB_REMOVEHACK
-#endif
-
-// for think function
-.vector warpzone_save_origin;
-.vector warpzone_save_angles;
-.vector warpzone_save_eorigin;
-.vector warpzone_save_eangles;
-
-// for all entities
-.vector warpzone_oldorigin, warpzone_oldvelocity, warpzone_oldangles;
-.float warpzone_teleport_time;
-.float warpzone_teleport_finishtime;
-.entity warpzone_teleport_zone;
-
-void WarpZone_StoreProjectileData(entity e)
-{
- e.warpzone_oldorigin = e.origin;
- e.warpzone_oldvelocity = e.velocity;
- e.warpzone_oldangles = e.angles;
-}
-
-void WarpZone_TeleportPlayer(entity teleporter, entity player, vector to, vector to_angles, vector to_velocity)
-{
- setorigin (player, to); // NOTE: this also aborts the move, when this is called by touch
- player.oldorigin = to; // for DP's unsticking
- player.angles = to_angles;
- player.fixangle = true;
- player.velocity = to_velocity;
-
- BITXOR_ASSIGN(player.effects, EF_TELEPORT_BIT);
-
- if(IS_PLAYER(player))
- BITCLR_ASSIGN(player.flags, FL_ONGROUND);
-
- WarpZone_PostTeleportPlayer_Callback(player);
-}
-
-bool WarpZone_Teleported_Send(entity to, int sf)
-{SELFPARAM();
- WriteByte(MSG_ENTITY, ENT_CLIENT_WARPZONE_TELEPORTED);
- WriteCoord(MSG_ENTITY, self.angles.x);
- WriteCoord(MSG_ENTITY, self.angles.y);
- WriteCoord(MSG_ENTITY, self.angles.z);
- return true;
-}
-
-float WarpZone_Teleport(entity wz, entity player, float f0, float f1)
-{
- vector o0, a0, v0, o1, a1, v1, o10;
-
- o0 = player.origin + player.view_ofs;
- v0 = player.velocity;
- a0 = player.angles;
-
- o10 = o1 = WarpZone_TransformOrigin(wz, o0);
- v1 = WarpZone_TransformVelocity(wz, v0);
- if (!IS_NOT_A_CLIENT(player))
- a1 = WarpZone_TransformVAngles(wz, player.v_angle);
- else
- a1 = WarpZone_TransformAngles(wz, a0);
-
- if(f0 != 0 || f1 != 0)
- {
- // retry last move but behind the warpzone!
- // we must first go back as far as we can, then forward again, to not cause double touch events!
-
- tracebox(o1 - player.view_ofs + v1 * frametime * f1, player.mins, player.maxs, o1 - player.view_ofs + v1 * frametime * f0, MOVE_WORLDONLY, player);
- {
- entity own;
- own = player.owner;
- player.owner = world;
- tracebox(trace_endpos, player.mins, player.maxs, o1 - player.view_ofs + v1 * frametime * f1, MOVE_NORMAL, player); // this should get us through the warpzone
- player.owner = own;
- }
- o1 = trace_endpos + player.view_ofs;
-
- float d, dv, md;
- md = max(vlen(player.mins), vlen(player.maxs));
- d = WarpZone_TargetPlaneDist(wz, o1);
- dv = WarpZone_TargetPlaneDist(wz, v1);
- if(d < 0)
- o1 = o1 - v1 * (d / dv);
- }
-
- // put him out of solid
- tracebox(o1 - player.view_ofs, player.mins, player.maxs, o1 - player.view_ofs, MOVE_NOMONSTERS, player);
- if(trace_startsolid)
- {
- setorigin(player, o1 - player.view_ofs);
- if(WarpZoneLib_MoveOutOfSolid(player))
- {
- o1 = player.origin + player.view_ofs;
- setorigin(player, o0 - player.view_ofs);
- }
- else
- {
- LOG_INFO("would have to put player in solid, won't do that\n");
- setorigin(player, o0 - player.view_ofs);
- return 0;
- }
- }
-
- // do the teleport
- WarpZone_RefSys_Add(player, wz);
- WarpZone_TeleportPlayer(wz, player, o1 - player.view_ofs, a1, v1);
- WarpZone_StoreProjectileData(player);
- player.warpzone_teleport_time = time;
- player.warpzone_teleport_finishtime = time;
- player.warpzone_teleport_zone = wz;
-
- // prevent further teleports back
- float dt = (o1 - o10) * v1 * (1 / (v1 * v1));
- if(dt < sys_frametime)
- player.warpzone_teleport_finishtime += sys_frametime - dt;
-
-#ifndef WARPZONE_USE_FIXANGLE
- if(IS_VEHICLE(player) && player.owner)
- player = player.owner; // hax
- if(IS_PLAYER(player))
- {
- // instead of fixangle, send the transform to the client for smoother operation
- player.fixangle = false;
-
- entity ts = spawn();
- setmodel(ts, MDL_Null);
- ts.SendEntity = WarpZone_Teleported_Send;
- ts.SendFlags = 0xFFFFFF;
- ts.drawonlytoclient = player;
- ts.think = SUB_Remove;
- ts.nextthink = time + 1;
- ts.owner = player;
- ts.enemy = wz;
- ts.effects = EF_NODEPTHTEST;
- ts.classname = "warpzone_teleported";
- ts.angles = wz.warpzone_transform;
- }
-#endif
-
- return 1;
-}
-
-void WarpZone_Touch (void)
-{SELFPARAM();
- if(other.classname == "trigger_warpzone")
- return;
-
- if(time <= other.warpzone_teleport_finishtime) // already teleported this frame
- return;
-
- // FIXME needs a better check to know what is safe to teleport and what not
- if(other.movetype == MOVETYPE_NONE || other.movetype == MOVETYPE_FOLLOW || other.tag_entity)
- return;
-
- if(WarpZoneLib_ExactTrigger_Touch())
- return;
-
- if(WarpZone_PlaneDist(self, other.origin + other.view_ofs) >= 0) // wrong side of the trigger_warpzone (don't teleport yet)
- return;
-
- float f;
- // number of frames we need to go back:
- // dist = 16*sqrt(2) qu
- // dist ~ 24 qu
- // 24 qu = v*t
- // 24 qu = v*frametime*n
- // n = 24 qu/(v*frametime)
- // for clients go only one frame though, may be too irritating otherwise
- // but max 0.25 sec = 0.25/frametime frames
- // 24/(0.25/frametime)
- // 96*frametime
- float d;
- d = 24 + max(vlen(other.mins), vlen(other.maxs));
- if(IS_NOT_A_CLIENT(other))
- f = -d / bound(frametime * d * 1, frametime * vlen(other.velocity), d);
- else
- f = -1;
- if(WarpZone_Teleport(self, other, f, 0))
- {
- string save1, save2;
- activator = other;
-
- save1 = self.target; self.target = string_null;
- save2 = self.target3; self.target3 = string_null;
- SUB_UseTargets();
- if (!self.target) self.target = save1;
- if (!self.target3) self.target3 = save2;
-
- setself(self.enemy);
- save1 = self.target; self.target = string_null;
- save2 = self.target2; self.target2 = string_null;
- SUB_UseTargets();
- if (!self.target) self.target = save1;
- if (!self.target2) self.target2 = save2;
- setself(this);
- }
- else
- {
- LOG_TRACE("WARPZONE FAIL AHAHAHAHAH))\n");
- }
-}
-
-bool WarpZone_Send(entity to, int sendflags)
-{SELFPARAM();
- WriteByte(MSG_ENTITY, ENT_CLIENT_WARPZONE);
-
- // we must send this flag for clientside to match properly too
- int f = 0;
- if(self.warpzone_isboxy)
- BITSET_ASSIGN(f, 1);
- if(self.warpzone_fadestart)
- BITSET_ASSIGN(f, 2);
- if(self.origin != '0 0 0')
- BITSET_ASSIGN(f, 4);
- WriteByte(MSG_ENTITY, f);
-
- // we need THESE to render the warpzone (and cull properly)...
- if(f & 4)
- {
- WriteCoord(MSG_ENTITY, self.origin.x);
- WriteCoord(MSG_ENTITY, self.origin.y);
- WriteCoord(MSG_ENTITY, self.origin.z);
- }
-
- WriteShort(MSG_ENTITY, self.modelindex);
- WriteCoord(MSG_ENTITY, self.mins.x);
- WriteCoord(MSG_ENTITY, self.mins.y);
- WriteCoord(MSG_ENTITY, self.mins.z);
- WriteCoord(MSG_ENTITY, self.maxs.x);
- WriteCoord(MSG_ENTITY, self.maxs.y);
- WriteCoord(MSG_ENTITY, self.maxs.z);
- WriteByte(MSG_ENTITY, bound(1, self.scale * 16, 255));
-
- // we need THESE to calculate the proper transform
- WriteCoord(MSG_ENTITY, self.warpzone_origin.x);
- WriteCoord(MSG_ENTITY, self.warpzone_origin.y);
- WriteCoord(MSG_ENTITY, self.warpzone_origin.z);
- WriteCoord(MSG_ENTITY, self.warpzone_angles.x);
- WriteCoord(MSG_ENTITY, self.warpzone_angles.y);
- WriteCoord(MSG_ENTITY, self.warpzone_angles.z);
- WriteCoord(MSG_ENTITY, self.warpzone_targetorigin.x);
- WriteCoord(MSG_ENTITY, self.warpzone_targetorigin.y);
- WriteCoord(MSG_ENTITY, self.warpzone_targetorigin.z);
- WriteCoord(MSG_ENTITY, self.warpzone_targetangles.x);
- WriteCoord(MSG_ENTITY, self.warpzone_targetangles.y);
- WriteCoord(MSG_ENTITY, self.warpzone_targetangles.z);
-
- if(f & 2)
- {
- WriteShort(MSG_ENTITY, self.warpzone_fadestart);
- WriteShort(MSG_ENTITY, self.warpzone_fadeend);
- }
-
- return true;
-}
-
-bool WarpZone_Camera_Send(entity to, int sendflags)
-{SELFPARAM();
- int f = 0;
- WriteByte(MSG_ENTITY, ENT_CLIENT_WARPZONE_CAMERA);
-
- if(self.warpzone_fadestart)
- BITSET_ASSIGN(f, 2);
- if(self.origin != '0 0 0')
- BITSET_ASSIGN(f, 4);
- WriteByte(MSG_ENTITY, f);
-
- // we need THESE to render the warpzone (and cull properly)...
- if(f & 4)
- {
- WriteCoord(MSG_ENTITY, self.origin.x);
- WriteCoord(MSG_ENTITY, self.origin.y);
- WriteCoord(MSG_ENTITY, self.origin.z);
- }
-
- WriteShort(MSG_ENTITY, self.modelindex);
- WriteCoord(MSG_ENTITY, self.mins.x);
- WriteCoord(MSG_ENTITY, self.mins.y);
- WriteCoord(MSG_ENTITY, self.mins.z);
- WriteCoord(MSG_ENTITY, self.maxs.x);
- WriteCoord(MSG_ENTITY, self.maxs.y);
- WriteCoord(MSG_ENTITY, self.maxs.z);
- WriteByte(MSG_ENTITY, bound(1, self.scale * 16, 255));
-
- // we need THESE to calculate the proper transform
- WriteCoord(MSG_ENTITY, self.enemy.origin.x);
- WriteCoord(MSG_ENTITY, self.enemy.origin.y);
- WriteCoord(MSG_ENTITY, self.enemy.origin.z);
- WriteCoord(MSG_ENTITY, self.enemy.angles.x);
- WriteCoord(MSG_ENTITY, self.enemy.angles.y);
- WriteCoord(MSG_ENTITY, self.enemy.angles.z);
-
- if(f & 2)
- {
- WriteShort(MSG_ENTITY, self.warpzone_fadestart);
- WriteShort(MSG_ENTITY, self.warpzone_fadeend);
- }
-
- return true;
-}
-
-#ifdef WARPZONELIB_KEEPDEBUG
-float WarpZone_CheckProjectileImpact(entity player)
-{SELFPARAM();
- vector o0, v0;
-
- o0 = player.origin + player.view_ofs;
- v0 = player.velocity;
-
- // if we teleported shortly before, abort
- if(time <= player.warpzone_teleport_finishtime + 0.1)
- return 0;
-
- // if player hit a warpzone, abort
- entity wz;
- wz = WarpZone_Find(o0 + player.mins, o0 + player.maxs);
- if(!wz)
- return 0;
-
-#ifdef WARPZONELIB_REMOVEHACK
- LOG_INFO("impactfilter found something - and it no longer gets handled correctly - please tell divVerent whether anything behaves broken now\n");
-#else
- LOG_INFO("impactfilter found something - and it even gets handled correctly - please tell divVerent that this code apparently gets triggered again\n");
-#endif
- LOG_INFO("Entity type: ", player.classname, "\n");
- LOG_INFO("Origin: ", vtos(player.origin), "\n");
- LOG_INFO("Velocity: ", vtos(player.velocity), "\n");
-
-#ifdef WARPZONELIB_REMOVEHACK
- return 0;
-#else
- // retry previous move
- setorigin(player, player.warpzone_oldorigin);
- player.velocity = player.warpzone_oldvelocity;
- if(WarpZone_Teleport(wz, player, 0, 1))
- {
- entity oldself;
- string save1, save2;
-
- oldself = self;
- self = wz;
- other = player;
- activator = player;
-
- save1 = self.target; self.target = string_null;
- save2 = self.target3; self.target3 = string_null;
- SUB_UseTargets();
- if (!self.target) self.target = save1;
- if (!self.target3) self.target3 = save2;
-
- self = self.enemy;
- save1 = self.target; self.target = string_null;
- save2 = self.target2; self.target2 = string_null;
- SUB_UseTargets();
- if (!self.target) self.target = save1;
- if (!self.target2) self.target2 = save2;
- self = oldself;
- }
- else
- {
- setorigin(player, o0 - player.view_ofs);
- player.velocity = v0;
- }
-
- return +1;
-#endif
-}
-#endif
-
-float WarpZone_Projectile_Touch()
-{SELFPARAM();
- if(other.classname == "trigger_warpzone")
- return true;
-
- // no further impacts if we teleported this frame!
- // this is because even if we did teleport, the engine still may raise
- // touch events for the previous location
- // engine now aborts moves on teleport, so this SHOULD not happen any more
- // but if this is called from TouchAreaGrid of the projectile moving,
- // then this won't do
- if(time == self.warpzone_teleport_time)
- return true;
-
-#ifdef WARPZONELIB_KEEPDEBUG
- // this SEEMS to not happen at the moment, but if it did, it would be more reliable
- {
- float save_dpstartcontents;
- float save_dphitcontents;
- float save_dphitq3surfaceflags;
- string save_dphittexturename;
- float save_allsolid;
- float save_startsolid;
- float save_fraction;
- vector save_endpos;
- vector save_plane_normal;
- float save_plane_dist;
- entity save_ent;
- float save_inopen;
- float save_inwater;
- save_dpstartcontents = trace_dpstartcontents;
- save_dphitcontents = trace_dphitcontents;
- save_dphitq3surfaceflags = trace_dphitq3surfaceflags;
- save_dphittexturename = trace_dphittexturename;
- save_allsolid = trace_allsolid;
- save_startsolid = trace_startsolid;
- save_fraction = trace_fraction;
- save_endpos = trace_endpos;
- save_plane_normal = trace_plane_normal;
- save_plane_dist = trace_plane_dist;
- save_ent = trace_ent;
- save_inopen = trace_inopen;
- save_inwater = trace_inwater;
- float f;
- if((f = WarpZone_CheckProjectileImpact(self)) != 0)
- return (f > 0);
- trace_dpstartcontents = save_dpstartcontents;
- trace_dphitcontents = save_dphitcontents;
- trace_dphitq3surfaceflags = save_dphitq3surfaceflags;
- trace_dphittexturename = save_dphittexturename;
- trace_allsolid = save_allsolid;
- trace_startsolid = save_startsolid;
- trace_fraction = save_fraction;
- trace_endpos = save_endpos;
- trace_plane_normal = save_plane_normal;
- trace_plane_dist = save_plane_dist;
- trace_ent = save_ent;
- trace_inopen = save_inopen;
- trace_inwater = save_inwater;
- }
-#endif
-
- if(WarpZone_Projectile_Touch_ImpactFilter_Callback())
- return true;
-
- return false;
-}
-
-void WarpZone_InitStep_FindOriginTarget()
-{SELFPARAM();
- if(self.killtarget != "")
- {
- self.aiment = find(world, targetname, self.killtarget);
- if(self.aiment == world)
- {
- error("Warp zone with nonexisting killtarget");
- return;
- }
- self.killtarget = string_null;
- }
-}
-
-void WarpZonePosition_InitStep_FindTarget()
-{SELFPARAM();
- if(self.target == "")
- {
- error("Warp zone position with no target");
- return;
- }
- self.enemy = find(world, targetname, self.target);
- if(self.enemy == world)
- {
- error("Warp zone position with nonexisting target");
- return;
- }
- if(self.enemy.aiment)
- {
- // already is positioned
- error("Warp zone position targeting already oriented warpzone");
- return;
- }
- self.enemy.aiment = self;
-}
-
-void WarpZoneCamera_Think(void)
-{SELFPARAM();
- if(self.warpzone_save_origin != self.origin
- || self.warpzone_save_angles != self.angles
- || self.warpzone_save_eorigin != self.enemy.origin
- || self.warpzone_save_eangles != self.enemy.angles)
- {
- WarpZone_Camera_SetUp(self, self.enemy.origin, self.enemy.angles);
- self.warpzone_save_origin = self.origin;
- self.warpzone_save_angles = self.angles;
- self.warpzone_save_eorigin = self.enemy.origin;
- self.warpzone_save_eangles = self.enemy.angles;
- }
- self.nextthink = time;
-}
-
-void WarpZoneCamera_InitStep_FindTarget()
-{SELFPARAM();
- entity e;
- float i;
- if(self.target == "")
- {
- error("Camera with no target");
- return;
- }
- self.enemy = world;
- for(e = world, i = 0; (e = find(e, targetname, self.target)); )
- if(random() * ++i < 1)
- self.enemy = e;
- if(self.enemy == world)
- {
- error("Camera with nonexisting target");
- return;
- }
- warpzone_cameras_exist = 1;
- WarpZone_Camera_SetUp(self, self.enemy.origin, self.enemy.angles);
- self.SendFlags = 0xFFFFFF;
- if(self.spawnflags & 1)
- {
- self.think = WarpZoneCamera_Think;
- self.nextthink = time;
- }
- else
- self.nextthink = 0;
-}
-
-void WarpZone_InitStep_UpdateTransform()
-{SELFPARAM();
- vector org, ang, norm, point;
- float area;
- vector tri, a, b, c, n;
- float i_s, i_t, n_t;
- string tex;
-
- org = self.origin;
- if(org == '0 0 0')
- org = 0.5 * (self.mins + self.maxs);
-
- norm = point = '0 0 0';
- area = 0;
- for(i_s = 0; ; ++i_s)
- {
- tex = getsurfacetexture(self, i_s);
- if (!tex)
- break; // this is beyond the last one
- if(tex == "textures/common/trigger" || tex == "trigger")
- continue;
- n_t = getsurfacenumtriangles(self, i_s);
- for(i_t = 0; i_t < n_t; ++i_t)
- {
- tri = getsurfacetriangle(self, i_s, i_t);
- a = getsurfacepoint(self, i_s, tri.x);
- b = getsurfacepoint(self, i_s, tri.y);
- c = getsurfacepoint(self, i_s, tri.z);
- n = cross(c - a, b - a);
- area = area + vlen(n);
- norm = norm + n;
- point = point + vlen(n) * (a + b + c);
- }
- }
- if(area > 0)
- {
- norm = norm * (1 / area);
- point = point * (1 / (3 * area));
- if(vlen(norm) < 0.99)
- {
- LOG_INFO("trigger_warpzone near ", vtos(self.aiment.origin), " is nonplanar. BEWARE.\n");
- area = 0; // no autofixing in this case
- }
- norm = normalize(norm);
- }
-
- ang = '0 0 0';
- if(self.aiment)
- {
- org = self.aiment.origin;
- ang = self.aiment.angles;
- if(area > 0)
- {
- org = org - ((org - point) * norm) * norm; // project to plane
- makevectors(ang);
- if(norm * v_forward < 0)
- {
- LOG_INFO("Position target of trigger_warpzone near ", vtos(self.aiment.origin), " points into trigger_warpzone. BEWARE.\n");
- norm = -1 * norm;
- }
- ang = vectoangles2(norm, v_up); // keep rotation, but turn exactly against plane
- ang.x = -ang.x;
- if(norm * v_forward < 0.99)
- LOG_INFO("trigger_warpzone near ", vtos(self.aiment.origin), " has been turned to match plane orientation (", vtos(self.aiment.angles), " -> ", vtos(ang), "\n");
- if(vlen(org - self.aiment.origin) > 0.5)
- LOG_INFO("trigger_warpzone near ", vtos(self.aiment.origin), " has been moved to match the plane (", vtos(self.aiment.origin), " -> ", vtos(org), ").\n");
- }
- }
- else if(area > 0)
- {
- org = point;
- ang = vectoangles(norm);
- ang.x = -ang.x;
- }
- else
- error("cannot infer origin/angles for this warpzone, please use a killtarget or a trigger_warpzone_position");
-
- self.warpzone_origin = org;
- self.warpzone_angles = ang;
-}
-
-void WarpZone_InitStep_ClearTarget()
-{SELFPARAM();
- if(self.enemy)
- self.enemy.enemy = world;
- self.enemy = world;
-}
-
-entity warpzone_first; .entity warpzone_next;
-void WarpZone_InitStep_FindTarget()
-{SELFPARAM();
- float i;
- entity e, e2;
-
- if(self.enemy)
- return;
-
- // this way only one of the two ents needs to target
- if(self.target != "")
- {
- self.enemy = self; // so the if(!e.enemy) check also skips self, saves one IF
-
- e2 = world;
- for(e = world, i = 0; (e = find(e, targetname, self.target)); )
- if(!e.enemy)
- if(e.classname == self.classname) // possibly non-warpzones may use the same targetname!
- if(random() * ++i < 1)
- e2 = e;
- if(!e2)
- {
- self.enemy = world;
- error("Warpzone with non-existing target");
- return;
- }
- self.enemy = e2;
- e2.enemy = self;
- }
-}
-
-void WarpZone_Think();
-void WarpZone_InitStep_FinalizeTransform()
-{SELFPARAM();
- if(!self.enemy || self.enemy.enemy != self)
- {
- error("Invalid warp zone detected. Killed.");
- return;
- }
-
- warpzone_warpzones_exist = 1;
- WarpZone_SetUp(self, self.warpzone_origin, self.warpzone_angles, self.enemy.warpzone_origin, self.enemy.warpzone_angles);
- self.touch = WarpZone_Touch;
- self.SendFlags = 0xFFFFFF;
- if(self.spawnflags & 1)
- {
- self.think = WarpZone_Think;
- self.nextthink = time;
- }
- else
- self.nextthink = 0;
-}
-
-float warpzone_initialized;
-//entity warpzone_first;
-entity warpzone_position_first;
-entity warpzone_camera_first;
-.entity warpzone_next;
-spawnfunc(misc_warpzone_position)
-{
- // "target", "angles", "origin"
- self.warpzone_next = warpzone_position_first;
- warpzone_position_first = self;
-}
-spawnfunc(trigger_warpzone_position)
-{
- spawnfunc_misc_warpzone_position(this);
-}
-spawnfunc(trigger_warpzone)
-{
- // warp zone entities must have:
- // "killtarget" pointing to a target_position with a direction arrow
- // that points AWAY from the warp zone, and that is inside
- // the warp zone trigger
- // "target" pointing to an identical warp zone at another place in
- // the map, with another killtarget to designate its
- // orientation
-
- if(!self.scale)
- self.scale = self.modelscale;
- if(!self.scale)
- self.scale = 1;
- string m;
- m = self.model;
- WarpZoneLib_ExactTrigger_Init();
- if(m != "")
- {
- precache_model(m);
- _setmodel(self, m); // no precision needed
- }
- setorigin(self, self.origin);
- if(self.scale)
- setsize(self, self.mins * self.scale, self.maxs * self.scale);
- else
- setsize(self, self.mins, self.maxs);
- self.SendEntity = WarpZone_Send;
- self.SendFlags = 0xFFFFFF;
- BITSET_ASSIGN(self.effects, EF_NODEPTHTEST);
- self.warpzone_next = warpzone_first;
- warpzone_first = self;
-}
-spawnfunc(func_camera)
-{
- if(!self.scale)
- self.scale = self.modelscale;
- if(!self.scale)
- self.scale = 1;
- if(self.model != "")
- {
- precache_model(self.model);
- _setmodel(self, self.model); // no precision needed
- }
- setorigin(self, self.origin);
- if(self.scale)
- setsize(self, self.mins * self.scale, self.maxs * self.scale);
- else
- setsize(self, self.mins, self.maxs);
- if(!self.solid)
- self.solid = SOLID_BSP;
- else if(self.solid < 0)
- self.solid = SOLID_NOT;
- self.SendEntity = WarpZone_Camera_Send;
- self.SendFlags = 0xFFFFFF;
- self.warpzone_next = warpzone_camera_first;
- warpzone_camera_first = self;
-}
-void WarpZones_Reconnect()
-{SELFPARAM();
- for(setself(warpzone_first); self; setself(self.warpzone_next))
- WarpZone_InitStep_ClearTarget();
- for(setself(warpzone_first); self; setself(self.warpzone_next))
- WarpZone_InitStep_FindTarget();
- for(setself(warpzone_camera_first); self; setself(self.warpzone_next))
- WarpZoneCamera_InitStep_FindTarget();
- for(setself(warpzone_first); self; setself(self.warpzone_next))
- WarpZone_InitStep_FinalizeTransform();
- setself(this);
-}
-
-void WarpZone_Think()
-{SELFPARAM();
- if(self.warpzone_save_origin != self.origin
- || self.warpzone_save_angles != self.angles
- || self.warpzone_save_eorigin != self.enemy.origin
- || self.warpzone_save_eangles != self.enemy.angles)
- {
- WarpZone_InitStep_UpdateTransform();
- setself(self.enemy);
- WarpZone_InitStep_UpdateTransform();
- setself(this);
- WarpZone_InitStep_FinalizeTransform();
- setself(self.enemy);
- WarpZone_InitStep_FinalizeTransform();
- setself(this);
- self.warpzone_save_origin = self.origin;
- self.warpzone_save_angles = self.angles;
- self.warpzone_save_eorigin = self.enemy.origin;
- self.warpzone_save_eangles = self.enemy.angles;
- }
- self.nextthink = time;
-}
-
-void WarpZone_StartFrame()
-{SELFPARAM();
- entity e;
- if(warpzone_initialized == 0)
- {
- warpzone_initialized = 1;
- for(setself(warpzone_first); self; setself(self.warpzone_next))
- WarpZone_InitStep_FindOriginTarget();
- for(setself(warpzone_position_first); self; setself(self.warpzone_next))
- WarpZonePosition_InitStep_FindTarget();
- for(setself(warpzone_first); self; setself(self.warpzone_next))
- WarpZone_InitStep_UpdateTransform();
- setself(this);
- WarpZones_Reconnect();
- WarpZone_PostInitialize_Callback();
- }
-
- entity oldother;
- oldother = other;
- for(e = world; (e = nextent(e)); )
- {
- if(warpzone_warpzones_exist) { WarpZone_StoreProjectileData(e); }
-
- if(IS_REAL_CLIENT(e))
- {
- if(e.solid == SOLID_NOT) // not spectating?
- if(e.movetype == MOVETYPE_NOCLIP || e.movetype == MOVETYPE_FLY || e.movetype == MOVETYPE_FLY_WORLDONLY) // not spectating? (this is to catch observers)
- {
- other = e; // player
-
- // warpzones
- if(warpzone_warpzones_exist) {
- setself(WarpZone_Find(e.origin + e.mins, e.origin + e.maxs));
- if(self)
- if(!WarpZoneLib_ExactTrigger_Touch())
- if(WarpZone_PlaneDist(self, e.origin + e.view_ofs) <= 0)
- WarpZone_Teleport(self, e, -1, 0); } // NOT triggering targets by this!
-
- // teleporters
- setself(Teleport_Find(e.origin + e.mins, e.origin + e.maxs));
- if(self)
- if(!WarpZoneLib_ExactTrigger_Touch())
- Simple_TeleportPlayer(self, other); // NOT triggering targets by this!
- }
- }
-
- if(IS_NOT_A_CLIENT(e))
- {
- if(warpzone_warpzones_exist)
- for (; (e = nextent(e)); )
- WarpZone_StoreProjectileData(e);
- break;
- }
- }
- setself(this);
- other = oldother;
-}
-
-.float warpzone_reconnecting;
-float visible_to_some_client(entity ent)
-{
- entity e;
- for(e = nextent(world); !IS_NOT_A_CLIENT(e); e = nextent(e))
- if(IS_PLAYER(e) && IS_REAL_CLIENT(e))
- if(checkpvs(e.origin + e.view_ofs, ent))
- return 1;
- return 0;
-}
-void trigger_warpzone_reconnect_use()
-{SELFPARAM();
- entity e;
- e = self;
- // NOTE: this matches for target, not targetname, but of course
- // targetname must be set too on the other entities
- for(setself(warpzone_first); self; setself(self.warpzone_next))
- self.warpzone_reconnecting = ((e.target == "" || self.target == e.target) && !((e.spawnflags & 1) && (visible_to_some_client(self) || visible_to_some_client(self.enemy))));
- for(setself(warpzone_camera_first); self; setself(self.warpzone_next))
- self.warpzone_reconnecting = ((e.target == "" || self.target == e.target) && !((e.spawnflags & 1) && visible_to_some_client(self)));
- for(setself(warpzone_first); self; setself(self.warpzone_next))
- if(self.warpzone_reconnecting)
- WarpZone_InitStep_ClearTarget();
- for(setself(warpzone_first); self; setself(self.warpzone_next))
- if(self.warpzone_reconnecting)
- WarpZone_InitStep_FindTarget();
- for(setself(warpzone_camera_first); self; setself(self.warpzone_next))
- if(self.warpzone_reconnecting)
- WarpZoneCamera_InitStep_FindTarget();
- for(setself(warpzone_first); self; setself(self.warpzone_next))
- if(self.warpzone_reconnecting || self.enemy.warpzone_reconnecting)
- WarpZone_InitStep_FinalizeTransform();
- setself(e);
-}
-
-spawnfunc(trigger_warpzone_reconnect)
-{
- self.use = trigger_warpzone_reconnect_use;
-}
-
-spawnfunc(target_warpzone_reconnect)
-{
- spawnfunc_trigger_warpzone_reconnect(this); // both names make sense here :(
-}
-
-void WarpZone_PlayerPhysics_FixVAngle(void)
-{SELFPARAM();
-#ifndef WARPZONE_DONT_FIX_VANGLE
- if(IS_REAL_CLIENT(self))
- if(self.v_angle.z <= 360) // if not already adjusted
- if(time - self.ping * 0.001 < self.warpzone_teleport_time)
- {
- self.v_angle = WarpZone_TransformVAngles(self.warpzone_teleport_zone, self.v_angle);
- self.v_angle_z += 720; // mark as adjusted
- }
-#endif
-}
+++ /dev/null
-#ifndef SERVER_H
-#define SERVER_H
-
-void WarpZone_StartFrame();
-float WarpZone_Projectile_Touch();
-
-// THESE must be defined by calling QC code:
-void WarpZone_PostTeleportPlayer_Callback(entity pl);
-float WarpZone_Projectile_Touch_ImpactFilter_Callback();
-
-// server must also define a float called ENT_CLIENT_WARPZONE for the initial byte of WarpZone entities
-//const float ENT_CLIENT_WARPZONE;
-//const float ENT_CLIENT_WARPZONE_CAMERA;
-
-void WarpZone_PlayerPhysics_FixVAngle(void);
-
-void WarpZone_PostInitialize_Callback(void);
-#endif
+++ /dev/null
-#include "util_server.qh"
-
-#include "common.qh"
-
-#if defined(CSQC)
-#elif defined(MENUQC)
-#elif defined(SVQC)
- #include "../csqcmodellib/sv_model.qh"
-#endif
-#include "common.qh"
-
-void WarpZoneLib_ExactTrigger_Init()
-{SELFPARAM();
- vector mi, ma;
- if (self.movedir == '0 0 0')
- if (self.angles != '0 0 0')
- {
- makevectors (self.angles);
- self.movedir = v_forward;
- }
- if(self.model == "")
- {
- // It's a box! No need to match with exacttriggers.
- self.warpzone_isboxy = 1;
- }
- else
- {
- mi = self.mins;
- ma = self.maxs;
- precache_model(self.model);
- _setmodel(self, self.model);
- // let mapper-set mins/maxs override the model's bounds if set
- if(mi != '0 0 0' || ma != '0 0 0')
- {
- // It's a box! No need to match with exacttriggers.
- self.mins = mi;
- self.maxs = ma;
- self.warpzone_isboxy = 1;
- }
- }
- setorigin(self, self.origin);
- if(self.scale)
- setsize(self, self.mins * self.scale, self.maxs * self.scale);
- else
- setsize(self, self.mins, self.maxs);
- self.movetype = MOVETYPE_NONE;
- self.solid = SOLID_TRIGGER;
- self.model = "";
-}
+++ /dev/null
-#ifndef UTIL_SERVER_H
-#define UTIL_SERVER_H
-
-float WarpZoneLib_MoveOutOfSolid(entity e);
-float WarpZoneLib_ExactTrigger_Touch();
-#ifdef SVQC
-void WarpZoneLib_ExactTrigger_Init();
-#endif
-#endif