# OpenFUT Modding Guide All game content is driven by JSON files in the `data/` directory. You can add new cards, packs, objectives, and SBCs without touching any Rust code. The server reloads data files on startup. After changing a file, restart `openfut-core` to pick up the changes. --- ## Cards — `data/cards/*.json` Each file contains a JSON array of `CardDefinition` objects. All files in the directory are loaded at startup. ```json [ { "id": "card_unique_id", "name": "Player Name", "overall": 82, "position": "ST", "nation": "Brazil", "league": "Brasileirao", "club": "Flamengo", "pace": 88, "shooting": 85, "passing": 74, "dribbling": 82, "defending": 38, "physical": 76, "rarity": "gold", "image_path": null } ] ``` **Valid rarity values:** `bronze`, `silver`, `gold`, `raregold`, `totw`, `hero`, `icon` **Valid positions:** `GK`, `CB`, `LB`, `RB`, `CDM`, `CM`, `CAM`, `LM`, `RM`, `LW`, `RW`, `ST`, `CF` - `id` must be unique across all card files. - `overall` is 1–99. - Attribute values are 1–99. - `image_path` can be a relative path to an image or `null`. --- ## Packs — `data/packs/*.json` Each file contains a JSON array of `PackDefinition` objects. ```json [ { "id": "my_custom_pack", "name": "Custom Pack", "description": "12 random gold players.", "cost_coins": 7500, "slots": [ { "count": 12, "min_overall": 75, "rarity_filter": ["gold", "raregold"], "guaranteed_rare": false } ] } ] ``` Each **slot** defines one group of cards to draw: - `count` — how many cards to draw for this slot. - `min_overall` — minimum overall rating (inclusive), or `null` for no filter. - `rarity_filter` — array of rarity strings to draw from, or `null` for any rarity. - `guaranteed_rare` — if `true`, this slot's cards are marked as rare. A pack can have multiple slots (e.g., one slot of 11 gold cards + one guaranteed rare slot of 1 card). --- ## Objectives — `data/objectives/*.json` Each file contains a JSON array of `ObjectiveDefinition` objects. ```json [ { "id": "daily_score_3", "title": "Hat-Trick Hero", "description": "Score 3 goals in one session.", "objective_type": "daily", "metric": "goalsscored", "target": 3, "reward_coins": 500, "reward_xp": 200, "reward_pack_id": null } ] ``` **Valid `objective_type` values:** `daily`, `weekly`, `milestone`, `lifetime`, `season` **Valid `metric` values:** | Metric | Incremented by | |------------------|--------------------------------------| | `matcheswon` | Winning a match | | `matchesplayed` | Playing any match | | `goalsscored` | Goals scored in a match | | `packsopened` | Opening a pack | | `sbcscompleted` | Completing an SBC | | `coinsearned` | Coins awarded after a match | - `reward_pack_id` must match a pack `id` from `data/packs/` or be `null`. - Daily objectives are auto-reset to 0 at midnight UTC. --- ## SBCs — `data/sbcs/*.json` Each file contains a JSON array of `SbcDefinition` objects. ```json [ { "id": "my_sbc", "name": "Custom SBC", "description": "Submit 5 gold players for a reward.", "expires_at": null, "requirements": { "squad_size": 5, "min_overall": 75, "max_overall": null, "required_leagues": ["Premier League"], "required_nations": [], "required_clubs": [], "min_players_from_same_league": null, "min_players_from_same_nation": null, "min_players_from_same_club": null }, "reward": { "coins": 1000, "xp": 300, "pack_id": null } } ] ``` **Requirements:** - `squad_size` — exact number of cards to submit (required). - `min_overall` — average overall must meet or exceed this (optional). - `max_overall` — no individual card may exceed this (optional). - `required_leagues` — at least one card from each listed league. - `required_nations` — at least one card from each listed nation. - `required_clubs` — at least one card from each listed club. - `min_players_from_same_league` — max count of any single league ≥ this. - `min_players_from_same_nation` — max count of any single nation ≥ this. - `min_players_from_same_club` — max count of any single club ≥ this. Cards submitted to an SBC are permanently consumed from the club's collection. --- ## Tips - Use unique `id` values that won't collide with future official cards. A good convention is `card__`, e.g. `card_br_042`. - You can have multiple JSON files per directory — they are all merged at load time. - Invalid JSON will prevent the server from starting; check the log output. - Cards referenced by packs must exist in `data/cards/` or they will not be drawn.