Skip to content

Full Config — Progression Catalog ​

The complete configs/shared/progression.lua for Smash Down: Los Santos — unlock content, catalog prices, and rewards.

TIP

See the setup guide for explanations and examples. Download this file as a reference, and merge changes with the config shipped in your installed version.

Download shared/progression.lua

lua
-- The progression CATALOG: everything a player can earn or buy, what it costs and where it comes
-- from. Shared on purpose - it is display data (the arcade's shop, season pass, collection and
-- achievements screens render it), but the UI never reads this file directly: the server builds
-- the wire Catalog from it at boot (server/brawl/progress_catalog.lua), validates it against the
-- game's roster, and sends it with BootReply. Every price, grant and unlock is decided server-side
-- from this very table, so editing a client's copy changes nothing.
--
-- The server-only knobs (anti-farm caps, streak bonus, challenge pool, reset times, the item shop's
-- rotation and discounts, the season's start date, the master switch) live in
-- configs/server/progression.lua.
--
-- Rewards are written with the helpers below and follow protocol.ts `Reward`:
--   CR(n)            n Credits              XP(n)       n account XP
--   FIGHTER(id)      unlock a fighter       SKIN(id, i) unlock costume i of a fighter
--   TITLE(id)        unlock a player title  BANNER(id)  unlock a player-card banner
--   (border: { kind = 'border', id } - granted by ranked only, see Borders below)
-- A fighter or skin granted to someone who already owns it converts to Credits instead
-- (configs/server/progression.lua DuplicateRate x its price). Titles/banners never duplicate-pay.
--
-- Labels are locale KEYS (locales/_lua_progression_en.json, merged into locales/en.json):
--   progression.titles.<id>, progression.banners.<id>, progression.achievements.<id>.name/.desc

---@param n integer
---@return table Reward
local function CR(n) return { kind = 'credits', amount = n } end
---@param n integer
---@return table Reward
local function XP(n) return { kind = 'xp', amount = n } end
---@param id string fighter id
---@return table Reward
local function FIGHTER(id) return { kind = 'fighter', id = id } end
---@param id string fighter id
---@param i integer costume index 1..5
---@return table Reward
local function SKIN(id, i) return { kind = 'skin', fighter = id, skin = i } end
---@param id string title id
---@return table Reward
local function TITLE(id) return { kind = 'title', id = id } end
---@param id string banner id
---@return table Reward
local function BANNER(id) return { kind = 'banner', id = id } end

-- ─────────────────────────────────────────────────────────────────────────────────────────────
-- Fighters & skins
-- ─────────────────────────────────────────────────────────────────────────────────────────────

---@type string[] Owned by every new profile (and never sold).
local STARTERS = { 'franklin', 'michael', 'trevor', 'lamar', 'cj', 'niko' }

---@type { id: string, rarity: string, series: string }[] Every fighter's rarity and series, in
---roster order. The rarity sets its shop price (FighterPrices). Starters are 'common' and not for
---sale. `series` is the fighter's series in the game itself (web/src/game/content/fighters/<id>.ts
---FighterDef.series: creator, vi, classic, story or pop - checked by tests/server.lua); the item
---shop's weekly Spotlight themes and Packs group fighters by it. Must list exactly the fighters in
---shared/roster.lua KNOWN_FIGHTERS (checked at boot).
local FIGHTERS = {
    { id = 'franklin', rarity = 'common', series = 'story' },
    { id = 'michael', rarity = 'common', series = 'story' },
    { id = 'trevor', rarity = 'common', series = 'story' },
    { id = 'lamar', rarity = 'common', series = 'story' },
    { id = 'impotentrage', rarity = 'epic', series = 'pop' },
    { id = 'pogo', rarity = 'rare', series = 'pop' },
    { id = 'commander', rarity = 'epic', series = 'pop' },
    { id = 'bubblegum', rarity = 'epic', series = 'pop' },
    { id = 'cj', rarity = 'common', series = 'classic' },
    { id = 'bigsmoke', rarity = 'rare', series = 'classic' },
    { id = 'niko', rarity = 'common', series = 'classic' },
    { id = 'tommy', rarity = 'epic', series = 'classic' },
    { id = 'lester', rarity = 'rare', series = 'story' },
    { id = 'lucia', rarity = 'legendary', series = 'vi' },
    { id = 'jason', rarity = 'legendary', series = 'vi' },
    { id = 'johnny', rarity = 'rare', series = 'classic' },
    { id = 'madrazo', rarity = 'epic', series = 'story' },
    { id = 'cris', rarity = 'epic', series = 'pop' },
    { id = 'wade', rarity = 'rare', series = 'story' },
    { id = 'bigfoot', rarity = 'legendary', series = 'pop' },
    { id = 'merryweather', rarity = 'rare', series = 'pop' },
    { id = 'maude', rarity = 'epic', series = 'story' },
    { id = 'phil', rarity = 'rare', series = 'classic' },
    { id = 'raspberryjam', rarity = 'epic', series = 'pop' },
    { id = 'james', rarity = 'mythic', series = 'creator' },
    { id = 'lotion', rarity = 'legendary', series = 'creator' },
    { id = 'aristides', rarity = 'epic', series = 'pop' },
    { id = 'bai', rarity = 'legendary', series = 'creator' },
    { id = 'vames', rarity = 'legendary', series = 'creator' },
    -- #30-45: FiveM creators (docs/refs/fighters-*.md), all legendary.
    { id = 'argee', rarity = 'legendary', series = 'creator' },
    { id = 'chatty', rarity = 'legendary', series = 'creator' },
    { id = 'thrasher', rarity = 'legendary', series = 'creator' },
    { id = 'isabella', rarity = 'legendary', series = 'creator' },
    { id = 'uyu', rarity = 'legendary', series = 'creator' },
    { id = 'jenn', rarity = 'legendary', series = 'creator' },
    { id = 'scorp', rarity = 'legendary', series = 'creator' },
    { id = 'night', rarity = 'legendary', series = 'creator' },
    { id = 'kuz', rarity = 'legendary', series = 'creator' },
    { id = 'brunx', rarity = 'legendary', series = 'creator' },
    { id = 'marcinhu', rarity = 'legendary', series = 'creator' },
    { id = 'kanikuly', rarity = 'legendary', series = 'creator' },
    { id = 'bablo', rarity = 'legendary', series = 'creator' },
    { id = 'samuel', rarity = 'legendary', series = 'creator' },
    { id = 'redutzu', rarity = 'legendary', series = 'creator' },
    { id = 'atenea', rarity = 'legendary', series = 'creator' },
    -- #46-49 (docs/refs/fighters-46-49.md): three more creators, legendary, and Solaire - a
    -- pop-culture homage (series pop), epic like Aristides.
    { id = 'seventeen', rarity = 'legendary', series = 'creator' },
    { id = 'chippy', rarity = 'legendary', series = 'creator' },
    { id = 'solaire', rarity = 'epic', series = 'pop' },
    { id = 'k4mb1', rarity = 'legendary', series = 'creator' },
    -- #50 (docs/refs/fighters-50.md): Ntanis of Critical Scripts, a creator, legendary.
    { id = 'ntanis', rarity = 'legendary', series = 'creator' },
}

