diff --git a/backend/src/lib/scraps.ts b/backend/src/lib/scraps.ts index babcaac..e825f32 100644 --- a/backend/src/lib/scraps.ts +++ b/backend/src/lib/scraps.ts @@ -91,23 +91,22 @@ export function calculateShopItemPricing( }; } -const MIN_ROLL_COST_PERCENT = 0.15; - export function calculateRollCost( basePrice: number, effectiveProbability: number, rollCostOverride?: number | null, + baseProbability?: number, ): number { - // If admin set a manual roll cost, use it if (rollCostOverride != null && rollCostOverride > 0) { return rollCostOverride; } - // Roll cost scales with effective probability (including upgrades). - // Floor at 15% of item price prevents exploitation on rare items where - // base probability is very low (e.g., 2% base → flat cost of 6 scraps). - const scaledCost = Math.round(basePrice * (effectiveProbability / 100)); - const floorCost = Math.round(basePrice * MIN_ROLL_COST_PERCENT); - return Math.max(1, scaledCost, floorCost); + // Roll cost is fixed based on base probability, does not scale with upgrades. + // Rare items (baseProbability < 15%) get a 20% floor. + const baseProb = baseProbability ?? effectiveProbability; + if (baseProb < 15) { + return Math.max(1, Math.round(basePrice * 0.20)); + } + return Math.max(1, Math.round(basePrice * (baseProb / 100))); } export function computeRollThreshold(probability: number): number { diff --git a/backend/src/lib/shop-pricing.ts b/backend/src/lib/shop-pricing.ts index 6b3b81a..88c53f0 100644 --- a/backend/src/lib/shop-pricing.ts +++ b/backend/src/lib/shop-pricing.ts @@ -64,8 +64,7 @@ export function computeItemPricing( ); } - // Roll cost at base probability (includes 15% floor to prevent exploitation on rare items) - const rollCost = calculateRollCost(price, prob); + const rollCost = calculateRollCost(price, prob, undefined, prob); const threshold = computeRollThreshold(prob); const expectedRollsAtBase = threshold > 0 ? Math.round((100 / threshold) * 10) / 10 : Infinity; diff --git a/backend/src/routes/shop.ts b/backend/src/routes/shop.ts index 69847c1..c84aee8 100644 --- a/backend/src/routes/shop.ts +++ b/backend/src/routes/shop.ts @@ -574,11 +574,11 @@ shop.post("/items/:id/try-luck", async ({ params, headers }) => { 100, ); - // Roll cost scales with effective probability (including upgrades) const rollCost = calculateRollCost( currentItem.price, effectiveProbability, currentItem.rollCostOverride, + currentItem.baseProbability, ); // Check if user can afford the roll cost diff --git a/frontend/src/routes/admin/shop/+page.svelte b/frontend/src/routes/admin/shop/+page.svelte index bf15129..f74bd5b 100644 --- a/frontend/src/routes/admin/shop/+page.svelte +++ b/frontend/src/routes/admin/shop/+page.svelte @@ -90,16 +90,20 @@ const SCRAPS_PER_DOLLAR = SCRAPS_PER_HOUR / DOLLARS_PER_HOUR; // Must match backend calculateRollCost exactly - // Roll cost scales with effective probability (including upgrades) function calculateRollCost( basePrice: number, effectiveProbability: number, - rollCostOverride?: number | null + rollCostOverride?: number | null, + baseProbability?: number ): number { if (rollCostOverride != null && rollCostOverride > 0) { return rollCostOverride; } - return Math.max(1, Math.round(basePrice * (effectiveProbability / 100))); + const baseProb = baseProbability ?? effectiveProbability; + if (baseProb < 15) { + return Math.max(1, Math.round(basePrice * 0.20)); + } + return Math.max(1, Math.round(basePrice * (baseProb / 100))); } // Must match backend computeRollThreshold exactly @@ -165,8 +169,7 @@ const boostPercent = k * boostAmount; const effectiveProbability = Math.min(baseProbability + boostPercent, 100); - // Roll cost scales with effective probability (including upgrades) - const rollCost = calculateRollCost(price, effectiveProbability, rollCostOverride); + const rollCost = calculateRollCost(price, effectiveProbability, rollCostOverride, baseProbability); // Cumulative upgrade cost (geometric series) let upgradeCostCumulative = 0; diff --git a/frontend/src/routes/shop/+page.svelte b/frontend/src/routes/shop/+page.svelte index f0b4d5b..be06c4f 100644 --- a/frontend/src/routes/shop/+page.svelte +++ b/frontend/src/routes/shop/+page.svelte @@ -26,8 +26,10 @@ if (item.rollCostOverride != null && item.rollCostOverride > 0) { return item.rollCostOverride; } - // Roll cost scales with effective probability (including upgrades) - return Math.max(1, Math.round(item.price * (item.effectiveProbability / 100))); + if (item.baseProbability < 15) { + return Math.max(1, Math.round(item.price * 0.20)); + } + return Math.max(1, Math.round(item.price * (item.baseProbability / 100))); } let selectedCategories = $state>(new Set());