From 09675a9f7fce63483aab14ec5d4a43296842a698 Mon Sep 17 00:00:00 2001 From: OpenFUT Agent Date: Thu, 13 Aug 2026 19:31:23 +0000 Subject: [PATCH] feat(fifa17): economy policy mappers (match reward, pack price) Pure FIFA17 policy: match_result_coins/match_reward_total (oracle MATCH_COINS won 400/draw 200/loss 100 + participation 0), result_from_end_reason (endReason enum -> outcome, draw default), pack_price (catalogue buy-now, None for unknown/owned-only). 3 unit tests. --- .../src/fut/economy_policy.rs | 87 +++++++++++++++++++ openfut-adapter-fifa17/src/fut/mod.rs | 1 + 2 files changed, 88 insertions(+) create mode 100644 openfut-adapter-fifa17/src/fut/economy_policy.rs diff --git a/openfut-adapter-fifa17/src/fut/economy_policy.rs b/openfut-adapter-fifa17/src/fut/economy_policy.rs new file mode 100644 index 0000000..574b695 --- /dev/null +++ b/openfut-adapter-fifa17/src/fut/economy_policy.rs @@ -0,0 +1,87 @@ +//! FIFA 17 economy POLICY mappers (pure, game-specific). +//! +//! These translate FIFA 17 wire semantics into the generic amounts the host +//! feeds to Core economy authority. They own NO state — Core owns balances and +//! inventory; these are the FIFA-specific numbers/derivations. Values are the +//! current OpenFUT economy (match rewards are the Python oracle's +//! `MATCH_COINS`/`MATCH_PARTICIPATION` at production defaults); pack prices come +//! from the Store catalogue. + +use crate::fut::store_catalog::pack_by_id; + +/// Normalized match outcome for reward purposes. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum MatchResult { + Win, + Draw, + Loss, +} + +/// Participation award added to every match reward (oracle `MATCH_PARTICIPATION` +/// default = 0). +pub const MATCH_PARTICIPATION: i64 = 0; + +/// Per-result match coins (oracle `MATCH_COINS`: won 400 / draw 200 / loss 100). +pub fn match_result_coins(result: MatchResult) -> i64 { + match result { + MatchResult::Win => 400, + MatchResult::Draw => 200, + MatchResult::Loss => 100, + } +} + +/// Total match reward = per-result coins + participation. +pub fn match_reward_total(result: MatchResult) -> i64 { + match_result_coins(result) + MATCH_PARTICIPATION +} + +/// Derive the outcome from the match `endReason` enum (the oracle's primary +/// signal, `_END_REASON`). Unknown/absent reasons default to `Draw`, matching +/// the oracle's conservative default. Score-based derivation is a fallback the +/// oracle also supports; the enum is authoritative when present. +pub fn result_from_end_reason(end_reason: Option<&str>) -> MatchResult { + match end_reason.unwrap_or("").to_ascii_uppercase().as_str() { + "WIN" | "DNF_WIN" => MatchResult::Win, + "LOSS" | "QUIT" | "DNF" | "DNF_LOSS" => MatchResult::Loss, + // "DRAW", "DNF_DRAW", "NO_CONTEST", unknown -> draw. + _ => MatchResult::Draw, + } +} + +/// The Store buy-now price for a pack id (`None` for unknown/owned-only packs, +/// which are never purchasable). +pub fn pack_price(pack_id: u64) -> Option { + pack_by_id(pack_id) + .filter(|p| !p.owned_only) + .map(|p| p.price) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn match_rewards_match_oracle() { + assert_eq!(match_reward_total(MatchResult::Win), 400); + assert_eq!(match_reward_total(MatchResult::Draw), 200); + assert_eq!(match_reward_total(MatchResult::Loss), 100); + } + + #[test] + fn end_reason_maps_to_outcome() { + assert_eq!(result_from_end_reason(Some("WIN")), MatchResult::Win); + assert_eq!(result_from_end_reason(Some("dnf_win")), MatchResult::Win); + assert_eq!(result_from_end_reason(Some("LOSS")), MatchResult::Loss); + assert_eq!(result_from_end_reason(Some("QUIT")), MatchResult::Loss); + assert_eq!(result_from_end_reason(Some("DRAW")), MatchResult::Draw); + assert_eq!(result_from_end_reason(None), MatchResult::Draw); + assert_eq!(result_from_end_reason(Some("weird")), MatchResult::Draw); + } + + #[test] + fn pack_price_rejects_unknown_and_owned_only() { + assert!(pack_price(1).is_some()); + assert_eq!(pack_price(65534), None); // sentinel absent from catalogue + assert_eq!(pack_price(70), None); // owned-only reward pack, not purchasable + } +} diff --git a/openfut-adapter-fifa17/src/fut/mod.rs b/openfut-adapter-fifa17/src/fut/mod.rs index d92f10e..d3b29c9 100644 --- a/openfut-adapter-fifa17/src/fut/mod.rs +++ b/openfut-adapter-fifa17/src/fut/mod.rs @@ -7,6 +7,7 @@ pub mod catalog; pub mod club_response; pub mod economy; +pub mod economy_policy; pub mod entities; pub mod item; pub mod owned_query;