diff --git a/fifa17-recon/tools/utas_server.py b/fifa17-recon/tools/utas_server.py index 70547ac..bffa2da 100755 --- a/fifa17-recon/tools/utas_server.py +++ b/fifa17-recon/tools/utas_server.py @@ -1297,6 +1297,42 @@ CLUB_CONSUMABLE_CATS = { } +_DATA_DIR_TABLES = os.path.join(os.path.dirname(os.path.abspath(__file__)), + "..", "data", "tables") +_CARDASSET_BY_RESOURCE = None + + +def _cardasset_map(): + """carddbid -> cardassetid, from the game's own fcc_* tables. + + THE GREEN "NOT FOUND" BOX. A consumable card draws its art from cardassetid, and + that is a SMALL art id in the fcc tables (3 training, 7 contract, 10 healing, + 45 misc), NOT the carddbid. fut_store._item copies the resourceId into + cardassetid, which is right for players and wrong here: the client looked for art + id 5003001, found none, and fell back to + external/ion_fut/artAssets/.../notfound.swf -- the green placeholder a player + photographed on 2026-08-05. + + Built from data/tables/fcc_*.json, which were dumped read-only from the client's + own database, so these are the game's ids and not a guess. + """ + global _CARDASSET_BY_RESOURCE + if _CARDASSET_BY_RESOURCE is None: + import glob + m = {} + for f in glob.glob(os.path.join(_DATA_DIR_TABLES, "fcc_*.json")): + try: + rows = json.load(open(f)).get("rows") or [] + except Exception: + continue + for r in rows: + if "carddbid" in r and "cardassetid" in r: + m[r["carddbid"]] = r["cardassetid"] + _CARDASSET_BY_RESOURCE = m + log(" CONSUMABLES: %d carddbid->cardassetid art mappings loaded" % len(m)) + return _CARDASSET_BY_RESOURCE + + def _consumable_stacks(items): """Wrap consumables as the STACK records this response class actually reads. @@ -1336,6 +1372,9 @@ def _consumable_stacks(items): item = dict(it) if not CONSUM_UNTRADEABLE: item["untradeable"] = False + art = _cardasset_map().get(rid) + if art is not None: + item["cardassetid"] = art s = stacks[rid] = {"count": 0, "discardValue": 0, "item": item, "resourceId": rid, "untradeableCount": 0} s["count"] += 1