feat(fifa17): manager contracts, from a Core-owned staff tier

ROOT CAUSE, one line. openfut-import-fifa17 emitted `"overall": 0` for every
non-player Core definition while `d.rating` already held EA's authoritative
`value` -- and the very next block wrote that same number correctly to the
adapter catalog. So the tier existed host-side but never reached Core:
Core overall 0 -> /collection effective_overall 0 -> CoreOwnedItem.rating 0 ->
tier_for_rating(0) = Bronze for a Gold (88) manager. That silent mis-grant is
exactly what the 409 was protecting against, so the refusal was correct.

The emitter now also writes `source_rating`, keeping `overall` at 0. Regenerating
the production pack changes exactly 18 entries and exactly one field each
(source_rating None -> value); same 1710 ids, same fingerprint 28c333f1e833338a.

WHY value IS the tier source, and why the thresholds are the player ladder:
LIVE_PROVEN, not inferred. The client re-rates staff from its own
managercards/*coachcards/physiocards by carddbid and applies discard_level's
65/75 ladder; coach_probe/discard_probe agree 4/4 (manager value 88 -> level 3,
coaches 66 -> level 2). The shipped coach tables corroborate: each family has
exactly 3 tiers x 2 rarities, and only 65/75 splits them 2/2/2.

Manager contracts stop refusing and now resolve the TARGET's tier from
Core-owned state. Still fail-closed everywhere it matters: a coach or physio is
`contract_target_not_a_manager` (only cardsubtypeid 4 is a manager), and a
manager Core carries no source_rating for is `manager_tier_unknown` rather than
a guessed tier. Core's own content_kind token is sent as target_kind, because
Core calls the squad manager `manager` while the catalog classifies it
`staff`+subtype 4.

NOT implemented, unchanged: STORED_MANAGER_BONUS and MATCH_CONTRACT_DECREMENT.
This commit is contained in:
funman300
2026-08-22 20:08:26 +00:00
parent f0c6dcf238
commit 9026220533
13 changed files with 541 additions and 68 deletions
+10
View File
@@ -1317,6 +1317,15 @@ pub fn emit_content(
.collect();
// Non-player CardDefinitions use NEUTRAL player fields + the honest family/
// role name; Core stores them like any other definition (no FIFA concept).
//
// `overall` STAYS 0 while `source_rating` carries EA's authored value, and
// both are correct at once because they feed different consumers: `overall`
// is what Core prices and projects from (a non-zero one would silently
// re-price every staff quick sell), while `source_rating` is the authored
// number the game's own tier rules read (bronze <65 / silver 65..=74 /
// gold >=75) — the number a manager-contract grant needs. `d.rating` is the
// SAME value written to the host catalog below as `"rating": d.rating`, so
// the content pack and the catalog can never disagree about a card's tier.
for d in &report.non_player.supported {
defs.push(serde_json::json!({
"id": d.card_id,
@@ -1334,6 +1343,7 @@ pub fn emit_content(
"physical": 0,
"rarity": "bronze",
"image_path": serde_json::Value::Null,
"source_rating": d.rating,
}));
}
let content_pack = content_dir.join("fifa17-production-cards.json");