← Home SEEBIE90 DOCS
The GTG Safe Zone mod is published under the Arma Public License (APL)Example code is free to useSuggestions: Discord to the author

GTG Safe Zone

Shooting from inside

Cancelling damage is easy. The trouble starts when the zone also cannot stop everything from firing: then it is a place where you can shoot out and nobody can shoot back.

In plain wordsWhat cannot be stopped is paid for. Whoever hits something from the quiet zone loses the protection until they leave, and so does the vehicle they sit in.

The gap

Infantry weapons, grenades and melee can be stopped. Vehicle turrets cannot, with the means available to a mod. The turret controller that vehicle and helicopter mounts use is an engine class, and the compiler refuses to extend it – and refuses in the worst possible way, by breaking the whole script module, so the game does not start. Resetting the fire input does not help, because turrets do not read their fire request through it, and the engine sets the request back after it has been cleared.

That leaves a tank that stands in the zone, is invulnerable, and fires out of it. Instead of stopping the shot, the zone charges for it.

Pay with the protection

Every hit on a character or a vehicle passes through the damage hook, so that is where an attack is noticed – whatever the weapon, character or turret. The hit names an instigator. If the instigator stands in a quiet zone, it forfeits its protection until it has left the zone once.

Example code – free to useMyAD_Forfeit.c
class MyAD_Forfeit
{
	protected static ref array<IEntity> s_aForfeited = {};

	static bool HasForfeited(IEntity entity)
	{
		return entity && s_aForfeited.Contains(entity);
	}

	// Called for EVERY hit, from the damage hook.
	static void NoteAttacker(Instigator instigator, EDamageType type, float value)
	{
		// Healing is no attack. Bleeding runs on from an earlier hit and must not make somebody an attacker afterwards.
		if (value <= 0 || type == EDamageType.HEALING || type == EDamageType.REGENERATION || type == EDamageType.BLEEDING)
			return;

		if (!instigator)
			return;

		IEntity attacker = instigator.GetInstigatorEntity();
		if (!attacker)
			return;

		// By place only - the faction does not matter for this.
		MyAD_Zone zone = MyAD_Zone.FindProtectingZoneOf(attacker);
		if (!zone || zone.IsAlarmActive())
			return;							// outside, or the alarm is up: firing is allowed, no penalty

		Register(attacker);
		Register(GetOccupiedVehicle(attacker));	// see below
	}

	protected static bool Register(IEntity entity)
	{
		if (!entity || s_aForfeited.Contains(entity))
			return false;

		s_aForfeited.Insert(entity);
		return true;
	}
}

The shield check from Place and faction already asks HasForfeited(), so from the next hit the shooter is no longer shielded. The call sits at the top of the function that discards damage, before anything returns (see Invulnerability): the hook sees every hit there, and it must see them all.

The shooter and the vehicle

For a turret shot the instigator is the gunner, not the tank. Marking only the gunner leaves the vehicle invulnerable, and the gap has only moved. Both are marked. The vehicle is found through the compartment the character occupies.

Example code – free to use
protected static IEntity GetOccupiedVehicle(IEntity entity)
{
	ChimeraCharacter character = ChimeraCharacter.Cast(entity);
	if (!character)
		return null;

	CompartmentAccessComponent access = character.GetCompartmentAccessComponent();
	if (!access)
		return null;

	BaseCompartmentSlot slot = access.GetCompartment();
	if (!slot)
		return null;

	return slot.GetVehicle();		// null on foot
}

Let go of it again

"Until the zone is left" needs somebody who notices that it was. Cleaning up only when damage arrives is too late: a shooter who walks out and returns without any hit in between would still be marked. The alarm scan sweeps the list on each run, and removes everyone who is no longer in any zone. The same pass drops entities that have been destroyed or deleted. Only one zone does the sweeping, so the list is not swept once per zone.

Example code – free to use
static void ReleaseThoseOutsideZones()
{
	for (int i = s_aForfeited.Count() - 1; i >= 0; i--)		// backwards: removing shifts the rest
	{
		IEntity entity = s_aForfeited[i];

		if (!entity || !MyAD_Zone.FindProtectingZoneOf(entity))
			s_aForfeited.Remove(i);
	}
}

What the rule does and does not do

SituationResult
Hit from the quiet zoneShooter and vehicle lose their protection until they leave the zone.
Shot that missesNo penalty. The state comes from the damage event, and without a hit there is none. For the abuse case – killing somebody from inside – that is exactly enough.
Fire during an alarmNo penalty. Firing is released on purpose, and defenders are meant to answer without losing their protection.
Shot from outside the zoneNot touched at all.

The precedent is vanilla: spawn protection ends the same way, as soon as the protected player attacks. It listens to the ammunition count changing, and to the melee event. The zone listens to the damage event, which also covers a turret.

NoteThe player is told once per forfeit, not on every hit, and for longer than any other message. It announces something serious – anyone who misses it still believes that nothing can hurt them. The server decides, but the player may sit on a remote client, so the message goes out as a broadcast Rpc that carries the player id, and only that player's machine shows it.
PitfallIf the zone also hands part of the damage back to the shooter (an optional setting, off by default), the returned damage runs through the same hook, which would notice it as a new attack and hand back again. A flag that makes the return invisible for the duration of its own application is not optional: without it the server loops until it hangs. Also keep to different calculations for a vehicle and a person: a tank hull has around sixteen thousand health, a character a hundred. The same amount for both would kill the gunner a hundred and sixty times sooner than the tank.
Example code on this page is free to use in your own projects (credit appreciated). The GTG Safe Zone mod is published under the Arma Public License (APL). Suggestions: Discord. Arma Reforger / Enfusion are trademarks of Bohemia Interactive.
Impressum · Privacy Policy