#include "../warpzonelib/mathlib.qh"
+ #include "../server/t_viewloc.qh"
+
.float death_time;
.int modelflags;
#elif defined(MENUQC)
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!
#include "wall.qh"
#include "waypointsprites.qh"
+#include "../server/t_viewloc.qh"
+
#include "../common/effects.qh"
#include "../common/vehicles/unit/bumblebee.qh"
case ENT_CLIENT_JAILCAMERA: ent_jailcamera(); break;
case ENT_CLIENT_MINIGAME: ent_read_minigame(); break;
case ENT_CLIENT_EFFECT: Read_Effect(bIsNewEntity); break;
+ case ENT_CLIENT_VIEWLOC: ent_viewloc(); break;
+ case ENT_CLIENT_VIEWLOC_TRIGGER: ent_viewloc_trigger(); break;
default:
//error(strcat(_("unknown entity type in CSQC_Ent_Update: %d\n"), self.enttype));
../server/movelib.qc
../server/t_items.qc
+../server/t_viewloc.qc
../warpzonelib/anglestransform.qc
../warpzonelib/client.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;
return false;
if(intermission)
return true;
+ if(self.viewloc)
+ return true;
if(spectatee_status >= 0)
{
if(hud != HUD_NORMAL && (autocvar_cl_eventchase_vehicle || spectatee_status > 0))
float tempcamera = (gametype == MAPINFO_TYPE_JAILBREAK && spectatee_status >= 0 && getstati(STAT_ROUNDLOST) && !getstati(STAT_PRISONED));
// crosshair goes VERY LAST
- if(!scoreboard_active && !camera_active && intermission != 2 &&
+ if(!scoreboard_active && !csqcplayer.viewloc && !camera_active && intermission != 2 &&
spectatee_status != -1 && hud == HUD_NORMAL && autocvar_chase_active >= 0 &&
!HUD_QuickMenu_IsOpened() && !tempcamera && !HUD_MinigameMenu_IsOpened() )
{
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
{
fovlock = -1;
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 if(fovlock > 0 && autocvar_cl_specfov) { setproperty(VF_FOV, GetCurrentFov(fovlock)); }
else { setproperty(VF_FOV, GetCurrentFov(fov)); }
const int ENT_CLIENT_EFFECT = 73;
const int ENT_CLIENT_CONQUEST_CONTROLPOINT = 74;
const int ENT_CLIENT_MINIGAME = 75;
+const int ENT_CLIENT_VIEWLOC = 76;
+const int ENT_CLIENT_VIEWLOC_TRIGGER = 77;
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_PROPERTY(512, float, ReadApproxPastTime, WriteApproxPastTime, anim_upper_time) \
CSQCMODEL_ENDIF \
CSQCMODEL_PROPERTY(1024, float, ReadAngle, WriteAngle, v_angle_x) \
- CSQCMODEL_PROPERTY_SCALED(4096, float, ReadByte, WriteByte, scale, 16, 0, 255)
+ CSQCMODEL_PROPERTY_SCALED(4096, float, ReadByte, WriteByte, scale, 16, 0, 255) \
+ CSQCMODEL_PROPERTY(8192, 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
#include "common.qh"
#include "cl_model.qh"
#include "cl_player.qh"
+ #include "../server/t_viewloc.qh"
#elif defined(MENUQC)
#elif defined(SVQC)
#endif
input_angles = view_angles;
}
-float CSQCPlayer_IsLocalPlayer()
+bool CSQCPlayer_IsLocalPlayer()
{
return (self == csqcplayer);
}
+void CSQCPlayer_SetViewLocation(int refdefflags)
+{
+ entity view = CSQCModel_server2csqc(player_localentnum);
+ if(!view) { return; }
+
+ if(view.viewloc && !wasfreed(view.viewloc) && view.viewloc.enemy && view.viewloc.goalentity)
+ {
+ vector level_start, level_end, camera_rail, camera_angle;
+ vector forward, backward;
+
+ level_start = view.viewloc.enemy.origin;
+ level_end = view.viewloc.goalentity.origin;
+
+ // there may already be a function for bouning a vector in this manner, however my very quick search did not reveal one -- Player_2
+
+ //bound the x axis
+ if (view.origin.x < min(level_start.x, level_end.x))
+ camera_rail.x = min(level_start.x, level_end.x);
+ else if (view.origin.x > max(level_start.x, level_end.x))
+ camera_rail.x = max(level_start.x, level_end.x);
+ else
+ camera_rail.x = view.origin.x;
+
+ //bound the y axis
+ if (view.origin.y < min(level_start.y, level_end.y))
+ camera_rail.y = min(level_start.y, level_end.y);
+ else if (view.origin.y > max(level_start.y, level_end.y))
+ camera_rail.y = max(level_start.y, level_end.y);
+ else
+ camera_rail.y = view.origin.y;
+
+ //bound the z axis
+ if (view.origin.z < min(level_start.z, level_end.z))
+ camera_rail.z = min(level_start.z, level_end.z);
+ else if (view.origin.z > max(level_start.z, level_end.z))
+ camera_rail.z = max(level_start.z, level_end.z);
+ else
+ camera_rail.z = view.origin.z;
+
+ //we float around x and y, but rotate around z
+ camera_angle.x = view.origin.x - camera_rail.x;
+ camera_angle.y = view.origin.y - camera_rail.y;
+ //camera_angle.z = camera_rail.z - view.origin.z;
+
+ //camera_angle.y = view.viewloc.enemy.angles_y;
+ camera_angle.z = view.viewloc.enemy.angles_z;
+
+ //get the angles actual
+ camera_angle = vectoangles(normalize(camera_angle));
+
+ //dprint(vtos(camera_rail), "\n");
+
+ freeze_org = getpropertyvec(VF_ORIGIN);
+ freeze_ang = getpropertyvec(VF_ANGLES);
+ setproperty(VF_ORIGIN, camera_rail);
+ setproperty(VF_ANGLES, camera_angle);
+
+ forward = vectoangles(normalize(level_end - level_start));
+ backward = vectoangles(normalize(level_start - level_end));
+
+ if(input_movevalues_y < 0) // left
+ view.angles = backward;
+ if(input_movevalues_y > 0) // favour right
+ view.angles = forward;
+
+ /*if(input_buttons & 512) // left
+ view.angles = backward;
+ else //if(input_buttons & 1024) // right
+ view.angles = forward;*/
+
+ if(input_buttons & 256) // forward
+ view.v_angle_x = view.angles_x = -50;
+
+ setproperty(VF_CL_VIEWANGLES, view.angles);
+ }
+}
+
void CSQCPlayer_SetCamera()
{
vector v0;
refdefflags |= REFDEFFLAG_INTERMISSION;
V_CalcRefdef(view, refdefflags);
+
+ CSQCPlayer_SetViewLocation(refdefflags);
}
else
{
#include "g_hook.qh"
#include "race.qh"
#include "playerdemo.qh"
+ #include "t_viewloc.qh"
#endif
.float race_penalty;
if(time < self.ladder_time)
self.disableclientprediction = 1;
+ if(self.viewloc)
+ {
+ self.disableclientprediction = 1;
+ 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)
+ self.v_angle_x = self.angles_x = -50;
+
+ if(!self.BUTTON_CROUCH)
+ self.BUTTON_CROUCH = (oldmovement_x < 0);
+ }
+
if(self.frozen)
{
if(autocvar_sv_dodging_frozen && IS_REAL_CLIENT(self))
t_quake.qc
t_swamp.qc
t_teleporters.qc
+t_viewloc.qc
waypointsprites.qc
bot/bot.qc
--- /dev/null
+#if defined(CSQC)
+#elif defined(MENUQC)
+#elif defined(SVQC)
+ #include "../dpdefs/progsdefs.qh"
+ #include "../warpzonelib/util_server.qh"
+ #include "defs.qh"
+ #include "t_viewloc.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