Compare commits

...

8 Commits

Author SHA1 Message Date
Marco Cawthorne ee58089a55
env_explosion: Get it up to Source Engine standards. 2024-02-23 13:23:19 -08:00
Marco Cawthorne 34884b68a3
ambient_generic: fix mix-up of small and medium radius.
also set s_nominaldistance to 1024.
2024-02-23 13:22:51 -08:00
Marco Cawthorne e2ee6987de
NSTraceAttack: save traceline endpos and plane_normal in case it gets set elsewhere 2024-02-23 13:21:44 -08:00
Marco Cawthorne 7d58b4a96a
NSRenderableEntity: Remove aggressive angle optimisation 2024-02-23 13:21:08 -08:00
Marco Cawthorne 8a18c2e992
PropData: gibs use a fixed angular velocity in fallback physics mode, and have a hitbox. 2024-02-23 13:20:53 -08:00
Marco Cawthorne 0f86ba61a1
DecalGroups_Place: cancel out if the group wasn't found. 2024-02-23 13:20:15 -08:00
Marco Cawthorne 5c90692873
SurfaceProperties: impacts now pass a proper dir vector to the particle system 2024-02-21 21:11:48 -08:00
Marco Cawthorne 6319f6166e
Skill.qc: readcmd() builtin usage replaced with a tiny config parser.
Context:
localcmd/stuffcmd are delayed enough that we can't use it to load
difficulty settings from skill.cfg before the game objects have spawned.

readcmd worked around this, but its usage is buggy and discouraged.

loading config files from the root game directory is forbidden by the
engine for security reasons (being able to read passwords etc.)
but we're allowed to do so from sub-directories.

