PropData: read info from Source Engine .phy files
This commit is contained in:
parent
8cd6d3967d
commit
73bec53034
17 changed files with 421 additions and 78 deletions
|
@ -126,4 +126,15 @@ destruction.
|
|||
this entity breaks.
|
||||
- **breakable_model <string>**: Which models to spawn when it breaks.
|
||||
- **breakable_count <int>**: The amount of models it'll spawn upon
|
||||
breaking.
|
||||
breaking.
|
||||
- **surfaceprop <string>**: Surfaceprop override for the object.
|
||||
|
||||
## Physics Object Commands {#physcommands}
|
||||
|
||||
These are only relevant for when you want to use a phyics object, or rather an object that's handled by NSPhysicsEntity, such as prop_physics.
|
||||
|
||||
- **mass <float>**: Mass of the object, in kilograms.
|
||||
- **volume <float>**: Volume of the object, in cubic meters.
|
||||
- **inertia <float>**: Inertia multiplier.
|
||||
- **damping <float>**: Linear movement damping multiplier.
|
||||
- **rotdamping <float>**: Angular movement damping multiplier.
|
|
@ -228,6 +228,9 @@ Event_Parse(float type)
|
|||
case EV_SHAKE:
|
||||
EV_Shake();
|
||||
break;
|
||||
case EV_BREAKMODELDATA:
|
||||
BreakModel_ReceiveClientData();
|
||||
break;
|
||||
case EV_BREAKMODEL:
|
||||
BreakModel_Receive();
|
||||
break;
|
||||
|
|
|
@ -255,8 +255,8 @@ View_DrawViewModel(void)
|
|||
if (Client_IsSpectator(cl) || XR_Available(pl) == false) {
|
||||
m_eViewModelL.origin = g_view.GetCameraOrigin();
|
||||
m_eViewModel.origin = g_view.GetCameraOrigin();
|
||||
m_eViewModel.angles = g_view.GetCameraAngle();
|
||||
m_eViewModelL.angles = g_view.GetCameraAngle();
|
||||
//m_eViewModel.angles = g_view.GetCameraAngle();
|
||||
//m_eViewModelL.angles = g_view.GetCameraAngle();
|
||||
|
||||
/* we only calculate bob on the right model, to avoid double speed bobbing */
|
||||
Viewmodel_CalcBob();
|
||||
|
|
|
@ -15,6 +15,7 @@ client/func_dustmotes.qc
|
|||
client/func_dustcloud.qc
|
||||
client/func_smokevolume.qc
|
||||
client/light_environment.qc
|
||||
client/prop_static.qc
|
||||
client/infodecal.qc
|
||||
client/sky_camera.qc
|
||||
client/info_notnull.qc
|
||||
|
|
|
@ -72,7 +72,7 @@ server/phys_keepupright.qc
|
|||
server/phys_hinge.qc
|
||||
server/phys_slideconstraint.qc
|
||||
//server/phys_constraintsystem.qc
|
||||
server/prop_static.qc
|
||||
//server/prop_static.qc
|
||||
server/point_camera.qc
|
||||
server/point_servercommand.qc
|
||||
server/point_trigger.qc
|
||||
|
|
|
@ -36,7 +36,7 @@ A force-centered explosion, primarily targetted at physics objects and optionall
|
|||
This entity was introduced in Half-Life 2 (2004).
|
||||
*/
|
||||
class
|
||||
env_physexplosion:NSPhysicsConstraint
|
||||
env_physexplosion:NSEntity
|
||||
{
|
||||
public:
|
||||
void env_physexplosion(void);
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
*/
|
||||
|
||||
static int g_ent_spawned;
|
||||
.bool gotData;
|
||||
|
||||
/** Called once every single tic on the server. */
|
||||
void
|
||||
|
@ -45,6 +46,7 @@ void
|
|||
ClientConnect(void)
|
||||
{
|
||||
int playercount = 0;
|
||||
self.gotData = false;
|
||||
|
||||
/* don't carry over team settings from a previous session */
|
||||
forceinfokey(self, "*team", "0");
|
||||
|
@ -268,6 +270,11 @@ SV_RunClientCommand(void)
|
|||
|
||||
cl.SharedInputFrame();
|
||||
cl.ServerInputFrame();
|
||||
|
||||
if (self.gotData == false) {
|
||||
BreakModel_SendClientData(self);
|
||||
self.gotData = true;
|
||||
}
|
||||
}
|
||||
|
||||
/** Any 'cmd' from the client get sent here and handled.
|
||||
|
@ -390,18 +397,21 @@ initents(void)
|
|||
precache_sound("misc/null.wav");
|
||||
precache_sound("common/null.wav");
|
||||
|
||||
Sound_Precache("player.gasplight");
|
||||
Sound_Precache("player.gaspheavy");
|
||||
Sound_Precache("player.waterenter");
|
||||
Sound_Precache("player.waterexit");
|
||||
Sound_Precache("player.GaspLight");
|
||||
Sound_Precache("player.GaspHeavy");
|
||||
Sound_Precache("player.WaterEnter");
|
||||
Sound_Precache("player.WaterExit");
|
||||
Sound_Precache("Player.Death");
|
||||
Sound_Precache("Player.Pain");
|
||||
Sound_Precache("Player.Wade");
|
||||
Sound_Precache("Player.Swim");
|
||||
Sound_Precache("Player.DenyWeaonSelection");
|
||||
Sound_Precache("Player.WeaponSelected");
|
||||
|
||||
Sound_Precache("damage_bullet.hit");
|
||||
Sound_Precache("player.spraylogo");
|
||||
Sound_Precache("step_wade.left");
|
||||
Sound_Precache("step_wade.right");
|
||||
Sound_Precache("step_ladder.left");
|
||||
Sound_Precache("step_ladder.right");
|
||||
Sound_Precache("step_swim.left");
|
||||
Sound_Precache("step_swim.right");
|
||||
|
||||
if (!g_grMode) {
|
||||
Game_InitRules();
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#ifdef CLIENT
|
||||
class NSDebris:NSRenderableEntity
|
||||
class NSDebris:NSPhysicsEntity
|
||||
{
|
||||
public:
|
||||
void NSDebris(void);
|
||||
|
|
|
@ -25,7 +25,7 @@ _NSPhysics_Log(string msg)
|
|||
|
||||
|
||||
var float autocvar_phys_pushscale = 1.0f;
|
||||
var float autocvar_phys_impactforcescale = 1.0f;
|
||||
var float autocvar_phys_impactforcescale = 100.0f;
|
||||
|
||||
#ifdef CLIENT
|
||||
var bool autocvar_r_showPhysicsInfo = false;
|
||||
|
@ -91,17 +91,19 @@ private:
|
|||
int m_iFlags;
|
||||
float m_flInertiaScale;
|
||||
float m_flBuoyancyRatio;
|
||||
bool m_bInvincible;
|
||||
float m_flVolume;
|
||||
|
||||
/* performance sanity checks */
|
||||
vector m_vecPrevOrigin;
|
||||
vector m_vecPrevAngles;
|
||||
float m_flCheckTime;
|
||||
PREDICTED_FLOAT(m_flMass)
|
||||
|
||||
virtual void _TouchThink(void);
|
||||
|
||||
#ifdef SERVER
|
||||
PREDICTED_VECTOR(m_vecNetAngles)
|
||||
PREDICTED_FLOAT_N(mass)
|
||||
|
||||
string m_strOnDamaged;
|
||||
#endif
|
||||
|
@ -127,6 +129,9 @@ public:
|
|||
virtual void postdraw(void);
|
||||
#endif
|
||||
|
||||
nonvirtual void _UpdateBuoyancy(void);
|
||||
nonvirtual void _UpdateMass(void);
|
||||
|
||||
/** Sets the friction multiplier for this entity. Default is 1.0 */
|
||||
nonvirtual void SetFriction(float);
|
||||
/** Returns the friction multiplayer for this entity. */
|
||||
|
|
|
@ -16,6 +16,11 @@
|
|||
|
||||
.float max_angular;
|
||||
|
||||
/* taken from VPhysics-Jolt */
|
||||
const float InchesToMeters = 0.0254f;
|
||||
const float MetersToInches = 1.0f / 0.0254f;
|
||||
|
||||
|
||||
void
|
||||
NSPhysicsEntity::NSPhysicsEntity(void)
|
||||
{
|
||||
|
@ -30,17 +35,18 @@ NSPhysicsEntity::NSPhysicsEntity(void)
|
|||
damp_linear = 1.0f;
|
||||
damp_angular = 1.0f;
|
||||
max_angular = -1.0f;
|
||||
m_flMass = 1.0f;
|
||||
|
||||
|
||||
cvar_set("physics_ode_quadtree_depth", "10");
|
||||
cvar_set("physics_ode_quadtree_depth", "5");
|
||||
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_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", "-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");
|
||||
|
@ -179,7 +185,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)
|
||||
EVALUATE_FIELD(m_flMass, RDENT_CHANGED_SOLIDMOVETYPE)
|
||||
}
|
||||
|
||||
float
|
||||
|
@ -269,7 +275,7 @@ NSPhysicsEntity::SendEntity(entity ePEnt, float flChanged)
|
|||
SENDENTITY_ANGLE(m_flBoneControl5, RDENT_CHANGED_CONTROLLER)
|
||||
|
||||
/* physics specific flags */
|
||||
SENDENTITY_FLOAT(mass, RDENT_CHANGED_SOLIDMOVETYPE)
|
||||
SENDENTITY_FLOAT(m_flMass, RDENT_CHANGED_SOLIDMOVETYPE)
|
||||
|
||||
return (1);
|
||||
}
|
||||
|
@ -333,7 +339,7 @@ NSPhysicsEntity::ReceiveEntity(float flNew, float flChanged)
|
|||
READENTITY_ANGLE(m_flBoneControl5, RDENT_CHANGED_CONTROLLER)
|
||||
|
||||
/* physics specific flags */
|
||||
READENTITY_FLOAT(mass, RDENT_CHANGED_SOLIDMOVETYPE)
|
||||
READENTITY_FLOAT(m_flMass, RDENT_CHANGED_SOLIDMOVETYPE)
|
||||
|
||||
if (scale == 0.0)
|
||||
scale = 1.0f;
|
||||
|
@ -344,6 +350,7 @@ NSPhysicsEntity::ReceiveEntity(float flNew, float flChanged)
|
|||
_UpdateGeomset();
|
||||
|
||||
movetype = MOVETYPE_NONE;
|
||||
_UpdateMass();
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -355,8 +362,11 @@ NSPhysicsEntity::postdraw(void)
|
|||
if not (PointMessage_Visible(WorldSpaceCenter(), g_view.GetCameraOrigin(), g_view.GetCameraAngle()))
|
||||
return;
|
||||
|
||||
string renderString = sprintf("Mass: %f\nEnergy: %f\nInertia:%d",
|
||||
GetMass(), GetEnergy(), GetInertia());
|
||||
float massVolume = (size[0] * size[1] * size[2]);
|
||||
massVolume *= 0.0254f; /* from meters to inches */
|
||||
|
||||
string renderString = sprintf("Mass: %.2f kg (%.2f ODE)\nEnergy: %f kj\nInertia:%d\nVolume: %.2f",
|
||||
GetMass(), mass, GetEnergy(), GetInertia(), massVolume);
|
||||
|
||||
PointMessage_StringAtPos(WorldSpaceCenter(), renderString);
|
||||
}
|
||||
|
@ -530,9 +540,10 @@ NSPhysicsEntity::Pain(void)
|
|||
if (force > 0.0f)
|
||||
ApplyForceOffset(forceDir * force, g_dmg_vecLocation);
|
||||
|
||||
/* if we don't have prop data, don't consider death */
|
||||
if (HasPropData() == false)
|
||||
/* HACK: */
|
||||
if (m_bInvincible) {
|
||||
health = 10000;
|
||||
}
|
||||
|
||||
/* make sure touch think is called */
|
||||
nextthink = time;
|
||||
|
@ -547,7 +558,11 @@ NSPhysicsEntity::Death(void)
|
|||
if (takedamage != DAMAGE_YES) {
|
||||
takedamage = (DAMAGE_YES);
|
||||
}
|
||||
health = 1000;
|
||||
|
||||
/* HACK: */
|
||||
if (m_bInvincible) {
|
||||
health = 10000;
|
||||
}
|
||||
|
||||
/* make sure touch think is called */
|
||||
nextthink = time;
|
||||
|
@ -567,22 +582,22 @@ NSPhysicsEntity::Respawn(void)
|
|||
#endif
|
||||
|
||||
Sleep();
|
||||
SetMass(10.0f);
|
||||
SetMass(1.0f);
|
||||
SetFriction(1.0f);
|
||||
SetBuoyancyRatio(1.0f);
|
||||
bouncefactor = 0.9f;
|
||||
bouncestop = 0.1f / cvar("sv_gravity");
|
||||
|
||||
//bouncefactor = 0.0f;
|
||||
//bouncestop = 0.0f;
|
||||
bouncefactor = 0.0f;
|
||||
bouncestop = 0.0f;
|
||||
|
||||
geomtype = GEOMTYPE_TRIMESH;
|
||||
friction = 100.0f;
|
||||
friction = 1.0f;
|
||||
|
||||
SetOrigin(GetSpawnOrigin());
|
||||
m_flBuoyancyRatio = 1.0f;
|
||||
|
||||
SetDamping(0.0f, 0.0f);
|
||||
SetDamping(1.0f, 1.0f);
|
||||
EnableGravity(true);
|
||||
|
||||
hitcontentsmaski &= ~CONTENTBITS_FLUID;
|
||||
|
@ -600,9 +615,25 @@ NSPhysicsEntity::Respawn(void)
|
|||
|
||||
#ifdef SERVER
|
||||
if (HasPropData()) {
|
||||
health = GetPropData(PROPINFO_HEALTH);
|
||||
SetHealth(GetPropData(PROPINFO_HEALTH));
|
||||
SetMass(GetPropData(PROPINFO_MASS));
|
||||
SetDamping(GetPropData(PROPINFO_DAMPING_LINEAR), GetPropData(PROPINFO_DAMPING_ANGULAR));
|
||||
SetInertia(GetPropData(PROPINFO_INERTIA));
|
||||
SetSurfaceData(GetPropData(PROPINFO_SURFACEPROP));
|
||||
} else {
|
||||
health = 100000;
|
||||
health = 0;
|
||||
}
|
||||
|
||||
/* no health set, either indistructible or too weak */
|
||||
if (health == 0) {
|
||||
/* it has no breakmodels set, therefore it cannot break. */
|
||||
if (GetPropData(PROPINFO_BREAKMODEL) == __NULL__) {
|
||||
m_bInvincible = true;
|
||||
health = 10000;
|
||||
} else {
|
||||
/* something like glass bottles, maybe. */
|
||||
health = 1.0f;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
@ -666,6 +697,8 @@ NSPhysicsEntity::ApplyForceCenter(vector vecForce)
|
|||
{
|
||||
Wake();
|
||||
|
||||
vecForce *= MetersToInches;
|
||||
|
||||
if (physics_supported() == TRUE) {
|
||||
vector finalForce = vecForce;
|
||||
physics_addforce(this, finalForce, GetMassCenter());
|
||||
|
@ -685,6 +718,8 @@ NSPhysicsEntity::ApplyForceOffset(vector vecForce, vector vecOffset)
|
|||
{
|
||||
Wake();
|
||||
|
||||
vecForce *= MetersToInches;
|
||||
|
||||
if (physics_supported() == TRUE) {
|
||||
vector finalForce = vecForce;
|
||||
physics_addforce(this, finalForce, vecOffset);
|
||||
|
@ -767,7 +802,7 @@ NSPhysicsEntity::GetEnergy(void)
|
|||
rotationalEnergy = (0.5f * GetMass() * vlen(GetAngularVelocity()));
|
||||
rotationalEnergy *= rotationalEnergy;
|
||||
|
||||
return linearEnergy + rotationalEnergy;
|
||||
return (linearEnergy + rotationalEnergy) / (InchesToMeters * InchesToMeters);
|
||||
}
|
||||
|
||||
float
|
||||
|
@ -791,7 +826,7 @@ NSPhysicsEntity::GetInvMass(void)
|
|||
float
|
||||
NSPhysicsEntity::GetMass(void)
|
||||
{
|
||||
return mass;
|
||||
return m_flMass;
|
||||
}
|
||||
|
||||
vector
|
||||
|
@ -897,10 +932,39 @@ NSPhysicsEntity::SetInertia(float val)
|
|||
m_flInertiaScale = val;
|
||||
}
|
||||
|
||||
void
|
||||
NSPhysicsEntity::_UpdateMass(void)
|
||||
{
|
||||
/* in ODE, mass is relative. */
|
||||
float massVolume = (size[0] * size[1] * size[2]);
|
||||
massVolume *= 0.0254f; /* from inches to meters */
|
||||
|
||||
mass = 1.0f;
|
||||
//mass = m_flMass / massVolume; /* really ODE's thing is density */
|
||||
}
|
||||
|
||||
void
|
||||
NSPhysicsEntity::_UpdateBuoyancy(void)
|
||||
{
|
||||
#if 0
|
||||
if ( m_flVolume != 0.0f )
|
||||
{
|
||||
float flVolume = max( m_flVolume, 5.0f ) * MeterstoInches * MeterstoInches * MeterstoInches;
|
||||
float flDensity = m_flMass / flVolume;
|
||||
m_flBuoyancyRatio = flDensity / m_flMaterialDensity;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_flBuoyancyRatio = 1.0f;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
NSPhysicsEntity::SetMass(float val)
|
||||
{
|
||||
mass = val;
|
||||
m_flMass = val;
|
||||
_UpdateMass();
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -659,9 +659,6 @@ NSSurfacePropEntity::Death(void)
|
|||
{
|
||||
m_flDeathTime = time;
|
||||
|
||||
print(sprintf("%S", m_strOnBreak));
|
||||
print("\n");
|
||||
|
||||
UseOutput(g_dmg_eAttacker, m_strOnBreak);
|
||||
|
||||
if (HasPropData() == false)
|
||||
|
@ -672,7 +669,8 @@ NSSurfacePropEntity::Death(void)
|
|||
} else if (GetPropData(PROPINFO_BREAKMODEL) != __NULL__) {
|
||||
string gibeffect = GetPropData(PROPINFO_BREAKMODEL);
|
||||
int breakcount = GetPropData(PROPINFO_BREAKCOUNT);
|
||||
BreakModel_Spawn(absmin, absmax, [0,0,0], 100, breakcount, gibeffect);
|
||||
BreakModel_Entity(this, normalize(GetOrigin() - g_dmg_vecLocation), g_dmg_iDamage);
|
||||
//BreakModel_Spawn(absmin, absmax, [0,0,0], 100, breakcount, gibeffect);
|
||||
Disappear();
|
||||
} else {
|
||||
Disappear();
|
||||
|
|
|
@ -377,6 +377,29 @@ string Util_FixModel(string mdl)
|
|||
return mdl;
|
||||
}
|
||||
|
||||
/** Returns a string (usually filename) with only the file extension
|
||||
at the end replaced with a given, new extension. */
|
||||
string
|
||||
Util_ChangeExtension(string baseString, string newExtension)
|
||||
{
|
||||
float stringOffset = 0;
|
||||
string tempString = "";
|
||||
float foundOffset = 0;
|
||||
|
||||
while ((tempString = substring(baseString, stringOffset, 1))) {
|
||||
if (tempString == ".")
|
||||
foundOffset = stringOffset;
|
||||
if (tempString == "")
|
||||
break;
|
||||
if not (tempString)
|
||||
break;
|
||||
|
||||
stringOffset++;
|
||||
}
|
||||
|
||||
return strcat(substring(baseString, 0, foundOffset), ".", newExtension);
|
||||
}
|
||||
|
||||
bool
|
||||
Util_IsSingleplayer(void)
|
||||
{
|
||||
|
|
|
@ -54,6 +54,7 @@ enum
|
|||
EV_SURFIMPACT,
|
||||
EV_SURFIMPACTID,
|
||||
EV_DECALGROUP,
|
||||
EV_BREAKMODELDATA,
|
||||
EV_BREAKMODEL,
|
||||
EV_BEAMCYLINDER,
|
||||
EV_MUZZLEFLASH,
|
||||
|
|
|
@ -34,6 +34,4 @@ typedef enum
|
|||
void PMove_StartFrame(void);
|
||||
#endif
|
||||
|
||||
void PMove_Init(void);
|
||||
|
||||
void PMove_Run(void);
|
||||
void PMove_Init(void);
|
|
@ -132,6 +132,7 @@ var float autocvar_pm_wateraccelerate = PMOVE_WATERACCELERATE;
|
|||
var float autocvar_pm_accelerate = PMOVE_ACCELERATE;
|
||||
var float autocvar_pm_maxspeed = PMOVE_MAXSPEED;
|
||||
var float autocvar_g_gravity = PMOVE_GRAVITY;
|
||||
var bool autocvar_pm_nospeedcap = false;
|
||||
|
||||
void
|
||||
PMove_Init(void)
|
||||
|
@ -167,7 +168,7 @@ PMove_StartFrame(void)
|
|||
#endif
|
||||
|
||||
/* simple bounds check */
|
||||
int
|
||||
bool
|
||||
PMove_IsStuck(entity eTarget, vector vOffset, vector vecMins, vector vecMaxs)
|
||||
{
|
||||
vector bound;
|
||||
|
@ -180,14 +181,3 @@ PMove_IsStuck(entity eTarget, vector vOffset, vector vecMins, vector vecMaxs)
|
|||
tracebox(bound, vecMins, vecMaxs, bound, MOVE_NORMAL, eTarget);
|
||||
return trace_startsolid;
|
||||
}
|
||||
|
||||
/* it all starts here, this function is called by both CLIENT and SERVER for
|
||||
obvious prediction purposes. The SERVER will usually do this in the
|
||||
Game_RunClientCommand function and the CLIENT will do so in both the
|
||||
prediction places of Predict_PreFrame and Player_ReceiveEntity */
|
||||
void
|
||||
PMove_Run(void)
|
||||
{
|
||||
player pl = (player)self;
|
||||
pl.Physics_Run();
|
||||
}
|
||||
|
|
|
@ -83,6 +83,12 @@ typedef struct
|
|||
string breakable_model; /* name of BreakableModels entry in PropData.txt */
|
||||
int breakable_count;
|
||||
float breakable_skin;
|
||||
float mass;
|
||||
float damping_linear;
|
||||
float damping_angular;
|
||||
float inertia;
|
||||
float volume;
|
||||
string surfaceprop;
|
||||
} propdata_t;
|
||||
|
||||
/* entity will have to have a .propdata field pointing to a propdata id */
|
||||
|
@ -113,7 +119,14 @@ typedef enum
|
|||
PROPINFO_EXPLOSIVE_RADIUS,
|
||||
PROPINFO_BREAKMODEL,
|
||||
PROPINFO_BREAKCOUNT,
|
||||
PROPINFO_SKIN
|
||||
PROPINFO_SKIN,
|
||||
/* physics related variables. */
|
||||
PROPINFO_MASS,
|
||||
PROPINFO_DAMPING_LINEAR,
|
||||
PROPINFO_DAMPING_ANGULAR,
|
||||
PROPINFO_INERTIA,
|
||||
PROPINFO_VOLUME,
|
||||
PROPINFO_SURFACEPROP
|
||||
} propinfo_t;
|
||||
__variant Prop_GetInfo(int, int);
|
||||
|
||||
|
@ -121,20 +134,25 @@ typedef struct
|
|||
{
|
||||
string name;
|
||||
string data;
|
||||
float modelindex; /* only used for networking */
|
||||
bool physics; /* differentiate between Source and GS */
|
||||
} breakmodel_t;
|
||||
|
||||
/* entity will have a .breakmodel field pointing to a breakmodel id */
|
||||
breakmodel_t *g_breakmodel;
|
||||
int g_breakmodel_count;
|
||||
int g_breakmodel_end;
|
||||
var hashtable g_hashbreakmodel;
|
||||
|
||||
|
||||
#ifdef CLIENT
|
||||
void BreakModel_SpawnID(vector smins, vector smaxs, vector dir, float speed, int count, int index);
|
||||
void BreakModel_Receive(void);
|
||||
void BreakModel_ReceiveClientData(void);
|
||||
#else
|
||||
void BreakModel_Spawn(vector pos, vector dir, vector spread, float speed, int count, string type);
|
||||
void BreakModel_Entity(NSSurfacePropEntity target, vector dir, float speed);
|
||||
void BreakModel_SendClientData(entity);
|
||||
#endif
|
||||
|
||||
/* necessary API functions */
|
||||
|
|
|
@ -71,6 +71,18 @@ Prop_GetInfo(int i, int type)
|
|||
return (__variant)g_propdata[i].breakable_count;
|
||||
case PROPINFO_SKIN:
|
||||
return (__variant)g_propdata[i].breakable_skin;
|
||||
case PROPINFO_MASS:
|
||||
return (__variant)g_propdata[i].mass;
|
||||
case PROPINFO_DAMPING_LINEAR:
|
||||
return (__variant)g_propdata[i].damping_linear;
|
||||
case PROPINFO_DAMPING_ANGULAR:
|
||||
return (__variant)g_propdata[i].damping_angular;
|
||||
case PROPINFO_INERTIA:
|
||||
return (__variant)g_propdata[i].inertia;
|
||||
case PROPINFO_VOLUME:
|
||||
return (__variant)g_propdata[i].volume;
|
||||
case PROPINFO_SURFACEPROP:
|
||||
return (__variant)g_propdata[i].surfaceprop;
|
||||
default:
|
||||
return __NULL__;
|
||||
}
|
||||
|
@ -80,6 +92,9 @@ void
|
|||
PropData_ParseField(int i, int a)
|
||||
{
|
||||
switch (argv(0)) {
|
||||
case "name":
|
||||
g_propdata[i].name = argv(1);
|
||||
break;
|
||||
case "base":
|
||||
g_propdata[i].base = argv(1);
|
||||
break;
|
||||
|
@ -122,6 +137,24 @@ PropData_ParseField(int i, int a)
|
|||
case "breakable_skin":
|
||||
g_propdata[i].breakable_skin = stof(argv(1));
|
||||
break;
|
||||
case "mass":
|
||||
g_propdata[i].mass = stof(argv(1));
|
||||
break;
|
||||
case "damping":
|
||||
g_propdata[i].damping_linear = stof(argv(1));
|
||||
break;
|
||||
case "rotdamping":
|
||||
g_propdata[i].damping_angular = stof(argv(1));
|
||||
break;
|
||||
case "inertia":
|
||||
g_propdata[i].inertia = stof(argv(1));
|
||||
break;
|
||||
case "volume":
|
||||
g_propdata[i].volume = stof(argv(1));
|
||||
break;
|
||||
case "surfaceprop":
|
||||
g_propdata[i].surfaceprop = argv(1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -179,6 +212,62 @@ PropData_Parse(int i, string line, string type)
|
|||
return (0);
|
||||
}
|
||||
|
||||
/* specific to parsing strings from binary .phy files */
|
||||
int
|
||||
PropData_ParsePhyFile(int i, string line, string type)
|
||||
{
|
||||
int c;
|
||||
string key;
|
||||
static string t_name;
|
||||
static int braced = 0i;
|
||||
|
||||
static string gibModel = "";
|
||||
static string gibFadeTime = "";
|
||||
|
||||
c = tokenize(line);
|
||||
|
||||
for (int x = 0i; x < c; x++) {
|
||||
key = argv(x);
|
||||
|
||||
switch(key) {
|
||||
case "{":
|
||||
braced++;
|
||||
break;
|
||||
case "}":
|
||||
if (braced == 1i && t_name == "break") {
|
||||
int bID = g_breakmodel_count; /* !!! increment after loading phy file! */
|
||||
g_breakmodel[bID].modelindex = getmodelindex(type);
|
||||
g_breakmodel[bID].name = g_propdata[i].name;
|
||||
g_breakmodel[bID].data = sprintf("%s%S %S\n", g_breakmodel[bID].data, gibModel, gibFadeTime);
|
||||
hash_add(g_hashbreakmodel, g_breakmodel[bID].name, (int)bID);
|
||||
g_propdata[i].breakable_model = g_breakmodel[bID].name;
|
||||
g_propdata[i].breakable_count++;
|
||||
gibModel = "";
|
||||
gibFadeTime = 0.0f;
|
||||
}
|
||||
braced--;
|
||||
t_name = "";
|
||||
break;
|
||||
default:
|
||||
if (braced == 1i && t_name == "solid") {
|
||||
PropData_ParseField(i, c);
|
||||
} else if (braced == 1i && t_name == "break") {
|
||||
switch (key) {
|
||||
case "model":
|
||||
gibModel = strcat("models/", argv(x+1), ".mdl");
|
||||
break;
|
||||
case "fadetime":
|
||||
gibFadeTime = argv(x+1);
|
||||
break;
|
||||
}
|
||||
} else if (braced == 0i) {
|
||||
t_name = strtolower(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* goes through and looks for a specifically named propdata type inside the scripts dir */
|
||||
int
|
||||
PropData_ForModel(string modelname)
|
||||
|
@ -196,9 +285,9 @@ PropData_ForModel(string modelname)
|
|||
index = g_propdata_count;
|
||||
modelname = strtolower(modelname);
|
||||
|
||||
dprint("[PROPDATA] Loading model propdata ");
|
||||
dprint(modelname);
|
||||
dprint("\n");
|
||||
print("[PROPDATA] Loading model propdata ");
|
||||
print(modelname);
|
||||
print("\n");
|
||||
|
||||
/* create the hash-table if it doesn't exist */
|
||||
if (!g_hashpropdata) {
|
||||
|
@ -220,7 +309,8 @@ PropData_ForModel(string modelname)
|
|||
g_propdata = (propdata_t *)memrealloc(g_propdata, sizeof(propdata_t), index, g_propdata_count);
|
||||
#else
|
||||
if (g_propdata_count >= PROPDATA_MAX) {
|
||||
error(sprintf("PropData_ForModel: Reached PROPDATA_MAX (%d)\n", PROPDATA_MAX));
|
||||
printf(sprintf("PropData_ForModel: Reached PROPDATA_MAX (%d)\n", PROPDATA_MAX));
|
||||
return -1i;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -228,9 +318,75 @@ PropData_ForModel(string modelname)
|
|||
|
||||
fh = fopen(strcat(modelname, ".propdata"), FILE_READ);
|
||||
if (fh < 0) {
|
||||
g_propdata_count--;
|
||||
NSLog("Can't find propdata for model %s", modelname);
|
||||
return -1;
|
||||
/* try the Source Engine version */
|
||||
fh = fopen(Util_ChangeExtension(modelname, "phy"), FILE_READ);
|
||||
|
||||
if (fh < 0) {
|
||||
g_propdata_count--;
|
||||
NSLog("Can't find propdata for model %s", modelname);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int fileSize;
|
||||
int phyID;
|
||||
int numSolids;
|
||||
int fileSum;
|
||||
int filePos = 0i;
|
||||
int surfaceSize;
|
||||
|
||||
filePos = fread(fh, (void*)&fileSize, 4i); /* header size, sanity check */
|
||||
|
||||
if (fileSize != 16i) {
|
||||
error("Only .phy files from Source are supported.");
|
||||
}
|
||||
|
||||
filePos = fread(fh, (void*)&phyID, 4i); /* some header id */
|
||||
filePos = fread(fh, (void*)&numSolids, 4i); /* read our number of solids. */
|
||||
filePos = fread(fh, (void*)&fileSum, 4i);
|
||||
|
||||
//print(sprintf("num fileSize: %i\n", fileSize));
|
||||
//print(sprintf("num phyID: %i\n", phyID));
|
||||
//print(sprintf("num numSolids: %i\n", numSolids));
|
||||
//print(sprintf("num fileSum: %i\n", fileSum));
|
||||
|
||||
/* HACK: We won't support ragdolls, for now. */
|
||||
if (numSolids > 1)
|
||||
return -1;
|
||||
|
||||
/* we skip over all these to get to the bottom of the file */
|
||||
for (int i = 0i; i < numSolids; i++) {
|
||||
filePos = fread(fh, (void*)&surfaceSize, 4);
|
||||
filePos = fseek(fh, filePos + surfaceSize + 16);
|
||||
}
|
||||
|
||||
/* now comes the propdata */
|
||||
while ((line = fgets(fh))) {
|
||||
//printf(line);
|
||||
//print("\n");
|
||||
PropData_ParsePhyFile(index, line, modelname);
|
||||
}
|
||||
|
||||
/* push up the breakmodel count by one if we've written into it */
|
||||
{
|
||||
string breakModel = Prop_GetInfo(index, PROPINFO_BREAKMODEL);
|
||||
|
||||
if (breakModel != "") {
|
||||
int bID = (int)hash_get(g_hashbreakmodel, Prop_GetInfo(index, PROPINFO_BREAKMODEL), -1);
|
||||
|
||||
if (bID != -1) {
|
||||
if (g_breakmodel[bID].data) {
|
||||
g_breakmodel[bID].physics = true;
|
||||
g_breakmodel_count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//print(sprintf("Added %S at id %i with name %S\n", modelname, index, g_propdata[index].name));
|
||||
hash_add(g_hashpropdata, modelname, (int)index);
|
||||
return index;
|
||||
|
||||
//error(sprintf("phy file (size %i): size: %i id: %i numSolids: %i\n", fsize(fh), fileSize, phyID, numSolids));
|
||||
}
|
||||
while ((line = fgets(fh))) {
|
||||
/* when we found it, quit */
|
||||
|
@ -456,8 +612,13 @@ PropData_Init(void)
|
|||
/* gotta tokenize our inputs again */
|
||||
x = tokenize(g_breakmodel[i].data);
|
||||
}
|
||||
|
||||
/* We're making assumptions here, but most physics props have their
|
||||
breakmodels defined along the model. */
|
||||
g_breakmodel[i].physics = false;
|
||||
}
|
||||
|
||||
g_breakmodel_end = g_breakmodel_count;
|
||||
print("PropData initialized.\n");
|
||||
}
|
||||
|
||||
|
@ -491,6 +652,9 @@ BreakModel_SpawnID(vector smins, vector smaxs, vector dir, float speed, int coun
|
|||
{
|
||||
float x = tokenize(g_breakmodel[index].data);
|
||||
int modelcount = x / 2;
|
||||
bool usePhysics = g_breakmodel[index].physics;
|
||||
|
||||
//print(sprintf("breaking into %i models\n", modelcount));
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
vector endpos;
|
||||
|
@ -498,7 +662,6 @@ BreakModel_SpawnID(vector smins, vector smaxs, vector dir, float speed, int coun
|
|||
string fullline;
|
||||
float fadetime;
|
||||
NSDebris gib;
|
||||
int r;
|
||||
int p;
|
||||
int bodygroup = 0;
|
||||
vector rendercolor = [1,1,1];
|
||||
|
@ -507,12 +670,17 @@ BreakModel_SpawnID(vector smins, vector smaxs, vector dir, float speed, int coun
|
|||
float renderfx = RFX_NORMAL;
|
||||
string impactDecal = __NULL__;
|
||||
|
||||
/* pick a model between 0 and num) */
|
||||
r = floor(random(0, modelcount));
|
||||
|
||||
/* two entries, always have to skip by 2 */
|
||||
fullline = mname = argv((r * 2));
|
||||
fadetime = stof(argv((r * 2) + 1));
|
||||
if (modelcount < count) {
|
||||
int r = floor(random(0, modelcount));
|
||||
fullline = mname = argv((r * 2));
|
||||
fadetime = stof(argv((r * 2) + 1));
|
||||
|
||||
} else {
|
||||
fullline = mname = argv((i * 2));
|
||||
fadetime = stof(argv((i * 2) + 1));
|
||||
}
|
||||
|
||||
p = tokenizebyseparator(mname, "#");
|
||||
|
||||
/* special char # detected to designate model submodel count */
|
||||
|
@ -572,16 +740,30 @@ BreakModel_SpawnID(vector smins, vector smaxs, vector dir, float speed, int coun
|
|||
gib.SetRenderFX(renderfx);
|
||||
gib.SetSize([0,0,0], [0,0,0]);
|
||||
gib.SetOrigin(endpos);
|
||||
makevectors(dir);
|
||||
gib.velocity = (v_forward * speed) * 0.75;
|
||||
gib.velocity[0] += (random() - 0.5) * (speed * 0.25);
|
||||
gib.velocity[1] += (random() - 0.5) * (speed * 0.25);
|
||||
gib.velocity[2] += (random() - 0.5) * (speed * 0.25);
|
||||
gib.SetAngularVelocity(vectoangles(gib.velocity));
|
||||
gib.SetMovetype(MOVETYPE_BOUNCE);
|
||||
gib.SetSolid(SOLID_BBOX);
|
||||
|
||||
if (usePhysics == false) {
|
||||
makevectors(dir);
|
||||
gib.velocity = (v_forward * speed) * 0.75;
|
||||
gib.velocity[0] += (random() - 0.5) * (speed * 0.25);
|
||||
gib.velocity[1] += (random() - 0.5) * (speed * 0.25);
|
||||
gib.velocity[2] += (random() - 0.5) * (speed * 0.25);
|
||||
gib.SetAngularVelocity(vectoangles(gib.velocity));
|
||||
gib.SetMovetype(MOVETYPE_BOUNCE);
|
||||
gib.SetSolid(SOLID_BBOX);
|
||||
} else {
|
||||
gib.SetMovetype(MOVETYPE_PHYSICS);
|
||||
gib.SetSolid(SOLID_NOT);
|
||||
gib.mass = 1.0f;
|
||||
gib.friction = 1.0f;
|
||||
gib.bouncefactor = 0.9f;
|
||||
gib.bouncestop = 0.1f / cvar("sv_gravity");
|
||||
gib.geomtype = GEOMTYPE_TRIMESH;
|
||||
gib.ApplyForceOffset(dir * speed, endpos);
|
||||
}
|
||||
|
||||
gib.ScheduleThink(NSEntity::Destroy, fadetime);
|
||||
gib.SetImpactDecal(impactDecal);
|
||||
print(sprintf("%S\n", mname));
|
||||
|
||||
#ifdef CLIENT
|
||||
gib.drawmask = MASK_ENGINE;
|
||||
|
@ -616,10 +798,49 @@ BreakModel_Receive(void)
|
|||
speed = readfloat();
|
||||
count = readbyte();
|
||||
|
||||
/* sanity check */
|
||||
if (index >= g_breakmodel_count) {
|
||||
print(sprintf("^1Unable to spawn breakmodel of id %i. Not cached on client. (Client knows of %i)\n", index, g_breakmodel_count));
|
||||
return;
|
||||
}
|
||||
|
||||
//print(sprintf("i: %i max: %i name: %S\n", index, g_breakmodel_count, g_breakmodel[index].name));
|
||||
|
||||
BreakModel_SpawnID(smins, smaxs, dir, speed, count, index);
|
||||
}
|
||||
|
||||
void
|
||||
BreakModel_ReceiveClientData(void)
|
||||
{
|
||||
int addToCount = readbyte();
|
||||
|
||||
for (int i = 0; i < addToCount; i++) {
|
||||
string modelName = modelnameforindex(readshort());
|
||||
PropData_ForModel(modelName);
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
void
|
||||
BreakModel_SendClientData(entity targetEnt)
|
||||
{
|
||||
int extraCount = g_breakmodel_count - g_breakmodel_end;
|
||||
int startOffset = g_breakmodel_end;
|
||||
|
||||
WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET);
|
||||
WriteByte(MSG_MULTICAST, EV_BREAKMODELDATA);
|
||||
WriteByte(MSG_MULTICAST, g_breakmodel_count - g_breakmodel_end);
|
||||
|
||||
//print(sprintf("%i %i\n", g_breakmodel_end, g_breakmodel_count));
|
||||
|
||||
for (int i = g_breakmodel_end; i < g_breakmodel_count; i++) {
|
||||
WriteShort(MSG_MULTICAST, g_breakmodel[i].modelindex);
|
||||
}
|
||||
|
||||
msg_entity = targetEnt;
|
||||
multicast(g_vec_null, MULTICAST_ONE_R);
|
||||
}
|
||||
|
||||
void
|
||||
BreakModel_Spawn(vector smins, vector smaxs, vector dir, float speed, int count, string type)
|
||||
{
|
||||
|
|
Loading…
Reference in a new issue