set g_throughfloor_damage 0.75
set g_throughfloor_force 0.75
set g_projectiles_damage -2 "\"-2\" = allow no damage to projectiles, \"-1\" = no damage other than the exceptions (electro combo, hagar join explode, ML mines), \"0\" = only damage from contents (lava/slime) or exceptions, \"1\" = only self damage or damage from contents or exceptions, \"2\" = allow all damage to projectiles"
+set g_projectiles_interact 2 "\"0\" = projectiles don't interact with each other (noclip through), \"1\" = projectiles don't interact except that blaster bolts detonate when hitting another projectile (deflect it), \"2\" = normal projectiles detonate and bouncy projectiles bounce off when hitting another projectile"
set g_projectiles_keep_owner 1
set g_projectiles_newton_style 0 "\"0\" = absolute velocity projectiles (like Quake), \"1\" = relative velocity projectiles, \"Newtonian\" (like Tribes 2), \"2\" = relative velocity projectiles, but aim is precorrected so projectiles hit the crosshair (NOTE: strafe rockets then are SLOWER than ones shot while standing, happens in 1 too when aiming correctly which is hard)"
set g_projectiles_newton_style_2_minfactor 0.8
if (!world.fullspawndata)
LOG_WARN("Engine lacks entity fullspawndata, on Quake 3 maps some entities will malfunction.");
+
+ if (!checkextension("DP_RM_CLIPGROUP"))
+ LOG_WARN("Engine lacks DP_RM_CLIPGROUP, g_projectiles_interact < 2 won't take effect.");
#endif
#ifdef GAMEQC
vector force_xyzscale = '1 1 1';
force_xyzscale.z = WEP_CVAR_PRI(WEP_BLASTER, force_zscale);
+ // slightly hacky but probably not worth adding a parameter for this special case
+ bool zero_damageforcescale = false;
+ if (autocvar_g_projectiles_interact == 1 && (toucher.flags & FL_PROJECTILE) && !toucher.damageforcescale)
+ {
+ toucher.damageforcescale = 1;
+ zero_damageforcescale = true;
+ }
+
RadiusDamageForSource(
this,
(this.origin + (this.mins + this.maxs) * 0.5),
toucher
);
+ if (zero_damageforcescale)
+ toucher.damageforcescale = 0;
+
delete(this);
}
bool WarpZone_Projectile_Touch_ImpactFilter_Callback(entity this, entity toucher)
{
- // owner check
if(toucher && toucher == this.owner)
return true;
+
+ if(autocvar_g_projectiles_interact == 1 && toucher.classname == "blasterbolt")
+ {
+ if (this.movetype == MOVETYPE_BOUNCE || this.movetype == MOVETYPE_BOUNCEMISSILE)
+ // We'll bounce off it due to limitations so let it deflect us
+ // to hide the problem, see PROJECTILE_MAKETRIGGER.
+ gettouch(toucher)(toucher, this);
+ return true;
+ }
+
if(SUB_NoImpactCheck(this, toucher))
{
if(this.classname == "nade")
#pragma once
int autocvar_g_projectiles_damage;
+int autocvar_g_projectiles_interact = 2;
bool autocvar_g_projectiles_keep_owner;
float autocvar_sv_strengthsound_antispam_refire_threshold;
float autocvar_sv_strengthsound_antispam_time;
#define PROJECTILE_TOUCH(e,t) MACRO_BEGIN if (WarpZone_Projectile_Touch(e,t)) return; MACRO_END
-#define PROJECTILE_MAKETRIGGER(e) (e).solid = SOLID_CORPSE; (e).dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_CORPSE
+// FIXME: some behaviours can't be achieved or would require kludges in physics code,
+// eg we can't make electro bolts hit electro orbs only (without causing other problems) when g_projectiles_interact == 1,
+// see also WarpZone_Projectile_Touch_ImpactFilter_Callback().
+// Ideally, extend engine to support any combination of 'hit a and b but not x or y' and 'take hits from c and d but not w or z'.
+#define PROJECTILE_MAKETRIGGER(e) \
+ (e).solid = SOLID_CORPSE; \
+ (e).dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_CORPSE; \
+ if (!autocvar_g_projectiles_interact || (autocvar_g_projectiles_interact == 1 && (e).classname != "blasterbolt")) \
+ (e).clipgroup = FL_PROJECTILE
+
// when doing this, hagar can go through clones
// #define PROJECTILE_MAKETRIGGER(e) (e).solid = SOLID_BBOX