diff --git a/README.md b/README.md index 5aa3139..923473d 100644 --- a/README.md +++ b/README.md @@ -47,7 +47,7 @@ New registrations receive **100.00** starting credits and **100 osu** (`STARTING ## Features - Login / register (username + password) -- **Inventory**: sort by recent or value, multi-select sell at **instance** value (float/wear), bulk **Sell below** threshold, **favorites** (★ — cannot be sold or auto-sold; can still be bet in battles) +- **Inventory**: sort by recent or value, multi-select sell at **instance** value (float/wear), bulk **Sell below** threshold, **favorites** (★ — cannot be sold or auto-sold; can still be bet in battles), **Vault** staking (up to **3** slots by default; +1 per Vault Capacity skill) — staked items earn dividends every **10 min** (10% value online / 1% offline, scaled by float² wear and rarity) - **Auto-sell**: optional threshold (shared with bulk sell); on **case opens**, credits are applied after the reel finishes; on **battle wins**, items taken from other players below the threshold are sold immediately - **Wear (float)**: each case drop gets a random float `0–1` and paint seed; grades FN / MW / FT / WW / BS (CS ranges). Catalog `marketValue` is the **Field-Tested** reference; instance `valueCents` scales by float within the wear band (FN up to +50%, MW +15–30%, FT mid ≈ catalog, WW down to −40%, BS lower) - **Feed Drop**: toggle in nav — live left column of latest case unboxes + MultiPlayers Battle winners @@ -61,7 +61,7 @@ New registrations receive **100.00** starting credits and **100 osu** (`STARTING - Profile (username in nav): avatar, bio, password, stats, logout - **Presence**: online status (Socket.IO), total play time, last connection — shown on own and public profiles - **Achievements** (`/achievements`, also from profile): 10 starter badges unlocked by play (cases, battles, catalog, prestige, shop, playtime); earned badges appear on the profile -- **Prestige**: pay **1 000 000 000 000.00** cr to reset; earn **Pr** (1000 base + ~1000 per **100 000 000 000.00** cr excess); spend Pr on a skill tree (chance / sell gain / key progress) +- **Prestige**: pay **1 000 000 000 000.00** cr to reset; earn **Pr** (1000 base + ~1000 per **100 000 000 000.00** cr excess); spend Pr on a skill tree (Fortune, Yield, Polish, Bulk, Discount, Quality, noTime, Refund, Respin, Vault Capacity, Offline/Online Yield) - **Leaderboard**: richest players (balance + inventory value); click a row to open a public **player profile** (`/player/:username`) with stats and inventory - Admin: overview dashboard, players (credit adjust), cases / items / drops (split pages, search filters) @@ -115,10 +115,10 @@ Typical **prod → local** flow: `docker-db-export.sh` on the server → `scp` t ## Notes - Balances and prices are integers in **cents**; **osu** is a separate demo currency for the shop -- Catalog item price = Field-Tested reference; each inventory row stores `floatValue`, `wear`, `paintSeed`, `valueCents`, `favorite` +- Catalog item price = Field-Tested reference; each inventory row stores `floatValue`, `wear`, `paintSeed`, `valueCents`, `favorite`, optional vault `staked` / `stakedAt` / `lastYieldAt` - Per-user auto-sell settings: `autoSellEnabled`, `autoSellThresholdCents` (default threshold **10.00** cr) - Case key progress stored in `CaseKeyProgress` (per user + case); backfill: `npm run db:backfill-case-keys --prefix server` -- Inventory items locked while deposited in a bet; unlocked or transferred on resolve +- Inventory items locked while deposited in a bet; unlocked or transferred on resolve; staked vault items cannot be sold, auto-sold, or bet - Avatars / uploads under `server/uploads/` (volume in Docker) - Real-time via Socket.IO; Docker Hub image name is lowercase: `foufure/casegambling` - Max **10** simultaneous browser windows/tabs per account (Socket.IO); override with `MAX_SOCKETS_PER_USER` diff --git a/client/src/admin/AdminDrops.jsx b/client/src/admin/AdminDrops.jsx index e17feb0..f15afb7 100644 --- a/client/src/admin/AdminDrops.jsx +++ b/client/src/admin/AdminDrops.jsx @@ -37,10 +37,12 @@ export default function AdminDrops() { [cases, selectedCaseId] ); - const percents = useMemo( - () => (selected ? dropPercents(selected.items) : []), - [selected] - ); + const poolItems = useMemo(() => { + if (!selected) return []; + return [...selected.items].sort((a, b) => Number(a.dropRate) - Number(b.dropRate)); + }, [selected]); + + const percents = useMemo(() => dropPercents(poolItems), [poolItems]); return (
- Cost {formatCredits(crate.price)} cr each · Balance{' '} - {formatCredits(user.balance)} cr + Cost {formatCredits(crate.price)} cr each + {crate.basePrice != null && Number(crate.basePrice) !== Number(crate.price) + ? ` (was ${formatCredits(crate.basePrice)})` + : ''}{' '} + · Balance {formatCredits(user.balance)} cr {crate.catalogLuckPercent > 0 ? ` · Catalog luck +${crate.catalogLuckPercent}%` : ''} @@ -435,6 +449,7 @@ export default function CasePage() { winnerId={spin.item.id} spinning={spin.spinning} onDone={() => onReelDone(spin.key)} + animReduction={animReduction} /> {spin.done && (
+ Stake up to {vault?.slots ?? 3} items. Online: 10% / 10 min · Offline: 1% / 10 min + (scales with wear & rarity). +
+No items staked — select items below and Stake.
+ )} +