From 541515b36ae057ee8e8889cd32e81d6b6125899a Mon Sep 17 00:00:00 2001 From: Mario Date: Sat, 1 Aug 2015 00:20:50 +1000 Subject: [PATCH] Some new features and stuff for triggers (plus side-scrolling mode) --- qcsrc/client/csqcmodel_hooks.qc | 7 + qcsrc/client/main.qc | 2 + qcsrc/client/progs.src | 3 + qcsrc/client/view.qc | 21 ++- qcsrc/common/constants.qh | 2 + qcsrc/common/csqcmodel_settings.qh | 13 +- qcsrc/common/p2mathlib.qc | 98 +++++++++++ qcsrc/common/p2mathlib.qh | 33 ++++ qcsrc/common/physics.qc | 4 + qcsrc/common/triggers/func/breakable.qc | 25 ++- qcsrc/common/triggers/func/door.qc | 3 + qcsrc/common/triggers/func/door.qh | 2 + qcsrc/common/triggers/target/changelevel.qc | 26 +++ qcsrc/common/triggers/trigger/impulse.qc | 12 +- qcsrc/common/triggers/trigger/include.qc | 1 + qcsrc/common/triggers/trigger/include.qh | 1 + qcsrc/common/triggers/trigger/multi.qc | 6 + qcsrc/common/triggers/trigger/viewloc.qc | 179 ++++++++++++++++++++ qcsrc/common/triggers/trigger/viewloc.qh | 15 ++ qcsrc/common/viewloc.qc | 157 +++++++++++++++++ qcsrc/common/viewloc.qh | 15 ++ qcsrc/csqcmodellib/cl_player.qc | 9 + qcsrc/server/progs.src | 2 + 23 files changed, 626 insertions(+), 10 deletions(-) create mode 100644 qcsrc/common/p2mathlib.qc create mode 100644 qcsrc/common/p2mathlib.qh create mode 100644 qcsrc/common/triggers/trigger/viewloc.qc create mode 100644 qcsrc/common/triggers/trigger/viewloc.qh create mode 100644 qcsrc/common/viewloc.qc create mode 100644 qcsrc/common/viewloc.qh diff --git a/qcsrc/client/csqcmodel_hooks.qc b/qcsrc/client/csqcmodel_hooks.qc index 92cef15ef..1981c171d 100644 --- a/qcsrc/client/csqcmodel_hooks.qc +++ b/qcsrc/client/csqcmodel_hooks.qc @@ -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! diff --git a/qcsrc/client/main.qc b/qcsrc/client/main.qc index 982056fcb..697dbd356 100644 --- a/qcsrc/client/main.qc +++ b/qcsrc/client/main.qc @@ -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; diff --git a/qcsrc/client/progs.src b/qcsrc/client/progs.src index a9d0c565a..94c92a54c 100644 --- a/qcsrc/client/progs.src +++ b/qcsrc/client/progs.src @@ -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 diff --git a/qcsrc/client/view.qc b/qcsrc/client/view.qc index ffdde41a9..a2ce907a5 100644 --- a/qcsrc/client/view.qc +++ b/qcsrc/client/view.qc @@ -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 diff --git a/qcsrc/common/constants.qh b/qcsrc/common/constants.qh index 939c6d7c1..5c48e6396 100644 --- a/qcsrc/common/constants.qh +++ b/qcsrc/common/constants.qh @@ -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; diff --git a/qcsrc/common/csqcmodel_settings.qh b/qcsrc/common/csqcmodel_settings.qh index 5e5ff42eb..3fa969e1b 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 @@ -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 index 000000000..ad569ffbc --- /dev/null +++ b/qcsrc/common/p2mathlib.qc @@ -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 index 000000000..a8dc7ab44 --- /dev/null +++ b/qcsrc/common/p2mathlib.qh @@ -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); diff --git a/qcsrc/common/physics.qc b/qcsrc/common/physics.qc index 60ca3b101..5e5a9d042 100644 --- a/qcsrc/common/physics.qc +++ b/qcsrc/common/physics.qc @@ -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(); diff --git a/qcsrc/common/triggers/func/breakable.qc b/qcsrc/common/triggers/func/breakable.qc index 85120eff2..e36cdb01b 100644 --- a/qcsrc/common/triggers/func/breakable.qc +++ b/qcsrc/common/triggers/func/breakable.qc @@ -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) diff --git a/qcsrc/common/triggers/func/door.qc b/qcsrc/common/triggers/func/door.qc index e3f82af03..1b6216341 100644 --- a/qcsrc/common/triggers/func/door.qc +++ b/qcsrc/common/triggers/func/door.qc @@ -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) diff --git a/qcsrc/common/triggers/func/door.qh b/qcsrc/common/triggers/func/door.qh index e91061f14..adfc060e8 100644 --- a/qcsrc/common/triggers/func/door.qh +++ b/qcsrc/common/triggers/func/door.qh @@ -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; diff --git a/qcsrc/common/triggers/target/changelevel.qc b/qcsrc/common/triggers/target/changelevel.qc index 1ec8cc9e7..8e5c31bfe 100644 --- a/qcsrc/common/triggers/target/changelevel.qc +++ b/qcsrc/common/triggers/target/changelevel.qc @@ -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 diff --git a/qcsrc/common/triggers/trigger/impulse.qc b/qcsrc/common/triggers/trigger/impulse.qc index 926268e7c..7b8ebcfde 100644 --- a/qcsrc/common/triggers/trigger/impulse.qc +++ b/qcsrc/common/triggers/trigger/impulse.qc @@ -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); diff --git a/qcsrc/common/triggers/trigger/include.qc b/qcsrc/common/triggers/trigger/include.qc index 14986348f..1c762fc35 100644 --- a/qcsrc/common/triggers/trigger/include.qc +++ b/qcsrc/common/triggers/trigger/include.qc @@ -22,3 +22,4 @@ #include "secret.qc" #include "swamp.qc" #include "teleport.qc" +#include "viewloc.qc" diff --git a/qcsrc/common/triggers/trigger/include.qh b/qcsrc/common/triggers/trigger/include.qh index 1601143ec..63cdb0190 100644 --- a/qcsrc/common/triggers/trigger/include.qh +++ b/qcsrc/common/triggers/trigger/include.qh @@ -7,5 +7,6 @@ #include "swamp.qh" #include "keylock.qh" #include "impulse.qh" +#include "viewloc.qh" #endif diff --git a/qcsrc/common/triggers/trigger/multi.qc b/qcsrc/common/triggers/trigger/multi.qc index b85258702..0e9a02016 100644 --- a/qcsrc/common/triggers/trigger/multi.qc +++ b/qcsrc/common/triggers/trigger/multi.qc @@ -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 index 000000000..008dc8e61 --- /dev/null +++ b/qcsrc/common/triggers/trigger/viewloc.qc @@ -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 index 000000000..c7fd150d5 --- /dev/null +++ b/qcsrc/common/triggers/trigger/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 diff --git a/qcsrc/common/viewloc.qc b/qcsrc/common/viewloc.qc new file mode 100644 index 000000000..60a253306 --- /dev/null +++ b/qcsrc/common/viewloc.qc @@ -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 index 000000000..a86d8b4a4 --- /dev/null +++ b/qcsrc/common/viewloc.qh @@ -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 diff --git a/qcsrc/csqcmodellib/cl_player.qc b/qcsrc/csqcmodellib/cl_player.qc index 436052767..ee0340dd5 100644 --- a/qcsrc/csqcmodellib/cl_player.qc +++ b/qcsrc/csqcmodellib/cl_player.qc @@ -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; diff --git a/qcsrc/server/progs.src b/qcsrc/server/progs.src index ab87642aa..2d110d2f9 100644 --- a/qcsrc/server/progs.src +++ b/qcsrc/server/progs.src @@ -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 -- 2.39.2