Some new features and stuff for triggers (plus side-scrolling mode)
authorMario <zacjardine@y7mail.com>
Fri, 31 Jul 2015 14:20:50 +0000 (00:20 +1000)
committerMario <zacjardine@y7mail.com>
Fri, 31 Jul 2015 14:20:50 +0000 (00:20 +1000)
23 files changed:
qcsrc/client/csqcmodel_hooks.qc
qcsrc/client/main.qc
qcsrc/client/progs.src
qcsrc/client/view.qc
qcsrc/common/constants.qh
qcsrc/common/csqcmodel_settings.qh
qcsrc/common/p2mathlib.qc [new file with mode: 0644]
qcsrc/common/p2mathlib.qh [new file with mode: 0644]
qcsrc/common/physics.qc
qcsrc/common/triggers/func/breakable.qc
qcsrc/common/triggers/func/door.qc
qcsrc/common/triggers/func/door.qh
qcsrc/common/triggers/target/changelevel.qc
qcsrc/common/triggers/trigger/impulse.qc
qcsrc/common/triggers/trigger/include.qc
qcsrc/common/triggers/trigger/include.qh
qcsrc/common/triggers/trigger/multi.qc
qcsrc/common/triggers/trigger/viewloc.qc [new file with mode: 0644]
qcsrc/common/triggers/trigger/viewloc.qh [new file with mode: 0644]
qcsrc/common/viewloc.qc [new file with mode: 0644]
qcsrc/common/viewloc.qh [new file with mode: 0644]
qcsrc/csqcmodellib/cl_player.qc
qcsrc/server/progs.src

index 92cef15..1981c17 100644 (file)
@@ -10,6 +10,7 @@
 #include "../common/animdecide.qh"
 #include "../common/csqcmodel_settings.qh"
 #include "../common/teams.qh"
+#include "../common/triggers/trigger/viewloc.qh"
 
 #include "../csqcmodellib/cl_model.qh"
 #include "../csqcmodellib/cl_player.qh"
@@ -400,6 +401,12 @@ void CSQCModel_AutoTagIndex_Apply(void)
        if(self.tag_entity && wasfreed(self.tag_entity))
                self.tag_entity = world;
 
+       if(self.viewloc && wasfreed(self.viewloc))
+               self.viewloc = world;
+
+       if(self.viewloc.entnum != self.tag_networkviewloc)
+               self.viewloc = findfloat(world, entnum, self.tag_networkviewloc);
+
        if(self.tag_networkentity)
        {
                // we are ATTACHED!
index 982056f..697dbd3 100644 (file)
@@ -873,6 +873,8 @@ void CSQC_Ent_Update(float bIsNewEntity)
                case ENT_CLIENT_SPAWNEVENT: Ent_ReadSpawnEvent(bIsNewEntity); break;
                case ENT_CLIENT_NOTIFICATION: Read_Notification(bIsNewEntity); break;
                case ENT_CLIENT_HEALING_ORB: ent_healer(); break;
+               case ENT_CLIENT_VIEWLOC: ent_viewloc(); break;
+               case ENT_CLIENT_VIEWLOC_TRIGGER: ent_viewloc_trigger(); break;
                case ENT_CLIENT_LADDER: ent_func_ladder(); break;
                case ENT_CLIENT_TRIGGER_PUSH: ent_trigger_push(); break;
                case ENT_CLIENT_TARGET_PUSH: ent_target_push(); break;
index a9d0c56..94c92a5 100644 (file)
@@ -50,10 +50,13 @@ weapons/projectile.qc // TODO
 ../common/notifications.qc
 ../common/physics.qc
 ../common/playerstats.qc
+../common/p2mathlib.qc
 ../common/test.qc
 ../common/urllib.qc
 ../common/util.qc
 
+../common/viewloc.qc
+
 ../common/items/all.qc
 
 ../common/monsters/all.qc
index ffdde41..a2ce907 100644 (file)
@@ -228,6 +228,16 @@ vector GetCurrentFov(float fov)
        return '1 0 0' * fovx + '0 1 0' * fovy;
 }
 
+vector GetViewLocationFOV(float fov)
+{
+       float frustumx, frustumy, fovx, fovy;
+       frustumy = tan(fov * M_PI / 360.0) * 0.75;
+       frustumx = frustumy * vid_width / vid_height / vid_pixelheight;
+       fovx = atan2(frustumx, 1) / M_PI * 360.0;
+       fovy = atan2(frustumy, 1) / M_PI * 360.0;
+       return '1 0 0' * fovx + '0 1 0' * fovy;
+}
+
 vector GetOrthoviewFOV(vector ov_worldmin, vector ov_worldmax, vector ov_mid, vector ov_org)
 {
        float fovx, fovy;
@@ -420,12 +430,14 @@ vector liquidcolor_prev;
 
 float eventchase_current_distance;
 float eventchase_running;
-float WantEventchase()
+bool WantEventchase()
 {
        if(autocvar_cl_orthoview)
                return false;
        if(intermission)
                return true;
+       if(self.viewloc)
+               return true;
        if(spectatee_status >= 0)
        {
                if(autocvar_cl_eventchase_nexball && gametype == MAPINFO_TYPE_NEXBALL && !(WepSet_GetFromStat() & WepSet_FromWeapon(WEP_PORTO)))
@@ -550,7 +562,7 @@ void UpdateCrosshair()
                        CSQC_common_hud();
 
        // crosshair goes VERY LAST
-       if(!scoreboard_active && !camera_active && intermission != 2 && spectatee_status != -1 && hud == HUD_NORMAL)
+       if(!scoreboard_active && !camera_active && intermission != 2 && spectatee_status != -1 && hud == HUD_NORMAL && !csqcplayer.viewloc)
        {
                if (!autocvar_crosshair_enabled) // main toggle for crosshair rendering
                        return;
@@ -1119,6 +1131,7 @@ void CSQC_UpdateView(float w, float h)
                        WarpZone_TraceBox(current_view_origin, autocvar_cl_eventchase_mins, autocvar_cl_eventchase_maxs, eventchase_target_origin, MOVE_WORLDONLY, self);
 
                        // If the boxtrace fails, revert back to line tracing.
+                       if(!self.viewloc)
                        if(trace_startsolid)
                        {
                                eventchase_target_origin = (current_view_origin - (v_forward * eventchase_current_distance));
@@ -1127,7 +1140,8 @@ void CSQC_UpdateView(float w, float h)
                        }
                        else { setproperty(VF_ORIGIN, trace_endpos); }
 
-                       setproperty(VF_ANGLES, WarpZone_TransformVAngles(WarpZone_trace_transform, view_angles));
+                       if(!self.viewloc)
+                               setproperty(VF_ANGLES, WarpZone_TransformVAngles(WarpZone_trace_transform, view_angles));
                }
                else if(autocvar_chase_active < 0) // time to disable chase_active if it was set by this code
                {
@@ -1349,6 +1363,7 @@ void CSQC_UpdateView(float w, float h)
        vid_pixelheight = autocvar_vid_pixelheight;
 
        if(autocvar_cl_orthoview) { setproperty(VF_FOV, GetOrthoviewFOV(ov_worldmin, ov_worldmax, ov_mid, ov_org)); }
+       else if(csqcplayer.viewloc) { setproperty(VF_FOV, GetViewLocationFOV(110)); } // enforce 110 fov, so things dont look odd
        else { setproperty(VF_FOV, GetCurrentFov(fov)); }
 
        // Camera for demo playback
index 939c6d7..5c48e63 100644 (file)
@@ -115,6 +115,8 @@ const int ENT_CLIENT_TRIGGER_IMPULSE = 68;
 const int ENT_CLIENT_SWAMP = 69;
 const int ENT_CLIENT_CORNER = 70;
 const int ENT_CLIENT_KEYLOCK = 71;
+const int ENT_CLIENT_VIEWLOC = 78;
+const int ENT_CLIENT_VIEWLOC_TRIGGER = 79;
 
 const int ENT_CLIENT_HEALING_ORB = 80;
 
index 5e5ff42..3fa969e 100644 (file)
 # define TAG_ENTITY_NAME tag_networkentity
 # define TAG_ENTITY_TYPE float
 .float tag_networkentity;
+
+# define TAG_VIEWLOC_NAME tag_networkviewloc
+# define TAG_VIEWLOC_TYPE int
+.float tag_networkviewloc;
 #else
 # define TAG_ENTITY_NAME tag_entity
 # define TAG_ENTITY_TYPE entity
+
+# define TAG_VIEWLOC_NAME viewloc
+# define TAG_VIEWLOC_TYPE entity
 #endif
 
 // new fields
@@ -53,7 +60,8 @@
        CSQCMODEL_ENDIF \
        CSQCMODEL_PROPERTY(1024, float, ReadAngle, WriteAngle, v_angle_x) \
        CSQCMODEL_PROPERTY_SCALED(4096, float, ReadByte, WriteByte, scale, 16, 0, 255) \
-       CSQCMODEL_PROPERTY(8192, int, ReadInt24_t, WriteInt24_t, dphitcontentsmask)
+       CSQCMODEL_PROPERTY(8192, int, ReadInt24_t, WriteInt24_t, dphitcontentsmask) \
+       CSQCMODEL_PROPERTY(16384, TAG_VIEWLOC_TYPE, ReadShort, WriteEntity, TAG_VIEWLOC_NAME) 
 // TODO get rid of colormod/glowmod here, find good solution for vortex charge glowmod hack; also get rid of some useless properties on non-players that only exist for CopyBody
 
 // add hook function calls here
@@ -63,7 +71,8 @@
        CSQCModel_Hook_PostUpdate(isnew, isplayer, islocalplayer);
 #define CSQCMODEL_HOOK_PREDRAW \
        CSQCModel_Hook_PreDraw(isplayer);
-#define CSQCPLAYER_HOOK_POSTCAMERASETUP
+#define CSQCPLAYER_HOOK_POSTCAMERASETUP \
+       CSQCPlayer_SetViewLocation();
 
 // force updates of player entities that often even if unchanged
 #define CSQCPLAYER_FORCE_UPDATES 0.25
diff --git a/qcsrc/common/p2mathlib.qc b/qcsrc/common/p2mathlib.qc
new file mode 100644 (file)
index 0000000..ad569ff
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+ Copyright (C) 2015 Micah Talkiewicz.
+ 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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+*/
+vector vec_bias(vector v, float f){
+       vector c;
+       c_x = v_x + f;
+       c_y = v_y + f;
+       c_z = v_z + f;
+       return c;
+}
+vector vec_to_min (vector a, vector b) {
+       vector c;
+       c_x = min (a_x, b_x);
+       c_y = min (a_y, b_y);
+       c_z = min (a_z, b_z);
+       return c;
+}
+vector vec_to_max (vector a, vector b) {
+       vector c;
+       c_x = max (a_x, b_x);
+       c_y = max (a_y, b_y);
+       c_z = max (a_z, b_z);
+       return c;
+}
+// there may already be a function for bounding a vector in this manner, however my very quick search did not reveal one -- Player_2
+vector vec_bounds_in (vector point, vector a, vector b) {
+       vector c, d, e;
+        
+       d = vec_to_min(a,b);
+       e = vec_to_max(a,b);
+        
+       c = vec_to_max(point, d);
+       c = vec_to_min(c, e);
+        
+       return c;
+        
+}
+vector vec_bounds_out (vector point, vector a, vector b) {
+       vector c, d, e;
+        
+       d = vec_to_max(a,b);
+       e = vec_to_min(a,b);
+        
+       c = vec_to_max(point, d);
+       c = vec_to_min(c, e);
+        
+       return c;
+        
+}
+float angle_snap_f (float f, float increment){
+        
+       float i;
+       for (i = 0; i <= 360; ){
+               if (f <= i - increment)
+                       return  i - increment;
+               i = i + increment;
+       }
+        
+       return 0;
+}
+vector angle_snap_vec (vector v,  float increment) {
+       vector c;
+       c_x = angle_snap_f (v_x, increment);
+       c_y = angle_snap_f (v_y, increment);
+       c_z = angle_snap_f (v_z, increment);
+       return c;
+}
+
+vector aim_vec (vector origin, vector target) {
+       vector v;
+       //we float around x and y, but rotate around z
+       v_x = target_x - origin_x;
+       v_y = target_y - origin_y;
+       v_z = origin_z - target_z;
+       //get the angles actual
+       return vectoangles(normalize(v));
+}
diff --git a/qcsrc/common/p2mathlib.qh b/qcsrc/common/p2mathlib.qh
new file mode 100644 (file)
index 0000000..a8dc7ab
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ Copyright (C) 2015 Micah Talkiewicz.
+ 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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+*/
+
+vector vec_bias(vector v, float f);
+
+
+vector vec_to_min (vector a, vector b);
+vector vec_to_max (vector a, vector b);
+
+// there may already be a function for bounding a vector in this manner, however my very quick search did not reveal one -- Player_2
+vector vec_bounds_in (vector point, vector a, vector b);
+vector vec_bounds_out (vector point, vector a, vector b);
+
+float angle_snap_f (float f, float increment);
+vector angle_snap_vec (vector v,  float increment);
+
+vector aim_vec (vector origin, vector target);
index 60ca3b1..5e5a9d0 100644 (file)
@@ -1,10 +1,12 @@
 #include "physics.qh"
 #include "triggers/trigger/swamp.qh"
 #include "triggers/trigger/jumppads.qh"
+#include "viewloc.qh"
 
 #ifdef SVQC
 
 #include "../server/miscfunctions.qh"
+#include "triggers/trigger/viewloc.qh"
 
 // client side physics
 bool Physics_Valid(string thecvar)
@@ -1762,6 +1764,8 @@ void PM_Main()
        self.disableclientprediction = 0;
 #endif
 
+       viewloc_PlayerPhysics();
+
        PM_check_spider();
 
        PM_check_frozen();
index 85120ef..e36cdb0 100644 (file)
@@ -38,6 +38,7 @@
 //   spawnflags:
 //     1 = start disabled (needs to be triggered to activate)
 //     2 = indicate damage
+//     4 = don't take direct damage (needs to be triggered to 'explode', then triggered again to restore)
 // notes:
 //   for mdl_dead to work, origin must be set (using a common/origin brush).
 //   Otherwise mdl_dead will be displayed at the map origin, and nobody would
@@ -137,6 +138,8 @@ void func_breakable_behave_destroyed()
        self.bot_attack = false;
        self.event_damage = func_null;
        self.state = 1;
+       if(self.spawnflags & 4)
+               self.use = func_null;
        func_breakable_colormod();
        if (self.noise1)
                stopsound (self, CH_TRIGGER_SINGLE);
@@ -150,9 +153,12 @@ void func_breakable_behave_restore()
                WaypointSprite_UpdateMaxHealth(self.sprite, self.max_health);
                WaypointSprite_UpdateHealth(self.sprite, self.health);
        }
-       self.takedamage = DAMAGE_AIM;
-       self.bot_attack = true;
-       self.event_damage = func_breakable_damage;
+       if(!(self.spawnflags & 4))
+       {
+               self.takedamage = DAMAGE_AIM;
+               self.bot_attack = true;
+               self.event_damage = func_breakable_damage;
+       }
        self.state = 0;
        self.nextthink = 0; // cancel auto respawn
        func_breakable_colormod();
@@ -231,6 +237,7 @@ void func_breakable_damage(entity inflictor, entity attacker, float damage, int
        if(self.team)
                if(attacker.team == self.team)
                        return;
+       self.pain_finished = time;
        self.health = self.health - damage;
        if(self.sprite)
        {
@@ -292,7 +299,17 @@ void spawnfunc_func_breakable()
        self.mdl = self.model;
        SetBrushEntityModel();
 
-       self.use = func_breakable_restore;
+       if(self.spawnflags & 4)
+               self.use = func_breakable_destroy;
+       else
+               self.use = func_breakable_restore;
+
+       if(self.spawnflags & 4)
+       {
+               self.takedamage = DAMAGE_NO;
+               self.event_damage = func_null;
+               self.bot_attack = false;
+       }
 
        // precache all the models
        if (self.mdl_dead)
index e3f82af..1b62163 100644 (file)
@@ -777,6 +777,9 @@ void spawnfunc_func_door()
        self.pos1 = self.SUB_ORIGIN;
        self.pos2 = self.pos1 + self.movedir*(fabs(self.movedir*self.size) - self.lip);
 
+       if(self.spawnflags & DOOR_NONSOLID)
+               self.solid = SOLID_NOT;
+
 // DOOR_START_OPEN is to allow an entity to be lighted in the closed position
 // but spawn in the open position
        if (self.spawnflags & DOOR_START_OPEN)
index e91061f..adfc060 100644 (file)
@@ -5,6 +5,8 @@ const int DOOR_TOGGLE = 32;
 
 const int DOOR_NOSPLASH = 256; // generic anti-splashdamage spawnflag
 
+const int DOOR_NONSOLID = 1024;
+
 const int SPAWNFLAGS_GOLD_KEY = 8;
 const int SPAWNFLAGS_SILVER_KEY = 16;
 
index 1ec8cc9..8e5c31b 100644 (file)
@@ -1,7 +1,31 @@
 #ifdef SVQC
 .string chmap, gametype;
+.entity chlevel_targ;
+
+
 void spawnfunc_target_changelevel_use()
 {
+       if(self.spawnflags & 2)
+       {
+               // simply don't react if a non-player triggers it
+               if(!IS_PLAYER(activator)) { return; }
+
+               activator.chlevel_targ = self;
+
+               entity head;
+               int plnum = 0;
+               int realplnum = 0;
+               // let's not count bots
+               FOR_EACH_REALPLAYER(head)
+               {
+                       ++realplnum;
+                       if(head.chlevel_targ == self)
+                               ++plnum;
+               }
+               if(plnum < ceil(realplnum * min(1, self.count))) // 70% of players
+                       return;
+       }
+
        if(self.gametype != "")
                MapInfo_SwitchGameType(MapInfo_Type_FromString(self.gametype));
 
@@ -14,5 +38,7 @@ void spawnfunc_target_changelevel_use()
 void spawnfunc_target_changelevel()
 {
        self.use = spawnfunc_target_changelevel_use;
+
+       if(!self.count) { self.count = 0.7; }
 }
 #endif
index 926268e..7b8ebcf 100644 (file)
@@ -35,7 +35,17 @@ void trigger_impulse_touch1()
        other.lastpushtime = time;
        if(!pushdeltatime) return;
 
-       other.velocity = other.velocity + normalize(targ.origin - self.origin) * str * pushdeltatime;
+       if(self.spawnflags & 64)
+       {
+               float addspeed = str - other.velocity * normalize(targ.origin - self.origin);
+               if (addspeed > 0)
+               {
+                       float accelspeed = min(8 * pushdeltatime * str, addspeed);
+                       other.velocity += accelspeed * normalize(targ.origin - self.origin);
+               }
+       }
+       else
+               other.velocity = other.velocity + normalize(targ.origin - self.origin) * str * pushdeltatime;
        other.flags &= ~FL_ONGROUND;
 #ifdef SVQC
        UpdateCSQCProjectile(other);
index 1498634..1c762fc 100644 (file)
@@ -22,3 +22,4 @@
 #include "secret.qc"
 #include "swamp.qc"
 #include "teleport.qc"
+#include "viewloc.qc"
index 1601143..63cdb01 100644 (file)
@@ -7,5 +7,6 @@
 #include "swamp.qh"
 #include "keylock.qh"
 #include "impulse.qh"
+#include "viewloc.qh"
 
 #endif
index b852587..0e9a020 100644 (file)
@@ -82,6 +82,12 @@ void multi_touch()
                        return;         // not facing the right way
        }
 
+       // if the trigger has pressed keys, check that the player is pressing those keys
+       if(self.pressedkeys)
+       if(IS_PLAYER(other)) // only for players
+       if(!(other.pressedkeys & self.pressedkeys))
+               return;
+
        EXACTTRIGGER_TOUCH;
 
        self.enemy = other;
diff --git a/qcsrc/common/triggers/trigger/viewloc.qc b/qcsrc/common/triggers/trigger/viewloc.qc
new file mode 100644 (file)
index 0000000..008dc8e
--- /dev/null
@@ -0,0 +1,179 @@
+#if defined(CSQC)
+#elif defined(MENUQC)
+#elif defined(SVQC)
+       #include "../../../dpdefs/progsdefs.qh"
+    #include "../../../warpzonelib/util_server.qh"
+    #include "../../../server/defs.qh"
+#endif
+
+#ifdef SVQC
+
+void viewloc_think()
+{
+       entity e;
+
+       // set myself as current viewloc where possible
+       for(e = world; (e = findentity(e, viewloc, self)); )
+               e.viewloc = world;
+
+               for(e = findradius((self.absmin + self.absmax) * 0.5, vlen(self.absmax - self.absmin) * 0.5 + 1); e; e = e.chain)
+                       if(!e.viewloc)
+                               if(IS_PLAYER(e)) // should we support non-player entities with this?
+                               //if(e.deadflag == DEAD_NO) // death view is handled separately, we can't override this just yet
+                               {
+                                       vector emin = e.absmin;
+                                       vector emax = e.absmax;
+                                       if(self.solid == SOLID_BSP)
+                                       {
+                                               emin -= '1 1 1';
+                                               emax += '1 1 1';
+                                       }
+                                       if(boxesoverlap(emin, emax, self.absmin, self.absmax)) // quick
+                                               if(WarpZoneLib_BoxTouchesBrush(emin, emax, self, e)) // accurate
+                                                       e.viewloc = self;
+                               }
+
+       self.nextthink = time;
+}
+
+bool trigger_viewloc_send(entity to, int sf)
+{
+       // CSQC doesn't need to know our origin (yet), as we're only available for referencing
+       WriteByte(MSG_ENTITY, ENT_CLIENT_VIEWLOC_TRIGGER);
+
+       WriteEntity(MSG_ENTITY, self.enemy);
+       WriteEntity(MSG_ENTITY, self.goalentity);
+
+       WriteCoord(MSG_ENTITY, self.origin_x);
+       WriteCoord(MSG_ENTITY, self.origin_y);
+       WriteCoord(MSG_ENTITY, self.origin_z);
+
+       return true;
+}
+
+void viewloc_init()
+{
+       entity e;
+       for(e = world; (e = find(e, targetname, self.target)); )
+               if(e.classname == "target_viewlocation_start")
+               {
+                       self.enemy = e;
+                       break;
+               }
+       for(e = world; (e = find(e, targetname, self.target2)); )
+               if(e.classname == "target_viewlocation_end")
+               {
+                       self.goalentity = e;
+                       break;
+               }
+
+       if(!self.enemy) { print("^1FAIL!\n"); remove(self); return; }
+
+       if(!self.goalentity)
+               self.goalentity = self.enemy; // make them match so CSQC knows what to do
+
+       Net_LinkEntity(self, false, 0, trigger_viewloc_send);
+
+       self.think = viewloc_think;
+       self.nextthink = time;
+}
+
+void spawnfunc_trigger_viewlocation()
+{
+       // we won't check target2 here yet, as it may not even need to exist
+       if(self.target == "") { print("^1FAIL!\n"); remove(self); return; }
+
+       EXACTTRIGGER_INIT;
+       InitializeEntity(self, viewloc_init, INITPRIO_FINDTARGET);
+}
+
+bool viewloc_send(entity to, int sf)
+{
+       WriteByte(MSG_ENTITY, ENT_CLIENT_VIEWLOC);
+
+       WriteByte(MSG_ENTITY, self.cnt);
+
+       WriteCoord(MSG_ENTITY, self.origin_x);
+       WriteCoord(MSG_ENTITY, self.origin_y);
+       WriteCoord(MSG_ENTITY, self.origin_z);
+
+       WriteCoord(MSG_ENTITY, self.angles_x);
+       WriteCoord(MSG_ENTITY, self.angles_y);
+       WriteCoord(MSG_ENTITY, self.angles_z);
+
+       return true;
+}
+
+.float angle;
+void viewloc_link()
+{
+       if(self.angle)
+               self.angles_y = self.angle;
+       Net_LinkEntity(self, false, 0, viewloc_send);
+}
+
+void spawnfunc_target_viewlocation_start()
+{
+       self.classname = "target_viewlocation_start";
+       self.cnt = 1;
+       viewloc_link();
+}
+void spawnfunc_target_viewlocation_end()
+{
+       self.classname = "target_viewlocation_end";
+       self.cnt = 2;
+       viewloc_link();
+}
+
+// compatibility
+void spawnfunc_target_viewlocation() { spawnfunc_target_viewlocation_start(); }
+
+#elif defined(CSQC)
+
+void trigger_viewloc_updatelink()
+{
+       self.enemy = findfloat(world, entnum, self.cnt);
+       self.goalentity = findfloat(world, entnum, self.count);
+}
+
+void ent_viewloc_trigger()
+{
+       float point1 = ReadShort();
+       float point2 = ReadShort();
+
+       self.enemy = findfloat(world, entnum, point1);
+       self.goalentity = findfloat(world, entnum, point2);
+
+       self.origin_x = ReadCoord();
+       self.origin_y = ReadCoord();
+       self.origin_z = ReadCoord();
+       setorigin(self, self.origin);
+
+       self.cnt = point1;
+       self.count = point2;
+
+       self.think = trigger_viewloc_updatelink;
+       self.nextthink = time + 1; // we need to delay this or else
+
+       self.classname = "trigger_viewlocation";
+       self.drawmask = MASK_NORMAL; // not so concerned, but better keep it alive
+}
+
+void ent_viewloc()
+{
+       self.cnt = ReadByte();
+
+       self.origin_x = ReadCoord();
+       self.origin_y = ReadCoord();
+       self.origin_z = ReadCoord();
+       setorigin(self, self.origin);
+
+       self.movedir_x = ReadCoord();
+       self.movedir_y = ReadCoord();
+       self.movedir_z = ReadCoord();
+
+       self.classname = ((self.cnt == 2) ? "target_viewlocation_end" : "target_viewlocation_start");
+       self.drawmask = MASK_NORMAL; // don't cull it
+}
+
+#endif
diff --git a/qcsrc/common/triggers/trigger/viewloc.qh b/qcsrc/common/triggers/trigger/viewloc.qh
new file mode 100644 (file)
index 0000000..c7fd150
--- /dev/null
@@ -0,0 +1,15 @@
+#ifndef T_VIEWLOC_H
+#define T_VIEWLOC_H
+
+.entity viewloc;
+
+#ifdef CSQC
+.entity goalentity;
+.entity enemy;
+.vector movedir;
+
+void ent_viewloc();
+void ent_viewloc_trigger();
+#endif
+
+#endif
\ No newline at end of file
diff --git a/qcsrc/common/viewloc.qc b/qcsrc/common/viewloc.qc
new file mode 100644 (file)
index 0000000..60a2533
--- /dev/null
@@ -0,0 +1,157 @@
+#include "util.qh"
+
+#if defined(CSQC)
+       #include "../dpdefs/csprogsdefs.qh"
+    #include "../client/defs.qh"
+    #include "constants.qh"
+#elif defined(MENUQC)
+#elif defined(SVQC)
+       #include "../server/defs.qh"
+#endif
+
+// client movement
+void viewloc_PlayerPhysics()
+{
+       if(self.viewloc)
+       {
+               vector oldmovement = self.movement;
+               self.movement_x = oldmovement_y;
+               self.movement_y = 0;
+
+               if(self.movement_x < 0)
+                       self.movement_x = -self.movement_x;
+
+               vector level_start, level_end;
+               level_start = self.viewloc.enemy.origin;
+               level_end = self.viewloc.goalentity.origin;
+               vector forward, backward;
+               forward = vectoangles(normalize(level_end - level_start));
+               backward = vectoangles(normalize(level_start - level_end));
+
+               if(self.movement_x < 0) // left
+                       self.angles = backward;
+               if(self.movement_x > 0) // right
+                       self.angles = forward;
+
+               if(oldmovement_x > 0)
+#ifdef CSQC
+                       input_angles_x =
+#endif
+                       self.v_angle_x = self.angles_x = -50;
+               else if(oldmovement_x < 0)
+#ifdef CSQC
+                       input_angles_x =
+#endif
+                       self.v_angle_x = self.angles_x = 50;
+
+               //if(!PHYS_INPUT_BUTTON_CROUCH(self) && !IS_DUCKED(self))
+#ifdef SVQC
+                       //self.BUTTON_CROUCH = (oldmovement_x < 0);
+                       if(oldmovement_x < 0)
+                               self.BUTTON_CROUCH = true;
+#elif defined(CSQC)
+                       if(oldmovement_x < 0) { input_buttons |= 16; self.flags |= FL_DUCKED; } //else { input_buttons &= ~16; self.flags &= ~FL_DUCKED; }
+#endif
+       }
+}
+
+#ifdef CSQC
+
+void viewloc_SetTags()
+{
+       if(self.viewloc && wasfreed(self.viewloc))
+               self.viewloc = world;
+
+       if(self.viewloc.entnum != self.tag_networkviewloc)
+       if(self.tag_networkviewloc == 0)
+               self.viewloc = world;
+       else
+               self.viewloc = findfloat(world, entnum, self.tag_networkviewloc);
+}
+
+vector old_camera_angle = '0 0 0';
+void viewloc_SetViewLocation()
+{
+       entity view = CSQCModel_server2csqc(player_localentnum);
+       if(!view) { return; }
+       //NOTE: the "cam_" cvars sould probably be changed out with a spawnflag or an entity key. I have it like this for my testing -- Player_2
+       if(view.viewloc && !wasfreed(view.viewloc) && view.viewloc.enemy && view.viewloc.goalentity)
+       {
+               vector position_a, position_b, camera_position, camera_angle, forward, backward;
+               //vector scratch;
+
+               position_a = view.viewloc.enemy.origin;
+               position_b = view.viewloc.goalentity.origin;
+
+#if 0          
+               /*TODO: have the camera only move when a player moves too much from the center of the camera
+                * basically the player can move around in a "box" in the center of th screen with out changing the camera position or angles
+               */
+               if (cvar("cam_box")) {
+                       camera_position = vec_bounds_in(view.origin, position_a, position_b);
+               }
+               else
+#endif
+                       camera_position = vec_bounds_in(view.origin, position_a, position_b);
+
+
+               camera_angle = '0 0 0';
+
+               // a tracking camera follows the player when it leaves the world box 
+               if (cvar("cam_track")) {
+                       camera_angle = aim_vec (camera_position, view.origin);
+               }
+       
+               // hard snap changes the angle as soon as it crosses over the nearest 90 degree mark
+               if (cvar("cam_snap_hard")){
+                       camera_angle = angle_snap_vec(aim_vec(camera_position, view.origin), 90);
+               }
+               
+               // tries to avoid snapping unless it *really* needs to
+               if (cvar("cam_snap_close")){
+                       
+                       // like hard snap, but don't snap angles yet.
+                       camera_angle = aim_vec(camera_position, view.origin);
+                       
+                       /* if the difference between the old and new angle is 60 degrees or more, switch angles.
+                        * NOTE: bug/feature: this will use non-snaped angles for one frame.
+                        * doing this resualts in less code, faster code, and a smoother transisition between angles.
+                        */
+                       float camera_angle_diff = max(camera_angle_y, old_camera_angle_y) - min(camera_angle_y, old_camera_angle_y);
+
+                       if ( camera_angle_diff >= 60)
+                               old_camera_angle_y = angle_snap_f(camera_angle_y, 90);
+                       else
+                               camera_angle_y = old_camera_angle_y;
+               }
+               
+               //unlocking this allows the camera to look up and down. this also allows a top-down view.
+               if (!cvar("cam_snap_unlock")) {
+                       camera_angle_x = 0;
+                       camera_angle_z = 0;
+               }
+               
+#if 0
+               dprint(vtos(camera_position), "\n");
+               dprint(vtos(old_camera_angle), "\n");
+               dprint(vtos(camera_angle), "\n");
+#endif
+
+               freeze_org = getpropertyvec(VF_ORIGIN);
+               freeze_ang = getpropertyvec(VF_ANGLES);
+               setproperty(VF_ORIGIN, camera_position);
+               setproperty(VF_ANGLES, camera_angle);
+               
+               forward = vectoangles(normalize(vec_to_min(position_b, position_a) - vec_to_max(position_b, position_a)));
+               backward = vectoangles(normalize(vec_to_max(position_b, position_a) - vec_to_min(position_b, position_a)));
+               
+               if(input_movevalues_y < 0) // left
+                       view.angles = backward;
+               if(input_movevalues_y > 0) // favour right
+                       view.angles = forward;
+
+               setproperty(VF_CL_VIEWANGLES, view.angles);
+       }
+}
+
+#endif
diff --git a/qcsrc/common/viewloc.qh b/qcsrc/common/viewloc.qh
new file mode 100644 (file)
index 0000000..a86d8b4
--- /dev/null
@@ -0,0 +1,15 @@
+#ifndef VIEWLOC_H
+#define VIEWLOC_H
+
+.entity viewloc;
+
+void viewloc_PlayerPhysics();
+
+#ifdef CSQC
+
+void viewloc_SetViewLocation();
+void viewloc_SetTags();
+
+#endif
+
+#endif
index 4360527..ee0340d 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * 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
@@ -23,6 +24,7 @@
        #include "../dpdefs/csprogsdefs.qh"
        #include "../client/defs.qh"
        #include "../common/constants.qh"
+       #include "../common/p2mathlib.qh"
        #include "../common/stats.qh"
        #include "../common/util.qh"
        #include "interpolate.qh"
@@ -30,6 +32,8 @@
        #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
@@ -205,6 +209,11 @@ bool CSQCPlayer_IsLocalPlayer()
        return (self == csqcplayer);
 }
 
+void CSQCPlayer_SetViewLocation()
+{
+       viewloc_SetViewLocation();
+}
+
 void CSQCPlayer_SetCamera()
 {
        vector v0;
index ab87642..2d110d2 100644 (file)
@@ -96,7 +96,9 @@ weapons/weaponsystem.qc
 ../common/notifications.qc
 ../common/physics.qc
 ../common/playerstats.qc
+../common/p2mathlib.qc
 ../common/test.qc
+../common/viewloc.qc
 ../common/triggers/include.qc
 ../common/urllib.qc
 ../common/util.qc