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.
This commit is contained in:
Marco Cawthorne 2023-10-17 16:48:03 -07:00
parent 81a498d49e
commit 11deea5b3d
Signed by: eukara
GPG Key ID: CE2032F0A2882A22
36 changed files with 2618 additions and 240 deletions

View File

@ -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

View File

@ -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;

View File

@ -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 */

View File

@ -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;

View File

@ -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));
}
}

View File

@ -19,5 +19,4 @@ client/infodecal.qc
client/sky_camera.qc
client/info_notnull.qc
client/point_message.qc
client/prop_static.qc
#endlist

View File

@ -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

View File

@ -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;
}
}

View File

@ -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);

View File

@ -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);

View File

@ -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)

View File

@ -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)
{

View File

@ -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);

View File

@ -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();
}

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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)

View File

@ -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)

View File

@ -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__;
}

View File

@ -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

View File

@ -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;

View File

@ -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;
}

View File

@ -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)

View File

@ -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

View File

@ -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 */

View File

@ -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;
};

View File

@ -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__;
}

View File

@ -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;

View File

@ -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;
}

View File

@ -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"

View File

@ -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 */

View File

@ -7,6 +7,7 @@ NSTimer.qc
NSRenderableEntity.qc
NSSurfacePropEntity.qc
NSMoverEntity.qc
NSPhysicsConstraint.qc
NSPhysicsEntity.qc
NSBrushTrigger.qc
NSPointTrigger.qc