Publish weapon fire refusals

This commit is contained in:
Meta Vens 2026-07-12 00:19:03 +01:00
commit 52b3664adf
3 changed files with 39 additions and 1 deletions

View file

@ -292,6 +292,7 @@ namespace GameCult.Aetheria.State.Verse
var triggerResult = CommitWeaponRound(attacker, weapon);
if (triggerResult == WeaponRoundResult.ReloadStarted)
AppendWeaponEvent(run, zone, attacker, weapon, frameId, "weapon.reload.started");
PublishWeaponRoundResult(run, zone, attacker, weapon, frameId, triggerResult);
if (triggerResult != WeaponRoundResult.Fired) continue;
}
weapon.State.BurstRemaining = weapon.BurstCount;
@ -309,6 +310,7 @@ namespace GameCult.Aetheria.State.Verse
var roundResult = CommitWeaponRound(attacker, weapon);
if (roundResult == WeaponRoundResult.ReloadStarted)
AppendWeaponEvent(run, zone, attacker, weapon, frameId, "weapon.reload.started");
PublishWeaponRoundResult(run, zone, attacker, weapon, frameId, roundResult);
if (roundResult != WeaponRoundResult.Fired)
{
weapon.State.BurstRemaining = 0;
@ -379,6 +381,39 @@ namespace GameCult.Aetheria.State.Verse
});
}
private static void PublishWeaponRoundResult(
AetheriaRuntimeRunCheckpointCommit run,
AetheriaRuntimeZoneSnapshotCommit zone,
AetheriaRuntimeEntitySnapshotCommit entity,
ResolvedWeapon weapon,
long frameId,
WeaponRoundResult result)
{
var reason = result == WeaponRoundResult.InsufficientEnergy ? "insufficient-energy" :
result == WeaponRoundResult.NoAmmo ? "no-ammunition" : "";
if (string.IsNullOrEmpty(reason))
{
weapon.State.LastRefusalReason = "";
return;
}
if (string.Equals(weapon.State.LastRefusalReason, reason, StringComparison.Ordinal))
return;
weapon.State.LastRefusalReason = reason;
AetheriaRuntimeGameEvents.Append(run, new AetheriaRuntimeGameEventCommit
{
EventId = $"frame:{frameId}:zone:{zone.ZoneIndex}:entity:{entity.EntityIndex}:weapon:{weapon.State.OwnerIndex}:{weapon.State.BehaviorIndex}:refused:{reason}",
Kind = "weapon.fire.refused",
FrameId = frameId,
ZoneIndex = zone.ZoneIndex,
SourceEntityIndex = entity.EntityIndex,
TargetEntityIndex = entity.TargetEntityIndex,
SubjectKey = reason,
ItemKey = weapon.ItemKey,
ScalarValue = weapon.State.Ammo
});
}
private static void UpdateWeaponLock(
AetheriaRuntimeEntitySnapshotCommit attacker,
AetheriaRuntimeEntitySnapshotCommit target,

View file

@ -1113,6 +1113,9 @@ namespace GameCult.Aetheria.State.Verse
[Key(18)]
public int LockTargetEntityIndex { get; set; } = -1;
[Key(19)]
public string LastRefusalReason { get; set; } = "";
}
[MessagePackObject]

View file

@ -36,7 +36,7 @@ validation proves contract shape, not wiring, exclusivity, lowering, or parity.
| Minimap/tactical map | `ZoneRenderer`, map cameras | daemon world facts | explicit map projections | Unity and Electron | facts fragmented; generic lowerers absent | same state in both runtimes |
| Fields/fog/gravity | zone renderer and shaders | daemon/plugin semantics | Fields plugin documents | EveUnity fields lowerer | owner-exists; basic lowering; composition parity absent | visual parity captures |
| HUD/schematic | ARPG HUD and `SchematicDisplay` | Eve composition from daemon facts | semantic status/components | EveUnity UI | reference | source-fact and timing matrix |
| Hit/lock/warnings | `ActionGameManager` feedback | daemon event facts | transient feedback stream | VFX/UI/audio | bounded deduplicated game-event ledger and Eve `feedback.stream` wired for pickup outcomes, projectile launch/impact, entity damage, alive-to-dead destruction, and authoritative weapon reload start/completion chronology; reload interval is proven projectile-free | add lock transitions, resource refusals, thermal and docking event families with runtime effect consumption |
| Hit/lock/warnings | `ActionGameManager` feedback | daemon event facts | transient feedback stream | VFX/UI/audio | bounded deduplicated game-event ledger and Eve `feedback.stream` wired for pickup outcomes, projectile launch/impact, entity damage, alive-to-dead destruction, authoritative weapon reload chronology, and episode-deduplicated `weapon.fire.refused` facts for insufficient energy or missing ammunition; refusal identity, authored weapon, target, reason, and remaining magazine state are runtime-independent cockpit inputs | add lock transitions, thermal and docking event families, negative transaction fixtures, and runtime effect consumption |
| Assets | `Assets/Resources` | Aetheria provider | asset catalog + CultMesh CDN | runtime cache/load | wired; basic lowering; not live-proven remotely | cold/warm remote-only live load and cache proof |
| Save/resume | original persistence paths | daemon/CultCache | published current state | clients rehydrate | owner-exists; not parity-proven | mid-loop restart |
| Terminus loop | original game composition | daemon/Ymir/Eve | pilot surface | Aetheria.Unity | incomplete; no scenario proof | fly/fire/loot/dock/trade |