Skip to content

feat: expose equipment targeting, skill trees and tooltip extensions - #313

Merged
fank merged 12 commits into
mainfrom
feat/tractor-beam-policies
Sep 17, 2026
Merged

fank merged 12 commits into
mainfrom
feat/tractor-beam-policies

Conversation

@fank

@fank fank commented Sep 16, 2026 •

Copy link
Copy Markdown
Member

Generic feature work: reusable, game-aligned capabilities so consumer mods need no Harmony patches. The API knows the game, never other mods.

Surfaces:

  • ITooltipService.RegisterShipModule — every native Behaviour.Equipment.Module family (17 ShipModuleKinds incl. Mining, Salvage, Shields, Reactor, Drones, Turret) via one centralized postfix per declaring SetMainSubStats implementation, including implementations on abstract intermediates such as AbstractTurret; typed ShipModule snapshot (Kind, DisplayName, QualityLevel, vanilla StatLines, tractor sub-snapshot). Native stat lists are plain text; documented.
  • ITooltipService.RegisterItem — shared UITooltip fill for item sources (ItemInfo).
  • ITooltipService.RegisterSkillTree — mastery-badge contributions.
  • ISkillTreeService.Get(CommanderSpecialization) — all nine specializations.
  • IEquipmentService.ConfigurePlayerTractorModules — typed targeting policy (abstain-by-null, first-non-null wins); tractor-named because that is the game's own system (TractorModule).

One TooltipRuntime + one Harmony instance per seam, fail-closed binding; per-frame claims prevent double-append; consumers never see game/Unity types.

Examples, named after the surfaces:

  • EquipmentTargeting — the equipment-targeting surface: one settings-gated decision (ExtraAutoBeam borrows one free manual beam; off ⇒ vanilla cap).
  • UiSurfaces (UI/settings home) — AnnotateModules typed preference gates module-family + Engineering-badge tooltip annotations.
  • StationCommerce — maker's tip on its own manufactured good via RegisterItem.

