From 11deea5b3d0c19708fa535bb68d01f6a47512b47 Mon Sep 17 00:00:00 2001 From: Marco Cawthorne Date: Tue, 17 Oct 2023 16:48:03 -0700 Subject: [PATCH] Add the following physics entities from Source: phys_ballsocket, phys_constraint, phys_constraintsystem, phys_convert, phys_hinge, phys_keepupright, phys_slideconstraint & env_physexplosion Add new class phys_rope, which will handle move_rope and keyframe_rope from Source. --- platform/base_glsl.pk3dir/glsl/vertexlit.glsl | 14 +- src/botlib/route.qc | 19 +- src/client/NSView.qc | 4 +- src/client/entities.qc | 3 + src/client/sky.qc | 27 +- src/gs-entbase/client.src | 1 - src/gs-entbase/server.src | 9 + src/gs-entbase/server/env_physexplosion.qc | 184 ++++++ src/gs-entbase/server/func_door.qc | 22 +- src/gs-entbase/server/func_physbox.qc | 6 +- src/gs-entbase/server/logic_auto.qc | 9 +- src/gs-entbase/server/logic_case.qc | 22 +- src/gs-entbase/server/path_corner.qc | 2 +- src/gs-entbase/server/phys_ballsocket.qc | 80 +++ src/gs-entbase/server/phys_constraint.qc | 74 +++ .../server/phys_constraintsystem.qc | 68 +++ src/gs-entbase/server/phys_convert.qc | 209 +++++++ src/gs-entbase/server/phys_hinge.qc | 144 +++++ src/gs-entbase/server/phys_keepupright.qc | 125 ++++ src/gs-entbase/server/phys_slideconstraint.qc | 149 +++++ src/gs-entbase/server/prop_dynamic.qc | 3 + src/gs-entbase/server/prop_physics.qc | 45 +- src/gs-entbase/server/prop_static.qc | 13 +- src/gs-entbase/shared.src | 1 + src/gs-entbase/shared/env_muzzleflash.qc | 53 +- src/gs-entbase/shared/env_shockwave.qc | 2 +- src/gs-entbase/shared/phys_rope.qc | 363 +++++++++++ .../shared/prop_physics_multiplayer.qc | 10 +- src/server/NSGameRules.qc | 2 +- src/shared/NSPhysicsConstraint.h | 111 ++++ src/shared/NSPhysicsConstraint.qc | 375 ++++++++++++ src/shared/NSPhysicsEntity.h | 136 ++++- src/shared/NSPhysicsEntity.qc | 570 +++++++++++++----- src/shared/defs.h | 1 + src/shared/entities.h | 1 + src/shared/include.src | 1 + 36 files changed, 2618 insertions(+), 240 deletions(-) create mode 100644 src/gs-entbase/server/env_physexplosion.qc create mode 100644 src/gs-entbase/server/phys_ballsocket.qc create mode 100644 src/gs-entbase/server/phys_constraint.qc create mode 100644 src/gs-entbase/server/phys_constraintsystem.qc create mode 100644 src/gs-entbase/server/phys_convert.qc create mode 100644 src/gs-entbase/server/phys_hinge.qc create mode 100644 src/gs-entbase/server/phys_keepupright.qc create mode 100644 src/gs-entbase/server/phys_slideconstraint.qc create mode 100644 src/gs-entbase/shared/phys_rope.qc create mode 100644 src/shared/NSPhysicsConstraint.h create mode 100644 src/shared/NSPhysicsConstraint.qc diff --git a/platform/base_glsl.pk3dir/glsl/vertexlit.glsl b/platform/base_glsl.pk3dir/glsl/vertexlit.glsl index 8b9a0352..1b84679e 100644 --- a/platform/base_glsl.pk3dir/glsl/vertexlit.glsl +++ b/platform/base_glsl.pk3dir/glsl/vertexlit.glsl @@ -123,15 +123,17 @@ varying mat3 invsurface; #if defined(BUMP) && r_skipEnvmap==0 vec3 cube_c; - #if r_showEnvCubemap == 0 - float refl = 1.0 - texture2D(s_normalmap, tex_c).a; - #else - float refl = 1.0; - #endif - cube_c = reflect(normalize(eyevector), normal_f.rgb); + float refl = 1.0 - texture2D(s_normalmap, tex_c).a; + + cube_c = reflect(normalize(eyevector), norm); cube_c = cube_c.x * invsurface[0] + cube_c.y * invsurface[1] + cube_c.z * invsurface[2]; cube_c = (m_model * vec4(cube_c.xyz, 0.0)).xyz; + + #if r_showEnvCubemap == 0 diff_f.rgb += textureCube(s_reflectcube, cube_c).rgb * refl; + #else + diff_f.rgb = textureCube(s_reflectcube, cube_c).rgb; + #endif #endif #if defined(FULLBRIGHT) && r_skipFullbright==0 diff --git a/src/botlib/route.qc b/src/botlib/route.qc index f881682a..8dbad11c 100644 --- a/src/botlib/route.qc +++ b/src/botlib/route.qc @@ -14,13 +14,6 @@ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -/* -* Begin calculating a route. -* The callback function will be called once the route has finished being calculated. -* The route must be memfreed once it is no longer needed. -* The route must be followed in reverse order (ie: the first node that must be reached -* is at index numnodes-1). If no route is available then the callback will be called with no nodes. -*/ int Route_RoundDistance(float flDist) { @@ -37,7 +30,7 @@ Route_RoundDistance(float flDist) /* returns a botinfo point that's nearest to us */ -entity +NSEntity Route_SelectFarthest(float type, vector org, optional vector lastpoi = [0,0,0]) { entity temp; @@ -61,7 +54,7 @@ Route_SelectFarthest(float type, vector org, optional vector lastpoi = [0,0,0]) } /* returns a botinfo point that's nearest to us */ -entity +NSEntity Route_SelectNearest(float type, vector org, optional vector lastpoi = [0,0,0]) { entity temp; @@ -85,7 +78,7 @@ Route_SelectNearest(float type, vector org, optional vector lastpoi = [0,0,0]) } /* returns a botinfo point belonging to our team */ -entity +NSEntity Route_SelectNearestTeam(float type, vector org, float tt) { entity temp; @@ -110,7 +103,7 @@ Route_SelectNearestTeam(float type, vector org, float tt) } /* returns a botinfo point belonging to the enemy team */ -entity +NSEntity Route_SelectNearestEnemyTeam(float type, vector org, float tt) { entity temp; @@ -140,7 +133,7 @@ Route_SelectNearestEnemyTeam(float type, vector org, float tt) Spawn_SelectRandom ================ */ -entity +NSEntity Route_SelectRandom(string sEntname) { static entity eLastSpot; @@ -153,7 +146,7 @@ Route_SelectRandom(string sEntname) Route_SelectRandomSpot ================ */ -entity +NSEntity Route_SelectRandomSpot(void) { static entity eLastSpot; diff --git a/src/client/NSView.qc b/src/client/NSView.qc index 6e415084..00748fe4 100644 --- a/src/client/NSView.qc +++ b/src/client/NSView.qc @@ -369,7 +369,9 @@ NSView::UpdateView(void) /* all 2D operations happen after this point */ for (entity b = world; (b = findfloat(b, ::isCSQC, 1));) { NSEntity pf = (NSEntity) b; - pf.postdraw(); + + if (pf.postdraw) + pf.postdraw(); } /* the blinding stuff */ diff --git a/src/client/entities.qc b/src/client/entities.qc index c1b8585c..8053124d 100644 --- a/src/client/entities.qc +++ b/src/client/entities.qc @@ -122,6 +122,9 @@ Entity_EntityUpdate(float type, float new) case ENT_PROPROPE: NSENTITY_READENTITY(prop_rope, new) break; + case ENT_PHYSROPE: + NSENTITY_READENTITY(phys_rope, new) + break; case ENT_BUBBLES: NSENTITY_READENTITY(env_bubbles, new) break; diff --git a/src/client/sky.qc b/src/client/sky.qc index 14b4d4cd..164a52a6 100644 --- a/src/client/sky.qc +++ b/src/client/sky.qc @@ -16,27 +16,26 @@ var string g_strSkyName; -bool -Sky_SourceBSPCheck(string sky) -{ - if not (whichpack(strcat("materials/skybox/", sky, "bk.vmt"))) - return false; - - return true; -} - void Sky_Update(int force) { if (g_strSkyName != serverkey("skyname") || force == TRUE) { + string skyPath; + string skyPrefix; + g_strSkyName = serverkey("skyname"); + skyPrefix = substring(g_strSkyName, 0, 4); + + print(sprintf("SKY PREFIX: %S\n", skyPrefix)); /* is it a Source Engine material? */ - if (Sky_SourceBSPCheck(g_strSkyName)) - localcmd(sprintf("sky \"materials/skybox/%s\"\n", g_strSkyName)); - else - localcmd(sprintf("sky \"%s\"\n", g_strSkyName)); + if (skyPrefix == "sky_") { + skyPath = strcat("materials/skybox/", g_strSkyName); + } else { + skyPath = g_strSkyName; + } - print(sprintf("sky update applying %s.\n", g_strSkyName)); + localcmd(sprintf("sky \"%s\"\n", skyPath)); + print(sprintf("sky update applying %s.\n", skyPath)); } } diff --git a/src/gs-entbase/client.src b/src/gs-entbase/client.src index 1d55f861..72b5566d 100644 --- a/src/gs-entbase/client.src +++ b/src/gs-entbase/client.src @@ -19,5 +19,4 @@ client/infodecal.qc client/sky_camera.qc client/info_notnull.qc client/point_message.qc -client/prop_static.qc #endlist diff --git a/src/gs-entbase/server.src b/src/gs-entbase/server.src index 035cd911..b749e603 100644 --- a/src/gs-entbase/server.src +++ b/src/gs-entbase/server.src @@ -17,6 +17,7 @@ server/env_fade.qc server/env_hudhint.qc server/env_spark.qc server/env_explosion.qc +server/env_physexplosion.qc server/env_render.qc server/env_shake.qc server/env_message.qc @@ -64,6 +65,14 @@ server/player_weaponstrip.qc server/player_loadsaved.qc server/prop_dynamic.qc server/prop_physics.qc +server/phys_ballsocket.qc +server/phys_convert.qc +server/phys_constraint.qc +server/phys_keepupright.qc +server/phys_hinge.qc +server/phys_slideconstraint.qc +//server/phys_constraintsystem.qc +server/prop_static.qc server/point_camera.qc server/point_servercommand.qc server/point_trigger.qc diff --git a/src/gs-entbase/server/env_physexplosion.qc b/src/gs-entbase/server/env_physexplosion.qc new file mode 100644 index 00000000..d6f5872a --- /dev/null +++ b/src/gs-entbase/server/env_physexplosion.qc @@ -0,0 +1,184 @@ +/* + * Copyright (c) 2023 Vera Visions LLC. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +/*!QUAKED env_physexplosion (1 .5 0) (-8 -8 -8) (8 8 8) NODAMAGE PUSHCLIENTS RADIAL TRACE SHAKE +# OVERVIEW +A force-centered explosion, primarily targetted at physics objects and optionally, players/clients. + +# KEYS +- "targetname" : Name +- "magnitude" : Amount of force applied. +- "radius" : Optional, overrides the radius of the 'explosion'. +- "targetentityname" : Optional, will only target the named entity if specified. +- "inner_radius" : Optional, will test from within this inner radius when TRACE (8) is set. + +# SPAWNFLAGS +- NODAMAGE (1) : Do not damage physics entities. +- PUSHCLIENTS (2) : Allow pushing of players. +- RADIAL (4) : Disables any up/down forces from being applied. +- TRACE (8) : Does a collision test with its targets. +- SHAKE (16) : Push the view of the players around a bit. + +# TRIVIA +This entity was introduced in Half-Life 2 (2004). +*/ +class +env_physexplosion:NSPhysicsConstraint +{ +public: + void env_physexplosion(void); + + /* overrides */ + virtual void Save(float); + virtual void Restore(string,string); + virtual void SpawnKey(string, string); + virtual void Respawn(void); + virtual void Input(entity, string, string); + + nonvirtual void TriggerExplosion(void); + +private: + float m_flMagnitude; + float m_flRadius; + string m_strTargetEntity; + float m_flInnerRadius; +}; + +void +env_physexplosion::env_physexplosion(void) +{ + m_flMagnitude = 0.0f; + m_flRadius = 0.0f; + m_strTargetEntity = __NULL__; + m_flInnerRadius = 0.0f; +} + +void +env_physexplosion::Save(float handle) +{ + super::Save(handle); + SaveFloat(handle, "m_flMagnitude", m_flMagnitude); + SaveFloat(handle, "m_flRadius", m_flRadius); + SaveString(handle, "m_strTargetEntity", m_strTargetEntity); + SaveFloat(handle, "m_flInnerRadius", m_flInnerRadius); +} + +void +env_physexplosion::Restore(string strKey, string strValue) +{ + switch (strKey) { + case "m_flMagnitude": + m_flMagnitude = ReadFloat(strValue); + break; + case "m_flRadius": + m_flRadius = ReadFloat(strValue); + break; + case "m_strTargetEntity": + m_strTargetEntity = ReadString(strValue); + break; + case "m_flInnerRadius": + m_flInnerRadius = ReadFloat(strValue); + break; + default: + super::Restore(strKey, strValue); + } +} + +void +env_physexplosion::SpawnKey(string keyName, string setValue) +{ + switch (keyName) { + case "magnitude": + m_flMagnitude = ReadFloat(setValue); + break; + case "radius": + m_flRadius = ReadFloat(setValue); + break; + case "targetentityname": + m_strTargetEntity = ReadString(setValue); + break; + case "inner_radius": + m_flInnerRadius = ReadFloat(setValue); + break; + default: + super::SpawnKey(keyName, setValue); + break; + } +} + +void +env_physexplosion::Respawn(void) +{ + SetOrigin(GetSpawnOrigin()); +} + +void +env_physexplosion::TriggerExplosion(void) +{ + float entityDistance = 0.0f; + float targetForce = 0.0f; + float explosionRadius = m_flRadius; + NSPhysicsEntity physEnt; + + if (m_flRadius == 0.0) { + explosionRadius = m_flMagnitude * 2.5; + } + + /* only target them if set */ + if (m_strTargetEntity) { + physEnt = (NSPhysicsEntity)find(world, ::targetname, m_strTargetEntity); + + if (!physEnt) { + NSEntWarning("Target set, but not found!"); + return; + } + + entityDistance = vlen(GetOrigin() - physEnt.WorldSpaceCenter()); + + if (entityDistance <= explosionRadius) { + targetForce = (explosionRadius - entityDistance) / explosionRadius; + targetForce = rint(m_flMagnitude * targetForce); + physEnt.ApplyForceOffset(v_forward * targetForce, origin); + } + return; + } + + for (entity e = world; (e = findfloat(e, ::isPhysics, true));) { + physEnt = (NSPhysicsEntity)e; + entityDistance = vlen(GetOrigin() - physEnt.WorldSpaceCenter()); + + if (entityDistance <= explosionRadius) { + makevectors(vectoangles(physEnt.WorldSpaceCenter() - GetOrigin())); + targetForce = (explosionRadius - entityDistance) / explosionRadius; + targetForce = rint(m_flMagnitude * targetForce); + physEnt.ApplyForceOffset(v_forward * targetForce, origin); + physEnt.velocity += v_forward * targetForce; + } + } +} + +void +env_physexplosion::Input(entity activatorEnt, string inputName, string dataString) +{ + switch (inputName) { + case "Explode": + TriggerExplosion(); + break; + default: + super::Input(activatorEnt, inputName, dataString); + break; + } +} \ No newline at end of file diff --git a/src/gs-entbase/server/func_door.qc b/src/gs-entbase/server/func_door.qc index 38df4304..1c2859e4 100644 --- a/src/gs-entbase/server/func_door.qc +++ b/src/gs-entbase/server/func_door.qc @@ -106,6 +106,8 @@ private: string m_strSndStop; int m_waterType; + + string m_strFullyClosed; }; void @@ -133,6 +135,7 @@ func_door::func_door(void) m_strSndStop = __NULL__; m_waterType = 0i; + m_strFullyClosed = __NULL__; } void @@ -158,6 +161,7 @@ func_door::Save(float handle) SaveString(handle, "m_strSndMove", m_strSndMove); SaveString(handle, "targetClose", targetClose); SaveInt(handle, "m_waterType", m_waterType); + SaveString(handle, "m_strFullyClosed", m_strFullyClosed); } void @@ -215,6 +219,9 @@ func_door::Restore(string strKey, string strValue) case "m_waterType": m_waterType = ReadInt(strValue); break; + case "m_strFullyClosed": + m_strFullyClosed = ReadString(strValue); + break; default: super::Restore(strKey, strValue); } @@ -281,6 +288,10 @@ func_door::SpawnKey(string strKey, string strValue) x = stoi(strValue); m_strUnlockedSfx = sprintf("func_button.hlsfx_%i", x+1i); break; + /* I/O */ + case "OnFullyClosed": + m_strFullyClosed = PrepareOutput(m_strFullyClosed, strValue); + break; default: super::SpawnKey(strKey, strValue); } @@ -305,6 +316,10 @@ func_door::Spawned(void) Sound_Precache(m_strLockedSfx); if (m_strUnlockedSfx) Sound_Precache(m_strUnlockedSfx); + + /* I/O */ + if (m_strFullyClosed) + m_strFullyClosed = CreateOutput(m_strFullyClosed); } void @@ -371,10 +386,10 @@ func_door::Input(entity eAct, string strInput, string strData) { switch (strInput) { case "Open": - Trigger(eAct, TRIG_OFF); + Trigger(eAct, TRIG_ON); break; case "Close": - Trigger(eAct, TRIG_ON); + Trigger(eAct, TRIG_OFF); break; case "Toggle": Trigger(eAct, TRIG_TOGGLE); @@ -420,6 +435,9 @@ func_door::MoverFinishesMoving(void) if (m_strSndMove) StartSound("common/null.wav", CHAN_WEAPON, 0, true); + if (m_strFullyClosed) + UseOutput(this, m_strFullyClosed); + } else if (GetMoverState() == MOVER_POS2) { if (m_strSndStop) { StartSoundDef(m_strSndStop, CHAN_VOICE, true); diff --git a/src/gs-entbase/server/func_physbox.qc b/src/gs-entbase/server/func_physbox.qc index 59c37cc9..fc11458d 100644 --- a/src/gs-entbase/server/func_physbox.qc +++ b/src/gs-entbase/server/func_physbox.qc @@ -49,9 +49,9 @@ func_physbox::Respawn(void) NSPhysicsEntity::Respawn(); if (HasSpawnFlags(FNCPHYBX_ASLEEP)) - PhysicsDisable(); + Sleep(); else - PhysicsEnable(); + Wake(); } void @@ -85,7 +85,7 @@ func_physbox::Death(void) void func_physbox::Respawn(void) { - NSSurfacePropEntity::Respawn(); + super::Respawn(); health = GetSpawnHealth(); SetTakedamage(DAMAGE_YES); SetSolid(SOLID_BBOX); diff --git a/src/gs-entbase/server/logic_auto.qc b/src/gs-entbase/server/logic_auto.qc index f18dac7d..ca0ca6ec 100644 --- a/src/gs-entbase/server/logic_auto.qc +++ b/src/gs-entbase/server/logic_auto.qc @@ -82,7 +82,7 @@ private: void logic_auto::logic_auto(void) { - m_iFromSaveGame = 0; + m_iFromSaveGame = 0i; m_strOnMapSpawn = __NULL__; m_strOnNewGame = __NULL__; m_strOnLoadGame = __NULL__; @@ -108,7 +108,7 @@ logic_auto::Save(float handle) void logic_auto::Restore(string strKey, string strValue) { - m_iFromSaveGame = 1; + m_iFromSaveGame = 1i; switch (strKey) { case "m_strOnMapSpawn": @@ -198,8 +198,6 @@ logic_auto::Spawned(void) void logic_auto::Respawn(void) { - - m_iFromSaveGame = 1; ScheduleThink(Processing, 0.2f); } @@ -220,8 +218,9 @@ logic_auto::Processing(void) cvar_set("_bsp_change_auto", ""); } else UseOutput(this, m_strOnLoadGame); - } else + } else { UseOutput(this, m_strOnNewGame); + } } else { /* TODO: more reliable way of figuring out round restarts */ if (time > 5) diff --git a/src/gs-entbase/server/logic_case.qc b/src/gs-entbase/server/logic_case.qc index 8cd95f99..b6da27c3 100644 --- a/src/gs-entbase/server/logic_case.qc +++ b/src/gs-entbase/server/logic_case.qc @@ -39,8 +39,8 @@ Compares an input value sent from another entity with one of the constant values # INPUTS - "InValue" : Compares the input in the data field with one of the constant values, then firing a matching output. -- "PickRandom" : Not yet implemented. Triggers a random, valid output. -- "PickRandomShuffle" : Not yet implemented. Triggers a random, valid output, but without repeats. +- "PickRandom" : Triggers a random, valid output. +- "PickRandomShuffle" : Triggers a random, valid output, but without repeats. # OUTPUTS - "OnCase01" - Triggered when Case01 matches the InValue input data. @@ -81,6 +81,8 @@ public: virtual void Input(entity, string, string); nonvirtual void CompareCase(entity, string); + nonvirtual void PickRandom(entity); + nonvirtual void PickRandomShuffle(entity); private: @@ -476,16 +478,28 @@ logic_case::Input(entity activatorEntity, string inputName, string dataField) CompareCase(activatorEntity, dataField); break; case "PickRandom": - error("Not implemented."); + PickRandom(activatorEntity); break; case "PickRandomShuffle": - error("Not implemented."); + PickRandomShuffle(activatorEntity); break; default: super::Input(activatorEntity, inputName, dataField); } } +void +logic_case::PickRandom(entity activatorEntity) +{ + NSEntWarning("Not implemented."); +} + +void +logic_case::PickRandomShuffle(entity activatorEntity) +{ + NSEntWarning("Not implemented."); +} + void logic_case::CompareCase(entity activatorEntity, string inputValue) { diff --git a/src/gs-entbase/server/path_corner.qc b/src/gs-entbase/server/path_corner.qc index d8a8d810..2858e2a3 100644 --- a/src/gs-entbase/server/path_corner.qc +++ b/src/gs-entbase/server/path_corner.qc @@ -51,7 +51,7 @@ path_corner:NSPointTrigger { public: void path_corner(void); - + virtual void Save(float); virtual void Restore(string,string); virtual void SpawnKey(string,string); diff --git a/src/gs-entbase/server/phys_ballsocket.qc b/src/gs-entbase/server/phys_ballsocket.qc new file mode 100644 index 00000000..276489a9 --- /dev/null +++ b/src/gs-entbase/server/phys_ballsocket.qc @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2023 Vera Visions LLC. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +/*!QUAKED phys_ballsocket (.5 .3 0) (-8 -8 -8) (8 8 8) BREAKCOLL x INACTIVE MASS NOCONNECT +# OVERVIEW +Creates a connection between two entities in the form of a 'ballsocket'. + +# KEYS +- "targetname" : Name +- "attach1" : Entity 1 +- "attach2" : Entity 2 + +# INPUTS +- "Break" : Forcefully break the constraint. +- "TurnOn" : Turn +- "TurnOff" : Disables the constraint + +# SPAWNFLAGS +- BREAKCOLL (1) : No collision until the connection breaks. +- INACTIVE (4) : Starts inactive. +- MASS (8) : Mass Hack. +- NOCONNECT (16) : Will not connect entities until turned on via Inputs. + +# TRIVIA +This entity was introduced in Half-Life 2 (2004). +*/ +class +phys_ballsocket:NSPhysicsConstraint +{ +public: + void phys_ballsocket(void); + + virtual void Respawn(void); + nonvirtual void AfterSpawn(void); +}; + +void +phys_ballsocket::phys_ballsocket(void) +{ + +} + +void +phys_ballsocket::Respawn(void) +{ + ScheduleThink(AfterSpawn, 0.0f); +} + +void +phys_ballsocket::AfterSpawn(void) +{ + vector centerPos; + SetConstraintType(CONSTRAINT_POINT); + SetOrigin(GetSpawnOrigin()); + + SetEntity1(find(world, ::targetname, m_strEnt1)); + + if (m_strEnt2) + SetEntity2(find(world, ::targetname, m_strEnt2)); + else + SetEntity2(this); + + centerPos = (enemy.origin + aiment.origin) / 2; + origin = velocity = centerPos; + + WakeTargets(); +} \ No newline at end of file diff --git a/src/gs-entbase/server/phys_constraint.qc b/src/gs-entbase/server/phys_constraint.qc new file mode 100644 index 00000000..24b2ba12 --- /dev/null +++ b/src/gs-entbase/server/phys_constraint.qc @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2023 Vera Visions LLC. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +/*!QUAKED phys_constraint (.5 .3 0) (-8 -8 -8) (8 8 8) BREAKCOLL x INACTIVE MASS NOCONNECT +# OVERVIEW +Creates a fixed connection between two entities. + +# KEYS +- "targetname" : Name +- "attach1" : Entity 1 +- "attach2" : Entity 2 + +# INPUTS +- "Break" : Forcefully break the constraint. +- "TurnOn" : Turn +- "TurnOff" : Disables the constraint + +# SPAWNFLAGS +- BREAKCOLL (1) : No collision until the connection breaks. +- INACTIVE (4) : Starts inactive. +- MASS (8) : Mass Hack. +- NOCONNECT (16) : Will not connect entities until turned on via Inputs. + +# TRIVIA +This entity was introduced in Half-Life 2 (2004). +*/ +class +phys_constraint:NSPhysicsConstraint +{ +public: + void phys_constraint(void); + + virtual void Respawn(void); + nonvirtual void AfterSpawn(void); +}; + +void +phys_constraint::phys_constraint(void) +{ + +} + +void +phys_constraint::Respawn(void) +{ + ScheduleThink(AfterSpawn, 0.0f); +} + +void +phys_constraint::AfterSpawn(void) +{ + SetConstraintType(CONSTRAINT_FIXED); + SetOrigin(GetSpawnOrigin()); + + SetEntity1(find(world, ::targetname, m_strEnt1)); + + if (m_strEnt2) + SetEntity2(find(world, ::targetname, m_strEnt2)); + else + SetEntity2(this); +} \ No newline at end of file diff --git a/src/gs-entbase/server/phys_constraintsystem.qc b/src/gs-entbase/server/phys_constraintsystem.qc new file mode 100644 index 00000000..f81c74ac --- /dev/null +++ b/src/gs-entbase/server/phys_constraintsystem.qc @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2023 Vera Visions LLC. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +var int g_phys_constraintsystems; + +/*!QUAKED phys_constraintsystem (.5 .3 0) (-8 -8 -8) (8 8 8) +# OVERVIEW +Turns a series of constraints into a ragdoll, so it can be managed better by the physics simulator. + +# KEYS +- "targetname" : Name + +# NOTES + +You specify the constraintsystem that the constraints belong to not in this entity, but +in the individual constraints themselves. Set the 'constraintsystem' key to an instance of this entity. + +# TRIVIA +This entity was introduced in Half-Life 2 (2004). +*/ +class +phys_constraintsystem:NSPhysicsConstraint +{ +public: + void phys_constraintsystem(void); + + virtual void Respawn(void); + nonvirtual void AfterSpawn(void); + nonvirtual float GetSystemID(void); +}; + +void +phys_constraintsystem::phys_constraintsystem(void) +{ + g_phys_constraintsystems += 1; + jointgroup = (float)g_phys_constraintsystems; +} + +void +phys_constraintsystem::Respawn(void) +{ + ScheduleThink(AfterSpawn, 0.0f); +} + +void +phys_constraintsystem::AfterSpawn(void) +{ + +} + +float +phys_constraintsystem::GetSystemID(void) +{ + return jointgroup; +} \ No newline at end of file diff --git a/src/gs-entbase/server/phys_convert.qc b/src/gs-entbase/server/phys_convert.qc new file mode 100644 index 00000000..f38b6c6f --- /dev/null +++ b/src/gs-entbase/server/phys_convert.qc @@ -0,0 +1,209 @@ +/* + * Copyright (c) 2023 Vera Visions LLC. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +/*!QUAKED phys_convert (.5 .3 0) (-8 -8 -8) (8 8 8) ASLEEP DEBRIS +# OVERVIEW +Turns a standard entity into a physically simulated one. + +# KEYS +- "targetname" : Name +- "target" : Entity 1 +- "swapmodel" : Model override. +- "massoverride" : Optional new mass. + +# INPUTS +- "ConvertTarget" : Triggers the conversion. + +# OUTPUTS +- "OnConvert" : Triggered after successful conversion. + +# SPAWNFLAGS +- ASLEEP (1) : Don't activate motion when conversion is done. +- DEBRIS (2) : Makes converted entity non-solid. + +# TRIVIA +This entity was introduced in Half-Life 2 (2004). +*/ +class +phys_convert:NSPhysicsConstraint +{ +public: + void phys_convert(void); + + virtual void Save(float); + virtual void Restore(string,string); + virtual void SpawnKey(string, string); + virtual void Spawned(void); + virtual void Respawn(void); + virtual void Input(entity, string, string); + nonvirtual void AfterSpawn(void); + nonvirtual void ConvertTarget(entity); + +private: + string m_strSwapModel; + float m_flMassOverride; + string m_strOnConvert; +}; + +void +phys_convert::phys_convert(void) +{ + m_strSwapModel = __NULL__; + m_flMassOverride = 0.0f; + m_strOnConvert = __NULL__; +} + +void +phys_convert::Save(float handle) +{ + super::Save(handle); + SaveString(handle, "m_strSwapModel", m_strSwapModel); + SaveFloat(handle, "m_flMassOverride", m_flMassOverride); + SaveString(handle, "m_strOnConvert", m_strOnConvert); +} + +void +phys_convert::Restore(string strKey, string strValue) +{ + switch (strKey) { + case "m_strSwapModel": + m_strSwapModel = ReadString(strValue); + break; + case "m_flMassOverride": + m_flMassOverride = ReadFloat(strValue); + break; + case "m_strOnConvert": + m_strOnConvert = ReadString(strValue); + break; + default: + super::Restore(strKey, strValue); + } +} + +void +phys_convert::SpawnKey(string keyName, string setValue) +{ + switch (keyName) { + case "target": + m_strEnt1 = ReadString(setValue); + break; + case "swapmodel": + m_strSwapModel = ReadString(setValue); + break; + case "massoverride": + m_flMassOverride = ReadFloat(setValue); + break; + case "OnConvert": + m_strOnConvert = PrepareOutput(m_strOnConvert, setValue); + break; + default: + super::SpawnKey(keyName, setValue); + break; + } +} + +void +phys_convert::Spawned(void) +{ + super::Spawned(); + + if (m_strOnConvert) + m_strOnConvert = CreateOutput(m_strOnConvert); +} + +void +phys_convert::Respawn(void) +{ + ScheduleThink(AfterSpawn, 0.0f); +} + +void +phys_convert::AfterSpawn(void) +{ + SetOrigin(GetSpawnOrigin()); + SetEntity1(find(world, ::targetname, m_strEnt1)); +} + +void +phys_convert::ConvertTarget(entity activatorEnt) +{ + NSEntity targetEnt = (NSEntity)GetEntity1(); + string targetModel; + vector targetAngle; + vector targetPos; + vector targetVelocity; + string targetName; + NSPhysicsEntity new; + + if (!targetEnt) { + NSEntWarning("Cannot find target to convert."); + return; + } + + targetModel = targetEnt.GetModel(); + targetAngle = targetEnt.GetAngles(); + targetPos = targetEnt.GetOrigin(); + targetVelocity = targetEnt.GetVelocity(); + targetName = targetEnt.targetname; + + new = spawn(NSPhysicsEntity); + new.Respawn(); + + /* may have an override */ + if (m_strSwapModel) { + targetModel = m_strSwapModel; + } + + new.SetModel(targetModel); + new.SetOrigin(targetPos); + new.SetAngles(targetAngle); + new.SetVelocity(targetVelocity); + new.targetname = (targetName); + + if (m_flMassOverride > 0.0f) { + new.SetMass(m_flMassOverride); + } + + /* Spawnflags ASLEEP */ + if (HasSpawnFlags(1)) { + new.Sleep(); + } else { + new.Wake(); + } + + /* Spawnflag DEBRIS */ + if (HasSpawnFlags(2)) { + new.SetSolid(SOLID_NOT); + } + + targetEnt.Destroy(); + + if (m_strOnConvert) + UseOutput(activatorEnt, m_strOnConvert); +} + +void +phys_convert::Input(entity activatorEnt, string inputName, string dataString) +{ + switch (inputName) { + case "ConvertTarget": + ConvertTarget(activatorEnt); + break; + default: + super::Input(activatorEnt, inputName, dataString); + break; + } +} \ No newline at end of file diff --git a/src/gs-entbase/server/phys_hinge.qc b/src/gs-entbase/server/phys_hinge.qc new file mode 100644 index 00000000..d4b65800 --- /dev/null +++ b/src/gs-entbase/server/phys_hinge.qc @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2023 Vera Visions LLC. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +/*!QUAKED phys_hinge (.5 .3 0) (-8 -8 -8) (8 8 8) BREAKCOLL x INACTIVE MASS NOCONNECT +# OVERVIEW +Creates a connection between two entities in the form of a hinge. + +# KEYS +- "targetname" : Name +- "attach1" : Entity 1 +- "attach2" : Entity 2 +- "hingefriction" : Friction in the hinge. +- "hingeaxis" : Axis of the hinge. Technically the position it is aiming at. + +# INPUTS +- "Break" : Forcefully break the constraint. +- "TurnOn" : Turn +- "TurnOff" : Disables the constraint +- "SetAngularVelocity" : Applies rotation to the hinge motor. + +# SPAWNFLAGS +- BREAKCOLL (1) : No collision until the connection breaks. +- INACTIVE (4) : Starts inactive. +- MASS (8) : Mass Hack. +- NOCONNECT (16) : Will not connect entities until turned on via Inputs. + +# TRIVIA +This entity was introduced in Half-Life 2 (2004). +*/ +class +phys_hinge:NSPhysicsConstraint +{ +public: + void phys_hinge(void); + + virtual void Save(float); + virtual void Restore(string,string); + virtual void SpawnKey(string, string); + virtual void Input(entity, string, string); + virtual void Respawn(void); + nonvirtual void AfterSpawn(void); + +private: + float m_flHingeFriction; + vector m_vecHingeAxis; +}; + +void +phys_hinge::phys_hinge(void) +{ + m_flHingeFriction = 1.0f; + m_vecHingeAxis = g_vec_null; +} + +void +phys_hinge::Save(float handle) +{ + super::Save(handle); + SaveFloat(handle, "m_flHingeFriction", m_flHingeFriction); + SaveVector(handle, "m_vecHingeAxis", m_vecHingeAxis); +} + +void +phys_hinge::Restore(string strKey, string strValue) +{ + switch (strKey) { + case "m_flHingeFriction": + m_flHingeFriction = ReadFloat(strValue); + break; + case "m_vecHingeAxis": + m_vecHingeAxis = ReadVector(strValue); + break; + default: + super::Restore(strKey, strValue); + } +} + +void +phys_hinge::SpawnKey(string keyName, string setValue) +{ + switch (keyName) { + case "hingefriction": + m_flHingeFriction = ReadFloat(setValue); + break; + case "hingeaxis": + m_vecHingeAxis = ReadVector(setValue); + break; + default: + super::SpawnKey(keyName, setValue); + break; + } +} + +void +phys_hinge::Respawn(void) +{ + ScheduleThink(AfterSpawn, 0.0f); +} + +void +phys_hinge::AfterSpawn(void) +{ + SetConstraintType(CONSTRAINT_HINGE); + SetOrigin(GetSpawnOrigin()); + + SetEntity1(find(world, ::targetname, m_strEnt1)); + + if (m_strEnt2) + SetEntity2(find(world, ::targetname, m_strEnt2)); + else + SetEntity2(this); + + SetAngles(vectoangles(m_vecHingeAxis - GetOrigin())); + SetSliderMaxVelocity(99999); + SetSliderFriction(m_flHingeFriction); + + WakeTargets(); +} + +void +phys_hinge::Input(entity activatorEnt, string inputName, string dataString) +{ + switch (inputName) { + case "SetAngularVelocity": + SetSliderVelocity(stof(dataString)); + break; + default: + super::Input(activatorEnt, inputName, dataString); + break; + } +} \ No newline at end of file diff --git a/src/gs-entbase/server/phys_keepupright.qc b/src/gs-entbase/server/phys_keepupright.qc new file mode 100644 index 00000000..cb3ca8d4 --- /dev/null +++ b/src/gs-entbase/server/phys_keepupright.qc @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2023 Vera Visions LLC. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +/*!QUAKED phys_keepupright (.5 .3 0) (-8 -8 -8) (8 8 8) INACTIVE +# OVERVIEW +Keep an entity upright. + +# KEYS +- "targetname" : Name +- "attach1" : Entity 1 +- "angularlimit" : The limit of angular velocity this can clamp. + +# INPUTS +- "TurnOn" : Make Entity 1 upright. +- "TurnOff" : Make Entity 1 no longer upright. + +# SPAWNFLAGS +- INACTIVE (1) : Starts inactive. + +# TRIVIA +This entity was introduced in Half-Life 2 (2004). +*/ +class +phys_keepupright:NSPhysicsConstraint +{ +public: + void phys_keepupright(void); + + virtual void Save(float); + virtual void Restore(string,string); + virtual void SpawnKey(string, string); + virtual void Respawn(void); + nonvirtual void AfterSpawn(void); + virtual void Input(entity, string, string); + +private: + float m_flAngularLimit; +}; + +void +phys_keepupright::phys_keepupright(void) +{ + m_flAngularLimit = -1.0f; +} + +void +phys_keepupright::Save(float handle) +{ + super::Save(handle); + SaveFloat(handle, "m_flAngularLimit", m_flAngularLimit); +} + +void +phys_keepupright::Restore(string strKey, string strValue) +{ + switch (strKey) { + case "m_flAngularLimit": + m_flAngularLimit = ReadFloat(strValue); + break; + default: + super::Restore(strKey, strValue); + } +} + +void +phys_keepupright::SpawnKey(string keyName, string setValue) +{ + switch (keyName) { + case "angularlimit": + m_flAngularLimit = ReadFloat(setValue); + break; + default: + super::SpawnKey(keyName, setValue); + break; + } +} + +void +phys_keepupright::Respawn(void) +{ + if (HasSpawnFlags(1) == false) + ScheduleThink(AfterSpawn, 0.0f); +} + +void +phys_keepupright::AfterSpawn(void) +{ + SetOrigin(GetSpawnOrigin()); + SetEntity1(find(world, ::targetname, m_strEnt1)); + NSEntity targetEnt = (NSEntity)GetEntity1(); + KeepUpright(targetEnt, targetEnt.GetSpawnAngles(), m_flAngularLimit); +} + +void +phys_keepupright::Input(entity activatorEnt, string inputName, string dataString) +{ + NSEntity targetEnt; + + switch (inputName) { + case "TurnOn": + targetEnt = (NSEntity)GetEntity1(); + KeepUpright(targetEnt, targetEnt.GetSpawnAngles(), m_flAngularLimit); + break; + case "TurnOff": + targetEnt = (NSEntity)GetEntity1(); + KeepUpright(targetEnt, targetEnt.GetSpawnAngles(), -1); + break; + default: + super::Input(activatorEnt, inputName, dataString); + break; + } +} \ No newline at end of file diff --git a/src/gs-entbase/server/phys_slideconstraint.qc b/src/gs-entbase/server/phys_slideconstraint.qc new file mode 100644 index 00000000..4d3cd921 --- /dev/null +++ b/src/gs-entbase/server/phys_slideconstraint.qc @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2023 Vera Visions LLC. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +/*!QUAKED phys_slideconstraint (.5 .3 0) (-8 -8 -8) (8 8 8) BREAKCOLL LIMITENDS +# OVERVIEW +Will slide an entity along a segment. + +# KEYS +- "targetname" : Name +- "attach1" : Entity 1 +- "attach2" : Entity 2 +- "slideaxis" : Axis onto which the entity slides along. Technically the position it is aiming at. +- "slidefriction" : Friction the entity experiences along the slide. + +# INPUTS +- "Break" : Forcefully break the constraint. +- "TurnOn" : Turn +- "TurnOff" : Disables the constraint + +# SPAWNFLAGS +- BREAKCOLL (1) : No collision until the connection breaks. +- LIMITENDS (2) : Limit endpoints. + +# TRIVIA +This entity was introduced in Half-Life 2 (2004). +*/ +class +phys_slideconstraint:NSPhysicsConstraint +{ +public: + void phys_slideconstraint(void); + + virtual void Save(float); + virtual void Restore(string,string); + virtual void SpawnKey(string, string); + virtual void Input(entity, string, string); + virtual void Respawn(void); + nonvirtual void AfterSpawn(void); + +private: + vector m_vecSliderAxis; + float m_flSliderFriction; +}; + +void +phys_slideconstraint::phys_slideconstraint(void) +{ + m_vecSliderAxis = g_vec_null; + m_flSliderFriction = 0.0f; +} + +void +phys_slideconstraint::Save(float handle) +{ + super::Save(handle); + SaveVector(handle, "m_vecSliderAxis", m_vecSliderAxis); + SaveFloat(handle, "m_flSliderFriction", m_flSliderFriction); +} + +void +phys_slideconstraint::Restore(string strKey, string strValue) +{ + switch (strKey) { + case "m_vecSliderAxis": + m_vecSliderAxis = ReadVector(strValue); + break; + case "m_flSliderFriction": + m_flSliderFriction = ReadFloat(strValue); + break; + default: + super::Restore(strKey, strValue); + } +} + +void +phys_slideconstraint::SpawnKey(string keyName, string setValue) +{ + switch (keyName) { + case "slideaxis": + m_vecSliderAxis = ReadVector(setValue); + break; + case "slidefriction": + m_flSliderFriction = ReadFloat(setValue); + break; + default: + super::SpawnKey(keyName, setValue); + break; + } +} + +void +phys_slideconstraint::Respawn(void) +{ + ScheduleThink(AfterSpawn, 0.0f); +} + +void +phys_slideconstraint::AfterSpawn(void) +{ + SetConstraintType(CONSTRAINT_SLIDER); + SetOrigin(GetSpawnOrigin()); + + SetEntity1(find(world, ::targetname, m_strEnt1)); + + if (m_strEnt2) + SetEntity2(find(world, ::targetname, m_strEnt2)); + else + SetEntity2(this); + + SetAngles(vectoangles(m_vecSliderAxis - GetOrigin())); + SetSliderMaxVelocity(99999); + SetSliderFriction(m_flSliderFriction); + + if (HasSpawnFlags(2)) + SetSliderStop(vlen(m_vecSliderAxis - GetOrigin())); + + WakeTargets(); +} + +void +phys_slideconstraint::Input(entity activatorEnt, string inputName, string dataString) +{ + switch (inputName) { + case "SetVelocity": + if (GetSliderVelocity() > 0.0f) + SetSliderVelocity(-stof(dataString)); + else + SetSliderVelocity(stof(dataString)); + + WakeTargets(); + break; + default: + super::Input(activatorEnt, inputName, dataString); + break; + } +} \ No newline at end of file diff --git a/src/gs-entbase/server/prop_dynamic.qc b/src/gs-entbase/server/prop_dynamic.qc index b8d724fa..c937bfde 100644 --- a/src/gs-entbase/server/prop_dynamic.qc +++ b/src/gs-entbase/server/prop_dynamic.qc @@ -86,3 +86,6 @@ prop_dynamic::Respawn(void) if (HasSpawnFlags(PRPDYN_NONSOLID)) SetSolid(SOLID_NOT); } + +CLASSEXPORT(prop_dynamic_override, prop_dynamic) +CLASSEXPORT(prop_dynamic_respawnable, prop_dynamic) diff --git a/src/gs-entbase/server/prop_physics.qc b/src/gs-entbase/server/prop_physics.qc index 9abb4840..a7342876 100644 --- a/src/gs-entbase/server/prop_physics.qc +++ b/src/gs-entbase/server/prop_physics.qc @@ -15,9 +15,21 @@ */ #ifndef PHYSICS_STATIC -#define PRPPHYS_ASLEEP 1 -/*!QUAKED prop_physics (1 0 0) (-16 -16 -16) (16 16 16) PRPPHYS_ASLEEP +enumflags +{ + PHYSPROPSFL_ASLEEP, + PHYSPROPSFL_NOPHYSDMG, + PHYSPROPSFL_DEBRIS, + PHYSPROPSFL_NOMOTION, + PHYSPROPSFL_UNUSED1, + PHYSPROPSFL_UNUSED2, + PHYSPROPSFL_PHYSDEVICE, + PHYSPROPSFL_NOROTOR, + PHYSPROPSFL_USEOUT, +}; + +/*!QUAKED prop_physics (1 0 0) (-16 -16 -16) (16 16 16) ASLEEP NOPHYSDMG DEBRIS NOMOTION x x PHYSDEVICE NOROTOR USEOUT # OVERVIEW Physics model @@ -25,7 +37,13 @@ Physics model - "targetname" : Name # SPAWNFLAGS -- PRPPHYS_ASLEEP (1) : Prop starts without physics and stays until it is impacted. +- ASLEEP (1) : Prop starts without physics and stays until it is impacted. +- NOPHYSDMG (2) : Will not take physics damage. +- DEBRIS (4) : Will not collide with players, or other types of debris +- NOMOTION (8) : Disable motion +- PHYSDEVICE (64) : Enable motion when grabbed with a physics device. +- NOROTOR (128) : Unaffected by rotor contraptions. +- USEOUT (256) : Generate output on +use. # TRIVIA This entity was introduced in Half-Life 2 (2004). @@ -50,19 +68,27 @@ prop_physics::SpawnKey(string strKey, string strValue) { switch (strKey) { default: - NSPhysicsEntity::SpawnKey(strKey, strValue); + super::SpawnKey(strKey, strValue); } } void prop_physics::Respawn(void) { - NSPhysicsEntity::Respawn(); + super::Respawn(); - if (HasSpawnFlags(PRPPHYS_ASLEEP)) - PhysicsDisable(); + if (HasSpawnFlags(PHYSPROPSFL_ASLEEP)) + Sleep(); else - PhysicsEnable(); + Wake(); + + if (HasSpawnFlags(PHYSPROPSFL_NOMOTION)) + Sleep(); + + if (HasSpawnFlags(PHYSPROPSFL_PHYSDEVICE)) + Sleep(); + + //Sleep(); } #else class @@ -85,3 +111,6 @@ prop_physics::Respawn(void) SetSolid(SOLID_BBOX); } #endif + +CLASSEXPORT(prop_physics_override, prop_physics) +CLASSEXPORT(prop_physics_respawnable, prop_physics) diff --git a/src/gs-entbase/server/prop_static.qc b/src/gs-entbase/server/prop_static.qc index fb24e6e7..c0b69f75 100644 --- a/src/gs-entbase/server/prop_static.qc +++ b/src/gs-entbase/server/prop_static.qc @@ -39,7 +39,7 @@ waste disk space and memory. Use wisely. This entity was introduced in Half-Life 2 (2004). */ class -prop_static:NSRenderableEntity +prop_static:NSPhysicsEntity { public: void prop_static(void); @@ -56,10 +56,13 @@ prop_static::prop_static(void) void prop_static::Respawn(void) { - SetSolid(SOLID_NOT); SetModel(GetSpawnModel()); - mins = [0,0,0]; - maxs = [0,0,0]; - SetSize(mins, maxs); SetOrigin(GetSpawnOrigin()); + SetMovetype(MOVETYPE_PHYSICS); + SetSolid(SOLID_PHYSICS_BOX); + geomtype = GEOMTYPE_TRIMESH; + + Sleep(); + SetTakedamage(DAMAGE_NO); + touch = __NULL__; } diff --git a/src/gs-entbase/shared.src b/src/gs-entbase/shared.src index c4ab3455..8bb778ab 100644 --- a/src/gs-entbase/shared.src +++ b/src/gs-entbase/shared.src @@ -35,5 +35,6 @@ shared/point_spotlight.qc shared/trigger_push.qc shared/func_conveyor.qc shared/prop_rope.qc +shared/phys_rope.qc shared/worldspawn.qc #endlist diff --git a/src/gs-entbase/shared/env_muzzleflash.qc b/src/gs-entbase/shared/env_muzzleflash.qc index 4dfeff0e..a6ad1280 100644 --- a/src/gs-entbase/shared/env_muzzleflash.qc +++ b/src/gs-entbase/shared/env_muzzleflash.qc @@ -37,6 +37,8 @@ public: void env_muzzleflash(void); #ifdef SERVER + virtual void Save(float); + virtual void Restore(string,string); virtual void SpawnKey(string, string); virtual void Input(entity, string, string); virtual void Input(entity, string, string); @@ -63,10 +65,50 @@ env_muzzleflash::env_muzzleflash(void) m_eOwner = __NULL__; m_strModel = __NULL__; m_strParticle = __NULL__; + m_eMuzzler = __NULL__; scale = 1.0f; } #ifdef SERVER +void +env_muzzleflash::Save(float handle) +{ + super::Save(handle); + SaveString(handle, "m_strAttachmentName", m_strAttachmentName); + SaveInt(handle, "m_iAttachment", m_iAttachment); + SaveEntity(handle, "m_eOwner", m_eOwner); + SaveString(handle, "m_strModel", m_strModel); + SaveString(handle, "m_strParticle", m_strParticle); + SaveEntity(handle, "m_eMuzzler", m_eMuzzler); +} + +void +env_muzzleflash::Restore(string strKey, string strValue) +{ + switch (strKey) { + case "m_strAttachmentName": + m_strAttachmentName = ReadString(strValue); + break; + case "m_iAttachment": + m_iAttachment = ReadInt(strValue); + break; + case "m_eOwner": + m_eOwner = (NSRenderableEntity)ReadEntity(strValue); + break; + case "m_strModel": + m_strModel = ReadString(strValue); + break; + case "m_strParticle": + m_strParticle = ReadString(strValue); + break; + case "m_eMuzzler": + m_eMuzzler = ReadEntity(strValue); + break; + default: + super::Restore(strKey, strValue); + } +} + void env_muzzleflash::SpawnKey(string keyName, string setValue) { @@ -107,11 +149,16 @@ env_muzzleflash::Input(entity eAct, string strKey, string strValue) void env_muzzleflash::Trigger(entity theActivator, triggermode_t triggerState) { - NSEntity targetEnt; + NSEntity targetEnt = __NULL__; vector targetPosition = GetOrigin(); if (m_parent) { targetEnt = (NSEntity)find(world, ::targetname, m_parent); + + if (!targetEnt) { + NSEntWarning("Entity specified but not found."); + return; + } } WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET); @@ -155,7 +202,7 @@ EV_MuzzleFlash_Parse(void) { env_muzzleflash tempMuzzle = spawn(env_muzzleflash); - tempMuzzle.m_eOwner = findfloat(world, entnum, readentitynum()); + tempMuzzle.m_eOwner = (NSRenderableEntity)findfloat(world, entnum, readentitynum()); tempMuzzle.m_iAttachment = readbyte(); tempMuzzle.scale = readfloat(); tempMuzzle.modelindex = readshort(); @@ -171,7 +218,7 @@ EV_MuzzleFlash_Create(entity muzzleOwner, int attachmentID, float muzzleScale, i { env_muzzleflash tempMuzzle = spawn(env_muzzleflash); - tempMuzzle.m_eOwner = muzzleOwner; + tempMuzzle.m_eOwner = (NSRenderableEntity)muzzleOwner; tempMuzzle.m_iAttachment = attachmentID; tempMuzzle.scale = muzzleScale; tempMuzzle.alpha = 1.0f; diff --git a/src/gs-entbase/shared/env_shockwave.qc b/src/gs-entbase/shared/env_shockwave.qc index 028cea06..d4506ac6 100644 --- a/src/gs-entbase/shared/env_shockwave.qc +++ b/src/gs-entbase/shared/env_shockwave.qc @@ -86,7 +86,7 @@ env_shockwave::env_shockwave(void) m_flHeight = 32.0f; m_flNoiseAmp = 0.0f; m_vecColor = [1,1,1]; - m_flBrightness = 1.0f;; + m_flBrightness = 1.0f; m_flScrollSpeed = 0.0f; } diff --git a/src/gs-entbase/shared/phys_rope.qc b/src/gs-entbase/shared/phys_rope.qc new file mode 100644 index 00000000..e3a0b6db --- /dev/null +++ b/src/gs-entbase/shared/phys_rope.qc @@ -0,0 +1,363 @@ +/* + * Copyright (c) 2016-2022 Vera Visions LLC. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +enumflags +{ + PHYSROPE_CHANGED_MAT, + PHYSROPE_CHANGED_SAG, + PHYSROPE_CHANGED_SWING, + PHYSROPE_CHANGED_SEGMENTS, + PHYSROPE_CHANGED_ORIGIN, + PHYSROPE_CHANGED_TARGET, + PHYSROPE_CHANGED_FLAGS, + PHYSROPE_CHANGED_WIDTH +}; + +/*!QUAKED phys_rope (1 0 0) (-8 -8 -8) (8 8 8) ROPE_VERTICAL +# OVERVIEW +Client-side decorative rope entity. +Connect the entity to a named info_notnull and watch it swing around. + +# KEYS +- "sag" : Multiplier on how much sagginess will be applied to the rope. +- "segments" : Number of total segments. Default is "16". +- "material" : The texture to use on the rope. +- "swingfactor" : Multiplier on how much the rope swings about. +- "target" : The info_notnull to connect the rope to. + +# SPAWNFLAGS +- ROPE_VERTICAL (1) : Only draw the first half of the rope, useful for vertical setups. + +# TRIVIA +This entity was introduced in The Wastes (2018). +*/ +class phys_rope:NSEntity +{ +public: + void phys_rope(void); + +#ifdef CLIENT + virtual float predraw(void); + virtual void ReceiveEntity(float,float); + virtual void DrawSegment(vector, vector, vector); +#else + virtual void SpawnKey(string,string); + virtual void Respawn(void); + virtual void Save(float); + virtual void Restore(string, string); + virtual void EvaluateEntity(void); + virtual float SendEntity(entity,float); +#endif + +private: + string m_strShader; + PREDICTED_FLOAT(m_flSag) + PREDICTED_FLOAT(m_flSwingFactor) + PREDICTED_INT(m_iSegments) + PREDICTED_VECTOR(m_vecTarget) + PREDICTED_FLOAT(m_flWidth) +}; + +#ifdef CLIENT + +void +phys_rope::DrawSegment(vector pos1, vector pos2, vector vecPlayer) +{ + vector lit1 = /*[0.1,0.1,0.1] */ getlight(pos1) / 255; + vector lit2 = /*[0.1,0.1,0.1] */ getlight(pos2) / 255; + + R_BeginPolygon(m_strShader, 0, 0); + R_PolygonVertex(pos1, [0,0], lit1, 1.0f); + R_PolygonVertex(pos2, [0,1], lit2, 1.0f); + R_EndPolygonRibbon(1, [-1,0]); +} + + +float +phys_rope::predraw(void) +{ + vector pos1; + vector pos2; + float segments; + vector vecPlayer; + NSClientPlayer pl; + + int s = (float)getproperty(VF_ACTIVESEAT); + pSeat = &g_seats[s]; + pl = (NSClientPlayer)pSeat->m_ePlayer; + vecPlayer = pl.GetEyePos(); + + /* draw the start/end without segments */ + if (autocvar_rope_debug == TRUE) { + R_BeginPolygon("", 0, 0); + R_PolygonVertex(origin, [0,1], [0,1,0], 1.0f); + R_PolygonVertex(m_vecTarget, [1,1], [0,1,0], 1.0f); + R_EndPolygon(); + } + + if (autocvar_rope_maxsegments > 0) + segments = bound(1, autocvar_rope_maxsegments, (float)m_iSegments); + else + segments = (float)m_iSegments; + + float travel = 1.0f / segments; + float progress= 0.0f; + pos1 = origin; + + makevectors(getproperty(VF_CL_VIEWANGLES)); + setproperty(VF_ORIGIN, vecPlayer); + + /* get the direction */ + makevectors(vectoangles(m_vecTarget - origin)); + + for (float i = 0; i < segments; i++) { + float sag = 0.0f; + float swing = 0.0f; + progress += travel; + float c1 = (ropecos(progress) * M_PI) * 2.25f; + + /* loose hanging rope */ + if (flags & 1) { + sag = c1 * m_flSag; + swing = c1 * m_flSwingFactor; + } else { + sag = c1 * m_flSag; + swing = c1 * m_flSwingFactor; + } + + /* travel further and sag */ + pos2[0] = Math_Lerp(origin[0], m_vecTarget[0], progress); + pos2[1] = Math_Lerp(origin[1], m_vecTarget[1], progress); + pos2[2] = Math_Lerp(origin[2], m_vecTarget[2], progress); + pos2 += (v_up * -sag) * autocvar_rope_sag; + + if (!autocvar_rope_fast) + pos2 += ((v_right * swing) * sin(time)) * autocvar_rope_swing; + + DrawSegment(pos1, pos2, vecPlayer); + pos1 = pos2; + } + + return (PREDRAW_NEXT); +} + +void +phys_rope::ReceiveEntity(float new, float flSendFlags) +{ + if (flSendFlags & PHYSROPE_CHANGED_MAT) + m_strShader = readstring(); + if (flSendFlags & PHYSROPE_CHANGED_SAG) + m_flSag = readfloat(); + if (flSendFlags & PHYSROPE_CHANGED_SWING) + m_flSwingFactor = readfloat(); + if (flSendFlags & PHYSROPE_CHANGED_SEGMENTS) + m_iSegments = readint(); + if (flSendFlags & PHYSROPE_CHANGED_ORIGIN) { + origin[0] = readcoord(); + origin[1] = readcoord(); + origin[2] = readcoord(); + setsize(this, [0,0,0], [0,0,0]); + setorigin(this, origin); + } + if (flSendFlags & PHYSROPE_CHANGED_TARGET) { + m_vecTarget[0] = readcoord(); + m_vecTarget[1] = readcoord(); + m_vecTarget[2] = readcoord(); + } + if (flSendFlags & PHYSROPE_CHANGED_FLAGS) + flags = readfloat(); + if (flSendFlags & PHYSROPE_CHANGED_WIDTH) + m_flWidth = readfloat(); +} +#else +void +phys_rope::Respawn(void) +{ + if (HasSpawnFlags(1)) { + flags = 1; + } + SetOrigin(GetSpawnOrigin()); + SetSize([0,0,0], [0,0,0]); +} +void +phys_rope::EvaluateEntity(void) +{ + if (!target) + return; + + entity eFind = find(world, ::targetname, target); + + if (!eFind) { + print(sprintf("phys_rope: Unable to find target %S\n", target)); + return; + } + + m_vecTarget = eFind.origin; + + if (ATTR_CHANGED(m_flSag)) { + SetSendFlags(PHYSROPE_CHANGED_SAG); + } + if (ATTR_CHANGED(m_flSwingFactor)) { + SetSendFlags(PHYSROPE_CHANGED_SWING); + } + if (ATTR_CHANGED(m_iSegments)) { + SetSendFlags(PHYSROPE_CHANGED_SEGMENTS); + } + + if (ATTR_CHANGED(origin)) { + SetSendFlags(PHYSROPE_CHANGED_ORIGIN); + } + if (ATTR_CHANGED(m_vecTarget)) { + SetSendFlags(PHYSROPE_CHANGED_TARGET); + } + if (ATTR_CHANGED(flags)) { + SetSendFlags(PHYSROPE_CHANGED_FLAGS); + } + if (ATTR_CHANGED(m_flWidth)) { + SetSendFlags(PHYSROPE_CHANGED_WIDTH); + } + + SAVE_STATE(m_flSag) + SAVE_STATE(m_flSwingFactor) + SAVE_STATE(m_iSegments) + SAVE_STATE(origin) + SAVE_STATE(m_vecTarget) + SAVE_STATE(flags) + SAVE_STATE(m_flWidth) +} + +float +phys_rope::SendEntity(entity ePVEnt, float flSendFlags) +{ + if (!target) + return 0; + + WriteByte(MSG_ENTITY, ENT_PHYSROPE); + WriteFloat(MSG_ENTITY, flSendFlags); + + if (flSendFlags & PHYSROPE_CHANGED_MAT) + WriteString(MSG_ENTITY, m_strShader); + if (flSendFlags & PHYSROPE_CHANGED_SAG) + WriteFloat(MSG_ENTITY, m_flSag); + if (flSendFlags & PHYSROPE_CHANGED_SWING) + WriteFloat(MSG_ENTITY, m_flSwingFactor); + if (flSendFlags & PHYSROPE_CHANGED_SEGMENTS) + WriteInt(MSG_ENTITY, m_iSegments); + if (flSendFlags & PHYSROPE_CHANGED_ORIGIN) { + WriteCoord(MSG_ENTITY, origin[0]); + WriteCoord(MSG_ENTITY, origin[1]); + WriteCoord(MSG_ENTITY, origin[2]); + } + if (flSendFlags & PHYSROPE_CHANGED_TARGET) { + WriteCoord(MSG_ENTITY, m_vecTarget[0]); + WriteCoord(MSG_ENTITY, m_vecTarget[1]); + WriteCoord(MSG_ENTITY, m_vecTarget[2]); + } + if (flSendFlags & PHYSROPE_CHANGED_FLAGS) { + WriteFloat(MSG_ENTITY, flags); + } + if (flSendFlags & PHYSROPE_CHANGED_WIDTH) { + WriteFloat(MSG_ENTITY, m_flWidth); + } + + return 1; +} + +void +phys_rope::SpawnKey(string strKey, string strValue) +{ + switch (strKey) { + case "sag": + m_flSag = stof(strValue); + break; + case "segments": + m_iSegments = stoi(strValue); + break; + case "shader": + m_strShader = strValue; + break; + case "swingfactor": + m_flSwingFactor = stof(strValue); + break; + case "NextKey": + target = ReadString(strValue); + break; + case "Width": + m_flWidth = ReadFloat(strValue); + break; + default: + super::SpawnKey(strKey, strValue); + } +} + +void +phys_rope::Save(float handle) +{ + super::Save(handle); + + SaveString(handle, "m_strShader", m_strShader); + SaveFloat(handle, "m_flSag", m_flSag); + SaveFloat(handle, "m_flSwingFactor", m_flSwingFactor); + SaveInt(handle, "m_iSegments", m_iSegments); + SaveVector(handle, "m_vecTarget", m_vecTarget); + SaveFloat(handle, "m_flWidth", m_flWidth); +} + +void +phys_rope::Restore(string strKey, string strValue) +{ + switch (strKey) { + case "RopeMaterial": + m_strShader = ReadString(strValue); + break; + case "m_flSag": + m_flSag = ReadFloat(strValue); + break; + case "m_flSwingFactor": + m_flSwingFactor = ReadFloat(strValue); + break; + case "Subdiv": + m_iSegments = ReadInt(strValue); + break; + case "m_vecTarget": + m_vecTarget = ReadVector(strValue); + break; + case "m_flWidth": + m_flWidth = ReadFloat(strValue); + break; + default: + super::Restore(strKey, strValue); + } +} +#endif + +void +phys_rope::phys_rope(void) +{ +#ifdef SERVER + m_flSwingFactor = random(); + m_flSag = 15.0f; + m_iSegments = 16; + m_flWidth = 2.0f; + m_strShader = "materials/cable/cable.vmt"; +#else + /* this is empty for a good reason */ + drawmask = MASK_ENGINE; +#endif +} + +CLASSEXPORT(move_rope, phys_rope) +CLASSEXPORT(keyframe_rope, phys_rope) \ No newline at end of file diff --git a/src/gs-entbase/shared/prop_physics_multiplayer.qc b/src/gs-entbase/shared/prop_physics_multiplayer.qc index a7c45eaa..c1a56fe5 100644 --- a/src/gs-entbase/shared/prop_physics_multiplayer.qc +++ b/src/gs-entbase/shared/prop_physics_multiplayer.qc @@ -75,9 +75,9 @@ prop_physics_multiplayer::TouchThink(void) if (trace_ent.flags & FL_CLIENT) { //print(sprintf("%s %f\n", trace_ent.classname, trace_fraction)); - PhysicsEnable(); + Wake(); makevectors(origin - trace_ent.origin); - ApplyForceCenter(v_forward * 64); + ApplyForceOffset(v_forward * 64, trace_endpos); velocity = v_forward * 64; } } @@ -86,7 +86,7 @@ prop_physics_multiplayer::TouchThink(void) } else { if (vlen(velocity) < 2) { velocity = [0,0,0]; - PhysicsDisable(); + Sleep(); } } @@ -127,9 +127,9 @@ prop_physics_multiplayer::Respawn(void) super::Respawn(); if (HasSpawnFlags(PRPPHYS_ASLEEP)) - PhysicsDisable(); + Sleep(); else - PhysicsEnable(); + Wake(); } void diff --git a/src/server/NSGameRules.qc b/src/server/NSGameRules.qc index 09b1675e..e7a9a4a1 100644 --- a/src/server/NSGameRules.qc +++ b/src/server/NSGameRules.qc @@ -451,7 +451,7 @@ NSGameRules::DamageRadius(vector org, entity attacker, float dmg, float r, int c new_dmg = rint(dmg * diff); if (diff > 0) { - g_dmg_vecLocation = org; + g_dmg_vecLocation = trace_endpos; DamageApply(e, attacker, new_dmg, w, DMG_EXPLODE); /* approximate, feel free to tweak */ diff --git a/src/shared/NSPhysicsConstraint.h b/src/shared/NSPhysicsConstraint.h new file mode 100644 index 00000000..e5e64770 --- /dev/null +++ b/src/shared/NSPhysicsConstraint.h @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2023 Vera Visions LLC. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +typedef enum +{ + CONSTRAINT_FIXED = -1, /**< Fixed constraint, aka weld, aka phys_constraint. */ + CONSTRAINT_INVALID, /**< Nothing. Default. */ + CONSTRAINT_POINT, /**< Point constraint, aka ballsocket or ball constraint, like phys_ballsocket. */ + CONSTRAINT_HINGE, /**< Hinge joint constraint. Like phys_hinge */ + CONSTRAINT_SLIDER, /**< Slider setup. Like phys_slideconstraint. */ + CONSTRAINT_UNIVERSAL, /**< Universal? TBA. */ + CONSTRAINT_HINGE2 /**< Hinge 2. TBA. */ +} constraint_t; + +/** This entity class represents constraints for physically-simulated entities. + +If you want to create an easy 'weld' type connection, a ballsocket or even a rope +type connection - this class is what you need.*/ +class +NSPhysicsConstraint:NSEntity +{ +public: + void NSPhysicsConstraint(void); + + + virtual void SpawnKey(string, string); + +#ifdef SERVER + virtual void Save(float); + virtual void Restore(string,string); + virtual void Input(entity, string, string); +#endif + + virtual void Spawned(void); + virtual void OnRemoveEntity(void); + + /** Awakes the entities this constraint is connected to. */ + nonvirtual void WakeTargets(void); + + /** Breaks the constraint. */ + nonvirtual void Break(entity); + + /** Returns the type of this constraint entity. */ + nonvirtual constraint_t GetConstraintType(void); + /** Returns the first entity connection. Should not return world/__NULL__ */ + nonvirtual entity GetEntity1(void); + /** Returns the second entity connection. Can also return world/__NULL__ */ + nonvirtual entity GetEntity2(void); + + /** Will override the constraint type this entity represents. See constraint_t for choices. */ + nonvirtual void SetConstraintType(constraint_t); + /** Sets the first entity in the connection. Needs to be set. */ + nonvirtual void SetEntity1(entity); + /** Sets the second entity in the connection. Can be world. */ + nonvirtual void SetEntity2(entity); + //nonvirtual void SetBone1(float); + //nonvirtual void SetBone2(float); + + /** Sets the velocity on a CONSTRAINT_SLIDER type NSPhysicsConstraint. */ + nonvirtual void SetSliderVelocity(float); + /** Sets the max velocity on a CONSTRAINT_SLIDER type NSPhysicsConstraint. */ + nonvirtual void SetSliderMaxVelocity(float); + /** Sets the maximum travel distance of the slider. */ + nonvirtual void SetSliderStop(float); + /** Sets the friction of the slider. */ + nonvirtual void SetSliderFriction(float); + + /** Returns the velocity of a CONSTRAINT_SLIDER type NSPhysicsConstraint. */ + nonvirtual float GetSliderVelocity(void); + /** Returns the max velocity of a CONSTRAINT_SLIDER type NSPhysicsConstraint. */ + nonvirtual float GetSliderMaxVelocity(void); + /** Returns the maximum travel distance of the slider. */ + nonvirtual float GetSliderStop(void); + /** Returns the friction of the slider. */ + nonvirtual float GetSliderFriction(void); + /** Returns the unique joint group ID associated with a phys_constraintsystem. */ + nonvirtual float GetConstraintSystemID(void); + + /** Creates a ballsocket constraint and returns it. */ + nonvirtual NSPhysicsConstraint Ballsocket(entity, entity, vector, vector, float, bool); + /** Creates a ballsocket constraint and returns it. */ + nonvirtual NSPhysicsConstraint Weld(entity, entity, float, float, float, bool, bool); + /** Creates a ballsocket constraint and returns it. */ + nonvirtual NSPhysicsConstraint Rope(entity, entity, vector, vector); + + nonvirtual NSPhysicsConstraint KeepUpright(entity, vector, float); + + nonvirtual void ConstraintThink(void); + +private: + + float m_flTorqueLimit; + float m_flForceLimit; + string m_strEnt1; + string m_strEnt2; + string m_strBreakSound; + string m_strOnBreak; + string m_strConstraintSystem; +}; \ No newline at end of file diff --git a/src/shared/NSPhysicsConstraint.qc b/src/shared/NSPhysicsConstraint.qc new file mode 100644 index 00000000..93fee309 --- /dev/null +++ b/src/shared/NSPhysicsConstraint.qc @@ -0,0 +1,375 @@ +/* + * Copyright (c) 2023 Vera Visions LLC. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +.vector movedir; +.entity aiment; + +void +NSPhysicsConstraint::NSPhysicsConstraint(void) +{ + m_flTorqueLimit = 0.0f; + m_flForceLimit = 0.0f; + m_strEnt1 = __NULL__; + m_strEnt2 = __NULL__; + m_strBreakSound = __NULL__; + m_strOnBreak = __NULL__; + m_strConstraintSystem = __NULL__; +} + +void +NSPhysicsConstraint::ConstraintThink(void) +{ + NSPhysicsEntity target1 = (NSPhysicsEntity)GetEntity1(); + NSPhysicsEntity target2 = (NSPhysicsEntity)GetEntity1(); + + /* never run again */ + if (m_flForceLimit <= 0 && m_flTorqueLimit <= 0) + return; + + if (m_flForceLimit > 0) + if (vlen(target1.GetVelocity()) > m_flForceLimit) { + Break(this); + return; + } + + if (m_flTorqueLimit > 0) + if (vlen(target1.GetAngularVelocity()) > m_flTorqueLimit) { + Break(this); + return; + } + + if (m_flForceLimit > 0) + if (vlen(target2.GetVelocity()) > m_flForceLimit) { + Break(this); + return; + } + + if (m_flTorqueLimit > 0) + if (vlen(target2.GetAngularVelocity()) > m_flTorqueLimit) { + Break(this); + return; + } + SetNextThink(0.0f); +} + +#ifdef SERVER +void +NSPhysicsConstraint::Save(float handle) +{ + super::Save(handle); + SaveFloat(handle, "m_flTorqueLimit", m_flTorqueLimit); + SaveFloat(handle, "m_flForceLimit", m_flForceLimit); + SaveString(handle, "m_strEnt1", m_strEnt1); + SaveString(handle, "m_strEnt2", m_strEnt2); + SaveString(handle, "m_strBreakSound", m_strBreakSound); + SaveString(handle, "m_strOnBreak", m_strOnBreak); + SaveString(handle, "m_strConstraintSystem", m_strConstraintSystem); +} + +void +NSPhysicsConstraint::Restore(string strKey, string strValue) +{ + switch (strKey) { + case "m_flTorqueLimit": + m_flTorqueLimit = ReadFloat(strValue); + break; + case "m_flForceLimit": + m_flForceLimit = ReadFloat(strValue); + break; + case "m_strEnt1": + m_strEnt1 = ReadString(strValue); + break; + case "m_strEnt2": + m_strEnt2 = ReadString(strValue); + break; + case "m_strBreakSound": + m_strBreakSound = ReadString(strValue); + break; + case "m_strOnBreak": + m_strOnBreak = ReadString(strValue); + break; + case "m_strConstraintSystem": + m_strConstraintSystem = ReadString(strValue); + break; + default: + super::Restore(strKey, strValue); + } +} +#endif + +void +NSPhysicsConstraint::SpawnKey(string keyName, string setValue) +{ + switch (keyName) { + case "attach1": + m_strEnt1 = ReadString(setValue); + break; + case "attach2": + m_strEnt2 = ReadString(setValue); + break; + case "torquelimit": + m_flTorqueLimit = ReadFloat(setValue); + break; + case "forcelimit": + m_flForceLimit = ReadFloat(setValue); + break; + case "breaksound": + m_strBreakSound = ReadString(setValue); + break; + case "teleportfollowdistance": + break; + case "constraintsystem": + m_strConstraintSystem = ReadString(setValue); + break; +#ifdef SERVER + case "OnBreak": + m_strOnBreak = PrepareOutput(m_strOnBreak, setValue); + break; +#endif + default: + super::SpawnKey(keyName, setValue); + break; + } +} + +float +NSPhysicsConstraint::GetConstraintSystemID(void) +{ + entity system; + + /* default to group 0 */ + if (!m_strConstraintSystem) + return 0; + + system = find(world, ::targetname, m_strConstraintSystem); + + /* must have been invalid/mappers error */ + if (!system) { + return 0; + } + + return system.jointgroup; +} + +void +NSPhysicsConstraint::Spawned(void) +{ + super::Spawned(); + +#ifdef SERVER + if (m_strOnBreak) + m_strOnBreak = CreateOutput(m_strOnBreak); +#endif +} + +#ifdef SERVER +void +NSPhysicsConstraint::Input(entity activatorEnt, string inputName, string dataString) +{ + switch (inputName) { + case "Break": + Break(activatorEnt); + break; + case "TurnOn": + break; + case "TurnOff": + break; + default: + super::Input(activatorEnt, inputName, dataString); + break; + } +} +#endif + +void +NSPhysicsConstraint::Break(entity activatingEnt) +{ +#ifdef SERVER + if (m_strOnBreak) + UseOutput(activatingEnt, m_strOnBreak); +#endif + + if (m_strBreakSound) + StartSoundDef(m_strBreakSound, CHAN_AUTO, true); + + Destroy(); +} + +void +NSPhysicsConstraint::OnRemoveEntity(void) +{ + WakeTargets(); +} + +void +NSPhysicsConstraint::WakeTargets(void) +{ + NSPhysicsEntity physTarget; + + if (enemy.isPhysics) { + physTarget = (NSPhysicsEntity)enemy; + physTarget.Wake(); + } + if (aiment.isPhysics) { + physTarget = (NSPhysicsEntity)aiment; + physTarget.Wake(); + } +} + +constraint_t +NSPhysicsConstraint::GetConstraintType(void) +{ + return (constraint_t )jointtype; +} + +entity +NSPhysicsConstraint::GetEntity1(void) +{ + return enemy; +} + +entity +NSPhysicsConstraint::GetEntity2(void) +{ + return aiment; +} + +void +NSPhysicsConstraint::SetConstraintType(constraint_t setValue) +{ + jointtype = (float)setValue; +} + +void +NSPhysicsConstraint::SetEntity1(entity targetEnt) +{ + enemy = targetEnt; + jointgroup = GetConstraintSystemID(); + + /* give it some time to think. */ + ScheduleThink(ConstraintThink, 0.25f); +} + +void +NSPhysicsConstraint::SetEntity2(entity targetEnt) +{ + aiment = targetEnt; +} + +void +NSPhysicsConstraint::SetSliderVelocity(float slideVel) +{ + movedir[0] = slideVel; +} + +void +NSPhysicsConstraint::SetSliderMaxVelocity(float maxVel) +{ + movedir[1] = -fabs(maxVel); +} + +void +NSPhysicsConstraint::SetSliderStop(float stopVal) +{ + movedir[2] = stopVal; +} + +void +NSPhysicsConstraint::SetSliderFriction(float frictionValue) +{ + friction = frictionValue; +} + +float +NSPhysicsConstraint::GetSliderVelocity(void) +{ + return movedir[0]; +} + +float +NSPhysicsConstraint::GetSliderMaxVelocity(void) +{ + return movedir[1]; +} + +float +NSPhysicsConstraint::GetSliderStop(void) +{ + return movedir[2]; +} + +float +NSPhysicsConstraint::GetSliderFriction(void) +{ + return friction; +} + +NSPhysicsConstraint +NSPhysicsConstraint::Ballsocket(entity firstEnt, entity secondEnt, vector pos1, vector pos2, float forceLimit, bool noCollide) +{ + NSPhysicsConstraint new = spawn(NSPhysicsConstraint); + new.SetConstraintType(CONSTRAINT_POINT); + new.SetEntity1(firstEnt); + new.SetEntity2(secondEnt); + new.origin = pos1; + new.velocity = pos2; + new.WakeTargets(); + + print(sprintf("Created ballsocket between %s and %s\n", firstEnt.classname, secondEnt.classname)); + return new; +} + +NSPhysicsConstraint +NSPhysicsConstraint::Weld(entity firstEnt, entity secondEnt, float bone1, float bone2, float forceLimit, bool noCollide, bool deleteEnt1OnBreak) +{ + if (firstEnt == secondEnt) { + print("^1Cannot weld the entity with itself!\n"); + return __NULL__; + } + + NSPhysicsConstraint new = spawn(NSPhysicsConstraint); + new.SetConstraintType(CONSTRAINT_FIXED); + new.SetEntity1(firstEnt); + new.SetEntity2(secondEnt); + new.WakeTargets(); + + print(sprintf("Created weld between %s and %s\n", firstEnt.classname, secondEnt.classname)); + return new; +} + +NSPhysicsConstraint +NSPhysicsConstraint::Rope(entity firstEnt, entity secondEnt, vector pos1, vector pos2) +{ + NSPhysicsConstraint new = spawn(NSPhysicsConstraint); + new.SetConstraintType(CONSTRAINT_POINT); + new.SetEntity1(firstEnt); + new.SetEntity2(secondEnt); + new.origin = pos1; + new.velocity = pos2; + new.WakeTargets(); + + print(sprintf("Created rope between %s and %s\n", firstEnt.classname, secondEnt.classname)); + return new; +} + +.float max_angular; +NSPhysicsConstraint +NSPhysicsConstraint::KeepUpright(entity firstEnt, vector uprightAngle, float angleLimit) +{ + firstEnt.angles = uprightAngle; + firstEnt.max_angular = angleLimit; + return __NULL__; +} \ No newline at end of file diff --git a/src/shared/NSPhysicsEntity.h b/src/shared/NSPhysicsEntity.h index 388377e1..58c744bd 100644 --- a/src/shared/NSPhysicsEntity.h +++ b/src/shared/NSPhysicsEntity.h @@ -14,6 +14,27 @@ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +var bool autocvar_phys_developer = false; +void +_NSPhysics_Log(string msg) +{ + if (autocvar_phys_developer == true) + print(sprintf("%f %s\n", time, msg)); +} +#define NSPhysics_Log(...) _NSPhysics_Log(sprintf(__VA_ARGS__)) + + +var float autocvar_phys_pushscale = 1.0f; +var float autocvar_phys_impactforcescale = 1.0f; + +#ifdef CLIENT +var bool autocvar_r_showPhysicsInfo = false; +#endif + +.float damp_linear; +.float damp_angular; +.float jointgroup; + enum { PHYSM_BOX, @@ -54,7 +75,13 @@ typedef enumflags PHYENT_CHANGED_RENDERMODE, } nsphyricsentity_changed_t; -/** This entity class represents physically-simulated entities. */ +/** This entity class represents physically-simulated entities. + +The physics simulator used is controlled by the engine and may be +subject to change. + +Units of mass is defined in kilograms, a standard unit of measurement. +You will find the API to be mostly compatible of that offered by Garry's Mod. */ class NSPhysicsEntity:NSSurfacePropEntity { private: @@ -63,11 +90,20 @@ private: int m_iMaterial; int m_iFlags; float m_flInertiaScale; + float m_flBuoyancyRatio; + + /* performance sanity checks */ + vector m_vecPrevOrigin; + vector m_vecPrevAngles; + float m_flCheckTime; virtual void _TouchThink(void); #ifdef SERVER PREDICTED_VECTOR(m_vecNetAngles) + PREDICTED_FLOAT_N(mass) + + string m_strOnDamaged; #endif public: @@ -77,49 +113,101 @@ public: virtual void Respawn(void); virtual void SpawnKey(string,string); #ifdef SERVER + virtual void Spawned(void); virtual void Pain(void); virtual void Death(void); virtual void EvaluateEntity(void); virtual float SendEntity(entity,float); virtual void Save(float); virtual void Restore(string,string); + virtual void Touch(entity); #endif #ifdef CLIENT virtual void ReceiveEntity(float,float); + virtual void postdraw(void); #endif - /** Sets the mass of the entity in ??? */ - nonvirtual void SetMass(float); - /** Returns the mass of the entity. */ - nonvirtual float GetMass(void); /** Sets the friction multiplier for this entity. Default is 1.0 */ nonvirtual void SetFriction(float); /** Returns the friction multiplayer for this entity. */ nonvirtual float GetFriction(void); - /** Sets the bounce factor for this entity. Default is 1.0 */ - nonvirtual void SetBounceFactor(float); - /** Returns the bounce factor of this entity. */ - nonvirtual float GetBounceFactor(void); - /** Sets the bounce stop factor for this entity. */ - nonvirtual void SetBounceStop(float); - /** Returns the bounce stop factor of this entity. */ - nonvirtual float GetBounceStop(void); - /** Sets the inertia modifier for this entity. */ - nonvirtual void SetInertia(float); - /** Returns the inertia modifier of this entity. */ - nonvirtual float GetInertia(void); - /** Call to enable physics simulation on this entity. */ - nonvirtual void PhysicsEnable(void); - /** Call to freeze physics simulation on this entity. */ - nonvirtual void PhysicsDisable(void); + + /** Called by the physics routine to figure out the impact damage. */ + nonvirtual float CalculateImpactDamage(int,int); + + /* this merely mirrors the GMod API: https://wiki.facepunch.com/gmod/PhysObj */ + + /** Call to align angles of the object to the ones passed. */ + nonvirtual vector AlignAngles(vector, vector); /** Call to apply a force (absolute velocity vector) to the center of the entity. */ nonvirtual void ApplyForceCenter(vector); - /** Call to apply force (absolute velocity vector) to a specific position on the entity. */ + /** Call to apply force (absolute velocity vector) to an absolute position on the entity. */ nonvirtual void ApplyForceOffset(vector,vector); /** Call to apply torque (angular velocity vector) to the center of the entity. */ nonvirtual void ApplyTorqueCenter(vector); - /** Called by the physics routine to figure out the impact damage. */ - nonvirtual float CalculateImpactDamage(int,int); + /** Call to set whether the entity should be affected by drag. */ + nonvirtual void EnableDrag(bool); + /** Call to set whether the entity should be affected by gravity. */ + nonvirtual void EnableGravity(bool); + /** Call to set whether the entity should be able to move. */ + nonvirtual void EnableMotion(bool); + /** Returns the linear damping of the entity. */ + nonvirtual float GetLinearDamping(void); + /** Returns the angular damping of the entity. */ + nonvirtual float GetAngularDamping(void); + /** Returns the linear and rotational kinetic energy combined. */ + nonvirtual float GetEnergy(void); + /** Returns the inertia modifier of this entity. */ + nonvirtual float GetInertia(void); + /** Returns 1 divided by the angular inertia of this entity. */ + nonvirtual float GetInvInertia(void); + /** Returns 1 divided by the mass of this entity. */ + nonvirtual float GetInvMass(void); + /** Returns the mass of the entity. */ + nonvirtual float GetMass(void); + /** Returns the center of mass of the entity. */ + nonvirtual vector GetMassCenter(void); + /** Returns the rotational damping of the entity. */ + nonvirtual float GetRotDamping(void); + /** Returns the speed damping of the entity. */ + nonvirtual float GetSpeedDamping(void); + /** Returns the surface area of the entity. */ + nonvirtual float GetSurfaceArea(void); + /** Returns the volume of the entity. */ + nonvirtual float GetVolume(void); + /** Returns whether the entity is at rest and not moving. */ + nonvirtual bool IsAsleep(void); + /** Returns whether the entity is able to collide with anything. */ + nonvirtual bool IsCollisionEnabled(void); + /** Returns whether the entity is affected by drag. */ + nonvirtual bool IsDragEnabled(void); + /** Returns whether the entity is affected by gravity. */ + nonvirtual bool IsGravityEnabled(void); + /** Returns whether the entity is able to move by itself. */ + nonvirtual bool IsMotionEnabled(void); + /** Returns whether the entity is able to move. */ + nonvirtual bool IsMoveable(void); + /** Returns whether the entity is penetrating another object. */ + nonvirtual bool IsPenetrating(void); + + /** Call to set the amount of rotational drag the entity experiences. */ + nonvirtual void SetAngleDragCoefficient(float); + /** Call to set the buoyancy ratio of the entity. 0 is not buoyant, 1 is very buoyant. */ + nonvirtual void SetBuoyancyRatio(float); + /** Call to set the linear and angular damping of the entity. */ + nonvirtual void SetDamping(float, float); + /** Call to set how much drag affects the entity. */ + nonvirtual void SetDragCoefficient(float); + + /** Sets the angular inertia for this entity. */ + nonvirtual void SetInertia(float); + /** Sets the mass of the entity in kilograms. */ + nonvirtual void SetMass(float); + + /** Call to enable physics simulation on this entity. */ + nonvirtual void Wake(void); + /** Call to freeze physics simulation on this entity. */ + nonvirtual void Sleep(void); }; noref .bool isPhysics; \ No newline at end of file diff --git a/src/shared/NSPhysicsEntity.qc b/src/shared/NSPhysicsEntity.qc index 08e7a132..c2b635d6 100644 --- a/src/shared/NSPhysicsEntity.qc +++ b/src/shared/NSPhysicsEntity.qc @@ -14,30 +14,64 @@ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#define ODE_MODE 1 +.float max_angular; void NSPhysicsEntity::NSPhysicsEntity(void) { - isPhysics = true; mass = 1.0f; + isPhysics = true; m_flInertiaScale = 1.0f; m_iEnabled = 0i; m_iShape = PHYSM_BOX; m_iMaterial = 0i; m_iFlags = 0i; + damp_linear = 1.0f; + damp_angular = 1.0f; + max_angular = -1.0f; -#ifdef ODE_MODE - cvar_set("physics_ode_iterationsperframe", "1"); - cvar_set("physics_ode_movelimit", "0.1"); -#else - cvar_set("physics_bullet_maxiterationsperframe", "10"); - cvar_set("physics_bullet_framerate", "60"); + + cvar_set("physics_ode_quadtree_depth", "10"); + cvar_set("physics_ode_contactsurfacelayer", "0"); + cvar_set("physics_ode_worldquickstep", "1"); + cvar_set("physics_ode_worldquickstep_iterations", "20"); + cvar_set("physics_ode_contact_mu", "-1"); + cvar_set("physics_ode_contact_erp", "0.96"); + cvar_set("physics_ode_contact_cfm", "0.001"); + cvar_set("physics_ode_world_damping", "1"); + cvar_set("physics_ode_world_damping_linear", "-1"); + cvar_set("physics_ode_world_damping_linear_threshold", "-1"); + cvar_set("physics_ode_world_damping_angular", "-1"); + cvar_set("physics_ode_world_damping_angular_threshold", "-1"); + cvar_set("physics_ode_world_erp", "-1"); + cvar_set("physics_ode_world_cfm", "-1"); + cvar_set("physics_ode_iterationsperframe", "4"); + cvar_set("physics_ode_movelimit", "0.5"); + cvar_set("physics_ode_spinlimit", "10000"); + cvar_set("physics_ode_autodisable", "1"); + cvar_set("physics_ode_autodisable_steps", "10"); + cvar_set("physics_ode_autodisable_time", "0.1"); + cvar_set("physics_ode_autodisable_threshold_linear", "0.2"); + cvar_set("physics_ode_autodisable_threshold_angular", "0.3"); + cvar_set("physics_ode_autodisable_threshold_samples", "5"); + +#ifdef SERVER + m_strOnDamaged = __NULL__; #endif } #ifdef SERVER +void +NSPhysicsEntity::Spawned(void) +{ + super::Spawned(); + + /* I/O */ + if (m_strOnDamaged) + m_strOnDamaged = CreateOutput(m_strOnDamaged); +} + void NSPhysicsEntity::Save(float handle) { @@ -47,6 +81,7 @@ NSPhysicsEntity::Save(float handle) SaveInt(handle, "m_iMaterial", m_iMaterial); SaveInt(handle, "m_iFlags", m_iFlags); SaveFloat(handle, "m_flInertiaScale", m_flInertiaScale); + SaveString(handle, "m_strOnDamaged", m_strOnDamaged); } void @@ -68,6 +103,9 @@ NSPhysicsEntity::Restore(string strKey, string strValue) case "m_flInertiaScale": m_flInertiaScale = ReadFloat(strValue); break; + case "m_strOnDamaged": + m_strOnDamaged = ReadString(strValue); + break; default: super::Restore(strKey, strValue); } @@ -141,6 +179,7 @@ NSPhysicsEntity::EvaluateEntity(void) EVALUATE_FIELD(m_flBoneControl3, RDENT_CHANGED_CONTROLLER) EVALUATE_FIELD(m_flBoneControl4, RDENT_CHANGED_CONTROLLER) EVALUATE_FIELD(m_flBoneControl5, RDENT_CHANGED_CONTROLLER) + EVALUATE_FIELD(mass, RDENT_CHANGED_SOLIDMOVETYPE) } float @@ -229,6 +268,9 @@ NSPhysicsEntity::SendEntity(entity ePEnt, float flChanged) SENDENTITY_ANGLE(m_flBoneControl4, RDENT_CHANGED_CONTROLLER) SENDENTITY_ANGLE(m_flBoneControl5, RDENT_CHANGED_CONTROLLER) + /* physics specific flags */ + SENDENTITY_FLOAT(mass, RDENT_CHANGED_SOLIDMOVETYPE) + return (1); } #else @@ -290,6 +332,9 @@ NSPhysicsEntity::ReceiveEntity(float flNew, float flChanged) READENTITY_ANGLE(m_flBoneControl4, RDENT_CHANGED_CONTROLLER) READENTITY_ANGLE(m_flBoneControl5, RDENT_CHANGED_CONTROLLER) + /* physics specific flags */ + READENTITY_FLOAT(mass, RDENT_CHANGED_SOLIDMOVETYPE) + if (scale == 0.0) scale = 1.0f; @@ -300,47 +345,23 @@ NSPhysicsEntity::ReceiveEntity(float flNew, float flChanged) movetype = MOVETYPE_NONE; } + +void +NSPhysicsEntity::postdraw(void) +{ + if not (autocvar_r_showPhysicsInfo) + return; + + if not (PointMessage_Visible(WorldSpaceCenter(), g_view.GetCameraOrigin(), g_view.GetCameraAngle())) + return; + + string renderString = sprintf("Mass: %f\nEnergy: %f\nInertia:%d", + GetMass(), GetEnergy(), GetInertia()); + + PointMessage_StringAtPos(WorldSpaceCenter(), renderString); +} #endif - -void -NSPhysicsEntity::PhysicsEnable(void) -{ - if (physics_supported() == TRUE) { - SetMovetype(MOVETYPE_PHYSICS); - SetSolid(SOLID_PHYSICS_BOX + m_iShape); - physics_enable(this, TRUE); - } else { - SetMovetype(MOVETYPE_BOUNCE); - SetSolid(SOLID_CORPSE); - } - m_iEnabled = TRUE; -} - -void -NSPhysicsEntity::PhysicsDisable(void) -{ - if (physics_supported() == TRUE) { - physics_enable(this, FALSE); - SetMovetype(MOVETYPE_NONE); - } else { - SetMovetype(MOVETYPE_NONE); - SetSolid(SOLID_BBOX); - } - m_iEnabled = FALSE; -} - -void -NSPhysicsEntity::SetMass(float val) -{ - mass = val; -} -float -NSPhysicsEntity::GetMass(void) -{ - return mass; -} - void NSPhysicsEntity::SetFriction(float val) { @@ -352,38 +373,6 @@ NSPhysicsEntity::GetFriction(void) return friction; } -void -NSPhysicsEntity::SetBounceFactor(float val) -{ - bouncefactor = val; -} -float -NSPhysicsEntity::GetBounceFactor(void) -{ - return bouncefactor; -} - -void -NSPhysicsEntity::SetBounceStop(float val) -{ - bouncestop = val; -} -float -NSPhysicsEntity::GetBounceStop(void) -{ - return bouncestop; -} - -void -NSPhysicsEntity::SetInertia(float val) -{ - m_flInertiaScale = val; -} -float -NSPhysicsEntity::GetInertia(void) -{ - return m_flInertiaScale; -} float NSPhysicsEntity::CalculateImpactDamage(int iDamage, int dmgType) @@ -403,57 +392,30 @@ NSPhysicsEntity::CalculateImpactDamage(int iDamage, int dmgType) filter |= (dmgType & DMG_SLOWFREEZE); if (filter == 0i) - return (float)iDamage * 100; + return (float)iDamage * autocvar_phys_impactforcescale; else return 0.0f; } -void -NSPhysicsEntity::ApplyForceCenter(vector vecForce) +int +NSPhysicsEntity_Contents(vector org) { - if (physics_supported() == TRUE) { - physics_addforce(this, vecForce, [0,0,0]); -#ifdef ODE_MODE - //velocity += vecForce; + int oldhitcontents = self.hitcontentsmaski; + self.hitcontentsmaski = -1; + traceline(org, org, MOVE_EVERYTHING, self); + self.hitcontentsmaski = oldhitcontents; + return trace_endcontentsi; +} + +#ifdef SERVER +void +NSPhysicsEntity::Touch(entity eToucher) +{ + if (eToucher) + if (eToucher.movetype) + Wake(); +} #endif - } else { - velocity += vecForce; - } - - /* make sure touch think is called */ - nextthink = time; -} - -void -NSPhysicsEntity::ApplyForceOffset(vector vecForce, vector vecOffset) -{ - if (physics_supported() == TRUE) { - physics_addforce(this, vecForce, vecOffset); -#ifdef ODE_MODE - //velocity += vecForce; -#endif - } else { - velocity += vecForce; - } - - /* make sure touch think is called */ - nextthink = time; -} - -void -NSPhysicsEntity::ApplyTorqueCenter(vector vecTorque) -{ - if (physics_supported() == TRUE) - physics_addtorque(this, vecTorque * m_flInertiaScale); - else { - avelocity = vecTorque; - velocity = vecTorque; - velocity[2] = 96; - } - - /* make sure touch think is called */ - nextthink = time; -} void NSPhysicsEntity::_TouchThink(void) @@ -473,21 +435,36 @@ NSPhysicsEntity::_TouchThink(void) if (trace_startsolid) { if (trace_ent.flags & FL_CLIENT) { if (trace_ent.absmin[2] < absmax[2]) { - PhysicsEnable(); - makevectors(vectoangles(origin - trace_ent.origin)); + Wake(); + makevectors(vectoangles(trace_endpos - trace_ent.origin)); ApplyTorqueCenter(v_forward * 240); } else { - PhysicsDisable(); + Sleep(); velocity = [0,0,0]; } } } } +#if 0 + if (m_flCheckTime < time) { + bool should + if (vlen(m_vecPrevAngles - angles) < 1) { + avelocity = [0,0,0]; + } + if (vlen(m_vecPrevOrigin - origin) < 1) { + velocity = [0,0,0]; + } + m_flCheckTime = time + 1.0f; + m_vecPrevOrigin = origin; + m_vecPrevAngles = angles; + } + /* If we barely move, disable the physics simulator */ - if (vlen(velocity) <= 1) { + if (velocity == g_vec_null && avelocity == g_vec_null) { if (m_iEnabled) { - PhysicsDisable(); + NSPhysics_Log("%s is now sleeping.", classname); + Sleep(); velocity = [0,0,0]; avelocity = [0,0,0]; } @@ -508,6 +485,18 @@ NSPhysicsEntity::_TouchThink(void) angles = vectoangles(newangle); } } +#endif + +#if 0 + if (m_flBuoyancyRatio > 0.0) + if (NSPhysicsEntity_Contents(origin) & CONTENTBIT_WATER) { + makevectors([0,0,0]); + velocity[2] += m_flBuoyancyRatio; + print(sprintf("in water... applying %v\n", v_up * (GetMass() * m_flBuoyancyRatio))); + } +#endif + + hitcontentsmaski &= ~CONTENTBITS_FLUID; if (physics_supported() == FALSE) { /* don't let players collide */ @@ -524,18 +513,22 @@ NSPhysicsEntity::_TouchThink(void) void NSPhysicsEntity::Pain(void) { + vector forceDir; float force; + if (m_strOnDamaged) + UseOutput(this, m_strOnDamaged); + if (m_iFlags & BPHY_NODMGPUSH) return; - PhysicsEnable(); + Wake(); - makevectors(vectoangles(origin - trace_endpos)); + forceDir = normalize(GetOrigin() - g_dmg_vecLocation); force = CalculateImpactDamage(g_dmg_iDamage, g_dmg_iFlags); if (force > 0.0f) - ApplyForceOffset(v_forward * force, origin - trace_endpos); + ApplyForceOffset(forceDir * force, g_dmg_vecLocation); /* if we don't have prop data, don't consider death */ if (HasPropData() == false) @@ -564,28 +557,35 @@ NSPhysicsEntity::Death(void) void NSPhysicsEntity::Respawn(void) { + ClearVelocity(); SetMovetype(MOVETYPE_PHYSICS); - SetSolid(SOLID_PHYSICS_BOX + m_iShape); + SetSolid(SOLID_BSP); SetModel(GetSpawnModel()); #ifdef SERVER SetTakedamage(DAMAGE_YES); #endif -#ifndef ODE_MODE - PhysicsDisable(); - SetFriction(4.0f); - SetBounceFactor(0.05f); - SetMass(1.0f); -#else - PhysicsDisable(); - SetMass(1.0f); + Sleep(); + SetMass(10.0f); SetFriction(1.0f); - SetBounceFactor(0.1f); + SetBuoyancyRatio(1.0f); + bouncefactor = 0.9f; + bouncestop = 0.1f / cvar("sv_gravity"); + + //bouncefactor = 0.0f; + //bouncestop = 0.0f; + geomtype = GEOMTYPE_TRIMESH; -#endif + friction = 100.0f; SetOrigin(GetSpawnOrigin()); + m_flBuoyancyRatio = 1.0f; + + SetDamping(0.0f, 0.0f); + EnableGravity(true); + + hitcontentsmaski &= ~CONTENTBITS_FLUID; if (physics_supported() == FALSE) { /* don't let players collide */ @@ -641,8 +641,292 @@ NSPhysicsEntity::SpawnKey(string strKey, string strValue) if (tempCheck == true) m_iFlags |= BPHY_SHARP; break; +#ifdef SERVER + case "OnDamaged": + m_strOnDamaged = PrepareOutput(m_strOnDamaged, strValue); + break; +#endif default: super::SpawnKey(strKey, strValue); break; } +} + +/* GMod API starts here */ + +vector +NSPhysicsEntity::AlignAngles(vector from, vector to) +{ + NSEntWarning("Not implemented"); + return angles; +} + +void +NSPhysicsEntity::ApplyForceCenter(vector vecForce) +{ + Wake(); + + if (physics_supported() == TRUE) { + vector finalForce = vecForce; + physics_addforce(this, finalForce, GetMassCenter()); + + NSPhysics_Log("%s::ApplyForceCenter: %v (val: %v)", + classname, finalForce, vecForce); + } else { + velocity += vecForce; + } + + /* make sure touch think is called */ + nextthink = time; +} + +void +NSPhysicsEntity::ApplyForceOffset(vector vecForce, vector vecOffset) +{ + Wake(); + + if (physics_supported() == TRUE) { + vector finalForce = vecForce; + physics_addforce(this, finalForce, vecOffset); + + NSPhysics_Log("%s::ApplyForceOffset: %v at pos %v (val: %v)", + classname, finalForce, vecOffset, vecForce); + +#ifdef ODE_MODE + //velocity += vecForce; +#endif + } else { + velocity += vecForce; + } + + /* make sure touch think is called */ + nextthink = time; +} + +void +NSPhysicsEntity::ApplyTorqueCenter(vector vecTorque) +{ + Wake(); + + if (physics_supported() == TRUE) { + vector finalTorque = vecTorque * m_flInertiaScale; + physics_addtorque(this, finalTorque); + + NSPhysics_Log("%s::ApplyTorqueCenter: %v (val: %v, scale: %f)", + classname, finalTorque, vecTorque, m_flInertiaScale); + } else { + avelocity = vecTorque; + velocity = vecTorque; + velocity[2] = 96; + } + + /* make sure touch think is called */ + nextthink = time; +} + +void +NSPhysicsEntity::EnableDrag(bool setEnabled) +{ + NSEntWarning("Not implemented"); +} + +void +NSPhysicsEntity::EnableGravity(bool setEnabled) +{ + /* the engine checks if .gravity is < 0.01...) */ + gravity = setEnabled ? 1.0f : 0.005; +} + +void +NSPhysicsEntity::EnableMotion(bool setEnabled) +{ + NSEntWarning("Not implemented"); +} + +float +NSPhysicsEntity::GetLinearDamping(void) +{ + return damp_linear; +} + +float +NSPhysicsEntity::GetAngularDamping(void) +{ + return damp_angular; +} + +float +NSPhysicsEntity::GetEnergy(void) +{ + float linearEnergy; + float rotationalEnergy; + + linearEnergy = (0.5f * GetMass() * vlen(GetVelocity())); + linearEnergy *= linearEnergy; + + rotationalEnergy = (0.5f * GetMass() * vlen(GetAngularVelocity())); + rotationalEnergy *= rotationalEnergy; + + return linearEnergy + rotationalEnergy; +} + +float +NSPhysicsEntity::GetInertia(void) +{ + return m_flInertiaScale; +} + +float +NSPhysicsEntity::GetInvInertia(void) +{ + return 1.0f / m_flInertiaScale; +} + +float +NSPhysicsEntity::GetInvMass(void) +{ + return 1.0f / mass; +} + +float +NSPhysicsEntity::GetMass(void) +{ + return mass; +} + +vector +NSPhysicsEntity::GetMassCenter(void) +{ + return WorldSpaceCenter(); +} + +float +NSPhysicsEntity::GetRotDamping(void) +{ + return 0.0f; +} + +float +NSPhysicsEntity::GetSpeedDamping(void) +{ + return 0.0f; +} + +float +NSPhysicsEntity::GetSurfaceArea(void) +{ + return vlen(size) / 4; +} + +float +NSPhysicsEntity::GetVolume(void) +{ + return vlen(size); +} + +bool +NSPhysicsEntity::IsAsleep(void) +{ + return false; +} + +bool +NSPhysicsEntity::IsCollisionEnabled(void) +{ + return true; +} + +bool +NSPhysicsEntity::IsDragEnabled(void) +{ + return true; +} + +bool +NSPhysicsEntity::IsGravityEnabled(void) +{ + return (gravity < 0.01) ? false : true; +} + +bool +NSPhysicsEntity::IsMotionEnabled(void) +{ + return true; +} + +bool +NSPhysicsEntity::IsMoveable(void) +{ + return true; +} + +bool +NSPhysicsEntity::IsPenetrating(void) +{ + return true; +} + +void +NSPhysicsEntity::SetAngleDragCoefficient(float setValue) +{ + NSEntWarning("Not implemented"); +} + +void +NSPhysicsEntity::SetBuoyancyRatio(float setValue) +{ + m_flBuoyancyRatio = setValue; +} + +void +NSPhysicsEntity::SetDamping(float linearDamp, float angleDamp) +{ + damp_linear = linearDamp; + damp_angular = angleDamp; +} + +void +NSPhysicsEntity::SetDragCoefficient(float dragValue) +{ + NSEntWarning("Not implemented"); +} + +void +NSPhysicsEntity::SetInertia(float val) +{ + m_flInertiaScale = val; +} + +void +NSPhysicsEntity::SetMass(float val) +{ + mass = val; +} + +void +NSPhysicsEntity::Wake(void) +{ + if (physics_supported() == TRUE) { + SetMovetype(MOVETYPE_PHYSICS); + SetSolid(SOLID_BSP); + physics_enable(this, TRUE); + } else { + SetMovetype(MOVETYPE_BOUNCE); + SetSolid(SOLID_CORPSE); + } + m_iEnabled = TRUE; +} + +void +NSPhysicsEntity::Sleep(void) +{ + if (physics_supported() == TRUE) { + ClearVelocity(); + physics_enable(this, FALSE); + SetMovetype(MOVETYPE_NONE); + } else { + SetMovetype(MOVETYPE_NONE); + SetSolid(SOLID_BBOX); + } + m_iEnabled = FALSE; } \ No newline at end of file diff --git a/src/shared/defs.h b/src/shared/defs.h index 9f5d8a31..9e20dc04 100644 --- a/src/shared/defs.h +++ b/src/shared/defs.h @@ -87,6 +87,7 @@ string __fullspawndata; #include "NSRenderableEntity.h" #include "NSSurfacePropEntity.h" #include "NSMoverEntity.h" +#include "NSPhysicsConstraint.h" #include "NSPhysicsEntity.h" #include "NSBrushTrigger.h" #include "NSPointTrigger.h" diff --git a/src/shared/entities.h b/src/shared/entities.h index 8f2453cd..9c3a74fd 100644 --- a/src/shared/entities.h +++ b/src/shared/entities.h @@ -50,6 +50,7 @@ typedef enum ENT_VEH_TANKMORTAR, /**< of type func_tankmortar */ ENT_VEH_4WHEEL, /**< of type prop_vehicle_driveable */ ENT_PROPROPE, /**< of type prop_rope */ + ENT_PHYSROPE, /**< of type phys_rope */ ENT_BUBBLES, /**< of type env_bubbles */ ENT_CONVEYOR, /**< of type func_conveyor */ ENT_WAYPOINT, /**< of type info_waypoint */ diff --git a/src/shared/include.src b/src/shared/include.src index b15d9dd4..8d3f2d45 100644 --- a/src/shared/include.src +++ b/src/shared/include.src @@ -7,6 +7,7 @@ NSTimer.qc NSRenderableEntity.qc NSSurfacePropEntity.qc NSMoverEntity.qc +NSPhysicsConstraint.qc NSPhysicsEntity.qc NSBrushTrigger.qc NSPointTrigger.qc