From 3ceed4382f440ef0fcf77c96fa0e70b1b781f5aa Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 30 Sep 2026 17:28:26 +0000 Subject: [PATCH] Admin Progression page: quest limits, XP curve and rates, per-player XP adjustments Co-Authored-By: Claude Sonnet 5.5 Claude-Session: https://claude.ai/code/session_01DjMbLQujBHunCCu5GpsHaT --- panel/web/src/App.svelte | 3 + panel/web/src/components/Sidebar.svelte | 3 +- panel/web/src/lib/types.ts | 30 ++ panel/web/src/pages/LevelingAdmin.svelte | 14 +- panel/web/src/pages/Progression.svelte | 409 +++++++++++++++++++++++ panel/web/src/pages/QuestEditor.svelte | 30 +- 6 files changed, 481 insertions(+), 8 deletions(-) create mode 100644 panel/web/src/pages/Progression.svelte diff --git a/panel/web/src/App.svelte b/panel/web/src/App.svelte index 07f0cb4..954de30 100644 --- a/panel/web/src/App.svelte +++ b/panel/web/src/App.svelte @@ -17,6 +17,7 @@ import QuestEditor from './pages/QuestEditor.svelte'; import AchievementEditor from './pages/AchievementEditor.svelte'; import LevelingAdmin from './pages/LevelingAdmin.svelte'; + import Progression from './pages/Progression.svelte'; import GuildsAdmin from './pages/GuildsAdmin.svelte'; import { get } from './lib/api'; import { route } from './lib/router.svelte'; @@ -86,6 +87,8 @@ {:else if route.name === 'leveling'} + {:else if route.name === 'progression'} + {:else if route.name === 'guilds'} {:else if route.name === 'capes'} diff --git a/panel/web/src/components/Sidebar.svelte b/panel/web/src/components/Sidebar.svelte index 0cc96f5..d834a8d 100644 --- a/panel/web/src/components/Sidebar.svelte +++ b/panel/web/src/components/Sidebar.svelte @@ -1,5 +1,5 @@ @@ -166,7 +169,8 @@

Total XP required for level L is given by:

- XP = round(L ^ 1.5 × 100) + XP = round(L ^ {curve.exponent} × {curve.base}) +

{curve.max ? `Level cap: ${curve.max}.` : 'No level cap.'} Tune the curve →

  • Level 5: {calcXp(5).toLocaleString()} XP
  • Level 10: {calcXp(10).toLocaleString()} XP
  • @@ -186,10 +190,10 @@
    #{i + 1}
    - UUID: {p.uuid.slice(0, 8)}... - {p.current_xp.toLocaleString()} XP + {(p as any).username ?? `UUID: ${p.uuid.slice(0, 8)}…`} + {Number((p as any).global_xp ?? p.current_xp ?? 0).toLocaleString()} XP
    - Lvl {p.level} + Lvl {(p as any).global_level ?? p.level}
    {/each} diff --git a/panel/web/src/pages/Progression.svelte b/panel/web/src/pages/Progression.svelte new file mode 100644 index 0000000..3f631ee --- /dev/null +++ b/panel/web/src/pages/Progression.svelte @@ -0,0 +1,409 @@ + + +
    +
    +
    +

    Progression

    +

    Decide how many quests players get, how fast they earn XP, how steep levels are — and fix any player's XP by hand.

    +
    +
    + +
    + + + +
    + + {#if tab === 'quests'} +
    + {#each quests as c (c.key)} +
    +
    + +

    {c.label}

    {c.hint}

    +
    +
    + +
    + + {s[c.key] === 0 ? 'every quest' : s[c.key] === 1 ? 'quest per player' : 'quests per player'} +
    + +
    + +
    + {c.pool.enabled} enabled in the pool + {c.pool.pinned} pinned (always included) +
    + {#if s[c.key] > 0 && s[c.key] < c.pool.pinned} +

    Only {s[c.key]} of the {c.pool.pinned} pinned quests fit — unpin some or raise the limit.

    + {/if} +
    + {/each} +
    + +
    +

    Rotation

    +
    + + +
    +

    + A player's set is fixed the first time they open their quests in a period, so edits here never swap out quests they've started. + Raising a limit adds quests, lowering it trims the list. Pin quests on the Quests page to always include them. +

    +
    + {:else if tab === 'xp'} +
    +
    +

    Level curve

    +

    XP needed for level L is round(base × Lexponent).

    +
    + + + +
    + + + + +
    Level 2Level {chart.top} · {n(chart.maxXp)} XP
    +
      + {#each sampleLevels as l}
    • Level {l}{n(xpFor(l))} XP
    • {/each} +
    + {#if curveChanged}

    Saving recalculates every player's stored level from their XP. Nobody loses XP.

    {/if} +
    + +
    +

    XP earned from play

    +
    + + + + + + +
    +

    Multipliers

    +
    + + +
    +

    These stack with the multipliers in each server's plugin/mod config (server levels default to 1.5× there). Quest and achievement XP is set per quest/achievement.

    +
    +
    + {:else} +
    + + {#if loadingPlayers && !players.length} +
    + {:else if !players.length} +

    No players match.

    + {:else} +
    + {#each players as p (p.uuid)} + + {/each} +
    + {/if} +
    + {/if} +
    + +{#if dirty} +
    + You have unsaved progression changes. + + +
    +{/if} + + + {#if detail} +
    + +
    + {detail.name} + Global level {detail.global.level} · {n(detail.global.xp)} XP +
    +
    +
    + + + +
    + {#each modes as m} + + {/each} +
    + + {#if mode !== 'reset'} + + {/if} + + +
    + {n(currentXp)} XP (level {levelFrom(currentXp)}) + → + {n(nextXp)} XP (level {levelFrom(nextXp)}) +
    + {/if} + {#snippet footer()} + + + {/snippet} +
    + + diff --git a/panel/web/src/pages/QuestEditor.svelte b/panel/web/src/pages/QuestEditor.svelte index a2826b9..9330550 100644 --- a/panel/web/src/pages/QuestEditor.svelte +++ b/panel/web/src/pages/QuestEditor.svelte @@ -1,6 +1,6 @@