Consumer-mod neutrality: the repository contains no references to any consumer mod. The former consumer-qualification case (referencing a sibling mod's settings and tooltip strings) is replaced by equipment-policy, which verifies vanilla cap, rule capacity, first-non-null precedence, rule hand-over on disposal, occupied-beam guard and teardown — with API-owned registrations only. Name-specific optional staging became a generic repeatable --stage argument (E2E_STAGE= in the Makefile); staged extras are recorded with sha256+mtime. Consumer verification lives in the consumer repositories.

Latent example breakage found and fixed while proving this: 0.2.8 BepInDependency pins blocked every pinned example under the deliberate 0.0.0 development version (earlier passes were tag-derived builds); in-repo pins removed. StationCommerce used a nonexistent vanilla id Carbon — its owned good had never materialized; now SalvageCarbon with the game's lazy-lookup path driven by the case. E2E staging list restored to all examples; ui-surfaces settings count updated to six (stale on main).

Verification: make test 1835; check-bindings 76; tooling tests OK. Live on the qualified build (sha 71636b8f…): equipment-policy, equipment-targeting, ui-surfaces, station-commerce, observation, story-missions pass with realSavesUnchanged/installationRestored. cargo-recovery/pocket-worlds now load (previously dependency-blocked) and fail at pre-existing movement-step drift, tracked separately. Unreleased feature: no version guessed; public 0.2.8 does not contain it.

@fank
fank marked this pull request as ready for review September 16, 2026 18:24
@fank

fank commented Sep 16, 2026

Copy link
Copy Markdown
Member Author

Read-only source review found the separated equipment/skill-tree/tooltip design respects game vocabulary and removes consumer coordination machinery. Verified vanilla AbstractUnit.IsPlayer(true) matches only GameplayManager.Instance.spaceShip; carrier fighters are excluded. Cached specialization identifiers to avoid repeated reflective mapping. Deterministic in-game E2E passed on game 0.8.2.4 with patch-free TractorAuto: zero/half/max mastery, auto promotion, manual borrowing, saturated/busy pool guards, native candidate eligibility/order/caps, cached module tooltip, styled 0% mastery tooltip, and disabled behavior. API tested as unreleased 0.0.0.0. Report records realSavesUnchanged=true, installationRestored=true; no game process/backup marker remains. Exact harness retained under artifacts/e2e/tractor-run/source. Host tests 1820 and installed binding tests 75 pass. Public 0.2.8 does NOT contain this surface.

@fank
fank force-pushed the feat/tractor-beam-policies branch from 61dad74 to a7719b5 Compare September 17, 2026 13:09
fank added 10 commits September 17, 2026 16:05
Replace RegisterTractorModule with game-aligned family surfaces:
RegisterShipModule dispatches one centralized stat-builder postfix per
native AbstractEquipment override with a typed ShipModule snapshot
(ShipModuleKind, StatLines, tractor sub-snapshot), and RegisterItem
hooks the shared tooltip content fill for item sources. All three
families dispatch from a single TooltipRuntime behind one Harmony
instance with fail-closed binding. E2E gains committed tractor-module
case with family and item checks.
Concrete equipment leaves (turrets) declare SetMainSubStats on abstract
intermediates such as AbstractTurret; skipping abstract types left those
overrides unpatched. Bind every declaring override with a body, map
*Turret types to the new ShipModuleKind.Turret, and extend installed
bindings plus the committed E2E family/item checks. Live E2E
tractor-module passes (realSavesUnchanged, installationRestored).
Claim stats/tooltip objects per frame so a future base-chaining native
override cannot double-append a contribution; document that the native
module stat list renders plain text only (TooltipTextStyle ignored for
RegisterShipModule); drop the dead dedupe set; the E2E now asserts
exactly-once contributions and rebuilds the live module stats after the
family check. Live E2E tractor-module passes (run-6).
…three tooltip families

One small patch-free mod shows the #313 surfaces as a modder uses them:
ExtraAutoBeam borrows one manual beam (abstain-by-null when off), the
ship-module registration renders live Engineering mastery on the
tractor stat list, the skill-tree registration annotates the
Engineering badge, and HighlightItem opt-in adds an item tip. Styles,
caching and disposal contracts documented in the example README.

Committed E2E case tractor-guide drives exactly that sequence (fresh
tooltip per fill, consumer isolated, all fixture state restored); both
tractor cases pass live on the qualified build. External consumer DLLs
(VGTractorAuto) became optional staging assemblies; in-repo examples
are required.
Record staged optional consumer DLLs (name/sha256/mtime) in the E2E
report so a stale out-of-repo DLL is attributable; fail closed when a
staged VGTractorAuto lacks the setting the case-isolation relies on;
drop redundant null-forgiving initializers in the example. Tooling
tests and live tractor-guide pass (guide-run-3).
…ock all examples

- RegisterShipModule/RegisterSkillTree join UiSurfaces behind a new
  typed "AnnotateModules" preference; RegisterItem joins StationCommerce
  as the maker tip on its own good. TractorGuide keeps exactly the one
  decision it owns: the settings-gated ExtraAutoBeam borrowing.
- Live cases extended: ui-surfaces asserts six settings plus
  exactly-once annotated module/badge lines; station-commerce forces the
  shared item fill and asserts both authors tips once, styled.
- Fix latent example breakage found while proving this:
  [BepInDependency("vgmodapi","0.2.8")] blocked every pinned example
  under the deliberate 0.0.0 development version (earlier "passes" were
  tag-derived 0.2.8 builds); drop the in-repo pins with a comment.
  StationCommerce used a non-existent vanilla id "Carbon"; the owned
  good never actually materialized. Use SalvageCarbon and trigger the
  game-side lazy lookup in the case.
- tools/e2e.py staging regressed to two example DLLs on this branch;
  restore the full set. observation and story-missions now pass live
  again; cargo-recovery and pocket-worlds load and advance to their
  movement steps, which time out (pre-existing flow drift, separate).
UiSurfaces registers its tooltip contributions before the settings-
provider early return so they depend only on their own ConfigEntry;
six-settings log wording; StationCommerce comment acknowledges the
cross-author name match it demonstrates; dependency-comment indentation.
… all consumer-mod awareness

The example is named after the surface it demonstrates (equipment
targeting), not a game feature theme. The API repository no longer
knows any consumer mod:

- TractorCase (a consumer-migration qualification case referencing
  VGTractorAuto settings and its tooltip strings) is replaced by
  EquipmentPolicyCase, which verifies the same native guarantees with
  API-owned registrations only: vanilla cap without rules, capacity of
  a registered rule, first-non-null precedence between two rules,
  disposal handing over to the remaining rule, occupied-beam guard and
  teardown to vanilla.
- OPTIONAL_ASSEMBLIES ("VGTractorAuto.dll") is replaced by a generic
  repeatable --stage argument; staged extras are recorded with sha256
  and mtime. E2E_STAGE= passes it through from the Makefile.
- EquipmentTargetingCase dropped its consumer-isolation block; nothing
  references another mod. Committed consumer verification stays in the
  consumer repositories.

Live: equipment-policy and equipment-targeting pass (realSavesUnchanged,
installationRestored); host 1835, bindings 76, tooling tests OK.
The example case now bumps and restores amountOfBonusBeams like the
policy case, so the borrow assertion no longer depends on the test save
happening to have a manual beam. --stage rejects anything but a bare
file name, keeping staged files inside the plugins directory.
Files this repo owns now speak the PR surface; names that mirror game
types keep the game vocabulary (TractorBeamRuntime binds the game\s
@fank
fank merged commit bb246ae into main Sep 17, 2026
4 checks passed
@fank
fank deleted the feat/tractor-beam-policies branch September 17, 2026 21:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant