Own combat and loot through daemon facts

This commit is contained in:
Meta Vens 2026-07-17 13:56:05 +01:00
commit 8e8c15a135
4 changed files with 44 additions and 13 deletions

View file

@ -3570,6 +3570,10 @@ internal sealed class AetheriaDaemonYmirSmokeChecks
source.DirectionX = 1;
source.DirectionY = 0;
source.TargetEntityIndex = 2;
source.Contacts = [new AetheriaRuntimeEntityContactCommit
{
TargetEntityIndex = 2, InfoGathered = 1, Visible = true, Hostile = true
}];
source.WeaponGroups = [new[] { 0 }];
source.Equipment =
[
@ -8200,7 +8204,10 @@ internal sealed class AetheriaDaemonYmirSmokeChecks
var catalog = new AetheriaRuntimeCatalogSnapshot([hull, salvage, cargoBay], [], []);
var ship = Entity(0, 0, "player"); ship.HullItemKey = hull.ItemKey; ship.CargoContents = [Cargo()];
ship.CargoBays = [new AetheriaRuntimeLoadoutItemSlotCommit { Item = new AetheriaRuntimeLoadoutItemCommit { ItemKey = cargoBay.ItemKey, Quantity = 1 } }];
var zone = new AetheriaRuntimeZoneSnapshotCommit { ZoneIndex = 0, Entities = [ship], DroppedPickups = [new AetheriaRuntimeDroppedPickupCommit { PickupIndex = 7, PositionX = AetheriaRuntimeTractorMechanics.CollectionDistance + 0.001, Item = new AetheriaRuntimeLoadoutItemCommit { ItemKey = salvage.ItemKey, Quantity = 1 }, LifetimeSeconds = 30 }] };
var ally = Entity(1, AetheriaRuntimeTractorMechanics.CollectionDistance, "player"); ally.HullItemKey = hull.ItemKey;
ally.CargoContents = [Cargo()];
ally.CargoBays = [new AetheriaRuntimeLoadoutItemSlotCommit { Item = new AetheriaRuntimeLoadoutItemCommit { ItemKey = cargoBay.ItemKey, Quantity = 1 } }];
var zone = new AetheriaRuntimeZoneSnapshotCommit { ZoneIndex = 0, Entities = [ship, ally], DroppedPickups = [new AetheriaRuntimeDroppedPickupCommit { PickupIndex = 7, PositionX = AetheriaRuntimeTractorMechanics.CollectionDistance + 0.001, Item = new AetheriaRuntimeLoadoutItemCommit { ItemKey = salvage.ItemKey, Quantity = 1 }, LifetimeSeconds = 30 }] };
var run = new AetheriaRuntimeRunCheckpointCommit { CurrentZoneIndex = 0, CurrentEntityKey = "zone.0.entity.0", Zones = [zone] };
var forbiddenCommand = AetheriaRuntimeDaemonCommandDocument.Create(AetheriaRuntimeDaemonCommandKinds.PickUpLoot, "pilot", "pickup-smoke", 0, "zone.0.entity.0");
forbiddenCommand.CommandId = "pickup-command-forbidden";
@ -8219,6 +8226,8 @@ internal sealed class AetheriaDaemonYmirSmokeChecks
new AetheriaRuntimeDaemonTickOptions { WorldPhysics = new ScriptedWorldPhysics(), FrameId = 2, FixedDeltaSeconds = 0.1, SimulationTimeSeconds = 0.2, Catalog = catalog, BuildPublications = false });
RequireEqual(0, zone.DroppedPickups.Count, "pickup at the 2D collection boundary must be consumed");
RequireEqual(1, CargoQuantity(ship, salvage.ItemKey), "one pickup identity must commit cargo exactly once");
RequireEqual(0, CargoQuantity(ally, salvage.ItemKey),
"a closer allied simulation ship must not steal pickup ownership from the current controlled ship");
RequireEqual(1, run.GameEvents.Count(value => value.Kind == "pickup.collected" && value.PickupIndex == 7),
"one consumed pickup identity must emit one collection event");
var collectionEvent = run.GameEvents.Single(value => value.Kind == "pickup.collected" && value.PickupIndex == 7);

View file

@ -41,7 +41,6 @@ internal static class AetheriaDaemonZoneGenerator
var corporationKeys = (catalog.Corporations ?? Array.Empty<AetheriaRuntimeCorporation>())
.Select(value => value.CorporationKey)
.Where(value => !string.IsNullOrWhiteSpace(value))
.OrderBy(value => value, StringComparer.Ordinal)
.ToArray();
if (corporationKeys.Length == 0)
{
@ -250,7 +249,7 @@ internal static class AetheriaDaemonZoneGenerator
var entities = new[]
{
Entity(loadouts, availabilityFactions, "Anchor Station", "station", -50, -30, 0, 0, "player", 760, keys[6], [keys[1], keys[2], keys[3], keys[4], keys[6], keys[7], keys[11]]),
Entity(loadouts, availabilityFactions, "Anchor Station", "station", -50, -30, 0, 0, "player", 760, keys[6], [keys[1], keys[2], keys[3], keys[4], keys[6], keys[7], keys[11]], 2, 900),
Entity(loadouts, availabilityFactions, "Vanguard One", "ship", -40, -30, 0, 0, "player", 540, keys[6], [keys[0], keys[2], keys[4], keys[6], keys[7]]),
Entity(loadouts, availabilityFactions, "Wing Two", "ship", 145, 125, -5, 7, "player", 450, keys[6], [keys[0], keys[1], keys[6], keys[8]]),
Entity(loadouts, availabilityFactions, "Torch Three", "ship", -235, 210, 8, -4, "player", 500, keys[7], [keys[0], keys[1], keys[7]]),
@ -386,17 +385,21 @@ internal static class AetheriaDaemonZoneGenerator
string faction,
double visibility,
string target,
string[] contactKeys)
string[] contactKeys,
int securityLevel = 0,
double securityRadius = 0)
{
var availabilityFaction = availabilityFactions[faction];
var loadout = loadouts[availabilityFaction].Build(kind, availabilityFaction);
var entity = AetheriaDaemonGeneratedEntityFactory.Create(
name,
kind,
faction,
availabilityFaction,
loadout,
x,
z);
z,
securityLevel: securityLevel,
securityRadius: securityRadius);
entity.Direction = Vec2(vx == 0 && vy == 0 ? 0 : vx, vx == 0 && vy == 0 ? 1 : vy);
entity.LookDirection = Vec2(vx == 0 && vy == 0 ? 0 : vx, vx == 0 && vy == 0 ? 1 : vy);
entity.Velocity = Vec2(vx, vy);
@ -405,8 +408,7 @@ internal static class AetheriaDaemonZoneGenerator
entity.Contacts = contactKeys.Select(key => new AetheriaEntityContactSnapshot
{
TargetEntityKey = key,
InfoGathered = 1,
Hostile = string.Equals(faction, "player", StringComparison.OrdinalIgnoreCase) && key.Contains(".entity.6.", StringComparison.Ordinal)
InfoGathered = 1
}).ToArray();
if (string.Equals(faction, "raider", StringComparison.OrdinalIgnoreCase))
entity.StatGrids[0].Values[0] = 85;

View file

@ -2494,6 +2494,7 @@ public class DaemonRuntimeDocumentTests
var pilot = new AetheriaRuntimeEntitySnapshotCommit
{
EntityIndex = 1,
EntityId = "global:aetheria.run_state.local-terminus.zone.0.entity.1.v1",
Kind = "ship",
FactionKey = "player",
IsActive = true,
@ -2508,6 +2509,17 @@ public class DaemonRuntimeDocumentTests
},
CargoContents = new[] { new AetheriaRuntimeCargoBayLoadoutCommit() }
};
var alliedShip = new AetheriaRuntimeEntitySnapshotCommit
{
EntityIndex = 2,
EntityId = "global:aetheria.run_state.local-terminus.zone.0.entity.2.v1",
Kind = "ship",
FactionKey = "player",
IsActive = true,
PositionX = 9,
CargoBays = pilot.CargoBays,
CargoContents = new[] { new AetheriaRuntimeCargoBayLoadoutCommit() }
};
var pickup = new AetheriaRuntimeDroppedPickupCommit
{
PickupIndex = 6,
@ -2525,7 +2537,7 @@ public class DaemonRuntimeDocumentTests
new AetheriaRuntimeZoneSnapshotCommit
{
ZoneIndex = 0,
Entities = new[] { pilot },
Entities = new[] { pilot, alliedShip },
DroppedPickups = new[] { pickup }
}
}
@ -2540,6 +2552,8 @@ public class DaemonRuntimeDocumentTests
catalog);
Assert.IsTrue(pilot.CargoContents.SelectMany(bay => bay.Items).Any(slot => slot.Item.ItemKey == "raider-salvage"));
Assert.IsFalse(alliedShip.CargoContents.SelectMany(bay => bay.Items).Any(slot => slot.Item.ItemKey == "raider-salvage"),
"an allied simulation ship must not steal cargo intended for the current controlled ship");
Assert.AreEqual(0, run.Zones[0].DroppedPickups.Count,
"daemon XZ proximity must own collection without tractor power or a Ymir contact fact");
}

View file

@ -438,13 +438,18 @@ namespace GameCult.Aetheria.State.Verse
AetheriaRuntimeCatalogSnapshot? catalog,
long frameId)
{
if (!AetheriaRuntimeRunCheckpointCommit.TryParseEntityKey(
run.CurrentEntityKey, out var currentZoneIndex, out var currentEntityIndex) ||
currentZoneIndex != zone.ZoneIndex)
return;
var attached = (entities ?? Array.Empty<AetheriaRuntimeEntitySnapshotCommit>())
.Where(value => value != null)
.SelectMany(value => value.ChildEntityIndices ?? Array.Empty<int>())
.ToHashSet();
var ships = (entities ?? Array.Empty<AetheriaRuntimeEntitySnapshotCommit>())
.Where(value => value != null && value.IsActive && value.WormholeTransition == null &&
!attached.Contains(value.EntityIndex) && IsPickupCollector(value, catalog))
!attached.Contains(value.EntityIndex) && IsPickupCollector(value, catalog) &&
value.EntityIndex == currentEntityIndex)
.Select(value => value!)
.OrderBy(value => value.EntityIndex)
.ToArray();
@ -2694,9 +2699,10 @@ namespace GameCult.Aetheria.State.Verse
AetheriaRuntimeEntitySnapshotCommit left,
AetheriaRuntimeEntitySnapshotCommit right)
{
return IsPlayerOwned(left) != IsPlayerOwned(right) &&
(string.Equals(left.FactionKey, "raider", StringComparison.OrdinalIgnoreCase) ||
string.Equals(right.FactionKey, "raider", StringComparison.OrdinalIgnoreCase));
return (left.Contacts ?? Array.Empty<AetheriaRuntimeEntityContactCommit>())
.Any(contact => contact != null &&
contact.TargetEntityIndex == right.EntityIndex &&
contact.Hostile);
}
private static double DefaultHull(