return {
    -- The progression currency, as the UI names it.
    Currency = { name = 'Credits', short = 'CR' },

    Starters = STARTERS,
    Fighters = FIGHTERS,

    -- Credits to unlock a non-starter fighter in the shop, by rarity.
    FighterPrices = { rare = 2000, epic = 3500, legendary = 5000, mythic = 8000 },

    -- The skin ladder, by costume index (every fighter has 6: 0..5). Skin 0 is the default and is
    -- always owned with the fighter. price = Credits in the shop; false = not sold.
    Skins = {
        [0] = { rarity = 'common', price = false },
        [1] = { rarity = 'common', price = 500 },
        [2] = { rarity = 'rare', price = 1000 },
        [3] = { rarity = 'epic', price = 1800 },
        [4] = { rarity = 'legendary', price = 3000 },
        [5] = { rarity = 'mythic', price = 5000 },
    },

    -- Fighters whose MYTHIC skin (costume 5) is exclusive to the Season 1 premium track: not sold
    -- in the shop. Keep in step with the premium rewards below (checked at boot).
    PassExclusiveMythics = { 'franklin', 'cj', 'trevor', 'michael', 'niko', 'lamar', 'tommy', 'bigfoot', 'lucia', 'james' },

    -- ─────────────────────────────────────────────────────────────────────────────────────────
    -- Player card: titles + banners
    -- ─────────────────────────────────────────────────────────────────────────────────────────

    -- Every title. Owned from the start: StarterTitles. The rest come from the pass, levels and
    -- achievements (the sources are derived from where they're granted).
    StarterTitles = { 'rookie' },
    Titles = {
        { id = 'rookie', rarity = 'common' },
        -- season 1 free track
        { id = 'contender', rarity = 'common' },
        { id = 'brawler', rarity = 'common' },
        { id = 'showdown_regular', rarity = 'rare' },
        { id = 'los_santos_finest', rarity = 'epic' },
        -- season 1 premium track
        { id = 'high_roller', rarity = 'rare' },
        { id = 'kingpin', rarity = 'epic' },
        { id = 'untouchable', rarity = 'epic' },
        { id = 'showstopper', rarity = 'legendary' },
        { id = 'season_one_legend', rarity = 'mythic' },
        -- account levels
        { id = 'up_and_comer', rarity = 'common' },
        { id = 'road_warrior', rarity = 'rare' },
        { id = 'veteran', rarity = 'rare' },
        { id = 'street_legend', rarity = 'epic' },
        { id = 'legend', rarity = 'legendary' },
        { id = 'hall_of_famer', rarity = 'legendary' },
        { id = 'smashdown_icon', rarity = 'mythic' },
        -- achievements
        { id = 'champion', rarity = 'legendary' },
        { id = 'online_menace', rarity = 'epic' },
        { id = 'unstoppable', rarity = 'mythic' },
        { id = 'knockout_artist', rarity = 'epic' },
        { id = 'wrecking_ball', rarity = 'legendary' },
        { id = 'heavy_hitter', rarity = 'epic' },
        { id = 'untouched', rarity = 'legendary' },
        { id = 'comeback_kid', rarity = 'epic' },
        { id = 'on_fire', rarity = 'legendary' },
        { id = 'collector', rarity = 'legendary' },
        { id = 'fashionista', rarity = 'mythic' },
        { id = 'taskmaster', rarity = 'epic' },
        { id = 'big_spender', rarity = 'epic' },
        { id = 'globetrotter', rarity = 'rare' },
        { id = 'seen_it_all', rarity = 'legendary' },
        { id = 'off_the_map', rarity = 'mythic' },
        { id = 'party_crasher', rarity = 'epic' },
        { id = 'supporter', rarity = 'rare' },
        { id = 'people_person', rarity = 'rare' },
        { id = 'pacifist', rarity = 'epic' },
        { id = 'gravity_tester', rarity = 'rare' },
        { id = 'family_feud', rarity = 'epic' },
        { id = 'wrong_house', rarity = 'epic' },
        -- achievements, draft 2 (docs/refs/achievements-02.md; High Roller reuses the pass title above)
        { id = 'unbreakable', rarity = 'legendary' },
        { id = 'combo_machine', rarity = 'epic' },
        { id = 'party_animal', rarity = 'epic' },
        { id = 'champ', rarity = 'rare' },
        { id = 'cup_royalty', rarity = 'legendary' },
        { id = 'socialite', rarity = 'epic' },
        { id = 'arcade_rat', rarity = 'legendary' },
        { id = 'purist', rarity = 'epic' },
        { id = 'local_legend', rarity = 'epic' },
        { id = 'one_trick', rarity = 'rare' },
    },

    -- Every banner: 2-3 gradient colour stops + a pattern the UI draws (protocol.ts
    -- CatalogBanner.pattern: none | stripes | rays | dots | chevrons | skyline | stars | flames |
    -- waves | grid). Owned from the start: StarterBanners.
    StarterBanners = { 'default' },
    Banners = {
        { id = 'default', rarity = 'common', colors = { '#2b2d42', '#1a1b2e' }, pattern = 'none' },
        -- season 1 free track
        { id = 'grove_green', rarity = 'common', colors = { '#0f5132', '#1f8f4e', '#9be15d' }, pattern = 'stripes' },
        { id = 'del_perro_dusk', rarity = 'rare', colors = { '#ff7e5f', '#feb47b', '#6a3093' }, pattern = 'waves' },
        { id = 'vinewood_lights', rarity = 'rare', colors = { '#141e30', '#243b55', '#f7c948' }, pattern = 'skyline' },
        { id = 'sandy_shores', rarity = 'rare', colors = { '#c2a36b', '#e6c88f', '#7a5c2e' }, pattern = 'dots' },
        { id = 'showdown_s1', rarity = 'epic', colors = { '#ff2e63', '#ff9a3c', '#1b1b2f' }, pattern = 'chevrons' },
        -- season 1 premium track
        { id = 'premium_gold', rarity = 'epic', colors = { '#b8860b', '#ffd700', '#fff4b0' }, pattern = 'rays' },
        { id = 'neon_nights', rarity = 'epic', colors = { '#0d0221', '#ff00c8', '#00f0ff' }, pattern = 'grid' },
        { id = 'flame_on', rarity = 'legendary', colors = { '#3a0000', '#ff4500', '#ffd000' }, pattern = 'flames' },
        { id = 'starfield', rarity = 'legendary', colors = { '#020024', '#2c1a6b', '#9fa8ff' }, pattern = 'stars' },
        { id = 'vice_waves', rarity = 'legendary', colors = { '#ff6ec7', '#7873f5', '#48e5c2' }, pattern = 'waves' },
        { id = 's1_champion', rarity = 'mythic', colors = { '#1b1b2f', '#ffd700', '#ff2e63' }, pattern = 'chevrons' },
        -- account levels
        { id = 'arcade_classic', rarity = 'common', colors = { '#1e1e3f', '#3b3b98' }, pattern = 'grid' },
        { id = 'blaine_county', rarity = 'rare', colors = { '#5d4037', '#a1887f', '#ffcc80' }, pattern = 'stripes' },
        { id = 'boomshine', rarity = 'rare', colors = { '#33691e', '#8bc34a', '#ff9800' }, pattern = 'flames' },
        { id = 'golden_hour', rarity = 'legendary', colors = { '#f12711', '#f5af19', '#fff1c1' }, pattern = 'rays' },
        { id = 'aurora', rarity = 'legendary', colors = { '#00c9ff', '#92fe9d', '#7b2ff7' }, pattern = 'waves' },
        { id = 'icon', rarity = 'mythic', colors = { '#000000', '#434343', '#ffd700' }, pattern = 'stars' },
        -- achievements
        { id = 'victory_lap', rarity = 'rare', colors = { '#1d976c', '#93f9b9' }, pattern = 'chevrons' },
        { id = 'online_elite', rarity = 'epic', colors = { '#0f2027', '#2c5364', '#00e5ff' }, pattern = 'grid' },
        { id = 'ko_king', rarity = 'legendary', colors = { '#8e0e00', '#e52d27', '#ffd200' }, pattern = 'rays' },
        { id = 'red_mist', rarity = 'epic', colors = { '#4b0000', '#b31217', '#ff6b6b' }, pattern = 'flames' },
        { id = 'flawless_white', rarity = 'legendary', colors = { '#e0eafc', '#cfdef3', '#ffffff' }, pattern = 'stars' },
        { id = 'hot_streak', rarity = 'legendary', colors = { '#ff512f', '#dd2476', '#ffb199' }, pattern = 'flames' },
        { id = 'full_roster', rarity = 'legendary', colors = { '#232526', '#414345', '#f7971e' }, pattern = 'skyline' },
        { id = 'wardrobe', rarity = 'mythic', colors = { '#fc466b', '#3f5efb', '#ffd6ff' }, pattern = 'dots' },
        { id = 'challenge_master', rarity = 'epic', colors = { '#134e5e', '#71b280' }, pattern = 'chevrons' },
        { id = 'season_complete', rarity = 'mythic', colors = { '#ff2e63', '#ffd700', '#08d9d6' }, pattern = 'rays' },
        { id = 'world_tour', rarity = 'rare', colors = { '#2193b0', '#6dd5ed', '#f9d423' }, pattern = 'skyline' },
        { id = 'passport', rarity = 'legendary', colors = { '#0b2545', '#134074', '#f4d35e' }, pattern = 'stars' },
        { id = 'glitch', rarity = 'epic', colors = { '#00ff41', '#0d0208', '#ff00ff' }, pattern = 'grid' },
    },

    -- ─────────────────────────────────────────────────────────────────────────────────────────
    -- Ranked rewards (docs/RANKED.md "Rank rewards"): ranked-only, never sold, never in the shop.
    -- The ids are `rank_<tier>`; the tier ladder is rookie, hustler, enforcer, lieutenant, boss,
    -- kingpin, legend. What a tier pays (Credits, season skin) lives in configs/server/ranked.lua;
    -- these are the cosmetics. Rookie gives a banner + border only (the starter title `rookie`
    -- stays as it is). Labels: progression.titles.rank_* / banners.rank_* / borders.rank_*.
    -- ─────────────────────────────────────────────────────────────────────────────────────────

    -- Titles merged into Titles (tier = the ladder tier that unlocks it).
    RankedTitles = {
        { id = 'rank_hustler', rarity = 'common', tier = 'hustler' },
        { id = 'rank_enforcer', rarity = 'rare', tier = 'enforcer' },
        { id = 'rank_lieutenant', rarity = 'rare', tier = 'lieutenant' },
        { id = 'rank_boss', rarity = 'epic', tier = 'boss' },
        { id = 'rank_kingpin', rarity = 'legendary', tier = 'kingpin' },
        { id = 'rank_legend', rarity = 'mythic', tier = 'legend' },
    },

    -- Banners merged into Banners; the art task's final colours, all on the `laurel` pattern.
    RankedBanners = {
        { id = 'rank_rookie', rarity = 'common', tier = 'rookie', colors = { '#2a1f18', '#5a4638', '#c0602e' }, pattern = 'laurel' },
        { id = 'rank_hustler', rarity = 'rare', tier = 'hustler', colors = { '#2e170b', '#6e3510', '#d0843f' }, pattern = 'laurel' },
        { id = 'rank_enforcer', rarity = 'rare', tier = 'enforcer', colors = { '#161b26', '#5d667a', '#c4cddc' }, pattern = 'laurel' },
        { id = 'rank_lieutenant', rarity = 'epic', tier = 'lieutenant', colors = { '#2b1d06', '#9a5608', '#ffc83d' }, pattern = 'laurel' },
        { id = 'rank_boss', rarity = 'epic', tier = 'boss', colors = { '#08261a', '#0e6a31', '#3bd16f' }, pattern = 'laurel' },
        { id = 'rank_kingpin', rarity = 'legendary', tier = 'kingpin', colors = { '#0a1d2e', '#2c7896', '#8ff0ff' }, pattern = 'laurel' },
        { id = 'rank_legend', rarity = 'mythic', tier = 'legend', colors = { '#7b2bd6', '#ff3d8b', '#ffe066' }, pattern = 'laurel' },
    },

    -- Player-card borders (reward kind `border`): one per tier, drawn in that tier's style.
    Borders = {
        { id = 'rank_rookie', rarity = 'common', style = 'rookie' },
        { id = 'rank_hustler', rarity = 'common', style = 'hustler' },
        { id = 'rank_enforcer', rarity = 'rare', style = 'enforcer' },
        { id = 'rank_lieutenant', rarity = 'rare', style = 'lieutenant' },
        { id = 'rank_boss', rarity = 'epic', style = 'boss' },
        { id = 'rank_kingpin', rarity = 'legendary', style = 'kingpin' },
        { id = 'rank_legend', rarity = 'mythic', style = 'legend' },
    },

    -- Ranked-exclusive skins: costume indices APPENDED after the 0..5 ladder (the art lives in
    -- web/src/game/content/fighters/<id>.ts costumes). Never sold, never in the shop rotation;
    -- earned only as a season-end ranked reward (configs/server/ranked.lua Rewards.SeasonSkins).
    RankedExclusiveSkins = { franklin = { 6, 7, 8, 9, 10 } },
    -- Rarity of each ranked-exclusive costume index (Enforcer ... Legend colourways).
    RankedSkinRarity = { [6] = 'rare', [7] = 'epic', [8] = 'epic', [9] = 'legendary', [10] = 'mythic' },

    -- ─────────────────────────────────────────────────────────────────────────────────────────
    -- Shop
    -- ─────────────────────────────────────────────────────────────────────────────────────────

    -- Credit packs bought with in-game cash (the arcade's economy account, configs/server/
    -- economy.lua). `bonus` Credits are added on top of `credits` and shown as "+X BONUS".
    CreditPacks = {
        { id = 'pack_s', credits = 500, cash = 5000 },
        { id = 'pack_m', credits = 1200, cash = 10000, bonus = 200 },
        { id = 'pack_l', credits = 3500, cash = 25000, bonus = 700 },
        { id = 'pack_xl', credits = 8000, cash = 50000, bonus = 2000 },
    },

    -- ─────────────────────────────────────────────────────────────────────────────────────────
    -- Earning (published in Catalog.earn; the server applies exactly these numbers)
    -- ─────────────────────────────────────────────────────────────────────────────────────────

    -- Per verified match. "Online" = at least one other human in the match. vs-CPU amounts are
    -- scaled by the average CPU level (CpuLevelScale.Min at level 1 -> Max at level 9) and capped
    -- per day (configs/server/progression.lua CpuDailyCap). PerKo is added per KO landed.
    Earn = {
        Online = { Win = 150, Loss = 60, PerKo = 15, XpWin = 250, XpLoss = 120 },
        Cpu = { Win = 50, Loss = 20, PerKo = 5, XpWin = 100, XpLoss = 50 },
        CpuLevelScale = { Min = 0.4, Max = 1.0 },
        FirstWin = { Credits = 300, Xp = 500 }, -- first ONLINE win of the day
        SeasonXpRate = 1.0, -- season XP = this x the account XP a match earns
    },

    -- ─────────────────────────────────────────────────────────────────────────────────────────
    -- Account levels
    -- ─────────────────────────────────────────────────────────────────────────────────────────

    -- XP to go from level L to L+1 = min(Max, Base + Step * (L - 1)).
    LevelCurve = { Base = 400, Step = 100, Max = 3000 },

    -- Levels stop here (XP past the cap still counts toward the season pass).
    MaxLevel = 999,

    -- Every level-up pays this; levels up to CatalogLevels are listed in Catalog.levelRewards.
    EveryLevel = { CR(100) },
    CatalogLevels = 100,

    -- Extra rewards on milestone levels (on top of EveryLevel).
    LevelMilestones = {
        [5] = { TITLE('up_and_comer'), CR(250) },
        [10] = { BANNER('arcade_classic'), SKIN('michael', 1), CR(500) },
        [15] = { FIGHTER('johnny'), TITLE('road_warrior') },
        [20] = { SKIN('lamar', 2), BANNER('blaine_county'), CR(500) },
        [25] = { TITLE('veteran'), CR(1000) },
        [30] = { FIGHTER('phil'), BANNER('boomshine') },
        [40] = { SKIN('cj', 3), TITLE('street_legend'), CR(1000) },
        [50] = { SKIN('niko', 4), TITLE('legend'), BANNER('golden_hour'), CR(2000) },
        [75] = { TITLE('hall_of_famer'), BANNER('aurora'), CR(3000) },
        [100] = { SKIN('pogo', 5), TITLE('smashdown_icon'), BANNER('icon'), CR(5000) },
    },

    -- ─────────────────────────────────────────────────────────────────────────────────────────
    -- Season 1 - "Los Santos Showdown"
    -- ─────────────────────────────────────────────────────────────────────────────────────────

    -- The season's id is what separates one season's pass progress from the next: change it (and
    -- the rewards + configs/server/progression.lua Season.StartDate) to start Season 2 - every
    -- profile's old pass state is archived and a fresh one begins. Dates live server-side.
    Season = {
        Id = 's1',
        Name = 'SEASON 1',
        Subtitle = 'Los Santos Showdown',
        Tiers = 50,
        XpPerTier = 1500,
        -- Premium track price in in-game cash (false = premium track disabled). Buyable any time
        -- during the season; unlocks every premium reward already reached, retroactively.
        PremiumCash = 50000,
        Colors = { '#ff2e63', '#ffd700', '#08d9d6' },

        -- tier -> { free = Reward?, premium = Reward? }. Free: every other tier + milestones.
        -- Premium: every tier (James at 1, his mythic at 50, the 10 pass-exclusive mythics).
        Rewards = {
            [1] = { free = TITLE('contender'), premium = FIGHTER('james') },
            [2] = { free = CR(150), premium = CR(300) },
            [3] = { premium = BANNER('premium_gold') },
            [4] = { free = SKIN('franklin', 1), premium = SKIN('michael', 3) },
            [5] = { free = BANNER('grove_green'), premium = SKIN('franklin', 5) },
            [6] = { free = CR(200), premium = CR(300) },
            [7] = { premium = TITLE('high_roller') },
            [8] = { free = SKIN('lamar', 1), premium = SKIN('trevor', 3) },
            [9] = { premium = SKIN('cj', 5) },
            [10] = { free = TITLE('brawler'), premium = CR(1000) },
            [11] = { premium = SKIN('lamar', 3) },
            [12] = { free = CR(250), premium = BANNER('neon_nights') },
            [13] = { premium = CR(400) },
            [14] = { free = SKIN('niko', 1), premium = SKIN('trevor', 5) },
            [15] = { free = BANNER('del_perro_dusk'), premium = SKIN('niko', 3) },
            [16] = { free = CR(300), premium = TITLE('kingpin') },
            [17] = { premium = CR(400) },
            [18] = { free = SKIN('cj', 2), premium = SKIN('michael', 5) },
            [19] = { premium = SKIN('james', 1) },
            [20] = { free = FIGHTER('wade'), premium = CR(1500) },
            [21] = { premium = SKIN('cj', 4) },
            [22] = { free = CR(300), premium = BANNER('flame_on') },
            [23] = { premium = SKIN('niko', 5) },
            [24] = { free = SKIN('michael', 2), premium = CR(500) },
            [25] = { free = TITLE('showdown_regular'), premium = FIGHTER('lucia') },
            [26] = { free = CR(350), premium = SKIN('james', 2) },
            [27] = { premium = TITLE('untouchable') },
            [28] = { free = BANNER('vinewood_lights'), premium = SKIN('lamar', 5) },
            [29] = { premium = CR(500) },
            [30] = { free = SKIN('trevor', 2), premium = CR(2000) },
            [31] = { premium = SKIN('lucia', 1) },
            [32] = { free = CR(400), premium = SKIN('tommy', 3) },
            [33] = { premium = SKIN('tommy', 5) },
            [34] = { free = SKIN('pogo', 1), premium = BANNER('starfield') },
            [35] = { free = BANNER('sandy_shores'), premium = CR(600) },
            [36] = { free = CR(400), premium = SKIN('james', 3) },
            [37] = { premium = SKIN('michael', 4) },
            [38] = { free = SKIN('bigsmoke', 2), premium = SKIN('bigfoot', 5) },
            [39] = { premium = TITLE('showstopper') },
            [40] = { free = FIGHTER('maude'), premium = CR(2500) },
            [41] = { premium = SKIN('lucia', 3) },
            [42] = { free = CR(450), premium = BANNER('vice_waves') },
            [43] = { premium = SKIN('james', 4) },
            [44] = { free = TITLE('los_santos_finest'), premium = SKIN('lucia', 5) },
            [45] = { free = BANNER('showdown_s1'), premium = CR(750) },
            [46] = { free = CR(500), premium = SKIN('trevor', 4) },
            [47] = { premium = TITLE('season_one_legend') },
            [48] = { free = SKIN('wade', 2), premium = BANNER('s1_champion') },
            [49] = { premium = CR(1000) },
            [50] = { free = SKIN('franklin', 4), premium = SKIN('james', 5) },
        },
    },

    -- ─────────────────────────────────────────────────────────────────────────────────────────
    -- Achievements
    -- ─────────────────────────────────────────────────────────────────────────────────────────

    -- Lifetime objectives, tracked server-side from verified matches and profile events. Tiered
    -- FAMILIES expand into one achievement per tier (id = <group>_<tier>, 1 = bronze ...); the
    -- two per-fighter families expand into one achievement per fighter (win_<id>, main_<id>);
    -- FEATS are visible one-offs (id as given); SECRET ones are hidden ("???") until done.
    --
    -- metric: what is counted (server/brawl/progress_achievements.lua defines them):
    --   wins, online_wins, matches, online_matches, kos, dealt, flawless, comebacks, best_streak,
    --   ffa_wins, stages_won, rivals, full_house, fighters_owned, skins_owned, titles_owned,
    --   level, challenges_done, pass_tier, credits_spent, premium, won:<fighter>,
    --   played:<fighter>,
    --   taken (lifetime damage taken), best_combo, party_matches, cup_match_wins, cups_won,
    --   ranked_tier (best ladder tier ever, 1 = rookie .. 7 = legend), friends (most friends at
    --   once), days_played, wager_won (lifetime wager winnings, $), items_off_wins,
    --   best_stage_wins (wins on your best single stage), series:<series> (fighters of that series
    --   you won with - the goal defaults to the series' size), fighters_won, best_played,
    --   feat_* / secret_* (single-match events, see server/brawl/progress_rewards.lua)
    -- Labels: progression.achievements.<id>.name / .desc with {n} = goal ({fighter} / {stage}).
    Achievements = {
        Families = {
            { group = 'wins', category = 'victory', metric = 'wins', tiers = {
                { goal = 1, rarity = 'common', rewards = { CR(100) } },
                { goal = 10, rarity = 'rare', rewards = { CR(300) } },
                { goal = 50, rarity = 'epic', rewards = { CR(1000), BANNER('victory_lap') } },
                { goal = 250, rarity = 'legendary', rewards = { CR(3000), TITLE('champion') } },
            } },
            { group = 'online_wins', category = 'social', metric = 'online_wins', tiers = {
                { goal = 1, rarity = 'common', rewards = { CR(150) } },
                { goal = 25, rarity = 'rare', rewards = { CR(750) } },
                { goal = 100, rarity = 'epic', rewards = { CR(2000), TITLE('online_menace'), BANNER('online_elite') } },
                { goal = 500, rarity = 'mythic', rewards = { CR(5000), TITLE('unstoppable') } },
            } },
            { group = 'matches', category = 'combat', metric = 'matches', tiers = {
                { goal = 10, rarity = 'common', rewards = { CR(100) } },
                { goal = 100, rarity = 'rare', rewards = { CR(500) } },
                { goal = 500, rarity = 'epic', rewards = { CR(1500) } },
                { goal = 1000, rarity = 'legendary', rewards = { CR(3000) } },
            } },
            { group = 'online_matches', category = 'social', metric = 'online_matches', tiers = {
                { goal = 10, rarity = 'common', rewards = { CR(150) } },
                { goal = 50, rarity = 'rare', rewards = { CR(500) } },
                { goal = 250, rarity = 'epic', rewards = { CR(1500) } },
            } },
            { group = 'rivals', category = 'social', metric = 'rivals', tiers = {
                { goal = 5, rarity = 'rare', rewards = { CR(300) } },
                { goal = 25, rarity = 'epic', rewards = { CR(1000), TITLE('people_person') } },
            } },
            { group = 'full_house', category = 'social', metric = 'full_house', tiers = {
                { goal = 1, rarity = 'rare', rewards = { CR(300) } },
            } },
            { group = 'kos', category = 'combat', metric = 'kos', tiers = {
                { goal = 10, rarity = 'common', rewards = { CR(100) } },
                { goal = 100, rarity = 'rare', rewards = { CR(500) } },
                { goal = 500, rarity = 'epic', rewards = { CR(1500), TITLE('knockout_artist') } },
                { goal = 2500, rarity = 'legendary', rewards = { CR(4000), TITLE('wrecking_ball'), BANNER('ko_king') } },
            } },
            { group = 'damage', category = 'combat', metric = 'dealt', tiers = {
                { goal = 1000, rarity = 'common', rewards = { CR(100) } },
                { goal = 10000, rarity = 'rare', rewards = { CR(750) } },
                { goal = 100000, rarity = 'epic', rewards = { CR(2500), TITLE('heavy_hitter'), BANNER('red_mist') } },
            } },
            { group = 'flawless', category = 'victory', metric = 'flawless', tiers = {
                { goal = 1, rarity = 'rare', rewards = { CR(250) } },
                { goal = 10, rarity = 'epic', rewards = { CR(1000) } },
                { goal = 50, rarity = 'legendary', rewards = { CR(3000), TITLE('untouched'), BANNER('flawless_white') } },
            } },
            { group = 'comeback', category = 'victory', metric = 'comebacks', tiers = {
                { goal = 1, rarity = 'rare', rewards = { CR(250) } },
                { goal = 10, rarity = 'epic', rewards = { CR(1000), TITLE('comeback_kid') } },
            } },
            { group = 'streak', category = 'victory', metric = 'best_streak', tiers = {
                { goal = 3, rarity = 'common', rewards = { CR(200) } },
                { goal = 5, rarity = 'rare', rewards = { CR(600) } },
                { goal = 10, rarity = 'legendary', rewards = { CR(2500), TITLE('on_fire'), BANNER('hot_streak') } },
            } },
            { group = 'ffa', category = 'victory', metric = 'ffa_wins', tiers = {
                { goal = 1, rarity = 'rare', rewards = { CR(300) } },
                { goal = 25, rarity = 'epic', rewards = { CR(1500), TITLE('party_crasher') } },
            } },
            -- Win on N different stages, of the 25 the game ships (the 6 neutral stages: the 4 launch
            -- stages + legion and golf; the 11 mechanics stages; the 8 phase-shift stages). Tiers are
            -- append-only (ids world_tour_<n>): 15 was "every stage" until #16-23 arrived; 20 means
            -- most of the later stages too. A tier above the number of ENABLED stages is flagged at
            -- boot as unobtainable.
            { group = 'world_tour', category = 'victory', metric = 'stages_won', tiers = {
                { goal = 5, rarity = 'rare', rewards = { CR(500), TITLE('globetrotter') } },
                { goal = 10, rarity = 'epic', rewards = { CR(1500), BANNER('world_tour') } },
                { goal = 15, rarity = 'legendary', rewards = { CR(4000), TITLE('seen_it_all'), BANNER('passport') } },
                { goal = 20, rarity = 'mythic', rewards = { CR(5000), TITLE('off_the_map') } },
            } },
            { group = 'unlock', category = 'collection', metric = 'fighters_owned', tiers = {
                { goal = 10, rarity = 'rare', rewards = { CR(500) } },
                { goal = 20, rarity = 'epic', rewards = { CR(1500) } },
                { goal = 50, rarity = 'legendary', rewards = { CR(5000), TITLE('collector'), BANNER('full_roster') } }, -- every fighter (50)
            } },
            { group = 'skins', category = 'collection', metric = 'skins_owned', tiers = {
                { goal = 25, rarity = 'rare', rewards = { CR(500) } },
                { goal = 75, rarity = 'epic', rewards = { CR(2000) } },
                { goal = 150, rarity = 'mythic', rewards = { CR(5000), TITLE('fashionista'), BANNER('wardrobe') } },
            } },
            { group = 'titles', category = 'collection', metric = 'titles_owned', tiers = {
                { goal = 10, rarity = 'rare', rewards = { CR(500) } },
                { goal = 25, rarity = 'epic', rewards = { CR(1500) } },
            } },
            { group = 'level', category = 'progression', metric = 'level', tiers = {
                { goal = 10, rarity = 'common', rewards = { CR(250) } },
                { goal = 25, rarity = 'rare', rewards = { CR(750) } },
                { goal = 50, rarity = 'epic', rewards = { CR(2000) } },
                { goal = 100, rarity = 'legendary', rewards = { CR(5000) } },
            } },
            { group = 'challenges', category = 'progression', metric = 'challenges_done', tiers = {
                { goal = 10, rarity = 'common', rewards = { CR(300) } },
                { goal = 50, rarity = 'rare', rewards = { CR(1000) } },
                { goal = 200, rarity = 'epic', rewards = { CR(3000), TITLE('taskmaster'), BANNER('challenge_master') } },
            } },
            { group = 'pass', category = 'progression', metric = 'pass_tier', tiers = {
                { goal = 25, rarity = 'rare', rewards = { CR(500) } },
                { goal = 50, rarity = 'mythic', rewards = { CR(2000), BANNER('season_complete') } },
            } },
            { group = 'spender', category = 'progression', metric = 'credits_spent', tiers = {
                { goal = 10000, rarity = 'rare', rewards = { CR(500) } },
                { goal = 100000, rarity = 'epic', rewards = { CR(3000), TITLE('big_spender') } },
            } },
            { group = 'supporter', category = 'progression', metric = 'premium', tiers = {
                { goal = 1, rarity = 'rare', rewards = { CR(500), TITLE('supporter') } },
            } },
            -- ── draft 2 (docs/refs/achievements-02.md section A) ──
            -- Total damage taken, in % (lifetime, from the replay verdicts).
            { group = 'punching_bag', category = 'combat', metric = 'taken', tiers = {
                { goal = 5000, rarity = 'common', rewards = { CR(200) } },
                { goal = 50000, rarity = 'rare', rewards = { CR(750) } },
                { goal = 250000, rarity = 'legendary', rewards = { CR(4000), TITLE('unbreakable') } },
            } },
            -- Longest combo landed in one match (the verifier's maxCombo).
            { group = 'combo', category = 'combat', metric = 'best_combo', tiers = {
                { goal = 5, rarity = 'common', rewards = { CR(150) } },
                { goal = 10, rarity = 'rare', rewards = { CR(500) } },
                { goal = 20, rarity = 'epic', rewards = { CR(1500), TITLE('combo_machine') } },
            } },
            -- Matches played in a party lobby.
            { group = 'party', category = 'social', metric = 'party_matches', tiers = {
                { goal = 10, rarity = 'common', rewards = { CR(150) } },
                { goal = 50, rarity = 'rare', rewards = { CR(500) } },
                { goal = 200, rarity = 'epic', rewards = { CR(1500), TITLE('party_animal') } },
            } },
            -- Tournament (cup) matches won.
            { group = 'cup_wins', category = 'victory', metric = 'cup_match_wins', tiers = {
                { goal = 1, rarity = 'common', rewards = { CR(200) } },
                { goal = 10, rarity = 'rare', rewards = { CR(750) } },
                { goal = 50, rarity = 'epic', rewards = { CR(1500) } },
            } },
            -- Tournaments won (the champion's prize delivered).
            { group = 'cups', category = 'victory', metric = 'cups_won', tiers = {
                { goal = 1, rarity = 'rare', rewards = { CR(750), TITLE('champ') } },
                { goal = 5, rarity = 'epic', rewards = { CR(1500) } },
                { goal = 20, rarity = 'legendary', rewards = { CR(5000), TITLE('cup_royalty') } },
            } },
            -- Ranked tier reached (ladder index: 2 Hustler, 4 Lieutenant, 6 Kingpin, 7 Legend).
            -- Credits only: ranked already pays each tier's cosmetics.
            { group = 'ladder', category = 'progression', metric = 'ranked_tier', tiers = {
                { goal = 2, rarity = 'common', rewards = { CR(250) } },
                { goal = 4, rarity = 'rare', rewards = { CR(750) } },
                { goal = 6, rarity = 'legendary', rewards = { CR(3000) } },
                { goal = 7, rarity = 'mythic', rewards = { CR(5000) } },
            } },
            -- Friends on your list (the most you have had at once).
            { group = 'friends', category = 'social', metric = 'friends', tiers = {
                { goal = 1, rarity = 'common', rewards = { CR(100) } },
                { goal = 10, rarity = 'rare', rewards = { CR(500) } },
                { goal = 25, rarity = 'epic', rewards = { CR(1000), TITLE('socialite') } },
            } },
            -- Different days with a verified match (the server's daily reset clock).
            { group = 'regular', category = 'progression', metric = 'days_played', tiers = {
                { goal = 7, rarity = 'common', rewards = { CR(250) } },
                { goal = 30, rarity = 'rare', rewards = { CR(750) } },
                { goal = 100, rarity = 'legendary', rewards = { CR(3000), TITLE('arcade_rat') } },
            } },
            -- Lifetime wager winnings in $ (payout minus your own stake, per wager won).
            { group = 'high_roller', category = 'social', metric = 'wager_won', tiers = {
                { goal = 10000, rarity = 'rare', rewards = { CR(500) } },
                { goal = 100000, rarity = 'epic', rewards = { CR(1500) } },
                { goal = 1000000, rarity = 'legendary', rewards = { CR(5000), TITLE('high_roller') } },
            } },
            -- Wins with items off.
            { group = 'fair_fight', category = 'victory', metric = 'items_off_wins', tiers = {
                { goal = 25, rarity = 'rare', rewards = { CR(500) } },
                { goal = 100, rarity = 'epic', rewards = { CR(1500), TITLE('purist') } },
            } },
            -- Wins on one single stage (your best one).
            { group = 'home_turf', category = 'victory', metric = 'best_stage_wins', tiers = {
                { goal = 25, rarity = 'rare', rewards = { CR(500) } },
                { goal = 100, rarity = 'epic', rewards = { CR(1500), TITLE('local_legend') } },
            } },
        },

        -- One per fighter each: "win a match as <fighter>" and "Main: <fighter>" (play N as them).
        PerFighter = {
            Win = { category = 'fighters', goal = 1, rarity = 'common', rewards = { CR(150) } },
            Main = { category = 'fighters', goal = 25, rarity = 'rare', rewards = { CR(400) } },
        },

        -- Hidden until done ("???" in the list). Tracked from single-match events by the server.
        Secret = {
            { id = 'secret_pacifist', metric = 'secret_pacifist', rarity = 'epic', rewards = { CR(750), TITLE('pacifist') } }, -- win without landing a KO
            { id = 'secret_gravity', metric = 'secret_gravity', rarity = 'rare', rewards = { CR(300), TITLE('gravity_tester') } }, -- self-destruct 3 times in one match
            { id = 'secret_marathon', metric = 'secret_marathon', rarity = 'epic', rewards = { CR(500), BANNER('glitch') } }, -- a match lasting over 7 minutes
            { id = 'secret_speedrun', metric = 'secret_speedrun', rarity = 'epic', rewards = { CR(750) } }, -- win a 3+ stock match in under a minute of play
            { id = 'secret_perfect_four', metric = 'secret_perfect_four', rarity = 'legendary', rewards = { CR(2000) } }, -- win a 4-fighter match without losing a stock
            { id = 'secret_family_feud', metric = 'secret_family_feud', rarity = 'epic', rewards = { CR(500), TITLE('family_feud') } }, -- beat a Trevor as Wade
            { id = 'secret_wrong_house', metric = 'secret_wrong_house', rarity = 'epic', rewards = { CR(500), TITLE('wrong_house') } }, -- beat a Deuce as Big Biscuit
            { id = 'secret_house_call', metric = 'secret_house_call', rarity = 'epic', rewards = { CR(500) } }, -- beat a Madrazo as Michael
            { id = 'secret_kifflom', metric = 'secret_kifflom', rarity = 'rare', rewards = { CR(300) } }, -- win as Cris on Vinewood
            { id = 'secret_good_doggo', metric = 'secret_good_doggo', rarity = 'legendary', rewards = { CR(1000) } }, -- win flawless as James
            -- draft 2 (docs/refs/achievements-02.md section D): lore & silly
            { id = 'secret_roast', metric = 'secret_roast', rarity = 'rare', rewards = { CR(300) } }, -- beat a Franklin as Lamar
            { id = 'secret_ludendorff', metric = 'secret_ludendorff', rarity = 'rare', rewards = { CR(300) } }, -- win as Michael or Trevor on North Yankton
            { id = 'secret_monkey_business', metric = 'secret_monkey_business', rarity = 'rare', rewards = { CR(300) } }, -- win as Pogo on Humane Labs
            { id = 'secret_sasquatch', metric = 'secret_sasquatch', rarity = 'rare', rewards = { CR(300) } }, -- win as Bigfoot on Chiliad Four Seasons
            { id = 'secret_private_contractor', metric = 'secret_private_contractor', rarity = 'rare', rewards = { CR(300) } }, -- win as the Merryweather Operator on Fort Zancudo
            { id = 'secret_big_score', metric = 'secret_big_score', rarity = 'rare', rewards = { CR(300) } }, -- win as Lester on The Big One
            { id = 'secret_space_rangers', metric = 'secret_space_rangers', rarity = 'rare', rewards = { CR(300) } }, -- win as the Commander on The Mothership
            { id = 'secret_bail_enforcement', metric = 'secret_bail_enforcement', rarity = 'rare', rewards = { CR(300) } }, -- beat a Trevor as Maude
            { id = 'secret_ride_or_die', metric = 'secret_ride_or_die', rarity = 'epic', rewards = { CR(500) } }, -- win a 2v2 with Colt and Marisol on your team
            { id = 'secret_grove_street', metric = 'secret_grove_street', rarity = 'rare', rewards = { CR(300) } }, -- win as Deuce on Grove Street
            { id = 'secret_house_wins', metric = 'secret_house_wins', rarity = 'epic', rewards = { CR(500) } }, -- win a wager match on the Diamond Casino
            { id = 'secret_flight_risk', metric = 'secret_flight_risk', rarity = 'epic', rewards = { CR(500) } }, -- win on Wing Walker without falling once
            { id = 'secret_carried', metric = 'secret_carried', rarity = 'epic', rewards = { CR(500) } }, -- win a 2v2 without dealing any damage
            { id = 'secret_self_own', metric = 'secret_self_own', rarity = 'rare', rewards = { CR(300) } }, -- lose a match where every fall was a self-destruct
        },

        -- Visible one-off feats (docs/refs/achievements-02.md sections B + C): shown like any other
        -- achievement, never hidden. goal defaults to 1; a series:<series> metric's goal defaults to
        -- that series' size (every fighter of it, from Fighters above). The single-match ones
        -- (feat_*) are counted by server/brawl/progress_rewards.lua (Underdog by the ranked service).
        Feats = {
            -- B. Series mastery: win a match with every fighter of a series.
            { id = 'series_story', category = 'fighters', metric = 'series:story', rarity = 'epic', rewards = { CR(1500) } },
            { id = 'series_classic', category = 'fighters', metric = 'series:classic', rarity = 'epic', rewards = { CR(1000) } },
            { id = 'series_pop', category = 'fighters', metric = 'series:pop', rarity = 'legendary', rewards = { CR(3000) } },
            { id = 'series_vi', category = 'fighters', metric = 'series:vi', rarity = 'epic', rewards = { CR(1500) } },
            { id = 'series_creator', category = 'fighters', metric = 'series:creator', rarity = 'mythic', rewards = { CR(5000) } },
            -- C. One-off feats.
            { id = 'feat_overkill', category = 'combat', metric = 'feat_overkill', rarity = 'epic', rewards = { CR(1000) } }, -- deal 500%+ in one match
            { id = 'feat_untouchable', category = 'combat', metric = 'feat_untouchable', rarity = 'epic', rewards = { CR(1000) } }, -- win a 3+ stock match taking under 50%
            { id = 'feat_iron_wall', category = 'combat', metric = 'feat_iron_wall', rarity = 'rare', rewards = { CR(500) } }, -- win after taking 400%+
            { id = 'feat_down_to_the_wire', category = 'victory', metric = 'feat_down_to_the_wire', rarity = 'rare', rewards = { CR(500) } }, -- win while sitting at 150%+
            { id = 'feat_last_one_standing', category = 'victory', metric = 'feat_last_one_standing', rarity = 'epic', rewards = { CR(1000) } }, -- win a 4-fighter FFA from your final stock
            { id = 'feat_clean_sweep', category = 'social', metric = 'feat_clean_sweep', rarity = 'epic', rewards = { CR(1000) } }, -- win a 2v2 without your team losing a stock
            { id = 'feat_carry', category = 'social', metric = 'feat_carry', rarity = 'rare', rewards = { CR(500) } }, -- win a 2v2 landing every one of your team's KOs
            { id = 'feat_squad_goals', category = 'social', metric = 'feat_squad_goals', rarity = 'common', rewards = { CR(200) } }, -- win a 2v2 with a friend as your teammate
            { id = 'feat_mirror_match', category = 'victory', metric = 'feat_mirror_match', rarity = 'rare', rewards = { CR(400) } }, -- win a match where everyone plays the same fighter
            { id = 'feat_high_stakes', category = 'social', metric = 'feat_high_stakes', rarity = 'epic', rewards = { CR(1500) } }, -- win a wager match with a $10,000+ pot
            { id = 'feat_dressed_to_kill', category = 'collection', metric = 'feat_dressed_to_kill', rarity = 'rare', rewards = { CR(500) } }, -- win wearing a mythic skin
            { id = 'feat_jack_of_all_trades', category = 'fighters', metric = 'fighters_won', goal = 25, rarity = 'epic', rewards = { CR(1500) } }, -- win with 25 different fighters
            { id = 'feat_one_trick', category = 'fighters', metric = 'best_played', goal = 100, rarity = 'rare', rewards = { CR(750), TITLE('one_trick') } }, -- play 100 matches as one fighter
            { id = 'feat_photo_finish', category = 'victory', metric = 'feat_photo_finish', rarity = 'rare', rewards = { CR(500) } }, -- win on time by one stock or less
            { id = 'feat_underdog', category = 'victory', metric = 'feat_underdog', rarity = 'epic', rewards = { CR(1500) } }, -- win a ranked 1v1 vs someone 2+ tiers above
        },
    },
}