Commit graph Aetheria/Assets/Scripts/ServerShared/Entity.cs
Author SHA1 Message Date
Meta Vens
da5a710030 F1: power supply multiplies the resolved stat, it does not interpolate
PerformanceStat.Evaluate folded PowerSupply into the same Min/Max lerp factor
as Heat/Durability/Quality, so zero supply bottomed out at Min instead of 0
(measured: a radiator at zero power still pumped 19-25% of full; three of Cut
7's eight authored terms had Min == Max and were completely inert, since
lerp(x, x, anything) == x).

PowerSupply terms are now accumulated into their own multiplier and applied to
the fully-interpolated, fully-modified value instead of to the interpolation
factor. The term still lives in PerformanceStat.Terms -- it is still authored
per stat and still censused/validated the same way -- it is just no longer
blended into `factor`. Corrected the comments that stated the old premise
(Radiator.cs, Entity.cs, ItemData.cs's IStatContext.PowerSupplyFactor doc).

Pinned in tests/Aetheria.Shared.Tests/PowerCurveTests.cs:
APowerSupplyTermedStatWithNonzeroMinResolvesToExactlyZeroAtZeroSupply and
AMinEqualsMaxStatStillRespondsToItsPowerSupplyTerm -- both fail under the
reverted (lerp-based) mutation.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-19 05:03:31 +02:00
Meta Vens
4cdfa22fb0 Cut 8: EquippedItem.ConditionRatio -- how healthy a behaviour actually is
Operator ask (2026-09-19): thruster particles only reflected thrust intent,
so a cooked or starved thruster looked identical to a healthy one.

EquippedItem.ConditionRatio (Entity.cs) resolves a stat's actual value
against what the same item, same lot, same modifiers would produce with
Heat/Durability/PowerSupply pinned to their identity (NominalContext) --
quality and lot are not part of "perfect", so a cheap item still reads 1
at full health. The nominal side bypasses the resolver's cache (which is
keyed only by (owner, stat) and would otherwise collide with the real
cached value) with one direct Evaluate call, a handful of flops.

Thruster.Condition and AetherDrive.Condition expose that ratio against
each behaviour's own governing stat (Thrust, Torque) for presentation to
read next.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-19 04:41:13 +02:00
Meta Vens
1c1249e700 Cut 6: wire StatSource.PowerSupply and refuse a power request that reads it
docs/stats-and-power-cut.md Cut 6, purely additive per its own map: the enum member, the
resolver's generation-cache and the bus's PowerSupply write already existed (Cuts 2/3/5)
as an identity everywhere. This lands the two things Cut 6 actually adds.

The curve: EquippedItem.PowerSupplyFactor now returns pow(PowerSupply, exponent) instead
of the Cut-3 identity stub, so a stat that declares a PowerSupply term degrades as this
tick's grant falls and reads unchanged at full supply (pow(1, n) == 1 regardless of n).
PowerBus.AllocateTiers now invalidates StatSource.PowerSupply once per draw per tick -- the
missing half of the wiring named in the cut map -- so the resolver actually recomputes when
the grant moves, using the same per-source generation scheme Heat/Durability already use
(unconditional, at most once per tick, never touching a stat that did not declare the
source).

