← 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

Blocking actions

While the zone is quiet, a defender who pulls the trigger inside it should get a shot that does not go off and a line that says why. Doing that is harder than it sounds, because most of the obvious switches are read too late.

In plain wordsOn the player's own machine, the shot, the throw and the swing are stopped. This is the part the player feels. The part that cannot be argued with is damage.

The condition

Actions are blocked when a character stands in a zone and the alarm is off. That is a different question from the damage guard: it asks only about place and alarm. While nothing is going on, nobody shoots in the zone, an intruder included; the moment an intruder is spotted, the lock drops for everyone.

Example code – free to use
// Client and server both ask this; it uses only the replicated outline and the replicated alarm flag.
static bool AreActionsBlockedFor(IEntity entity)
{
	MyAD_Zone zone = FindProtectingZoneOf(entity);
	return zone != null && !zone.m_bAlarmActive;
}

Three locks on different levels

None of them is enough on its own, and they work on different things.

LockWhat it stopsWhy that level
SetWeaponNoFireTime()The shotA state with a duration, not a flag. It does not matter where in the frame the code runs.
SetFireWeaponWanted(false)The fire request of AI and mountsCleared every frame. For a player the request has been read by the time this runs.
Input contextThrow and meleeBoth can be cancelled while they happen. A shot that has gone off cannot.

Here is the hook on the character controller, cut down to the three:

Example code – free to useMyAD_ActionGuard.c
modded class SCR_CharacterControllerComponent
{
	protected bool m_bMyAD_Blocked;
	protected static const float NO_FIRE_SECONDS = 0.5;		// const inside a class has to be static

	override void OnPrepareControls(IEntity owner, ActionManager am, float dt, bool player)
	{
		super.OnPrepareControls(owner, am, dt, player);

		m_bMyAD_Blocked = MyAD_Zone.AreActionsBlockedFor(owner);
		if (!m_bMyAD_Blocked)
			return;

		SetFireWeaponWanted(false);

		// Refreshed every frame, a little longer than a frame, so one hitch does not open a gap.
		if (!player || MyAD_IsWeaponInPlay())
			SetWeaponNoFireTime(NO_FIRE_SECONDS);

		CharacterInputContext input = GetInputContext();
		if (!input)
			return;

		input.SetWeaponPullTrigger(false);
		input.SetMeleeAttack(false);

		if (input.GetThrow())
		{
			input.SetThrow(false);
			input.CancelThrow();
		}
	}

	override bool GetCanMeleeAttack()
	{
		if (m_bMyAD_Blocked)
			return false;

		return super.GetCanMeleeAttack();
	}
}

Do not stop the player from drawing

The time lock has a price. Vanilla uses SetWeaponNoFireTime() in one place, after a gadget is put away, and in that state the animation also holds the weapon down. Refreshed every frame for everyone in the zone, it meant that nobody could equip a weapon at all – players on a keyboard hardly noticed, because the weapon tends to be up already when they enter, but console players did.

The cure is to arm the lock for a player only once the weapon is really out. A weapon that is not in the hand cannot be in any of the states below, so drawing stays free and the lock engages afterwards. It is not enough to ask whether the weapon is raised: from low ready, which is a partly lowered weapon, that question answers no, and a pull of the trigger raises and fires in the same moment.

Example code – free to use
// True as soon as the weapon is out, in whatever stance.
protected bool MyAD_IsWeaponInPlay()
{
	return IsWeaponRaised()
		|| IsPartiallyLowered()		// low ready
		|| IsWeaponADS()
		|| GetIsWeaponDeployed()
		|| GetIsWeaponDeployedBipod();
}

The visible consequence is that inside a zone a weapon cannot be kept raised; it sinks back down. That is a side effect to accept, not a fault to fix.

This is feedback

All of the above runs on the client, where the input is. Treat it accordingly: it makes the zone feel like a zone, and it tells the player why nothing happens. The messages ("Safe Zone Active!") are local lines, limited to one every two seconds – without that, holding the trigger would write sixty lines a second. What you can rely on is the damage check on the server, described on Invulnerability.

PitfallMessages that belong to events the client already knows about must not travel over the network. The geometry is on every machine and the lock is evaluated there; a server message for every blocked shot would be traffic for something the client could already say itself.
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