feat(fifa17): import consumable + staff content as first-class Core content

Close 20 of the 33-record content gap (17 consumables + 3 staff; 13 Legends are
unrecoverable from PC data). Verdict A (no Core change): consumables/staff become
ordinary Core CardDefinitions (neutral player fields + honest family/role names)
and owned instances via the SAME generic import path; a catalog kind lets the
adapter exclude them from the player-only /club projection.

- adapter fut::content_taxonomy: evidence-based cardsubtypeid->family/label
  (Ghidra-derived ranges) + staff role map; unknown subtype => defer, never fabricate.
- adapter catalog: Fifa17CardIdentity/RawCard gain optional kind+subtype
  (backward-compat: legacy catalogs load as player); kind_of/subtype_of lookups.
- adapter item/club_response: shape_club_response excludes non-player kinds
  (ShapeStats.excluded_non_player); ItemIdentityResolver::kind_of default=Player.
- host Fifa17IdentityResolver overrides kind_of to delegate to the catalog so
  /club excludes consumables/staff in production.
- import: Item gains cardsubtypeid/cardassetid/amount/contract; plan_non_player_definitions
  (resourceId-grouped, subtype-consistency gated); emit_content writes non-player
  defs + catalog kind + manifest; apply mints owned instances via owned_item_id.

Real profile 33068179: 1962 players + 20 non-player = 1982 owned; 18 non-player
defs (16 consumable + 2 staff, dup resourceIds shared); 0 deferred non-player; 0 blockers.
This commit is contained in:
funman300
2026-08-14 05:26:02 +00:00
parent f5a33eb58c
commit abe9e663c1
11 changed files with 913 additions and 8 deletions
+25 -2
View File
@@ -181,6 +181,23 @@ pub fn plan_apply(
});
}
}
// Non-player (consumable/staff) owned instances mint via the IDENTICAL
// generic path: deterministic OwnedItemId per (persona, wire), an identity
// mapping, and a GenericOwned with card_id = fifa17_<resourceId>.
for def in &report.non_player.supported {
for &wire in &def.wire_ids {
let core_id = owned_item_id(persona, wire);
wire_to_owned.insert(wire, core_id.clone());
owned.push(GenericOwned {
owned_item_id: core_id.clone(),
card_id: def.card_id.clone(),
});
mappings.push(IdentityMapping {
core_id,
wire_id: wire,
});
}
}
// Canonical squad + opaque extension, built by the SAME adapter code the live
// squad-write path uses, over the raw source squad. The resolver maps every
@@ -253,8 +270,14 @@ pub fn plan_apply(
request,
mappings,
watermark: report.identity.source_watermark,
supported_instances: report.identity.import_wire_ids.len(),
deferred_instances: report.deferred_instances(),
supported_instances: report.identity.import_wire_ids.len()
+ report
.non_player
.supported
.iter()
.map(|d| d.wire_ids.len())
.sum::<usize>(),
deferred_instances: report.deferred_instances() + report.non_player.deferred_instances(),
source_fingerprint: snapshot_fingerprint.to_string(),
})
}