The request-independence rule: a stat that decides a power request may not depend on power
supply, directly or through a modifier chain. Direct dependency is checked statically
(StatValidation.ValidateNoPowerSupplyOnRequest, ItemData.cs) against a fixed registry of
(BehaviorData type, field) pairs -- the one PerformanceStat each IPowerConsumer's own
PowerRequest evaluates as its request, a compile-time fact rather than something authored
per instance -- and runs at catalog load (AetheriaStores.Open), at Upsert, and again at
equip (EquippedItem's constructor) as defense against the two named bypasses that write a
catalog document without going through Upsert. The modifier-chain half is dynamic
(StatModifier.ValidateNoPowerSupplyChain): a modifier's own magnitude stat is walked
through the same magnitude->target edges ValidateNoCycle already builds for the self-cycle
check, and refused at Initialize (equip time) if it is power-tainted and targets a known
request stat, naming both items.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-19 03:47:07 +02:00
Meta Vens
55249beb98 Cut 5: priority tiers replace PowerBus's single shared GrantRatio
PowerTiers.cs names five tiers (Critical/High/Medium/Low/Utility) and their
per-behaviour-kind defaults: Radiator is Critical (the closest thing this game
has to life support -- a starved radiator cascades into wear and shutdown
across the whole hull), Shield is High, Thruster/AetherDrive are Medium,
ConstantWeapon/InstantWeapon are Low, and Sensor/EnergyDraw are Utility.
IPowerConsumer.DefaultPowerTier lets each behaviour own its own default.

EquippableItem.PowerTier (Key 12) is the stored, player-choosable tier on the
unit rather than the design; EquippedItem's constructor seeds a kind default
the first time a unit is equipped, only when the field is still
PowerTiers.Unassigned, and never again -- a tier is a stored choice, not
something the bus re-derives.

PowerBus.AllocateTiers walks tiers lowest-number-first: a tier is granted in
full before the next tier sees anything left, an empty tier passes its whole
share down, and within a tier every consumer rations by the same
grant/demand fraction so unequal requests still divide proportionally. A new
TierGrantRatio[] exposes each tier's own ratio for a future UI (Cut 8) to
read starvation from directly, instead of reconstructing it from every item's
PowerSupply.

Deletes EquippedItem.SortPosition, IOrderedBehavior and Reactor.Order (its
only implementer): the bus already steps before any equipment's Execute, so
equip/behaviour order never decided who got fed even before this cut, and
nothing else needed the interface (subject to the map's O3 operator check).

docs/stats-and-power-cut.md Cut 5 / §1.3.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-19 02:58:38 +02:00
Meta Vens
8dacabb3d8 Cut 4: input capacitors close the last direct capacitor spend
Deletes Entity.CanSpendCapacitorCharge/TrySpendCapacitorCharge (and the
now-dead _capacitors bookkeeping list that only fed them) -- Cut 3's
named, temporary exception for the four instant draws. InstantWeapon
(both fire sites), Sensor.Ping, and Shield.CanTakeHit/TakeHit now spend
from their own InputCapacitor, filled continuously by PowerBus like
every other IPowerConsumer instead of touching the entity's shared bus
capacitors directly.

InputCapacitor's Capacity/Rate derive from the owning item's Energy and
Cooldown, with an authored-override hook (ChargedWeapon repurposes its
previously-unused ChargeEnergy stat as that override, per the map's own
instruction, rather than adding a new field). A weapon/sensor fire
attempt only ever spends exactly Capacity, so it only ever succeeds at
full charge -- no fractional shot. Shield is the map's named exception:
its reserve is continuous, not an activation buffer, so it allows a
partial draw; its size (no Energy/Cooldown pair exists on Shield) is a
flagged judgment call, not a spec'd formula.

Adds InputCapacitorTests.cs (8 tests) and
mutation_tests_stats_power_cut4.py (7 mutations, all behave as
declared); removes the one PowerBusTests.cs test and Cut 3 mutation
case that exercised the now-deleted Entity methods.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-18 22:52:15 +02:00
Meta Vens
f1fd64ac60 Cut 3: PowerBus replaces TryConsumeEnergy/CanConsumeEnergy
Deletes Entity.CanConsumeEnergy/TryConsumeEnergy and Reactor's own capacitor
draining/filling (the demand-mechanism halves of Reactor.cs:90-141); adds
PowerBus (one per Entity, stepped once per tick before equipment executes),
IPowerConsumer on the five continuous draw sites (EnergyDraw, Thruster,
AetherDrive, Radiator, ConstantWeapon), and EquippedItem.PowerSupply as the
bus's grant ratio. Reactor keeps only its heat/throttle arithmetic, now driven
by PowerBus.NetDraw instead of an accumulator any behaviour could add into.

The four instant draws (InstantWeapon's burst/shot, Sensor's ping, Shield's hit
taken) are out of scope for the bus this cut per the map; they spend capacitor
charge directly and atomically via the new Entity.TrySpendCapacitorCharge/
CanSpendCapacitorCharge, replacing TryConsumeEnergy's old partial-drain-then-
refuse bug for that path too. Named, temporary exception closed by Cut 4.

docs/stats-and-power-cut.md Cut 3.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-18 21:39:38 +02:00
Meta Vens
b0d3354357 Gate 1: give the resolver a Forget(owner); correct the recompute-cost comment
StatResolver's _cache, _modifiers and _generations were keyed by owner and
nothing ever removed an entry, contradicting Sec 0b's promise that a resolver
entry is "destroyed at unequip / entity teardown." A ConsumableItemEffect that
expired still had a generations entry, and an unequipped EquippedItem's entries
lived on for the rest of the process -- the same shape of leak the catalog
object had before Cut 2, one level down.

Forget(owner) drops every entry for that owner across all three dictionaries;
Entity.TryUnequip and the consumable-expiry branch in Entity.Update now call it.
Also adds GenerationOwnerCount/CacheEntryCount/ModifierEntryCount, a test-only
surface a mutation test pins the retained-set claim against.

F3: InvalidateSource's comment claimed a stat "recomputes only when a term's
source changes." Entity.cs's UpdatePerformance calls it unconditionally every
tick for Heat and Durability, regardless of whether the value moved, so the
true invariant is "at most once per tick per (item, stat)." Corrected, and
noted Quality as the one source this scheme still meaningfully defends
against a real no-op tick (see F6 in ItemData.cs).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-18 20:41:42 +02:00
Meta Vens
dbb8be39d9 Cut 2: give every entity its own StatResolver, delete the catalog leak
PerformanceStat kept two Dictionary<Entity, ...> fields for attached modifiers,
grown by one key per entity that ever evaluated the stat and never trimmed
(docs/stats-and-power-cut.md §0.3). StatResolver.cs is the new sole owner of
every resolved (item, stat) value and every attached modifier, one instance
per Entity and reachable only from it. EquippedItem and ConsumableItemEffect
now read and write through it instead of through the catalog object; the two
dictionaries and their four accessors are deleted outright, not hidden.

StatModifier no longer resolves its (type name, field name) reference by
runtime reflection search on every equip: StatValidation.ResolveStatField
does it once, memoized, and StatValidation.ValidateStatModifiers checks it at
catalog load and at Upsert, naming the record instead of failing with an
opaque NullReferenceException the first time something equips it. A modifier
whose own magnitude stat is (directly or transitively, through another
modifier on the same entity) one of the stats it writes is refused at equip:
resolving it would depend on a value computed from itself one tick late.

A resolved value now caches by the generation of the sources its declared
Terms actually read, invalidated per source by EquippedItem.UpdatePerformance
(Heat, Durability) and ConsumableItemEffect.Update (ConsumableProgress), so a
stat recomputes only when a term it declared moves.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-18 19:53:59 +02:00
Meta Vens
05b5a209db Cut 1: terms replace the four fixed exponents; R-heat
PerformanceStat's four fixed fields (HeatExponentMultiplier,
DurabilityExponentMultiplier, QualityExponent, FromRole) tombstone into a
declared List<StatTerm> (source + exponent + role), walked by the one
evaluation path Cut 0 unified. A stat with no terms resolves to Max (the
same identity a stat with all-zero exponents already produced), so the
migration is exact: a zero multiplier becomes no term, not a term with
exponent 0.

R-heat (operator, docs/stats-and-power-cut.md): EquippableItemData's
HeatPerformanceCurve (a BezierCurve, plus the 100-sample scan that inferred
OptimalTemperature and its cache) is retired in favor of an authored
minimum, maximum, optimum and plateau width, with linear falloff outside
the plateau. StatValidation refuses an optimum outside its bounds or a
negative plateau width, loudly, at catalog load. The plateau's consequence
rides along for free: Wear's thermal term is pow(ThermalPerformance, ...),
and Performance() now returns exactly 1 across the plateau, so a
well-managed item takes wear only from deltaTemp there -- the
operational-lifespan lever the operator asked for.

ConsumableItemEffect's progress-through-duration substitution for "heat"
becomes its own StatSource (ConsumableProgress) rather than an unconditional
override applied regardless of what a stat declares.

Tests: LoadoutTests' four PerformanceStat-shaped fixtures rebuilt against
Terms/authored heat fields; HeatResponseTests.cs (new) pins load
validation, the plateau shape, and the three wear-lever claims (held
inside takes no thermal wear, held outside does, a fast swing across still
wears) each under its own mutation in tests/mutation_tests_stats_power_cut1.py
(new, 10 rules including a no-op control; all pass).

64 pre-Cut-1 tests still pass unmodified; 71 total with the 7 new ones.
Unity batchmode: 0 error CS. ShieldPanelCut1Verify and Cut2Verify pass;
Cut3Verify fails with an unrelated pre-existing "Kernel 'KInit' not found"
compute-shader error, reproduced against Cut 0's own landing state with no
shader/compute file in this diff -- flagged for Soul, not fixed here.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-18 17:18:06 +02:00
Meta Vens
e9b688de51 Cut 0: collapse the three disagreeing stat evaluate bodies into one
ItemManager.Evaluate (unequipped), ConsumableItemEffect.Evaluate and
EquippedItem.Evaluate each hand-computed the same quality/durability/heat/
modifier arithmetic with small, undocumented disagreements (docs/stats-and-
power-cut.md Sec 0.2). Collapse the arithmetic into one function,
PerformanceStat.Evaluate(IStatContext), fed by three IStatContext
implementations: EquippedItem (the only one with heat, live durability and
modifiers), ConsumableItemEffect (progress substitutes for heat,
unconditionally, matching its prior behaviour exactly) and a new
UnequippedStatContext in ItemManager (no heat, no modifiers, by name).

No behaviour change beyond the two disagreements the cut map names as
defects. Each context's original NaN handling is preserved exactly
(unequipped throws with diagnostic detail; equipped/consumable fall back to
Min). Behavior.Evaluate(PerformanceStat) keeps its name and signature, so
the ~81 existing call sites are untouched.

Verified: dotnet test tests/Aetheria.Shared.Tests unchanged at 61/61 before
new tests are added; a real-catalog probe over GameData/Aetheria.cc found
the new unequipped path reproduces the old formula exactly across 3123
(item, quality, durability, stat) cases; Unity batchmode compiles with 0
error CS and the Cut 1/2/3 shield probes still pass; AetherDb census,
factions, dangling, station-fit, loadout 1 and hardpoint-fit are
byte-identical before and after.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-18 16:19:22 +02:00
Meta Vens
0c2da217f3 Cut 0: give Entity its own EntitySettings copy, delete the duplicate copies
Entity's constructor now takes a MessagePack round-trip copy of the settings
it is given, so no entity can alias GameplaySettings.DefaultEntitySettings.
That makes ActionGameManager.NewEntitySettings and EntitySerializer.Unpack's
null-coalescing copy redundant; both are deleted, and TradeMenu buys a ship
straight off the template (Entity's ctor copies it).

EntitiesDoNotShareDefaultEntitySettings pins the invariant: writing
ShutdownPerformance on one ship must not move the default for a sibling or
the template (settings-globals-cut.md, Cut 0).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 18:22:22 +02:00
Meta Vens
2b33550cc1 Add manual IFF overrides, event-driven grudges, and detection-gated stance
Entity.SetIff(other, hostile?) is the one owner of a runtime-only, per-entity
override (a ReactiveDictionary, not persisted in packs). IsHostileTo checks it
first, but only for the top-level (non-recursive) query: the derived rule's
internal reciprocity check (recursive=true) skips overrides entirely, so one
entity's override can never leak into another entity's own derived hostility.
An override change refreshes EntityHostility immediately via a dedicated
subscription, so dependents (weapons, HUD, grudges) never need a tick to see
it. Overrides are cleared when an entity leaves the zone (existing removal
subscription).

Non-player entities (ships not IsPlayerShip, stations, turrets) hold a grudge:
WatchForGrudge subscribes to another entity's EntityHostility changes toward
this one, plus this entity's own VisibleEntities.ObserveAdd, so a hostile
stance grudges immediately whether the marking or the detection happens
second -- purely event-driven, no per-tick polling. A grudge only takes while
this entity currently detects the marker (VisibleEntities), and once set it
never re-evaluates (sticky; forgiveness is out of scope until a utility-
evaluation pass exists). Player-controlled entities never grudge automatically.

PerceivedStanceOf(other) exposes another entity's stance toward this one for
UI/AI, gated the same way: unknown (null) until this entity detects them.

Known follow-up: leaving the zone clears overrides, which also clears a
grudge. Left as a known gap per the operator's ruling, not solved here.
Of the per-tick derived-hostility refresh's inputs (Entity.cs ~955-964),
PresencePermitted is already a ReactiveProperty; Faction and zone ownership
are not, so converting that refresh to fully event-driven is future work.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 14:00:41 +02:00
Meta Vens
415f8e1152 Add the lot ledger and mint every crafted instance through it
Quality, Ingredients and Product leave CraftedItemInstance; a unit now
carries only a LotId into the run-scoped ProvenanceLedger. ItemManager
gains the one lot-minting path (CreateLot/CreateInstance(int)), GetLot,
and Brand(), which derives a maker and product from provenance instead
of a stored field. RunSave persists the ledger pruned to what the saved
zones' entities still reach; the live ledger is never pruned, so a
floor pickup collected after a save still resolves. CopyBuild is gone:
matching hardpoints now share the same lot instead of copying values.
PropertiesPanel shows the derived brand where the design's manufacturer
used to be. AetherDb's census flags duplicate (maker, design) products
and its save command checks every crafted instance's lot against a real
run store.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 04:16:23 +02:00
Meta Vens
890063b8bb Remove an unequipped weapon from its weapon groups (Cut 10 fix)
TryUnequip removed a weapon from _weapons but left it in WeaponGroups, so an
unequipped weapon could still be fired from its action-bar group and
EntitySerializer.Pack wrote -1 for it. The removal mirrors the group toggle in
WeaponGroupAssignment.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 22:30:40 +02:00
Meta Vens
a2b54889b4 Move the ServerShared data model onto CultLib's CultCache
Cut 8b. Aetheria.Shared builds headless against GameCult.Caching and
GameCult.Caching.MessagePack at the pinned CultLib; the pin guard also
checks the Caching project exists.

Documents: [CultDocument("aetheria.<type>", "1")] with [MessagePackObject]
and integer [Key]s on the Q7 concrete types (the eight ItemData leaves,
Faction, FactionProductData, PersonalityAttribute, NameFile, OrbitData, the
four BodyData leaves, SavedZone, SavedGame, PlayerSettings, InputLayout).
SavedGame and PlayerSettings are [CultGlobal]; Name members are [CultName].
ItemData, CraftedItemData, EquippableItemData, BodyData and AgentTask lose
[MessagePackObject]; BodyData and AgentTask lose their [Union]s. InputLayout
moves into ServerShared so AetheriaStores can route it.

References: Guid and DatabaseLink<T> members become CultRecordRef<T>;
Guid-keyed maps become ref-keyed with [CultReference(..., many: true)];
ZonePack holds refs to orbit and body records. Runtime maps (Zone orbits,
bodies and belts, cargo ItemsOfType, agent targets) key by CultRecordKey.
Identity is the record key: ID comparisons become reference or key
comparisons. StatModifierData.RequireBehavior is a simple type name.

AetheriaStores.Open composes the catalog (read-only unless writable), run
and player stores, one home per type. CultRecordRefs carries IsSet, a
null-safe Get(ref), RefOf (key from TryGetHandle) and Upsert.
ItemManager.GetData is the one item resolution path. MathResolver is
declared by [assembly: CultCacheFormatterResolver].

Inspector attributes: text, range and asset path map onto CultLib's
CultInspector attributes (asset path only on string members); every
replaced Aetheria attribute keeps [Inspectable] for the runtime panel.

Builds: Aetheria.Shared. Does not build: AetherDb and the Unity project,
which follow.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 19:00:42 +02:00
Meta Vens
55c3fc6a49 Delete AetheriaMath in favour of CultMath
AetheriaMath predates the CultMath swap and re-implemented helpers CultMath
now owns. Callers use CultMath directly: decay (Ship, AetherDrive, Entity),
damp (VelocityConversion, FieldTester, FieldDriver), first_order_intercept
(TurretController, Combat, GuidedProjectile), quadratic_bezier (FieldDriver)
and smootherstep (Ship). Parameter order matches in every case, and those
formulas have the same float evaluation order, so behaviour is unchanged.
Galaxy's commented-out segment check names distance_to_segment.

The other overloads had no callers and are dropped without a CultMath
counterpart being needed: the double Damp overloads, FindDistanceToSegment,
GetCatmullRomPosition, GetCubicSplinePosition, Smoothstep(x), and the
Deg2Rad/Rad2Deg constants.

AetherDb census, factions and hardpoint-fit are byte-identical to the
pre-cut captures.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 18:02:41 +02:00
Meta Vens
6140a06a0a Swap Unity.Mathematics usings for CultMath
Every Unity.Mathematics using, alias and qualified name under Assets\Scripts
and tools now names CultMath. The nine `using static ...noise;` lines are
gone: snoise lives on CultMath's math. UnityExtensions loses float4.ToColor
and Color.ToFloat4, which duplicate the bridge's ToColor and ToCultMath; the
float3 colour pair stays because the bridge has none.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 17:39:12 +02:00
Meta Vens
6bd3e17690 Fix station docking bays, save-safe instance keys, and zero-weight draws
One rule now governs interior space: an empty hardpoint cell is usable, in
both UnoccupiedSpace and ItemFits. ItemFits used to refuse hardpoint cells
that UnoccupiedSpace offered, so stations failed to equip a docking bay.

CraftedItemInstance.Ingredients and Product move from keys 3 and 4 to 9 and
10, past every key the derived instances use. At 3 and 4 an older save reads
nil into a non-nullable Guid and fails to load.

WeightedRandomElements returns an empty array when every weight is zero,
instead of an array of nulls. LoadoutGenerator treats a missing Galaxy as
"every product available", the seam a fixture needs to generate loadouts
without a galaxy. ZoneGenerator skips a story station that fails to generate
and logs it, instead of throwing.

Verified: Aetheria.Shared and tools/AetherDb build headless; Unity 6000.3.24f1
batchmode compile succeeds; AetherDb doctor, station-fit and loadout pass.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-12 23:22:36 +02:00
Meta Vens
8d93b937bc Author manufacturer variation as faction products
A design lists role names; a faction product is a manufacturer's branded
build of one, carrying its name, its flavor text, and a pseudo-gaussian
quality distribution per role whose mean is the technology put in and
whose deviation is their quality control. A PerformanceStat may name the
role whose quality it reads, unset reading the item own workmanship as
before, so old saves behave unchanged. Generation selects products rather
than designs, which makes what a faction makes the availability rule.

This is Njordr shape at one dimension: role names, per-role derivation
and per-product distributions carry over when quality widens into
dimensions. No parts exist yet; the product authors the role quality.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-12 01:12:12 +02:00
Meta Vens
25ecb46fcd Generate stations that respect empty hardpoints and required items
UnoccupiedSpace now counts empty hardpoint cells as free, matching
ItemFits: general gear may use a hardpoint until hardpoint gear claims
it. LoadoutGenerator equips hardpoint gear before the docking bay, so the
bay cannot take the reactor's slot. Hulls, docking bays, controllers,
cargo bays and capacitors are required: if no manufacturer present in the
galaxy and known to the faction makes one, any manufacturer is used and
the database gap is logged, so a galaxy without AU still gets stations.
Manufacturer preference is allegiance / (1 + hops to headquarters); the
distance term previously never applied due to operator precedence, and
would have divided by zero in the headquarters zone.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-11 23:23:19 +02:00
Meta Vens
3dcfaf9783 Delete commented-out code blocks
Git keeps the history. SimpleCommodityData records its retired
MessagePack keys so they are not reused against old data.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-11 22:14:58 +02:00
Meta Vens
7006a6b0dd Restore the stable legacy Unity project 2026-08-10 14:57:50 +01:00
Meta Vens
5363714cde Persist daemon consumable behavior state 2026-07-13 16:19:35 +01:00
Meta Vens
4d7bb65868 Integrate CultMesh UI and URP cleanup 2026-06-30 19:14:49 +01:00
Meta Vens
b353652316 Query asteroid render poses from daemon state 2026-06-21 15:47:39 +01:00
Meta Vens
ff02979056 Render entities from daemon frame identity 2026-06-21 15:38:59 +01:00
Meta Vens
be1207c585 Move runtime API namespace to Verse 2026-06-21 09:54:15 +01:00
Meta Vens
39a0dc8737 Sync daemon authority migration 2026-06-21 09:37:54 +01:00
Meta Vens
8ea97a08f8 Use faction keys for shell identity 2026-06-16 11:43:37 +01:00
Meta Vens
75d3f05fd9 Restore Continue stat grids from typed state 2026-06-16 07:18:14 +01:00
Meta Vens
2a047cba28 Restore Continue session facts from typed state 2026-06-16 07:01:50 +01:00
Meta Vens
eac886d783 Remove cargo GUID lookup overloads 2026-06-15 03:12:24 +01:00
Meta Vens
0b92a2faae Remove item instance legacy ID projection 2026-06-15 02:57:50 +01:00
Meta Vens
ffe9d6d7bf Make runtime item catalog key-only 2026-06-15 01:31:16 +01:00
Meta Vens
c245538793 Use typed item keys in server item decisions 2026-06-15 01:12:22 +01:00
Meta Vens
df59a9da62 Index cargo by typed item keys 2026-06-14 23:09:49 +01:00
Meta Vens
eabe0961e6 Move behavior grouping onto runtime instances 2026-06-14 17:27:16 +01:00
Meta Vens
6bcf6444b9 Persist entity session state in checkpoints 2026-06-14 13:58:46 +01:00
Meta Vens
22e7d0a972 Expose item reference API on runtime instances 2026-06-14 11:19:27 +01:00
Meta Vens
d163b3064c Expose item identity on runtime instances 2026-06-14 11:12:48 +01:00
Meta Vens
37f4ef4a12 Route runtime behavior construction through item manager 2026-06-14 10:29:17 +01:00
Meta Vens
301b1251b1 Use typed runtime item category tokens 2026-06-14 09:38:55 +01:00
Meta Vens
1a7ea0a0e5 Rename runtime item reader around typed catalog 2026-06-14 09:19:53 +01:00
Meta Vens
19297f2252 Remove dead behavior data query API 2026-06-14 08:13:47 +01:00
Meta Vens
bc04526d35 Require typed consumable activation 2026-06-14 08:04:05 +01:00
Meta Vens
c0d0fbf656 Delete runtime item DTO hydration 2026-06-14 07:52:18 +01:00
Meta Vens
5d96355439 Use typed state for consumable activation 2026-06-14 07:20:06 +01:00
Meta Vens
6707dcaba8 Use typed audio stats for equipped items 2026-06-14 07:08:06 +01:00
Meta Vens
d55eb38a3a Use typed thermal resilience for equipped items 2026-06-14 06:57:53 +01:00
Meta Vens
feae643dfe Use typed thermal curves for equipped items 2026-06-14 06:52:29 +01:00