#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"
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!
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;
../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
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;
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)))
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;
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));
}
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
{
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
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;
# 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
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
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
--- /dev/null
+/*
+ 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));
+}
--- /dev/null
+/*
+ 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);
#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)
self.disableclientprediction = 0;
#endif
+ viewloc_PlayerPhysics();
+
PM_check_spider();
PM_check_frozen();
// 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
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);
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();
if(self.team)
if(attacker.team == self.team)
return;
+ self.pain_finished = time;
self.health = self.health - damage;
if(self.sprite)
{
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)
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)
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;
#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));
void spawnfunc_target_changelevel()
{
self.use = spawnfunc_target_changelevel_use;
+
+ if(!self.count) { self.count = 0.7; }
}
#endif
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);
#include "secret.qc"
#include "swamp.qc"
#include "teleport.qc"
+#include "viewloc.qc"
#include "swamp.qh"
#include "keylock.qh"
#include "impulse.qh"
+#include "viewloc.qh"
#endif
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;
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#ifndef VIEWLOC_H
+#define VIEWLOC_H
+
+.entity viewloc;
+
+void viewloc_PlayerPhysics();
+
+#ifdef CSQC
+
+void viewloc_SetViewLocation();
+void viewloc_SetTags();
+
+#endif
+
+#endif
/*
* 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
#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"
#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
return (self == csqcplayer);
}
+void CSQCPlayer_SetViewLocation()
+{
+ viewloc_SetViewLocation();
+}
+
void CSQCPlayer_SetCamera()
{
vector v0;
../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