← Home SEEBIE90 DOCS
The RIM-116 & Phalanx mod is published under the Arma Public License (APL)Example code is free to useSuggestions: Discord to the author

RIM-116 & Phalanx

Damage model

A mount that only knows “intact” or “scrap” is dull: it either sweeps the sky or it is gone. Assemblies that fail one at a time give a middle ground – and the hit position decides which one.

In plain wordsShooting the traverse drive stops the mount turning without destroying it, and an engineer can repair it. Where you hit decides what breaks.

Only the structure has a hit zone

The structure is the mount's default zone and catches every round. Which assembly suffers is decided by the hit position: BaseDamageContext carries hitPosition, and the mount knows which of its moving parts sits there. The zones therefore rotate with the turret, and it costs not one polygon on the model.

Example code – free to use
MyAD_ESubsystem MyAD_Mount_AssemblyAt(vector hit)
{
	if (Contains(m_Launcher, hit))
		return MyAD_ESubsystem.LAUNCHER;

	if (Contains(m_Arms, hit))
		return MyAD_ESubsystem.ELEVATION;	// the arms tilt the weapon

	if (Contains(m_Base, hit))
		return MyAD_ESubsystem.TRAVERSE;	// the slew ring sits in the base plate

	return MyAD_ESubsystem.STRUCTURE;
}
NoteAvoid the dice roll: giving a share of every hit to a RANDOMLY chosen intact assembly, on the grounds that “which corner a fragment caught cannot be determined without separate colliders”, is simply wrong. The hit position is attached to the damage event.

Blast finds the nearest part, not the part underneath

A round hits a point; a charge works across an area. A rocket that went off two metres beside a mount did nothing, because no part sat at the impact point and the concrete base was invulnerable. Blast and fragments do not stop at a box boundary. So for explosive damage, ask which part is nearest, not which part sits there. On a direct hit the distance is zero and it reduces to the point-accurate mapping.

A second approach that failed

Written down so nobody tries it twice. The idea: give every moving part its own damage manager, so a round on the barrel cluster hits the launcher directly. It fails because SCR_DamageManagerComponent requires an RplComponent on the same entity. Plain GenericEntity parts have no replication of their own, all prefabs stopped loading, and the editor showed nothing but the base:

Example code – free to use
ENTITY (E): GenericEntity is missing component RplComponent
            as required by component SCR_DamageManagerComponent

Armour as a threshold

A mount that intercepts cruise missiles must not fall to small arms. Override ComputeEffectiveDamage in a hit zone subclass – an event, and therefore overridable, unlike proto external methods which are only callable. It filters before the damage is applied; a filter in OnDamage would be too late, the hit is already counted there.

Example code – free to use
class MyAD_ArmouredHitZone : SCR_HitZone
{
	[Attribute("150")] protected float m_fMinDamage;		// example value
	[Attribute("0.25")] protected float m_fPassthrough;		// example value

	override float ComputeEffectiveDamage(notnull BaseDamageContext ctx, bool isDOT)
	{
		if (ctx.damageType == EDamageType.TRUE)		// the editor's "destroy" must really destroy
			return super.ComputeEffectiveDamage(ctx, isDOT);

		if (ctx.damageValue < m_fMinDamage)
			return 0;

		return super.ComputeEffectiveDamage(ctx, isDOT) * m_fPassthrough;
	}
}

Fragments are the main damage, not the blast

Measured with an anti-tank rocket on the mount: one hit produces two damage events, and they are nothing like each other – a blast event below one point and a fragmentation event of more than a thousand. A filter that only lets explosive and kinetic damage through discards the main share of every explosion. The report was “I fired several times at the gun and the rounds went straight through”.

PitfallA charge without a fragmentation casing – plastic explosive, TNT – produces only the blast event. Against an armoured prop it is therefore worthless, while a rocket with a steel case works. Check what a warhead produces before blaming the armour.
PitfallEDamageType.TRUE must pass unfiltered – the enum says so itself: “ignores damage multipliers, damage reduction and threshold”. A custom armour routine does not honour that by itself. Here it turned 3,000 into about a third, so the Game Master's destroy tool worked at a fraction of its effect.
NoteComputeEffectiveDamage is called MULTIPLE times per damage event – it is a calculation, not an event. Anything with a side effect belongs in OnDamage, which fires exactly once. One hit with three calls destroyed three assemblies at once, which read in game as “the mount is either fine or gone”.

Repair needs a remainder

The engine locks a destroyed default hit zone against any healing – SetHealth and SetHealthScaled fail there silently, with no error. Measured in one run:

Example code – free to use
launcher box    destroyed=1 | health 0 -> 800   state after: 0   (worked)
base structure  destroyed=1 | health 0 -> 0     state after: 2   (did not)

Both zones were destroyed; the only difference is which one is the default. So clamp the damage to always leave a tiny remainder, which keeps the zone out of DESTROYED. It changes nothing about play – a remainder that small is far below any damage grade you display, so the assembly still reads as failed. It is merely repairable.

Example code – free to use
float MyAD_Zone_Clamp(float damage)
{
	float room = GetHealth() - GetMaxHealth() * 0.001;	// always leave a per-mille
	return Math.Clamp(damage, 0, Math.Max(room, 0));
}
Example code on this page is free to use in your own projects (credit appreciated). The RIM-116 & Phalanx mod is published under the Arma Public License (APL). Suggestions: Discord. Arma Reforger / Enfusion are trademarks of Bohemia Interactive.
Impressum · Privacy Policy