Commit graph Aetheria/tests/Aetheria.Shared.Tests/StatResolverTests.cs
Author SHA1 Message Date
Meta Vens
29c5f7aba1 Consumable host: one initialiser for gear and running effects; tests pin overlap, lifetime, per-design cooldown, refit
Entity.Activate re-initialises the running consumable effects through the same
InitializeBehaviors that initialises gear and ActivateConsumable, so a consumable
active across deactivate/refit/activate retargets to the new items.
ConsumableTests gains the stackable overlap/multiply, shared-entry detach,
expiry-boundary, per-design cooldown and refit facts. StatResolverTests' expiry
test builds its consumable through ItemManager.CreateInstance.
Not yet built or run; verification follows in a later commit message.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 23:24:54 +02:00
Meta Vens
a7f801c4b9 Subtype modifier tests: deactivate the ship before equipping the guns
TryEquip refuses on a deployed ship.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-08 22:54:11 +02:00
Meta Vens
7ee40255ca Modifiers reach a behaviour type and its subtypes: TargetsOf matches assignable types
A StatReference naming WeaponData.Damage validated and attached to nothing,
because TargetsOf compared exact types. All three filters now use
targetType.IsAssignableFrom; no exact-match path remains.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-08 22:01:47 +02:00
Meta Vens
0a971ec4d8 Pin Gate 1, Gate 2 and F8's mutant fix with tests and mutations
StatResolverTests: UnequipReturnsTheResolversRetainedSetToBaseline and
ConsumableExpiryReturnsTheResolversRetainedSetToBaseline drive 20 equip/
evaluate/unequip and activate/evaluate/expire cycles and assert the
resolver's own entry counts return to baseline, pinning Gate 1 at the
retained-set level rather than only via one WeakReference.
ARefitTargetReattachesTheModifierAfterReactivation drives the exact refit
scenario Gate 2 fixes and asserts the replacement target ends up boosted.

tests/mutation_tests_stats_power_cut2.py: one mutation per Forget() call site
(each expected red under deletion of that call), one for Gate 2's detach line,
and the StatOwner mutant swapped for Entity to match its removal.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-18 20:42:05 +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