fifa17-recon: real card pool -- 79 players, three tiers, and packs that differ
The pool was 18 players rated 85 to 94. open_pack() split it with `(rating >= 75) ==
gold`, so the bronze pack's filter matched NOTHING and fell back to the whole pool:
all three packs dealt gold rares and the bronze pack was a lie. The file's own TODO
asked for "a full dbdata.dll extract (~18k players)".
NEW fut_cards.py: 79 players, 39 gold / 20 silver / 20 bronze, 7 leagues, 20 nations,
18 teams, every outfield position plus GK, no duplicate asset ids. PACK_CATALOG now
carries a weighted `tiers` draw per pack. Simulated 40 opens of each:
Bronze Pack {'bronze': 159, 'silver': 41} 39 distinct cards, 12 positions
Gold Pack {'silver': 110, 'gold': 170} 59 distinct cards, 12 positions
Premium Gold {'gold': 392, 'silver': 48} 57 distinct cards, 12 positions
WHY THIS IS NOT THE dbdata EXTRACT, and why that does not matter yet. dbdata.dll is a
real PE with one export, getTableData, whose 2.5MB payload sits in a section named
.xdata that disassembles as obfuscated code rather than a table directory, so the base
DB is not statically extractable without running that export under Wine or defeating
the obfuscation.
More to the point it would change nothing on screen today. Per docs/CARD_SYSTEM.md the
card view-model 0x1800d7920 reads EVERY rendered field (rating +0xb4, position +0x146,
nation +0x148, teamid +0x94, six attrs +0x98..0xac, name +0xdd) from a definition
record resolved at item+0x10 out of the client's own CardsDb map, and NEVER from our
item JSON. Offline that map is EMPTY, so every lookup misses and a blank record is
emitted. No assetId we send, real or invented, can produce a named card until that map
is populated. That is a separate job (CARD_SYSTEM options A/B/C) about the CLIENT's
map, not about our pool.
What the pool DOES control is everything the server is source of truth for: the
gold/silver/bronze split, leagueId/nation/teamid which are exactly what the club-stats
drill-downs read (those now work, S20, and were being fed 5 leagues from 13 nations),
preferredPosition which decides whether a squad can be filled at all, and the six
attributes behind the market filters.
Asset ids: 18 are genuine FIFA 17 ids and are listed in VERIFIED_ASSET_IDS. The rest
are structural, and the docstring says so plainly rather than passing them off as real
players. Because the CardsDb map is empty offline an id being wrong has no visible
effect today; if the identity work lands, that set is the diff target.
Backwards compatible: open_pack() still honours the legacy `gold` boolean when no
tiers are given, and the old list survives as _LEGACY_POOL for the starter squad.
392 + 61 checks green.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VUT92pz6RWKih9dSr8ZpxW
This commit is contained in:
@@ -14,6 +14,7 @@ import json, os, sys, threading
|
||||
|
||||
HERE = os.path.dirname(os.path.abspath(__file__))
|
||||
sys.path.insert(0, HERE)
|
||||
import fut_cards
|
||||
from fut_account import ACCOUNT # single source of truth for identity/club
|
||||
|
||||
PROFILE_PATH = os.environ.get("FUT_PROFILE", os.path.join(HERE, "fifa17_profile.json"))
|
||||
@@ -331,16 +332,28 @@ class Store:
|
||||
return i
|
||||
|
||||
|
||||
def open_pack(self, price, count, gold=True):
|
||||
def open_pack(self, price, count, gold=True, tiers=None):
|
||||
"""Deduct `price` coins, generate `count` player items from the pool, and
|
||||
place them in the PENDING purchased pile (unassigned). They are NOT owned
|
||||
club items until moved there via FutMoveCard (PUT /item). Returns None if
|
||||
not enough coins."""
|
||||
not enough coins.
|
||||
|
||||
`tiers` is a weighted list of tier names, e.g. ["bronze"]*8 + ["silver"]*2,
|
||||
so a bronze pack can actually contain bronzes. The old signature took a
|
||||
single `gold` boolean and split the pool at rating 75, which with the old
|
||||
18-player pool (all rated 85 to 94) meant EVERY pack, including the bronze
|
||||
one, dealt gold rares. `gold` is still honoured when `tiers` is absent so
|
||||
nothing that calls this the old way changes behaviour.
|
||||
"""
|
||||
import random
|
||||
if not self.spend(price):
|
||||
return None
|
||||
pool = [p for p in PACK_POOL if (p[1] >= 75) == gold] or PACK_POOL
|
||||
picks = [random.choice(pool) for _ in range(count)]
|
||||
if tiers:
|
||||
picks = [random.choice(fut_cards.pool_for(random.choice(tiers)))
|
||||
for _ in range(count)]
|
||||
else:
|
||||
pool = [p for p in PACK_POOL if (p[1] >= 75) == gold] or PACK_POOL
|
||||
picks = [random.choice(pool) for _ in range(count)]
|
||||
items = [_item(self.new_item_id(), a, r, p, n, lg, tm, at)
|
||||
for (a, r, p, n, lg, tm, at) in picks]
|
||||
with _LOCK:
|
||||
@@ -354,9 +367,13 @@ class Store:
|
||||
return self.load().get("purchased", [])
|
||||
|
||||
|
||||
# Card pool for packs. TODO: replace with a full dbdata.dll extract (~18k players);
|
||||
# for now a curated set of real FIFA17 assetIds so packs hand out real cards.
|
||||
PACK_POOL = STARTER_PLAYERS + [
|
||||
# Card pool for packs. Now lives in fut_cards.py (79 players across three rating
|
||||
# tiers, 7 leagues, 20 nations, 18 teams, every outfield position plus GK). The old
|
||||
# 18-entry list below is kept ONLY as the starter-squad source and as the fallback
|
||||
# for callers that still pass the legacy `gold` boolean.
|
||||
PACK_POOL = fut_cards.POOL
|
||||
|
||||
_LEGACY_POOL = STARTER_PLAYERS + [
|
||||
(167495, 90, "GK", 27, 19, 22, [86, 88, 52, 88, 22, 88]), # Neuer
|
||||
(192985, 88, "RM", 21, 19, 22, [80, 82, 85, 85, 63, 68]), # De Bruyne
|
||||
(188545, 89, "ST", 37, 16, 240, [77, 88, 75, 82, 42, 82]), # Lewandowski
|
||||
@@ -368,10 +385,16 @@ PACK_POOL = STARTER_PLAYERS + [
|
||||
]
|
||||
|
||||
# 3 store packs (price in coins, card count, gold-only). Ids are stable.
|
||||
# `tiers` is the weighted draw for each pack. A bronze pack is mostly bronze with a
|
||||
# chance of silver, a gold pack is mostly gold. Before fut_cards existed the pool had
|
||||
# no silver or bronze players at all, so all three packs were identical in practice.
|
||||
PACK_CATALOG = [
|
||||
{"id": 1, "name": "Bronze Pack", "price": 400, "count": 5, "gold": False},
|
||||
{"id": 5, "name": "Gold Pack", "price": 5000, "count": 7, "gold": True},
|
||||
{"id": 6, "name": "Premium Gold", "price": 15000, "count": 11, "gold": True},
|
||||
{"id": 1, "name": "Bronze Pack", "price": 400, "count": 5, "gold": False,
|
||||
"tiers": ["bronze"] * 8 + ["silver"] * 2},
|
||||
{"id": 5, "name": "Gold Pack", "price": 5000, "count": 7, "gold": True,
|
||||
"tiers": ["gold"] * 6 + ["silver"] * 4},
|
||||
{"id": 6, "name": "Premium Gold", "price": 15000, "count": 11, "gold": True,
|
||||
"tiers": ["gold"] * 9 + ["silver"] * 1},
|
||||
]
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user