From 3d9042ee46910cf00987d3a3dadd8629fff3b7f7 Mon Sep 17 00:00:00 2001 From: Mario Date: Thu, 26 Feb 2015 21:24:40 +1100 Subject: [PATCH] Experimental view location trigger, new branch as it may interfere with regular modes in its curren state --- qcsrc/client/csqcmodel_hooks.qc | 8 ++ qcsrc/client/main.qc | 4 + qcsrc/client/progs.src | 1 + qcsrc/client/view.qc | 19 ++- qcsrc/common/constants.qh | 2 + qcsrc/common/csqcmodel_settings.qh | 10 +- qcsrc/csqcmodellib/cl_player.qc | 82 ++++++++++++- qcsrc/server/cl_physics.qc | 30 +++++ qcsrc/server/progs.src | 1 + qcsrc/server/t_viewloc.qc | 180 +++++++++++++++++++++++++++++ qcsrc/server/t_viewloc.qh | 15 +++ 11 files changed, 348 insertions(+), 4 deletions(-) create mode 100644 qcsrc/server/t_viewloc.qc create mode 100644 qcsrc/server/t_viewloc.qh diff --git a/qcsrc/client/csqcmodel_hooks.qc b/qcsrc/client/csqcmodel_hooks.qc index a262721ff..cfadcd0eb 100644 --- a/qcsrc/client/csqcmodel_hooks.qc +++ b/qcsrc/client/csqcmodel_hooks.qc @@ -15,6 +15,8 @@ #include "../warpzonelib/mathlib.qh" + #include "../server/t_viewloc.qh" + .float death_time; .int modelflags; #elif defined(MENUQC) @@ -402,6 +404,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! diff --git a/qcsrc/client/main.qc b/qcsrc/client/main.qc index 49bdaa0b1..b00a4c6aa 100644 --- a/qcsrc/client/main.qc +++ b/qcsrc/client/main.qc @@ -13,6 +13,8 @@ #include "wall.qh" #include "waypointsprites.qh" +#include "../server/t_viewloc.qh" + #include "../common/effects.qh" #include "../common/vehicles/unit/bumblebee.qh" @@ -1030,6 +1032,8 @@ void CSQC_Ent_Update(float bIsNewEntity) 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)); diff --git a/qcsrc/client/progs.src b/qcsrc/client/progs.src index aabcdfa6c..ece5da7b7 100644 --- a/qcsrc/client/progs.src +++ b/qcsrc/client/progs.src @@ -78,6 +78,7 @@ weapons/projectile.qc // TODO ../server/movelib.qc ../server/t_items.qc +../server/t_viewloc.qc ../warpzonelib/anglestransform.qc ../warpzonelib/client.qc diff --git a/qcsrc/client/view.qc b/qcsrc/client/view.qc index 8b36f5c34..99e8ebf4a 100644 --- a/qcsrc/client/view.qc +++ b/qcsrc/client/view.qc @@ -224,6 +224,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,6 +430,8 @@ float WantEventchase() 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)) @@ -553,7 +565,7 @@ void UpdateCrosshair() 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() ) { @@ -1213,6 +1225,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)); @@ -1221,7 +1234,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 { @@ -1453,6 +1467,7 @@ void CSQC_UpdateView(float w, float h) 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)); } diff --git a/qcsrc/common/constants.qh b/qcsrc/common/constants.qh index 9691d222b..b795a0f41 100644 --- a/qcsrc/common/constants.qh +++ b/qcsrc/common/constants.qh @@ -85,6 +85,8 @@ const int ENT_CLIENT_JAILCAMERA = 72; 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; diff --git a/qcsrc/common/csqcmodel_settings.qh b/qcsrc/common/csqcmodel_settings.qh index af693c298..58ff0e3ba 100644 --- a/qcsrc/common/csqcmodel_settings.qh +++ b/qcsrc/common/csqcmodel_settings.qh @@ -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 @@ -52,7 +59,8 @@ 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 diff --git a/qcsrc/csqcmodellib/cl_player.qc b/qcsrc/csqcmodellib/cl_player.qc index 0c740a253..549ebca26 100644 --- a/qcsrc/csqcmodellib/cl_player.qc +++ b/qcsrc/csqcmodellib/cl_player.qc @@ -30,6 +30,7 @@ #include "common.qh" #include "cl_model.qh" #include "cl_player.qh" + #include "../server/t_viewloc.qh" #elif defined(MENUQC) #elif defined(SVQC) #endif @@ -177,11 +178,88 @@ void CSQCPlayer_PredictTo(float endframe, float apply_error) 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; @@ -302,6 +380,8 @@ void CSQCPlayer_SetCamera() refdefflags |= REFDEFFLAG_INTERMISSION; V_CalcRefdef(view, refdefflags); + + CSQCPlayer_SetViewLocation(refdefflags); } else { diff --git a/qcsrc/server/cl_physics.qc b/qcsrc/server/cl_physics.qc index c0b7d3e33..db4eed221 100644 --- a/qcsrc/server/cl_physics.qc +++ b/qcsrc/server/cl_physics.qc @@ -24,6 +24,7 @@ #include "g_hook.qh" #include "race.qh" #include "playerdemo.qh" + #include "t_viewloc.qh" #endif .float race_penalty; @@ -862,6 +863,35 @@ void SV_PlayerPhysics() 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)) diff --git a/qcsrc/server/progs.src b/qcsrc/server/progs.src index 3ce98cebf..848c49d44 100644 --- a/qcsrc/server/progs.src +++ b/qcsrc/server/progs.src @@ -60,6 +60,7 @@ t_quake3.qc t_quake.qc t_swamp.qc t_teleporters.qc +t_viewloc.qc waypointsprites.qc bot/bot.qc diff --git a/qcsrc/server/t_viewloc.qc b/qcsrc/server/t_viewloc.qc new file mode 100644 index 000000000..4d59406a0 --- /dev/null +++ b/qcsrc/server/t_viewloc.qc @@ -0,0 +1,180 @@ +#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 diff --git a/qcsrc/server/t_viewloc.qh b/qcsrc/server/t_viewloc.qh new file mode 100644 index 000000000..c7fd150d5 --- /dev/null +++ b/qcsrc/server/t_viewloc.qh @@ -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 -- 2.39.2