feat(fifa17): real player-contract consumable apply, replacing the probe
POST /ut/game/fifa17/item/resource/<rid> {"apply":[{"id":N}]} now performs a
durable atomic contract application instead of falling through to Python.
THE RULE. grant = fcc_contractcards[card][tier(TARGET.rating)], then
min(99, contract + grant). The column is keyed on the TARGET's tier, NOT the
card's own -- all 36 cells of EA's shipped table match the published FIFA 17
matrix, and staging discriminates the two readings outright: a bronze-RARE
card on a rating-89 player granted 3 (the gold column), where the card-level
reading predicts 15.
No client binary reads fcc_contractcards -- a string scan of every .exe/.dll
in the install finds it referenced nowhere, and CardsDLL reads only 14 fcc_
tables (fcc_discardcoins among them, which is why quick-sell prices locally).
Consumable effects are server-authoritative, so EA's shipped table is the only
non-invented source and the client renders whatever we persist and re-serve.
The host computes the grant, Core owns the mutation -- the same split
quick-sell already uses (host prices via discard_value, Core performs
sell_item), and what migration 0027 means by "Core defines NO per-category
formula".
FAILS CLOSED, never 200-and-do-nothing: manager contracts 409 because staff
ratings are unimported so the target tier is unknowable; every other family
409 as unproven; batch 400; unresolvable operand 404. Core's deterministic
refusals pass through with their own status instead of collapsing to 503,
which would tell the client to retry a request that can never succeed.
`contract: 7` stops being a hardcode in shape_item/shape_staff_item and
becomes the fallback for an instance Core tracks no contract for. `fitness: 99`
is the same class of hardcode and is deliberately untouched.
CLEAN CUTOVER: Route::ConsumableApplyProbe, its handler, apply_probe_enabled,
the OPENFUT_FIFA17_APPLY_PROBE gate and both probe scripts are deleted. A
handler no classifier can reach is this repo's recurring defect class, and the
new economy arm preempts the probe. fifa17-migration-rehearse.py also drove
the probe (spelled "apply probe", so an apply-probe grep missed it) and would
have eaten a card off the rehearsal profile; retargeted to a non-mutating
assertion.
Not implemented, on purpose: the stored-manager bonus (real mechanic, rule
appears in no shipped table -- guessing it would corrupt the proven part) and
contract decrement per match (nothing spends contracts yet).
This commit is contained in:
@@ -26,6 +26,7 @@
|
||||
|
||||
use serde_json::{json, Value};
|
||||
|
||||
use openfut_adapter_fifa17::fut::contract_cards::PACK_FRESH_CONTRACT_MATCHES;
|
||||
use openfut_adapter_fifa17::fut::entities::ReverseEntityResolver;
|
||||
use openfut_adapter_fifa17::fut::item::{shape_item, ItemIdentityResolver};
|
||||
use openfut_adapter_fifa17::fut::item_state;
|
||||
@@ -284,7 +285,19 @@ pub fn resolve_market_list<E: ReverseEntityResolver>(
|
||||
let identity = resolver.resolve(&owned);
|
||||
let resource_id = identity.map(|id| id.resource_id as i64);
|
||||
let item_json = identity
|
||||
.map(|id| shape_item(&owned, id, ent, resolver.discard_value(&owned)))
|
||||
.map(|id| {
|
||||
shape_item(
|
||||
&owned,
|
||||
id,
|
||||
ent,
|
||||
resolver.discard_value(&owned),
|
||||
// A listing snapshot must show the contract the seller's card
|
||||
// actually holds, so a part-used card cannot render as fresh.
|
||||
owned
|
||||
.contract_matches
|
||||
.unwrap_or(PACK_FRESH_CONTRACT_MATCHES),
|
||||
)
|
||||
})
|
||||
.and_then(|card| serde_json::to_string(&card).ok());
|
||||
Some(ResolvedListing {
|
||||
item_id,
|
||||
@@ -806,8 +819,8 @@ pub async fn handle_move_items(
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::{
|
||||
CoreMatchCompletion, CoreMatchReceipt, EconomyEntitlement, EconomyGrantItem,
|
||||
EconomyPurchase, EconomySale, EconomySaleReceipt,
|
||||
ConsumableApplyOutcome, ConsumableApplyRequest, CoreMatchCompletion, CoreMatchReceipt,
|
||||
EconomyEntitlement, EconomyGrantItem, EconomyPurchase, EconomySale, EconomySaleReceipt,
|
||||
};
|
||||
use std::collections::HashMap;
|
||||
use std::sync::atomic::{AtomicI64, AtomicU64, AtomicUsize, Ordering};
|
||||
@@ -968,6 +981,13 @@ mod tests {
|
||||
// Match completion is not exercised through the market double.
|
||||
Err(CoreError::Status(501))
|
||||
}
|
||||
fn apply_consumable(
|
||||
&self,
|
||||
_req: &ConsumableApplyRequest<'_>,
|
||||
) -> Result<ConsumableApplyOutcome, CoreError> {
|
||||
// Consumable apply is not exercised through the market double.
|
||||
Err(CoreError::Status(501))
|
||||
}
|
||||
}
|
||||
|
||||
// ---- SquadWireResolver double -----------------------------------------
|
||||
@@ -1019,6 +1039,7 @@ mod tests {
|
||||
league: String::new(),
|
||||
club: String::new(),
|
||||
attributes: [80, 80, 80, 80, 80, 80],
|
||||
contract_matches: None,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user