feat(core): own the EA-authored non-player definition rating
CI / Build, lint & test (push) Successful in 3m23s

Adds `CardDefinition.source_rating: Option<u8>` -- the authoritative rating a
NON-PLAYER definition carries in EA's own tables (a staff card's `value` from
managercards/*coachcards/physiocards, a consumable's rating).

WHY NOT `overall`. `overall` feeds quick-sell pricing and squad projection, and
it is deliberately 0 for every non-player. Reusing it would silently revalue
staff, which is out of scope for the manager-contract milestone. `source_rating`
is a separate number read only by tier rules, so both pricing paths stay
byte-identical: Core's `quick_sell_coins(card.overall)` and the host's
`legacy_discard_value(item.rating)` see exactly what they saw before, and
`effective_overall` is unchanged.

MUST stay Option: CardDefinition has no `#[serde(default)]`, so a required field
would reject every already-shipped content pack, whereas a missing Option
deserializes to None. A test pins that backwards compatibility, because it is
the property that lets Core and the emitter be deployed independently.

No migration: Core does not persist definitions at all -- they are JSON content
packs parsed at startup into an immutable in-memory CardDb. `/collection`
embeds the serialized definition wholesale, so `card.source_rating` reaches the
host with no projection change.
This commit is contained in:
funman300
2026-08-22 20:08:09 +00:00
parent e8be289660
commit 82c3d2c85a
3 changed files with 119 additions and 0 deletions
+14
View File
@@ -231,6 +231,20 @@ pub struct CardDefinition {
pub physical: u8,
pub rarity: Rarity,
pub image_path: Option<String>,
/// EA's authored definition rating for a NON-PLAYER: a staff card's `value`
/// from its shipped family table, or a consumable's own rating.
///
/// This is deliberately NOT `overall`. `overall` feeds pricing and squad
/// projection, so it stays 0 for every non-player; `source_rating` is the
/// separate authoritative number the game's own tier rules read (bronze
/// `<65`, silver `65..=74`, gold `>=75`). `None` for players, whose rating
/// IS `overall`, and `None` whenever Core tracks no authored value — a
/// caller MUST fail closed rather than substitute a tier.
///
/// MUST stay `Option`: `CardDefinition` has no `#[serde(default)]`, so a
/// required field would reject every already-shipped content pack, whereas
/// a missing `Option` deserializes to `None`.
pub source_rating: Option<u8>,
}
/// One owned content INSTANCE (stored in DB).
+2
View File
@@ -817,6 +817,8 @@ mod tests {
physical: 60,
rarity: Rarity::Bronze,
image_path: None,
// A player's rating IS `overall`; no separate authored value.
source_rating: None,
}
}