by emulating the source engine behaviour we become compatible with their
convention, while also working around said incompatibility.
2024-02-21 21:08:57 -08:00
10 changed files with 213 additions and 80 deletions

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2016-2022 Vera Visions LLC.
* Copyright (c) 2016-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
@ -14,17 +14,16 @@
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
enumflags
enum
{
ENVEXPLO_NODAMAGE,
ENVEXPLO_REPEATABLE,
ENVEXPLO_NOBALL,
ENVEXPLO_NOSMOKE,
ENVEXPLO_NODECAL,
ENVEXPLO_NOSPARKS
ENVEXPLO_NODAMAGE = 1,
ENVEXPLO_REPEATABLE = 2,
ENVEXPLO_NOSOUND = 64,
ENVEXPLO_NOTUNDERWATER = 8192,
ENVEXPLO_GENERICDAMAGE = 16384
};
/*!QUAKED env_explosion (1 .5 0) (-8 -8 -8) (8 8 8) ENVEXPLO_NODAMAGE ENVEXPLO_REPEATABLE ENVEXPLO_NOBALL ENVEXPLO_NOSMOKE ENVEXPLO_NODECAL ENVEXPLO_NOSPARKS
/*!QUAKED env_explosion (1 .5 0) (-8 -8 -8) (8 8 8) NO_DAMAGE REPEATABLE x x x NO_SOUND
# OVERVIEW
When triggered, creates an explosion at its location.
@ -33,14 +32,18 @@ When triggered, creates an explosion at its location.
- "target" : Target when triggered.
- "killtarget" : Target to kill when triggered.
- "iMagnitude" : Magnitude of the explosion.
- "explosion_custom_effect" : Sets a custom explosion particle effect to appear.
- "explosion_custom_sound" : Sets a custom explosion sound to play.
# INPUTS
- "Explode" : Triggers the explosion effect.
# SPAWNFLAGS
- ENVEXPLO_NODAMAGE (1) : Make this explosion purely decorative, without radius damage.
- ENVEXPLO_REPEATABLE (2) : Makes this explosion triggerable more than once.
- ENVEXPLO_NOBALL (4) : Spawn no fireball.
- ENVEXPLO_NOSMOKE (8) : Spawn no smoke.
- ENVEXPLO_NODECAL (16) : Leave no decal upon explosion.
- ENVEXPLO_NOSPARKS (32) : Don't spawn any sparks upon exploding.
- NO_DAMAGE (1) : Make this explosion purely decorative, without radius damage.
- REPEATABLE (2) : Makes this explosion triggerable more than once.
- NO_SOUND (64) : Do not play the sound effect.
- NOTUNDERWATER (8192) : Don't cause damage underwater.
- GENERIC_DAMAGE (16384) : Act as if damage wasn't caused by an explosion type, making gibbing more difficult - some games may ignore it however.
# TRIVIA
This entity was introduced in Half-Life (1998).
@ -52,23 +55,74 @@ public:
void env_explosion(void);
/* overrides */
virtual void SpawnKey(string,string);
virtual void Spawned(void);
virtual void Respawn(void);
virtual void Save(float);
virtual void Restore(string,string);
virtual void Trigger(entity, triggermode_t);
virtual void SpawnKey(string,string);
virtual void Respawn(void);
virtual void Input(entity, string, string);
/** Returns the radius of the explosion. */
nonvirtual float GetExplosionRadius(void);
/** Returns the maximum damage the explosion delivers. */
nonvirtual int GetExplosionDamage(void);
private:
int m_iMagnitude;
float m_flMaxDelay;
int m_flRadiusOverride;
bool m_bEnabled;
string m_strExplosionParticle;
string m_strExplosionSound;
int m_iExplosionParticle;
};
void
env_explosion::env_explosion(void)
{
m_iMagnitude = 0;
m_flMaxDelay = 0.0f;
m_iMagnitude = 0i;
m_flRadiusOverride = -1;
m_bEnabled = true;
m_strExplosionParticle = "fx_explosion.main";
m_strExplosionSound = "fx.explosion";
m_iExplosionParticle = 0i;
}
void
env_explosion::SpawnKey(string strKey, string strValue)
{
switch (strKey) {
case "iMagnitude":
m_iMagnitude = ReadInt(strValue);
break;
case "iRadiusOverride":
m_flRadiusOverride = ReadFloat(strValue);
break;
case "explosion_custom_effect":
m_strExplosionParticle = ReadString(strValue);
break;
case "explosion_custom_sound":
m_strExplosionSound = ReadString(strValue);
break;
default:
super::SpawnKey(strKey, strValue);
}
}
void
env_explosion::Spawned(void)
{
super::Spawned();
m_iExplosionParticle = particleeffectnum(m_strExplosionParticle);
Sound_Precache(m_strExplosionSound);
}
void
env_explosion::Respawn(void)
{
InitPointTrigger();
m_bEnabled = true;
}
@ -77,8 +131,11 @@ env_explosion::Save(float handle)
{
super::Save(handle);
SaveInt(handle, "m_iMagnitude", m_iMagnitude);
SaveFloat(handle, "m_flMaxDelay", m_flMaxDelay);
SaveBool(handle, "m_bEnabled", m_bEnabled);
SaveFloat(handle, "m_flRadiusOverride", m_flRadiusOverride);
SaveString(handle, "m_strExplosionParticle", m_strExplosionParticle);
SaveString(handle, "m_strExplosionSound", m_strExplosionSound);
SaveInt(handle, "m_iExplosionParticle", m_iExplosionParticle);
}
void
@ -88,45 +145,79 @@ env_explosion::Restore(string strKey, string strValue)
case "m_iMagnitude":
m_iMagnitude = ReadInt(strValue);
break;
case "m_flMaxDelay":
m_flMaxDelay = ReadFloat(strValue);
break;
case "m_bEnabled":
m_bEnabled = ReadBool(strValue);
break;
case "m_flRadiusOverride":
m_flRadiusOverride = ReadFloat(strValue);
break;
case "m_strExplosionParticle":
m_strExplosionParticle = ReadString(strValue);
break;
case "m_strExplosionSound":
m_strExplosionSound = ReadString(strValue);
break;
case "m_iExplosionParticle":
m_iExplosionParticle = ReadInt(strValue);
break;
default:
super::Restore(strKey, strValue);
}
}
void
env_explosion::SpawnKey(string strKey, string strValue)
{
switch (strKey) {
case "iMagnitude":
m_iMagnitude = stoi(strValue);
break;
default:
super::SpawnKey(strKey, strValue);
}
}
void
env_explosion::Respawn(void)
{
m_bEnabled = true;
}
void
env_explosion::Trigger(entity act, triggermode_t state)
{
pointparticles(particleeffectnum("fx_explosion.main"), origin, [0,0,0], 1);
bool shouldDamage = true;
if (!HasSpawnFlags(ENVEXPLO_NODAMAGE)) {
Damage_Radius(origin, this, m_iMagnitude, m_iMagnitude * 2.5f, TRUE, 0);
if (HasSpawnFlags(ENVEXPLO_NOTUNDERWATER) == true && WaterLevel() > 0) {
shouldDamage = false;
} else if (HasSpawnFlags(ENVEXPLO_NODAMAGE) == true) {
shouldDamage = false;
}
if (!HasSpawnFlags(ENVEXPLO_REPEATABLE)) {
/* TODO: Should be pass the nearest surface normal? Just an idea. */
pointparticles(m_iExplosionParticle, GetOrigin(), [0,0,0], 1);
if (HasSpawnFlags(ENVEXPLO_NOSOUND) == false) {
StartSoundDef(m_strExplosionSound, CHAN_BODY, true);
}
if (shouldDamage == true) {
damageType_t damageType = DMG_EXPLODE;
if (HasSpawnFlags(ENVEXPLO_GENERICDAMAGE) == true) {
damageType = DMG_GENERIC;
}
Damage_Radius(GetOrigin(), this, GetExplosionDamage(), GetExplosionRadius(), damageType, 0);
}
if (HasSpawnFlags(ENVEXPLO_REPEATABLE) == false) {
m_bEnabled = false;
}
}
void
env_explosion::Input(entity entityActivator, string inputName, string dataField)
{
switch (inputName) {
case "Explode":
Trigger(entityActivator, TRIG_TOGGLE);
break;
default:
super::Input(entityActivator, inputName, dataField);
}
}
float
env_explosion::GetExplosionRadius(void)
{
return (m_flRadiusOverride == -1) ? (float)m_iMagnitude * 2.5f : m_flRadiusOverride;
}
int
env_explosion::GetExplosionDamage(void)
{
return (m_iMagnitude);
}

View File

@ -240,9 +240,9 @@ ambient_generic::Respawn(void)
if (HasSpawnFlags(AS_ARADIUS)) {
m_flRadius = ATTN_NONE;
} else if (HasSpawnFlags(AS_SRADIUS)) {
m_flRadius = ATTN_IDLE;
} else if (HasSpawnFlags(AS_MRADIUS)) {
m_flRadius = ATTN_STATIC;
} else if (HasSpawnFlags(AS_MRADIUS)) {
m_flRadius = ATTN_IDLE;
} else if (HasSpawnFlags(AS_LRADIUS)) {
m_flRadius = ATTN_NORM;
} else {

View File

@ -140,6 +140,8 @@ void
NSTraceAttack::_FireSingle(vector vecPos, vector vecAngles, float flDamage, float flRange)
{
vector range;
vector planeNormal;
vector endPos;
if (flRange <= 0)
return;
@ -157,6 +159,8 @@ NSTraceAttack::_FireSingle(vector vecPos, vector vecAngles, float flDamage, floa
m_eOwner.hitcontentsmaski = CONTENTBITS_POINTSOLID | CONTENTBIT_CORPSE | CONTENTBIT_WATER | CONTENTBIT_SLIME | CONTENTBIT_LAVA | CONTENTBIT_PROJECTILE;
traceline(vecPos, vecPos + range, MOVE_LAGGED | MOVE_HITMODEL, m_eOwner);
m_eOwner.hitcontentsmaski = oldhitcontents;
planeNormal = trace_plane_normal;
endPos = trace_endpos;
flRange -= trace_plane_dist;
@ -170,14 +174,14 @@ NSTraceAttack::_FireSingle(vector vecPos, vector vecAngles, float flDamage, floa
/* water impact */
if (trace_endcontentsi & CONTENTBIT_WATER) {
SurfData_ImpactOfNamedType("water", trace_endpos, trace_plane_normal);
_FireSingle(trace_endpos + (v_forward * 2), vecAngles, flDamage / 2, flRange);
SurfData_ImpactOfNamedType("water", endPos, planeNormal);
_FireSingle(endPos + (v_forward * 2), vecAngles, flDamage / 2, flRange);
} else if (trace_endcontentsi & CONTENTBIT_SLIME) {
SurfData_ImpactOfNamedType("slime", trace_endpos, trace_plane_normal);
_FireSingle(trace_endpos + (v_forward * 2), vecAngles, flDamage / 2, flRange);
SurfData_ImpactOfNamedType("slime", endPos, planeNormal);
_FireSingle(endPos + (v_forward * 2), vecAngles, flDamage / 2, flRange);
} else if (trace_endcontentsi & CONTENTBIT_LAVA) {
SurfData_ImpactOfNamedType("lama", trace_endpos, trace_plane_normal);
_FireSingle(trace_endpos + (v_forward * 2), vecAngles, flDamage / 2, flRange);
SurfData_ImpactOfNamedType("lama", endPos, planeNormal);
_FireSingle(endPos + (v_forward * 2), vecAngles, flDamage / 2, flRange);
}
if (trace_ent.takedamage != DAMAGE_NO && trace_ent.iBleeds) {
@ -219,21 +223,22 @@ NSTraceAttack::_FireSingle(vector vecPos, vector vecAngles, float flDamage, floa
#ifdef WASTES
player pl1 = (player)self;
if (pl1.m_iWillpowerValue > 0) {
FX_Crit(trace_endpos, vectoangles(trace_endpos - pl1.origin), 0);
FX_Crit(endPos, vectoangles(endPos - pl1.origin), 0);
}
#endif
/* impact per bullet */
if (trace_ent.iBleeds == 0) {
if (m_strDecalGroup)
DecalGroups_Place(m_strDecalGroup, trace_endpos + (v_forward * -2));
DecalGroups_Place(m_strDecalGroup, endPos + (v_forward * -2));
SurfData_Impact(trace_ent, trace_endpos, trace_plane_normal);
SurfData_Impact(trace_ent, endPos, planeNormal);
}
/* combine them into one single Damage_Apply call later */
if (trace_ent.takedamage != DAMAGE_NO) {
if (trace_ent != m_eMultiTarget) {
trace_endpos = endPos;
_ApplyDamage();
m_eMultiTarget = (NSSurfacePropEntity)trace_ent;
m_iMultiValue = flDamage;
@ -251,16 +256,16 @@ NSTraceAttack::_FireSingle(vector vecPos, vector vecAngles, float flDamage, floa
/* check if this wall is 6 units thick... */
if (m_iTotalPenetrations > 0) {
cont = pointcontents(trace_endpos + v_forward * 5);
cont = pointcontents(endPos + v_forward * 5);
if (cont == CONTENT_SOLID)
m_iTotalPenetrations -= 1; /* deduct 1 penetration power */
}
cont = pointcontents(trace_endpos + v_forward * m_flMaxThickness);
cont = pointcontents(endPos + v_forward * m_flMaxThickness);
if (cont == CONTENT_EMPTY)
_FireSingle(trace_endpos + (v_forward * 2), vecAngles, flDamage / 2, flRange);
_FireSingle(endPos + (v_forward * 2), vecAngles, flDamage / 2, flRange);
}
#endif
}

View File

@ -463,7 +463,7 @@ initents(void)
g_ents_initialized = TRUE;
/* engine hacks for dedicated servers */
cvar_set("s_nominaldistance", "2048");
cvar_set("s_nominaldistance", "1024");
/* other engine hacks */
cvar_set("sv_nqplayerphysics", "0");

View File

@ -16,4 +16,7 @@
*/
void Skill_Init(void);
/** Return a skill variable's value or return a defaultvalue if it's undefined. */
float Skill_GetValue(string, float);
bool Skill_ParseConfig(string fileName);

View File

@ -27,21 +27,19 @@ This will almost always result in them using default values, or (worst case) 0.
void
Skill_Init(void)
{
/* sometimes we have extra overrides that the original does not
provide. so we execute our mod-specific config here */
readcmd(sprintf("exec skill_%s.cfg\n", cvar_string("game")));
readcmd(sprintf("exec maps/%s_skl.cfg\n", mapname));
Skill_ParseConfig("cfg/skill_manifest.cfg");
Skill_ParseConfig(sprintf("maps/%s_skl.cfg", mapname));
}
/*
=================
Skill_GetValue
Return a skill variable's value or return a defaultvalue if it's undefined.
Return a skill variable's value or return a defaultValue if it's undefined.
=================
*/
float
Skill_GetValue(string variable, float defaultvalue)
Skill_GetValue(string variable, float defaultValue)
{
float skill = cvar("skill");
@ -49,7 +47,7 @@ Skill_GetValue(string variable, float defaultvalue)
skill = 2; /* default to medium */
float val = fabs(cvar(sprintf("sk_%s%d", variable, skill)));
return (val == 0) ? defaultvalue : val;
return (val == 0) ? defaultValue : val;
}
/* input string is potentially a skill variable */
@ -61,4 +59,33 @@ Skill_GetDefValue(string variable)
}
return stof(variable);
}
bool
Skill_ParseConfig(string fileName)
{
string tempString;
filestream configFile = fopen(fileName, FILE_READ);
if (configFile < 0) {
print(sprintf("^1Warning: Unable to exec %S for parsing.\n", fileName));
return (false);
}
while ((tempString = fgets(configFile))) {
int argCount = (int)tokenize_console(tempString);
string firstArg = argv(0);
if (argCount == 2i) {
if (firstArg == "exec") {
Skill_ParseConfig(sprintf("cfg/%s", argv(1)));
}
} else if (argCount == 3i) {
if (firstArg == "set" || firstArg == "seta")
cvar_set(argv(1), argv(2));
}
}
fclose(configFile);
return (true);
}

View File

@ -113,12 +113,9 @@ NSRenderableEntity::EvaluateEntity(void)
EVALUATE_VECTOR(origin, 0, RDENT_CHANGED_ORIGIN_X)
EVALUATE_VECTOR(origin, 1, RDENT_CHANGED_ORIGIN_Y)
EVALUATE_VECTOR(origin, 2, RDENT_CHANGED_ORIGIN_Z)
angles = Math_FixDeltaVector(angles);
EVALUATE_VECTOR(angles, 0, RDENT_CHANGED_ANGLES_X)
EVALUATE_VECTOR(angles, 1, RDENT_CHANGED_ANGLES_Y)
EVALUATE_VECTOR(angles, 2, RDENT_CHANGED_ANGLES_Z)
EVALUATE_FIELD(modelindex, RDENT_CHANGED_MODELINDEX)
EVALUATE_FIELD(colormap, RDENT_CHANGED_MODELINDEX)
EVALUATE_FIELD(solid, RDENT_CHANGED_SOLIDMOVETYPE)

View File

@ -194,12 +194,17 @@ DecalGroups_NumForName(string group)
void
DecalGroups_Place(string group, vector org)
{
int index;
int index = -1i;
if (g_decalgroup_count <= 0i)
if (g_decalgroup_count <= 0i) {
return;
}
index = (int)hash_get(g_hashdecalgroup, strtolower(group), -1);
index = (int)hash_get(g_hashdecalgroup, strtolower(group), -1i);
if (index == -1i) {
return;
}
#ifdef SERVER
/* on the server we only need to tell the clients in the PVS

View File

@ -738,8 +738,9 @@ BreakModel_SpawnID(vector smins, vector smaxs, vector dir, float speed, int coun
gib.SetRenderAmt(renderamt);
gib.SetRenderMode(rendermode);
gib.SetRenderFX(renderfx);
gib.SetSize([0,0,0], [0,0,0]);
gib.SetSize([-8,-8,-8],[8,8,8]);
gib.SetOrigin(endpos);
gib.SetScale(1.0f);
if (usePhysics == false) {
makevectors(dir);
@ -747,9 +748,9 @@ BreakModel_SpawnID(vector smins, vector smaxs, vector dir, float speed, int coun
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.SetAngularVelocity([300,300,300]);
gib.SetMovetype(MOVETYPE_BOUNCE);
gib.SetSolid(SOLID_BBOX);
gib.SetSolid(SOLID_NOT);
} else {
gib.SetMovetype(MOVETYPE_PHYSICS);
gib.SetSolid(SOLID_NOT);

View File

@ -456,14 +456,18 @@ void
SurfData_Impact_Parse(void)
{
entity surfnum = __NULL__;
vector impactorg = [0.0f, 0.0f, 0.0f];
vector impactorg = g_vec_null;
vector impactang = g_vec_null;
float impactSurface;
impactorg[0] = readcoord();
impactorg[1] = readcoord();
impactorg[2] = readcoord();
surfnum = findfloat(world, ::entnum, readentitynum()-1);
string tex_name = getsurfacetexture(surfnum, getsurfacenearpoint(surfnum, impactorg));
impactSurface = getsurfacenearpoint(surfnum, impactorg);
impactang = getsurfacenormal(surfnum, impactSurface);
string tex_name = getsurfacetexture(surfnum, impactSurface);
string impactsfx = SurfData_GetInfo(SurfData_TexToSurfData(tex_name), SURFDATA_SND_BULLETIMPACT);
@ -476,7 +480,7 @@ SurfData_Impact_Parse(void)
NSLog("\temitting impact fx %S at %v", impactfx, impactorg);
Sound_PlayAt(impactorg, impactsfx);
pointparticles( impactid, impactorg, [0,0,0], 1 );
pointparticles( impactid, impactorg, impactang, 1 );
}
/** Called by EV_SURFIMPACTID */