feat: skins upgrade

This commit is contained in:
Milo
2025-12-26 04:26:29 +01:00
parent 85b18c0eec
commit d5532fda48
4 changed files with 158 additions and 6 deletions

View File

@@ -17,6 +17,13 @@ import { getUserInventory } from "../../database/index.js";
* @param {string} interactionId - The unique ID of the interaction.
*/
export async function handleInventoryCommand(req, res, client, interactionId) {
return res.send({
type: InteractionResponseType.CHANNEL_MESSAGE_WITH_SOURCE,
data: {
content: `La commande /inventory est désactivée. Tu peux consulter ton inventaire sur FlopoSite.`,
flags: InteractionResponseFlags.EPHEMERAL,
},
});
const { member, guild_id, token, data } = req.body;
const commandUserId = member.user.id;
// User can specify another member, otherwise it defaults to themself

View File

@@ -25,7 +25,7 @@ import {
updateUserCoins,
} from "../../database/index.js";
import { client } from "../client.js";
import { drawCaseContent, drawCaseSkin } from "../../utils/caseOpening.js";
import { drawCaseContent, drawCaseSkin, getDummySkinUpgradeProbs } from "../../utils/caseOpening.js";
// Constants for the AI rate limiter
const MAX_REQUESTS_PER_INTERVAL = parseInt(process.env.MAX_REQUESTS || "5");
@@ -196,6 +196,28 @@ async function handleAdminCommands(message) {
const [command, ...args] = message.content.split(" ");
switch (command) {
case "?sp":
let msgText = ""
for (let skinTierRank = 1; skinTierRank <= 4; skinTierRank++) {
msgText += `\n--- Tier Rank: ${skinTierRank} ---\n`;
let skinMaxLevels = 4;
let skinMaxChromas = 4;
for (let skinLevel = 1; skinLevel < skinMaxLevels; skinLevel++) {
msgText += (`Levels: ${skinLevel}/${skinMaxLevels}, MaxChromas: ${1}/${skinMaxChromas} - `);
msgText += (`${getDummySkinUpgradeProbs(skinLevel, 1, skinTierRank, skinMaxLevels, skinMaxChromas, 15).successProb.toFixed(4)}, `);
msgText += (`${getDummySkinUpgradeProbs(skinLevel, 1, skinTierRank, skinMaxLevels, skinMaxChromas, 15).destructionProb.toFixed(4)}, `);
msgText += (`${getDummySkinUpgradeProbs(skinLevel, 1, skinTierRank, skinMaxLevels, skinMaxChromas, 15).upgradePrice}\n`);
}
for (let skinChroma = 1; skinChroma < skinMaxChromas; skinChroma++) {
msgText += (`Levels: ${skinMaxLevels}/${skinMaxLevels}, MaxChromas: ${skinChroma}/${skinMaxChromas} - `);
msgText += (`${getDummySkinUpgradeProbs(skinMaxLevels, skinChroma, skinTierRank, skinMaxLevels, skinMaxChromas, 15).successProb.toFixed(4)}, `);
msgText += (`${getDummySkinUpgradeProbs(skinMaxLevels, skinChroma, skinTierRank, skinMaxLevels, skinMaxChromas, 15).destructionProb.toFixed(4)}, `);
msgText += (`${getDummySkinUpgradeProbs(skinMaxLevels, skinChroma, skinTierRank, skinMaxLevels, skinMaxChromas, 15).upgradePrice}\n`);
}
message.reply(msgText);
msgText = "";
}
break;
case "?v":
console.log("Active Polls:", activePolls);
break;
@@ -331,6 +353,7 @@ async function handleAdminCommands(message) {
console.log(e);
message.reply(`Error during case test: ${e.message}`);
}
break;
case `${prefix}:refund-skins`:
try {
const DBskins = getAllSkins.all();

View File

@@ -34,7 +34,7 @@ import { DiscordRequest } from "../../api/discord.js";
import { ActionRowBuilder, ButtonBuilder, ButtonStyle, EmbedBuilder } from "discord.js";
import { emitDataUpdated, socketEmit } from "../socket.js";
import { handleCaseOpening } from "../../utils/marketNotifs.js";
import { drawCaseContent, drawCaseSkin } from "../../utils/caseOpening.js";
import { drawCaseContent, drawCaseSkin, getSkinUpgradeProbs } from "../../utils/caseOpening.js";
// Create a new router instance
const router = express.Router();
@@ -123,13 +123,13 @@ export function apiRoutes(client, io) {
let caseTypeVal;
switch (caseType) {
case "standard":
caseTypeVal = 500;
caseTypeVal = 1000;
break;
case "premium":
caseTypeVal = 750;
caseTypeVal = 2000;
break;
case "ultra":
caseTypeVal = 1000;
caseTypeVal = 4000;
break;
default:
return res.status(400).json({ error: "Invalid case type." });
@@ -166,7 +166,7 @@ export function apiRoutes(client, io) {
});
console.log(
`[${Date.now()}] ${userId} opened a ${caseType} Valorant case and received skin ${result.randomSelectedSkinUuid}`,
`${commandUser.username} opened a ${caseType} Valorant case and received skin ${result.randomSelectedSkinUuid}`,
);
const updatedSkin = getSkin.get(result.randomSkinData.uuid);
await handleCaseOpening(caseType, userId, result.randomSelectedSkinUuid, client);
@@ -214,6 +214,112 @@ export function apiRoutes(client, io) {
}
});
router.get("/skin-upgrade/:uuid/fetch", (req, res) => {
try {
const skin = getSkin.get(req.params.uuid);
const skinData = skins.find((s) => s.uuid === skin.uuid);
const { successProb, destructionProb, upgradePrice } = getSkinUpgradeProbs(skin, skinData);
const segments = [
{ id: 'SUCCEEDED', color: '5865f2', percent: successProb, label: 'Réussie' },
{ id: 'DESTRUCTED', color: 'f26558', percent: destructionProb, label: 'Détruit' },
{ id: 'NONE', color: '18181818', percent: 1 - successProb - destructionProb, label: 'Échec' },
]
res.json({ segments, upgradePrice });
} catch (error) {
console.log(error)
res.status(500).json({ error: "Failed to fetch skin upgrade." });
}
});
router.post("/skin-upgrade/:uuid", async (req, res) => {
const { userId } = req.body;
try {
const skin = getSkin.get(req.params.uuid);
const skinData = skins.find((s) => s.uuid === skin.uuid);
if (
!skinData ||
(skin.currentLvl >= skinData.levels.length && skin.currentChroma >= skinData.chromas.length)
) {
return res.status(403).json({ error: "Skin is already maxed out or invalid skin." });
}
if (skin.user_id !== userId) {
return res.status(403).json({ error: "User does not own this skin." });
}
const upgradePrice = Math.floor(parseFloat(skin.maxPrice) / 10);
const commandUser = getUser.get(userId);
if (!commandUser) {
return res.status(404).json({ error: "User not found." });
}
if (commandUser.coins < upgradePrice) {
return res.status(403).json({ error: `Pas assez de FlopoCoins (${upgradePrice} requis).` });
}
insertLog.run({
id: `${userId}-${Date.now()}`,
user_id: userId,
action: "VALO_SKIN_UPGRADE",
target_user_id: null,
coins_amount: -upgradePrice,
user_new_amount: commandUser.coins - upgradePrice,
});
updateUserCoins.run({
id: userId,
coins: commandUser.coins - upgradePrice,
});
let succeeded = false;
let destructed = false;
const { successProb, destructionProb } = getSkinUpgradeProbs(skin, skinData);
const roll = Math.random();
if (roll < destructionProb) {
destructed = true;
} else if (roll < successProb + destructionProb) {
succeeded = true;
}
if (succeeded) {
const isLevelUpgrade = skin.currentLvl < skinData.levels.length;
if (isLevelUpgrade) {
skin.currentLvl++;
} else {
skin.currentChroma++;
}
const calculatePrice = () => {
let result = parseFloat(skin.basePrice);
result *= 1 + skin.currentLvl / Math.max(skinData.levels.length, 2);
result *= 1 + skin.currentChroma / 4;
return parseFloat(result.toFixed(0));
};
skin.currentPrice = calculatePrice();
updateSkin.run({
uuid: skin.uuid,
user_id: skin.user_id,
currentLvl: skin.currentLvl,
currentChroma: skin.currentChroma,
currentPrice: skin.currentPrice,
});
} else if (destructed) {
updateSkin.run({
uuid: skin.uuid,
user_id: null,
currentLvl: null,
currentChroma: null,
currentPrice: null,
});
}
console.log(`${commandUser.username} attempted to upgrade skin ${skin.uuid} - ${succeeded ? "SUCCEEDED" : destructed ? "DESTRUCTED" : "FAILED"}`);
res.json({ wonId: succeeded ? "SUCCEEDED" : destructed ? "DESTRUCTED" : "NONE" });
} catch (error) {
console.error("Error fetching skin upgrade:", error);
res.status(500).json({ error: "Failed to fetch skin upgrade." });
}
});
router.get("/users/by-elo", (req, res) => {
try {
const users = getUsersByElo.all();

View File

@@ -127,3 +127,19 @@ export function drawCaseSkin(caseContent) {
randomSelectedSkinIndex,
};
}
export function getSkinUpgradeProbs(skin, skinData) {
const successProb =
(1 - (((skin.currentChroma + skin.currentLvl + skinData.chromas.length + skinData.levels.length) / 18) * (parseInt(skin.tierRank) / 4)))/2;
const destructionProb = ((skin.currentChroma + skinData.levels.length) / (skinData.chromas.length + skinData.levels.length)) * (parseInt(skin.tierRank) / 5) * 0.075;
const upgradePrice = Math.max(Math.floor(parseFloat(skin.currentPrice) * (1 - successProb)), 1);
return { successProb, destructionProb, upgradePrice };
}
export function getDummySkinUpgradeProbs(skinLevel, skinChroma, skinTierRank, skinMaxLevels, skinMaxChromas, skinMaxPrice) {
const successProb =
1 - (((skinChroma + skinLevel + (skinMaxChromas + skinMaxLevels)) / 18) * (parseInt(skinTierRank) / 4));
const destructionProb = ((skinChroma + skinMaxLevels) / (skinMaxChromas + skinMaxLevels)) * (parseInt(skinTierRank) / 5) * 0.1;
const upgradePrice = Math.max(Math.floor((parseFloat(skinMaxPrice) * (1 - successProb))), 1);
return { successProb, destructionProb, upgradePrice };
}