RIM-116 & Phalanx
Battery coordination
Several mounts form a network. They alternate fire and cover different targets; if one misses, the next follows up immediately without waiting for its own cycle.
Three separate levels
These get conflated easily, so they are three mechanisms with three jobs.
| Level | Question it answers |
|---|---|
| Target selection | Who takes what, so two mounts do not shoot the same thing |
| Turn order | After a salvo budget, hand over to the next – and let the round robin come back |
| Per-target cap | The emergency brake: no mount empties its magazine into one target |
A static registry, because the hierarchy has nothing
Separately placed world entities have no runtime relationship – GetChildren() on the apparent
neighbour returns null. Every mount registers itself on initialisation, and the registration order is the
alternating fire order.
class MyAD_Network
{
protected static ref array<MyAD_Mount> s_aMounts = {};
static void Register(MyAD_Mount m) { if (s_aMounts.Find(m) < 0) s_aMounts.Insert(m); }
static void Unregister(MyAD_Mount m) { s_aMounts.RemoveItem(m); }
}
Rounds, not a hard lock
The brief: at most two missiles per target, then a seamless handover to the next mount; if that one misses too, on to the next, or back to the first if the second is busy elsewhere. Implement it as rounds, not as a lock. Each mount spends its salvo budget and is one round further. Whoever stands in a lower round goes first; when everyone is level, the first may go again. The round robin falls out by itself, with no pointer that dangles when a mount drops out.
| Event | Mount 1 | Mount 2 | Mount 3 |
|---|---|---|---|
| Start | round 0 | round 0 | round 0 |
| Mount 1 fires its salvo | round 1 | round 0 | round 0 |
| Mount 2 fires its salvo | round 1 | round 1 | round 0 |
| Mount 3 fires its salvo | round 1 | round 1 | round 1 – mount 1 may go again |
bool MyAD_Network_IsInTurn(MyAD_Mount me, IEntity target)
{
int budget = Math.Max(me.SalvoBudget(), 1);
int mine = ShotsBy(me, target);
if (mine < budget)
return true; // my own round is not full yet
int myRound = mine / budget;
foreach (MyAD_Mount other : s_aMounts)
{
if (other == me || !other.CanFire()) // a mount that cannot fire does not hold the queue up
continue;
int hisRound = ShotsBy(other, target) / Math.Max(other.SalvoBudget(), 1);
if (hisRound < myRound)
return QuietFor(target) > TURN_WAIT * 2; // fallback: if it never steps up, let the target through
}
return true;
}
The fallback matters: the queue only holds while it is running. If the mount that should go next stays quiet, waiting would let the target through while two mounts defer to each other.
Count per mount, not only network-wide
A network-wide counter alone makes the handover impossible: a cap of two missiles per target would lock the target out for the whole battery after the second shot instead of passing it to the next mount. Count per mount; the network total only feeds the emergency brake.
When the miss report never arrives
Normally the battery hands over on a call: mount 1 misses, its missile reports it, mount 2 follows up. Some paths never send that report – the missile loses the target, it is shot down, or the target briefly drops out of detection. Then both mounts stand still. Treat a mount that has used its salvo budget and sent nothing since the wait window as finished, so the next may take over.
Zones give priority
Give each mount an outer zone radius. The weapon with the tightest zone covering a target has priority – a point-defence gun outranks a long-range launcher for something close in. But a mount yields nothing while it cannot actually fire, so a damaged or empty mount does not block its own zone.