Commit b6b2f6f6 authored by Mario's avatar Mario

Merge branch 'Mario/triggers' into 'master'

Merge branch Mario/triggers (L merge request)

Adds various useful flags to existing triggers and brushes, and a new side-scroller trigger for extra fun.

See merge request !161
parents 02e07278 4e0ee1f2
......@@ -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!
......
......@@ -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;
......
......@@ -47,7 +47,7 @@ void skeleton_loadinfo(entity e)
}
else
dprint("No model parameters for ", e.model, "\n");
dprint(e.model, " uses ", ftos(e.bone_upperbody), " ", ftos(e.fixbone), "\n");
//dprint(e.model, " uses ", ftos(e.bone_upperbody), " ", ftos(e.fixbone), "\n");
get_model_parameters(string_null, 0);
e.skeleton_info_modelindex = e.modelindex;
e.skeleton_info_skin = e.skin;
......
......@@ -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
......
......@@ -228,6 +228,15 @@ vector GetCurrentFov(float fov)
return '1 0 0' * fovx + '0 1 0' * fovy;
}
vector GetViewLocationFOV(float fov)
{
float frustumy = tan(fov * M_PI / 360.0) * 0.75;
float frustumx = frustumy * vid_width / vid_height / vid_pixelheight;
float fovx = atan2(frustumx, 1) / M_PI * 360.0;
float 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 +429,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 +561,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;
......@@ -1123,6 +1134,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));
......@@ -1131,7 +1143,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
{
......@@ -1353,6 +1366,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
......
......@@ -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;
......
......@@ -15,9 +15,16 @@
# 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
......
/*
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));
}
/*
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)
......@@ -1762,6 +1764,8 @@ void PM_Main()
self.disableclientprediction = 0;
#endif
viewloc_PlayerPhysics();
PM_check_spider();
PM_check_frozen();
......
......@@ -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)
......
......@@ -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)
......
......@@ -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;
......
#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
......@@ -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);
......
......@@ -22,3 +22,4 @@
#include "secret.qc"
#include "swamp.qc"
#include "teleport.qc"
#include "viewloc.qc"
......@@ -7,5 +7,6 @@
#include "swamp.qh"
#include "keylock.qh"
#include "impulse.qh"
#include "viewloc.qh"
#endif
......@@ -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;
......
#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;
// we abuse this method, rather than using normal .touch, because touch isn't reliable with multiple clients inside the same trigger, and can't "untouch" entities
// 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
#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
#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_y = backward_y;
if(self.movement_x > 0) // right
self.angles_y = forward_y;
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;