Restore progressive weapon lock
This commit is contained in:
parent
4601c584c5
commit
664e4c2de4
4 changed files with 79 additions and 9 deletions
|
|
@ -509,6 +509,8 @@ internal sealed class AetheriaDaemonYmirSmokeChecks
|
|||
};
|
||||
var sawTargetCommand = false;
|
||||
var sawFireCommand = false;
|
||||
var sawPartialLock = false;
|
||||
var firstLaunchFrame = -1;
|
||||
for (var frame = 0; frame < 60; frame++)
|
||||
{
|
||||
var tick = AetheriaRuntimeDaemonTickRunner.Tick(
|
||||
|
|
@ -526,12 +528,18 @@ internal sealed class AetheriaDaemonYmirSmokeChecks
|
|||
});
|
||||
sawTargetCommand |= tick.OperationResult.AppliedCommandIds.Any(id => id.EndsWith(":target", StringComparison.Ordinal));
|
||||
sawFireCommand |= tick.OperationResult.AppliedCommandIds.Any(id => id.EndsWith(":fire", StringComparison.Ordinal));
|
||||
var weapon = (agent.WeaponStates ?? Array.Empty<AetheriaRuntimeWeaponStateCommit>()).Single();
|
||||
sawPartialLock |= weapon.LockProgress > 0 && weapon.LockProgress < 0.99;
|
||||
if (firstLaunchFrame < 0 && run.GameEvents.Any(value => value.Kind == "projectile.launched"))
|
||||
firstLaunchFrame = frame;
|
||||
if (string.Equals(run.AgentTasks.Single().Status, AetheriaRuntimeAgentTaskStatuses.Completed, StringComparison.Ordinal))
|
||||
break;
|
||||
}
|
||||
|
||||
Require(sawTargetCommand, "attack agent must target through the shared target command");
|
||||
Require(sawFireCommand, "attack agent must fire through the shared weapon-group command");
|
||||
Require(sawPartialLock, "attack agent must acquire a persisted partial weapon lock before firing");
|
||||
Require(firstLaunchFrame > 1, "accepted fire intent must not bypass progressive weapon lock acquisition");
|
||||
Require(run.GameEvents.Any(value => value.Kind == "projectile.launched" && value.SourceEntityIndex == agent.EntityIndex),
|
||||
"accepted fire control must emit authoritative projectile launch chronology");
|
||||
Require(!target.IsActive,
|
||||
|
|
|
|||
|
|
@ -249,16 +249,16 @@ namespace GameCult.Aetheria.State.Verse
|
|||
!IsAlive(target) ||
|
||||
!Hostile(attacker, target))
|
||||
{
|
||||
weaponState.LockTargetEntityIndex = -1;
|
||||
weaponState.LockProgress = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (DistanceSq(attacker, target) > settings.AttackRange * settings.AttackRange)
|
||||
UpdateWeaponLock(attacker, target, weaponState, deltaSeconds, settings);
|
||||
if (DistanceSq(attacker, target) > settings.AttackRange * settings.AttackRange ||
|
||||
weaponState.LockProgress <= 0.99)
|
||||
continue;
|
||||
|
||||
Face(attacker, target.PositionX - attacker.PositionX, target.PositionZ - attacker.PositionZ);
|
||||
weaponState.LockTargetEntityIndex = target.EntityIndex;
|
||||
weaponState.LockProgress = 1.0;
|
||||
|
||||
if (weaponState.CooldownProgress > 0)
|
||||
continue;
|
||||
|
||||
|
|
@ -308,6 +308,43 @@ namespace GameCult.Aetheria.State.Verse
|
|||
}
|
||||
}
|
||||
|
||||
private static void UpdateWeaponLock(
|
||||
AetheriaRuntimeEntitySnapshotCommit attacker,
|
||||
AetheriaRuntimeEntitySnapshotCommit target,
|
||||
AetheriaRuntimeWeaponStateCommit weaponState,
|
||||
double deltaSeconds,
|
||||
AetheriaRuntimeDaemonSimulationSettings settings)
|
||||
{
|
||||
if (weaponState.LockTargetEntityIndex != target.EntityIndex)
|
||||
{
|
||||
weaponState.LockTargetEntityIndex = target.EntityIndex;
|
||||
weaponState.LockProgress = 0;
|
||||
}
|
||||
|
||||
var targetDirection = Normalize(target.PositionX - attacker.PositionX, target.PositionZ - attacker.PositionZ);
|
||||
var lookDirection = Normalize(attacker.DirectionX, attacker.DirectionY);
|
||||
var dot = Math.Max(-1.0, Math.Min(1.0,
|
||||
targetDirection.X * lookDirection.X + targetDirection.Y * lookDirection.Y));
|
||||
var angleDegrees = Math.Acos(dot) * 180.0 / Math.PI;
|
||||
if (angleDegrees >= settings.WeaponLockAngleDegrees)
|
||||
{
|
||||
weaponState.LockProgress = Clamp01(
|
||||
weaponState.LockProgress - deltaSeconds * settings.WeaponLockDecayPerSecond);
|
||||
return;
|
||||
}
|
||||
|
||||
var contact = (attacker.Contacts ?? Array.Empty<AetheriaRuntimeEntityContactCommit>())
|
||||
.FirstOrDefault(candidate => candidate != null && candidate.TargetEntityIndex == target.EntityIndex);
|
||||
var information = Clamp01(contact?.InfoGathered ?? 0);
|
||||
var directionalQuality = Math.Max(0, 1.0 - angleDegrees / 90.0);
|
||||
var acquisition =
|
||||
Math.Pow(directionalQuality, settings.WeaponLockDirectionImpact) *
|
||||
deltaSeconds *
|
||||
settings.WeaponLockSpeed *
|
||||
Math.Pow(information, settings.WeaponLockSensorImpact);
|
||||
weaponState.LockProgress = Clamp01(weaponState.LockProgress + acquisition);
|
||||
}
|
||||
|
||||
private static void PrepareProjectiles(
|
||||
AetheriaRuntimeZoneSnapshotCommit zone,
|
||||
IReadOnlyDictionary<int, AetheriaRuntimeEntitySnapshotCommit> entities,
|
||||
|
|
|
|||
|
|
@ -29,7 +29,12 @@ namespace GameCult.Aetheria.State.Verse
|
|||
playerEntityHull: 120.0,
|
||||
raiderEntityHull: 80.0,
|
||||
stationShield: 120.0,
|
||||
entityShield: 45.0);
|
||||
entityShield: 45.0,
|
||||
weaponLockSpeed: 2.0,
|
||||
weaponLockSensorImpact: 1.0,
|
||||
weaponLockAngleDegrees: 45.0,
|
||||
weaponLockDirectionImpact: 1.0,
|
||||
weaponLockDecayPerSecond: 1.0);
|
||||
|
||||
public AetheriaRuntimeDaemonSimulationSettings(
|
||||
double pawnSpeed,
|
||||
|
|
@ -52,7 +57,12 @@ namespace GameCult.Aetheria.State.Verse
|
|||
double playerEntityHull,
|
||||
double raiderEntityHull,
|
||||
double stationShield,
|
||||
double entityShield)
|
||||
double entityShield,
|
||||
double weaponLockSpeed = AetheriaDefaultRaw.WeaponLockSpeed,
|
||||
double weaponLockSensorImpact = AetheriaDefaultRaw.WeaponLockSensorImpact,
|
||||
double weaponLockAngleDegrees = AetheriaDefaultRaw.WeaponLockAngleDegrees,
|
||||
double weaponLockDirectionImpact = AetheriaDefaultRaw.WeaponLockDirectionImpact,
|
||||
double weaponLockDecayPerSecond = AetheriaDefaultRaw.WeaponLockDecayPerSecond)
|
||||
{
|
||||
PawnSpeed = PositiveOr(pawnSpeed, AetheriaDefaultRaw.PawnSpeed);
|
||||
RaiderSpeed = PositiveOr(raiderSpeed, AetheriaDefaultRaw.RaiderSpeed);
|
||||
|
|
@ -75,6 +85,11 @@ namespace GameCult.Aetheria.State.Verse
|
|||
RaiderEntityHull = PositiveOr(raiderEntityHull, AetheriaDefaultRaw.RaiderEntityHull);
|
||||
StationShield = NonNegativeOr(stationShield, AetheriaDefaultRaw.StationShield);
|
||||
EntityShield = NonNegativeOr(entityShield, AetheriaDefaultRaw.EntityShield);
|
||||
WeaponLockSpeed = PositiveOr(weaponLockSpeed, AetheriaDefaultRaw.WeaponLockSpeed);
|
||||
WeaponLockSensorImpact = NonNegativeOr(weaponLockSensorImpact, AetheriaDefaultRaw.WeaponLockSensorImpact);
|
||||
WeaponLockAngleDegrees = PositiveOr(weaponLockAngleDegrees, AetheriaDefaultRaw.WeaponLockAngleDegrees);
|
||||
WeaponLockDirectionImpact = NonNegativeOr(weaponLockDirectionImpact, AetheriaDefaultRaw.WeaponLockDirectionImpact);
|
||||
WeaponLockDecayPerSecond = NonNegativeOr(weaponLockDecayPerSecond, AetheriaDefaultRaw.WeaponLockDecayPerSecond);
|
||||
}
|
||||
|
||||
public double PawnSpeed { get; }
|
||||
|
|
@ -98,6 +113,11 @@ namespace GameCult.Aetheria.State.Verse
|
|||
public double RaiderEntityHull { get; }
|
||||
public double StationShield { get; }
|
||||
public double EntityShield { get; }
|
||||
public double WeaponLockSpeed { get; }
|
||||
public double WeaponLockSensorImpact { get; }
|
||||
public double WeaponLockAngleDegrees { get; }
|
||||
public double WeaponLockDirectionImpact { get; }
|
||||
public double WeaponLockDecayPerSecond { get; }
|
||||
|
||||
private static double PositiveOr(double value, double fallback)
|
||||
{
|
||||
|
|
@ -137,6 +157,11 @@ namespace GameCult.Aetheria.State.Verse
|
|||
public const double RaiderEntityHull = 80.0;
|
||||
public const double StationShield = 120.0;
|
||||
public const double EntityShield = 45.0;
|
||||
public const double WeaponLockSpeed = 2.0;
|
||||
public const double WeaponLockSensorImpact = 1.0;
|
||||
public const double WeaponLockAngleDegrees = 45.0;
|
||||
public const double WeaponLockDirectionImpact = 1.0;
|
||||
public const double WeaponLockDecayPerSecond = 1.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,11 +19,11 @@ validation proves contract shape, not wiring, exclusivity, lowering, or parity.
|
|||
| Entity identity/loadout | `ServerShared/Entity*` | daemon | typed entity and inventory surfaces | EveUnity/Electron | owner-exists; wired; projected; not proven | loadout round trip and reconnect |
|
||||
| Flight intent | `Ship`, `Thruster`, `AetherDrive`, input paths | daemon intent + Ymir physics | analog movement/look/tractor | EveUnity input driver | movement intents feed Ymir-owned body integration; authored thrust allocation, torque, energy and heat remain simplified | prove original drive/equipment behavior ordering and runtime interpolation |
|
||||
| Collision | Unity physics and entity handlers | Ymir | collision/result facts | visual interpolation only | Ymir.Core world stepping wired for entity bodies, radial fields and contacts; missing-owner negative proof passes | publish authoritative collision events and prove Unity cannot write transforms back |
|
||||
| Targeting/contacts | sensors + `ActionGameManager` | daemon | contact/target semantics | target HUD and indicators | simplified owner wired/projected; not parity | visibility/nearest/explicit/lock cases |
|
||||
| Targeting/contacts | sensors + `ActionGameManager` | daemon | contact/target semantics | target HUD and indicators | simplified owner wired/projected; daemon weapon lock now acquires and decays from facing, elapsed time, and observer-local contact confidence instead of granting an immediate perfect lock | target-switch reset, angular decay, visibility/nearest/explicit cases, and runtime lock feedback |
|
||||
| Projectile flight | weapon/projectile classes | Ymir | projectile entity/event projection | native projectile visual | Ymir.Core adapter wired for spawn/travel/contact/despawn; native presentation and broader weapon parity incomplete | prove every authored projectile family and runtime visual lifecycle |
|
||||
| Shield/hull damage | energy-funded shield, armor cells, equipment and hull | daemon | entity status + damage events | meters, impacts, damage VFX | synthetic scalar owner wired; not parity | typed absorption, penetration, item damage and death causes |
|
||||
| Thermal/energy | cell conduction and capacitor/reactor/radiator network | daemon | ship/equipment status and warnings | schematic/post effects | cell conduction/radiation wired; energy network and medical thresholds remain reference | capacitor/reactor/radiator ordering, shared energy and medical timelines |
|
||||
| Weapons | ServerShared behaviors + Unity effects | daemon + Ymir where spatial | action capability, weapon state, attack events | effects/audio/HUD | one synthetic weapon wired; original engine reference-only | each weapon family |
|
||||
| Weapons | ServerShared behaviors + Unity effects | daemon + Ymir where spatial | action capability, weapon state, attack events | effects/audio/HUD | one synthetic projectile weapon wired through progressive fossil-derived lock, cooldown, heat, Ymir flight/contact, and daemon damage; original behavior families remain reference-only | catalog-authored weapon stats and each weapon family |
|
||||
| Destruction/drops | entity death and pickup paths | daemon | death/drop events and world entities | effects and pickups | fragments wired; chronology and original drop transaction absent | exactly-once drops and cleanup |
|
||||
| Tractor/scoop | analog tractor and physical timed pickups | daemon + Ymir | tractor power, pickup body and receipt | beam/interaction feedback | fossil 2/s power ramp and authored forward radius/traction/distance feed pickup bodies into the shared Ymir world; shield contact collects exactly once when capacity permits or applies the fossil 25-unit rejection kick; 30s daemon lifetime; instant cargo teleport deleted | prove generic runtime beam, contact, success and rejection feedback |
|
||||
| Dock/undock | wormhole-first interact; first eligible bay; component prerequisites | daemon + Ymir placement | dock commands/context | camera/UI transition | daemon path wired/projected; Ymir placement and parity unproven | priority, eligibility, placement and reconnect |
|
||||
|
|
|
|||
Loading…
Reference in a new issue