fix(fifa17): club items are zero-value, not a fallback to an invented price
`value_for_definition` declined for anything non-player without a catalog rating,
which sent club items into the legacy ladder and paid an invented 150 each.
That is wrong, and the client says so. `shape_club_item` sends neither `rating`
nor `discardValue`, and cardtype 7/9 are NOT re-rated by the client (the merge
jump table sends them to the shared tail), so the client computes for itself from
record +0xb4 == 0: level 1, `0 * price / 100` == 0. It DISPLAYS 0. Paying 150
invents value the player was never shown.
Move the decline boundary onto the real distinction, which is `client_rerates`:
* NOT re-rated (cardtypes 1, 6, 7, 8, 9) -> the server's rating is what the
client prices with, so Core's value is authoritative even at 0.
* RE-RATED (2, 3, 4, 5, 10 -- the staff families) -> the client substitutes its
own database value, so without a catalog rating we genuinely cannot match it
and must decline rather than guess.
Over the 1717-definition corpus this takes "declined -> legacy" from 6 to ZERO:
every definition is now priced by the one authoritative table and no generic
fallback is reachable in the current corpus. The six club items price at exactly
0; staff and consumables are unchanged.
Adapter 248 lib, host 120 lib, fmt and clippy clean.
This commit is contained in:
@@ -256,17 +256,23 @@ pub fn discard_value(cardtype: u8, rating: u8, rare: i64) -> i64 {
|
||||
/// and the number credited cannot diverge.
|
||||
///
|
||||
/// `None` means "not known", never "worthless", and the caller falls back to the
|
||||
/// legacy ladder rather than paying 0:
|
||||
/// legacy ladder rather than inventing a price:
|
||||
///
|
||||
/// * `cardtype == 0` — the subtype decodes to no table row at all.
|
||||
/// * a NON-PLAYER with no `catalog_rating`. Core models every non-player's
|
||||
/// `overall` as 0, and rating 0 prices at 0 coins, so trusting it would pay
|
||||
/// nothing for a real card.
|
||||
/// * a CLIENT-RE-RATED cardtype ([`client_rerates`]: the five staff families)
|
||||
/// with no `catalog_rating`. Those price from the client's OWN database, so
|
||||
/// without that value we cannot match what it displays.
|
||||
///
|
||||
/// A PLAYER with no catalog rating legitimately uses Core's rating: cardtype 1
|
||||
/// is not re-rated by the client, so Core is authoritative there. For the
|
||||
/// cardtypes that ARE re-rated ([`client_rerates`]) the catalog rating is the
|
||||
/// client's own value and must be present, or we cannot match its display.
|
||||
/// Everything else uses `catalog_rating`, falling back to Core's rating. For the
|
||||
/// cardtypes the client does NOT re-rate (1, 6, 7, 8, 9) the server's rating is
|
||||
/// authoritative — whatever we send is what the client prices with — so Core's
|
||||
/// value is the right answer even when it is 0.
|
||||
///
|
||||
/// That zero is not a gap. A club item's wire record
|
||||
/// ([`super::item::shape_club_item`]) carries neither `rating` nor
|
||||
/// `discardValue`, so the client computes for itself from `+0xb4 == 0`: level 1,
|
||||
/// and `0 * price / 100 == 0`. **The client displays 0, so 0 is the correct
|
||||
/// payout.** Paying anything else would invent value the player was never shown.
|
||||
pub fn value_for_definition(
|
||||
subtype: i64,
|
||||
rareflag: i64,
|
||||
@@ -279,7 +285,10 @@ pub fn value_for_definition(
|
||||
}
|
||||
let rating = match catalog_rating {
|
||||
Some(r) => r,
|
||||
None if cardtype == 1 => core_rating,
|
||||
// Not re-rated by the client => whatever the server sends is what it
|
||||
// prices with, so Core's rating is authoritative even at 0.
|
||||
None if !client_rerates(cardtype) => core_rating,
|
||||
// Re-rated => the client substitutes its own value and we cannot match it.
|
||||
None => return None,
|
||||
};
|
||||
Some(discard_value(cardtype, rating, rareflag))
|
||||
@@ -417,6 +426,37 @@ mod tests {
|
||||
assert_eq!(value_for_definition(6, 0, Some(66), 0), Some(36));
|
||||
}
|
||||
|
||||
/// CLUB ITEMS ARE ZERO-VALUE under the current projection, and that is a
|
||||
/// derived fact rather than a gap. `shape_club_item` sends no `rating` and no
|
||||
/// `discardValue`, so the client computes for itself from record `+0xb4 == 0`:
|
||||
/// level 1, `0 * price / 100 == 0`. It DISPLAYS 0, so 0 is the only payout
|
||||
/// that matches. The old ladder invented 150 for each of these.
|
||||
#[test]
|
||||
fn club_items_are_zero_value_not_a_fallback_to_an_invented_price() {
|
||||
use crate::fut::content_taxonomy as tax;
|
||||
for subtype in [
|
||||
tax::KIT_SUBTYPE,
|
||||
tax::STADIUM_SUBTYPE,
|
||||
tax::BADGE_SUBTYPE,
|
||||
tax::BALL_SUBTYPE,
|
||||
tax::LEAGUE_LOGO_SUBTYPE,
|
||||
] {
|
||||
let ct = cardtype_for_subtype(subtype);
|
||||
assert!(
|
||||
!client_rerates(ct),
|
||||
"subtype {subtype} must not be re-rated"
|
||||
);
|
||||
assert_eq!(
|
||||
value_for_definition(subtype, 0, None, 0),
|
||||
Some(0),
|
||||
"subtype {subtype} must price at exactly 0, not decline to a ladder"
|
||||
);
|
||||
}
|
||||
// A staff family, by contrast, DECLINES without its rating -- we cannot
|
||||
// know what the client re-rated it to.
|
||||
assert_eq!(value_for_definition(6, 0, None, 0), None);
|
||||
}
|
||||
|
||||
/// The subtype decode must agree with the settled club-item subtypes and the
|
||||
/// staff family selector this crate already carries.
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user