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.
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.
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.
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.
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
| Situation | Result |
|---|---|
| Hit from the quiet zone | Shooter and vehicle lose their protection until they leave the zone. |
| Shot that misses | No 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 alarm | No penalty. Firing is released on purpose, and defenders are meant to answer without losing their protection. |
| Shot from outside the zone | Not 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.