mirror of
https://github.com/cassoule/flopobot_v2.git
synced 2026-03-18 13:30:36 +01:00
wip
This commit is contained in:
74
pnpm-lock.yaml
generated
74
pnpm-lock.yaml
generated
@@ -31,12 +31,18 @@ importers:
|
||||
express:
|
||||
specifier: ^4.18.2
|
||||
version: 4.22.1
|
||||
jsonwebtoken:
|
||||
specifier: ^9.0.3
|
||||
version: 9.0.3
|
||||
node-cron:
|
||||
specifier: ^3.0.3
|
||||
version: 3.0.3
|
||||
openai:
|
||||
specifier: ^4.104.0
|
||||
version: 4.104.0(ws@8.19.0)(zod@4.3.6)
|
||||
pnpm:
|
||||
specifier: ^10.29.2
|
||||
version: 10.30.2
|
||||
pokersolver:
|
||||
specifier: ^2.1.4
|
||||
version: 2.1.4
|
||||
@@ -1105,6 +1111,11 @@ packages:
|
||||
resolution:
|
||||
{ integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw== }
|
||||
|
||||
jsonwebtoken@9.0.3:
|
||||
resolution:
|
||||
{ integrity: sha512-MT/xP0CrubFRNLNKvxJ2BYfy53Zkm++5bX9dtuPbqAeQpTVe0MQTFhao8+Cp//EmJp244xt6Drw/GVEGCUj40g== }
|
||||
engines: { node: ">=12", npm: ">=6" }
|
||||
|
||||
jwa@2.0.1:
|
||||
resolution:
|
||||
{ integrity: sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg== }
|
||||
@@ -1127,10 +1138,38 @@ packages:
|
||||
{ integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== }
|
||||
engines: { node: ">=10" }
|
||||
|
||||
lodash.includes@4.3.0:
|
||||
resolution:
|
||||
{ integrity: sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w== }
|
||||
|
||||
lodash.isboolean@3.0.3:
|
||||
resolution:
|
||||
{ integrity: sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg== }
|
||||
|
||||
lodash.isinteger@4.0.4:
|
||||
resolution:
|
||||
{ integrity: sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA== }
|
||||
|
||||
lodash.isnumber@3.0.3:
|
||||
resolution:
|
||||
{ integrity: sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw== }
|
||||
|
||||
lodash.isplainobject@4.0.6:
|
||||
resolution:
|
||||
{ integrity: sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA== }
|
||||
|
||||
lodash.isstring@4.0.1:
|
||||
resolution:
|
||||
{ integrity: sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw== }
|
||||
|
||||
lodash.merge@4.6.2:
|
||||
resolution:
|
||||
{ integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== }
|
||||
|
||||
lodash.once@4.1.1:
|
||||
resolution:
|
||||
{ integrity: sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg== }
|
||||
|
||||
lodash.snakecase@4.1.1:
|
||||
resolution:
|
||||
{ integrity: sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw== }
|
||||
@@ -1361,6 +1400,12 @@ packages:
|
||||
resolution:
|
||||
{ integrity: sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig== }
|
||||
|
||||
pnpm@10.30.2:
|
||||
resolution:
|
||||
{ integrity: sha512-Ns3HB+e3lAqYjJwez4jQhPhRS1w/CF9TouJEwpIdOyVFvCDdTr4fwkX+7EY7spiuzqemPtH3aAuHfcY3nY0MtA== }
|
||||
engines: { node: ">=18.12" }
|
||||
hasBin: true
|
||||
|
||||
pokersolver@2.1.4:
|
||||
resolution:
|
||||
{ integrity: sha512-vmgZS+K8H8r1RePQykFM5YyvlKo1v3xVec8FMBjg9N6mR2Tj/n/X415w+lG67FWbrk71D/CADmKFinDgaQlAsw== }
|
||||
@@ -2682,6 +2727,19 @@ snapshots:
|
||||
|
||||
json-stable-stringify-without-jsonify@1.0.1: {}
|
||||
|
||||
jsonwebtoken@9.0.3:
|
||||
dependencies:
|
||||
jws: 4.0.1
|
||||
lodash.includes: 4.3.0
|
||||
lodash.isboolean: 3.0.3
|
||||
lodash.isinteger: 4.0.4
|
||||
lodash.isnumber: 3.0.3
|
||||
lodash.isplainobject: 4.0.6
|
||||
lodash.isstring: 4.0.1
|
||||
lodash.once: 4.1.1
|
||||
ms: 2.1.3
|
||||
semver: 7.7.4
|
||||
|
||||
jwa@2.0.1:
|
||||
dependencies:
|
||||
buffer-equal-constant-time: 1.0.1
|
||||
@@ -2706,8 +2764,22 @@ snapshots:
|
||||
dependencies:
|
||||
p-locate: 5.0.0
|
||||
|
||||
lodash.includes@4.3.0: {}
|
||||
|
||||
lodash.isboolean@3.0.3: {}
|
||||
|
||||
lodash.isinteger@4.0.4: {}
|
||||
|
||||
lodash.isnumber@3.0.3: {}
|
||||
|
||||
lodash.isplainobject@4.0.6: {}
|
||||
|
||||
lodash.isstring@4.0.1: {}
|
||||
|
||||
lodash.merge@4.6.2: {}
|
||||
|
||||
lodash.once@4.1.1: {}
|
||||
|
||||
lodash.snakecase@4.1.1: {}
|
||||
|
||||
lodash@4.17.23: {}
|
||||
@@ -2864,6 +2936,8 @@ snapshots:
|
||||
exsolve: 1.0.8
|
||||
pathe: 2.0.3
|
||||
|
||||
pnpm@10.30.2: {}
|
||||
|
||||
pokersolver@2.1.4: {}
|
||||
|
||||
prelude-ls@1.2.1: {}
|
||||
|
||||
5
pnpm-workspace.yaml
Normal file
5
pnpm-workspace.yaml
Normal file
@@ -0,0 +1,5 @@
|
||||
onlyBuiltDependencies:
|
||||
- "@prisma/client"
|
||||
- "@prisma/engines"
|
||||
- prisma
|
||||
- protobufjs
|
||||
57
src/api/cs.js
Normal file
57
src/api/cs.js
Normal file
@@ -0,0 +1,57 @@
|
||||
import { csSkinsData, csSkinsPrices } from "../utils/cs.state.js";
|
||||
|
||||
const params = new URLSearchParams({
|
||||
app_id: 730,
|
||||
currency: "EUR",
|
||||
});
|
||||
|
||||
export const fetchSuggestedPrices = async () => {
|
||||
try {
|
||||
const response = await fetch(`https://api.skinport.com/v1/items?${params}`, {
|
||||
method: "GET",
|
||||
headers: { "Accept-Encoding": "br" },
|
||||
});
|
||||
const data = await response.json();
|
||||
data.forEach((skin) => {
|
||||
if (skin.market_hash_name) {
|
||||
csSkinsPrices[skin.market_hash_name] = {
|
||||
suggested_price: skin.suggested_price,
|
||||
min_price: skin.min_price,
|
||||
max_price: skin.max_price,
|
||||
mean_price: skin.mean_price,
|
||||
median_price: skin.median_price,
|
||||
};
|
||||
}
|
||||
});
|
||||
return data;
|
||||
} catch (error) {
|
||||
console.error("Error parsing JSON:", error);
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
export const fetchSkinsData = async () => {
|
||||
try {
|
||||
const response = await fetch(
|
||||
`https://raw.githubusercontent.com/ByMykel/CSGO-API/main/public/api/en/skins.json`,
|
||||
);
|
||||
const data = await response.json();
|
||||
let rarities = {};
|
||||
data.forEach((skin) => {
|
||||
if (skin.market_hash_name) {
|
||||
csSkinsData[skin.market_hash_name] = skin;
|
||||
} else if (skin.name) {
|
||||
csSkinsData[skin.name] = skin;
|
||||
}
|
||||
if (skin.rarity && skin.rarity.name) {
|
||||
rarities[skin.rarity.name] = (rarities[skin.rarity.name] || 0) + 1;
|
||||
}
|
||||
|
||||
});
|
||||
console.log(rarities)
|
||||
return data;
|
||||
} catch (error) {
|
||||
console.error("Error fetching skins data:", error);
|
||||
return null;
|
||||
}
|
||||
};
|
||||
@@ -1,5 +1,6 @@
|
||||
import { handleMessageCreate } from "./handlers/messageCreate.js";
|
||||
import { getAkhys } from "../utils/index.js";
|
||||
import { fetchSuggestedPrices, fetchSkinsData } from "../api/cs.js";
|
||||
|
||||
/**
|
||||
* Initializes and attaches all necessary event listeners to the Discord client.
|
||||
@@ -18,6 +19,8 @@ export function initializeEvents(client, io) {
|
||||
await getAkhys(client);
|
||||
console.log("[Startup] Setting up scheduled tasks...");
|
||||
//setupCronJobs(client, io);
|
||||
await fetchSuggestedPrices();
|
||||
await fetchSkinsData();
|
||||
console.log("--- FlopoBOT is fully operational ---");
|
||||
});
|
||||
|
||||
|
||||
@@ -19,6 +19,9 @@ import * as skinService from "../../services/skin.service.js";
|
||||
import * as logService from "../../services/log.service.js";
|
||||
import { client } from "../client.js";
|
||||
import { drawCaseContent, drawCaseSkin, getDummySkinUpgradeProbs } from "../../utils/caseOpening.js";
|
||||
import { fetchSuggestedPrices, fetchSkinsData } from "../../api/cs.js";
|
||||
import { csSkinsData, csSkinsPrices } from "../../utils/cs.state.js";
|
||||
import { getRandomSkinWithRandomSpecs } from "../../utils/cs.utils.js";
|
||||
|
||||
// Constants for the AI rate limiter
|
||||
const MAX_REQUESTS_PER_INTERVAL = parseInt(process.env.MAX_REQUESTS || "5");
|
||||
@@ -379,6 +382,99 @@ async function handleAdminCommands(message) {
|
||||
message.reply(`Error during refund skins ${e.message}`);
|
||||
}
|
||||
|
||||
break;
|
||||
case `${prefix}:cs-search`:
|
||||
try {
|
||||
const searchTerm = args.join(" ");
|
||||
if (!searchTerm) {
|
||||
message.reply("Please provide a search term.");
|
||||
return;
|
||||
}
|
||||
const filteredData = csSkinsData
|
||||
? Object.values(csSkinsData).filter((skin) => {
|
||||
const name = skin.market_hash_name.toLowerCase();
|
||||
return args.every((word) => name.includes(word.toLowerCase()));
|
||||
})
|
||||
: [];
|
||||
if (filteredData.length === 0) {
|
||||
message.reply(`No skins found matching "${searchTerm}".`);
|
||||
return;
|
||||
} else if (filteredData.length <= 10) {
|
||||
const skinList = filteredData
|
||||
.map(
|
||||
(skin) =>
|
||||
`${skin.market_hash_name} - ${
|
||||
csSkinsPrices[skin.market_hash_name]
|
||||
? "Sug " +
|
||||
csSkinsPrices[skin.market_hash_name].suggested_price +
|
||||
" | Min " +
|
||||
csSkinsPrices[skin.market_hash_name].min_price +
|
||||
" | Max " +
|
||||
csSkinsPrices[skin.market_hash_name].max_price +
|
||||
" | Avg " +
|
||||
csSkinsPrices[skin.market_hash_name].mean_price +
|
||||
" | Med " +
|
||||
csSkinsPrices[skin.market_hash_name].median_price
|
||||
: "N/A"
|
||||
}`,
|
||||
)
|
||||
.join("\n");
|
||||
message.reply(`Skins matching "${searchTerm}":\n${skinList}`);
|
||||
} else {
|
||||
message.reply(`Found ${filteredData.length} skins matching "${searchTerm}".`);
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
message.reply(`Error searching CS:GO skins: ${e.message}`);
|
||||
}
|
||||
break;
|
||||
case `${prefix}:open-cs`:
|
||||
try {
|
||||
const randomSkin = getRandomSkinWithRandomSpecs(args[0] ? parseFloat(args[0]) : null);
|
||||
message.reply(
|
||||
`You opened a CS:GO case and got: ${randomSkin.name} (${randomSkin.data.rarity.name}, ${
|
||||
randomSkin.isStattrak ? "StatTrak, " : ""
|
||||
}${randomSkin.isSouvenir ? "Souvenir, " : ""}${randomSkin.wearState} - float ${randomSkin.float})\nBase Price: ${
|
||||
randomSkin.price ?? "N/A"
|
||||
} Flopos\nImage url: [url](${randomSkin.data.image || "N/A"})`,
|
||||
);
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
message.reply(`Error opening CS:GO case: ${e.message}`);
|
||||
}
|
||||
break;
|
||||
case `${prefix}:simulate-cs`:
|
||||
try {
|
||||
const caseCount = parseInt(args[0]) || 100;
|
||||
const caseType = args[1] || "default";
|
||||
let totalResValue = 0;
|
||||
let highestSkinPrice = 0;
|
||||
const priceTiers = {
|
||||
"Consumer Grade": 0,
|
||||
"Industrial Grade": 0,
|
||||
"Mil-Spec Grade": 0,
|
||||
"Restricted": 0,
|
||||
"Classified": 0,
|
||||
"Covert": 0,
|
||||
"Extraordinary": 0,
|
||||
};
|
||||
|
||||
for (let i = 0; i < caseCount; i++) {
|
||||
const result = getRandomSkinWithRandomSpecs();
|
||||
totalResValue += parseInt(result.price);
|
||||
if (parseInt(result.price) > highestSkinPrice) {
|
||||
highestSkinPrice = parseInt(result.price);
|
||||
}
|
||||
priceTiers[result.data.rarity.name]++;
|
||||
}
|
||||
console.log(totalResValue / caseCount);
|
||||
message.reply(
|
||||
`${totalResValue / caseCount} average skin price over ${caseCount} ${caseType} cases.\nHighest skin price: ${highestSkinPrice}\nPrice tier distribution: ${JSON.stringify(priceTiers)}`,
|
||||
);
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
message.reply(`Error during case simulation: ${e.message}`);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1297,10 +1297,6 @@ export function apiRoutes(client, io) {
|
||||
},
|
||||
});
|
||||
|
||||
console.log(
|
||||
`[CHECKOUT] New session for user ${userId}: ${session.id}, offer: ${offer.id} (${offer.coins} coins for ${offer.amount_cents} cents)`,
|
||||
);
|
||||
|
||||
res.json({ sessionId: session.id, url: session.url });
|
||||
} catch (error) {
|
||||
console.error("Error creating checkout session:", error);
|
||||
|
||||
@@ -23,7 +23,7 @@ router.get("/discord", (req, res) => {
|
||||
response_type: "code",
|
||||
scope: "identify",
|
||||
});
|
||||
console.log("Redirecting to Discord OAuth2 with params:", params.toString());
|
||||
|
||||
res.redirect(`${DISCORD_API}/oauth2/authorize?${params.toString()}`);
|
||||
});
|
||||
|
||||
@@ -105,7 +105,7 @@ router.get("/me", async (req, res) => {
|
||||
const user = await userService.getUser(payload.discordId);
|
||||
if (!user) {
|
||||
console.warn("User not found for Discord ID in token:", payload.discordId);
|
||||
return res.json({discordId: payload.discordId});
|
||||
return res.json({ discordId: payload.discordId });
|
||||
}
|
||||
|
||||
res.json({ user, discordId: user.id });
|
||||
|
||||
@@ -264,12 +264,15 @@ async function onSnakeGameStateUpdate(client, eventData) {
|
||||
winnerId = lobby.p2.id;
|
||||
}
|
||||
// If scores are equal, winnerId remains null (draw)
|
||||
io.emit("snakegameOver", { gameKey: lobby.gameKey, game: lobby, winner: winnerId });
|
||||
await onGameOver(client, "snake", playerId, winnerId, "", { p1: lobby.p1.score, p2: lobby.p2.score });
|
||||
} else if (lobby.p1.win || lobby.p2.win) {
|
||||
// One player won by filling the grid
|
||||
const winnerId = lobby.p1.win ? lobby.p1.id : lobby.p2.id;
|
||||
io.emit("snakegameOver", { gameKey: lobby.gameKey, game: lobby, winner: winnerId });
|
||||
await onGameOver(client, "snake", playerId, winnerId, "", { p1: lobby.p1.score, p2: lobby.p2.score });
|
||||
}
|
||||
delete activeSnakeGames[lobby.gameKey];
|
||||
}
|
||||
|
||||
export async function onGameOver(client, gameType, playerId, winnerId, reason = "", scores = null) {
|
||||
@@ -284,14 +287,17 @@ export async function onGameOver(client, gameType, playerId, winnerId, reason =
|
||||
await eloHandler(game.p1.id, game.p2.id, 0.5, 0.5, title.toUpperCase(), scores);
|
||||
resultText = "Égalité";
|
||||
} else {
|
||||
await eloHandler(
|
||||
game.p1.id,
|
||||
game.p2.id,
|
||||
game.p1.id === winnerId ? 1 : 0,
|
||||
game.p2.id === winnerId ? 1 : 0,
|
||||
title.toUpperCase(),
|
||||
scores,
|
||||
);
|
||||
// Temp fix: Don't update ELO for Snake since it's not in a stable state yet.
|
||||
if (gameType !== "snake") {
|
||||
await eloHandler(
|
||||
game.p1.id,
|
||||
game.p2.id,
|
||||
game.p1.id === winnerId ? 1 : 0,
|
||||
game.p2.id === winnerId ? 1 : 0,
|
||||
title.toUpperCase(),
|
||||
scores,
|
||||
);
|
||||
}
|
||||
const winnerName = game.p1.id === winnerId ? game.p1.name : game.p2.name;
|
||||
resultText = `Victoire de ${winnerName}`;
|
||||
}
|
||||
|
||||
3
src/utils/cs.state.js
Normal file
3
src/utils/cs.state.js
Normal file
@@ -0,0 +1,3 @@
|
||||
export let csSkinsData = {};
|
||||
|
||||
export let csSkinsPrices = {};
|
||||
128
src/utils/cs.utils.js
Normal file
128
src/utils/cs.utils.js
Normal file
@@ -0,0 +1,128 @@
|
||||
import { csSkinsData, csSkinsPrices } from "./cs.state.js";
|
||||
|
||||
const StateFactoryNew = "Factory New";
|
||||
const StateMinimalWear = "Minimal Wear";
|
||||
const StateFieldTested = "Field-Tested";
|
||||
const StateWellWorn = "Well-Worn";
|
||||
const StateBattleScarred = "Battle-Scarred";
|
||||
|
||||
export const RarityToColor = {
|
||||
Gold: 0xffd700, // Standard Gold
|
||||
Covert: 0xeb4b4b, // Red
|
||||
Classified: 0xd32ce6, // Pink/Magenta
|
||||
Restricted: 0x8847ff, // Purple
|
||||
"Mil-Spec Grade": 0x4b69ff, // Dark Blue
|
||||
"Industrial Grade": 0x5e98d9, // Light Blue
|
||||
"Consumer Grade": 0xb0c3d9, // Light Grey/White
|
||||
};
|
||||
|
||||
const basePriceRanges = {
|
||||
"Consumer Grade": { min: 1, max: 5 },
|
||||
"Industrial Grade": { min: 2, max: 10 },
|
||||
"Mil-Spec Grade": { min: 3, max: 70 },
|
||||
"Restricted": { min: 17, max: 400 },
|
||||
"Classified": { min: 70, max: 1700 },
|
||||
"Covert": { min: 350, max: 17000 },
|
||||
"Gold": { min: 10000, max: 100000 },
|
||||
"Extraordinary": { min: 10000, max: 100000 },
|
||||
};
|
||||
|
||||
const wearStateMultipliers = {
|
||||
[StateFactoryNew]: 1,
|
||||
[StateMinimalWear]: 0.75,
|
||||
[StateFieldTested]: 0.65,
|
||||
[StateWellWorn]: 0.6,
|
||||
[StateBattleScarred]: 0.5,
|
||||
};
|
||||
|
||||
export function randomSkinRarity() {
|
||||
const roll = Math.random();
|
||||
|
||||
const goldLimit = 0.003;
|
||||
const covertLimit = goldLimit + 0.014;
|
||||
const classifiedLimit = covertLimit + 0.04;
|
||||
const restrictedLimit = classifiedLimit + 0.2;
|
||||
const milSpecLimit = restrictedLimit + 0.5;
|
||||
const industrialLimit = milSpecLimit + 0.2;
|
||||
|
||||
if (roll < goldLimit) return "Extraordinary";
|
||||
if (roll < covertLimit) return "Covert";
|
||||
if (roll < classifiedLimit) return "Classified";
|
||||
if (roll < restrictedLimit) return "Restricted";
|
||||
if (roll < milSpecLimit) return "Mil-Spec Grade";
|
||||
if (roll < industrialLimit) return "Industrial Grade";
|
||||
return "Consumer Grade";
|
||||
}
|
||||
|
||||
export function generatePrice(rarity, float, isStattrak, isSouvenir, wearState) {
|
||||
const ranges = basePriceRanges[rarity] || basePriceRanges["Industrial Grade"];
|
||||
console.log(ranges)
|
||||
|
||||
let basePrice = ranges.min + (Math.random()) * (ranges.max - ranges.min);
|
||||
console.log(basePrice)
|
||||
|
||||
const stateMultiplier = wearStateMultipliers[wearState] ?? 1.0;
|
||||
console.log(stateMultiplier)
|
||||
|
||||
let finalPrice = basePrice * stateMultiplier;
|
||||
console.log(finalPrice)
|
||||
|
||||
const isExtraordinary = rarity === "Extraordinary";
|
||||
|
||||
if (isSouvenir && !isExtraordinary) {
|
||||
finalPrice *= 4 + (Math.random()) * (10.0 - 4);
|
||||
} else if (isStattrak && !isExtraordinary) {
|
||||
finalPrice *= 3 + (Math.random()) * (5.0 - 3);
|
||||
}
|
||||
console.log(finalPrice)
|
||||
finalPrice /= 1 + float; // Avoid division by zero and ensure float has a significant impact
|
||||
|
||||
if (finalPrice < 1) finalPrice = 1;
|
||||
|
||||
return finalPrice.toFixed(0);
|
||||
}
|
||||
|
||||
export function isStattrak(canBeStattrak) {
|
||||
if (!canBeStattrak) return false;
|
||||
return Math.random() < 0.15;
|
||||
}
|
||||
|
||||
export function isSouvenir(canBeSouvenir) {
|
||||
if (!canBeSouvenir) return false;
|
||||
return Math.random() < 0.15;
|
||||
}
|
||||
|
||||
export function getRandomFloatInRange(min, max) {
|
||||
return min + (Math.random()) * (max - min);
|
||||
}
|
||||
|
||||
export function getWearState(wear) {
|
||||
const clamped = Math.max(0.0, Math.min(1.0, wear));
|
||||
|
||||
if (clamped < 0.07) return StateFactoryNew;
|
||||
if (clamped < 0.15) return StateMinimalWear;
|
||||
if (clamped < 0.38) return StateFieldTested;
|
||||
if (clamped < 0.45) return StateWellWorn;
|
||||
return StateBattleScarred;
|
||||
}
|
||||
|
||||
export function getRandomSkinWithRandomSpecs(u_float=null) {
|
||||
const skinNames = Object.keys(csSkinsData);
|
||||
const randomRarity = randomSkinRarity();
|
||||
console.log(randomRarity)
|
||||
const filteredSkinNames = skinNames.filter(name => csSkinsData[name].rarity.name === randomRarity);
|
||||
const randomIndex = Math.floor(Math.random() * filteredSkinNames.length);
|
||||
|
||||
const skinName = filteredSkinNames[randomIndex];
|
||||
const skinData = csSkinsData[skinName];
|
||||
const float = u_float !== null ? u_float : getRandomFloatInRange(skinData.min_float, skinData.max_float);
|
||||
return {
|
||||
name: skinName,
|
||||
data: skinData,
|
||||
isStattrak: isStattrak(skinData.stattrak),
|
||||
isSouvenir: isSouvenir(skinData.souvenir),
|
||||
wearState: getWearState(float),
|
||||
float: float,
|
||||
price: generatePrice(skinData.rarity.name, float, isStattrak(skinData.stattrak), isSouvenir(skinData.souvenir), getWearState(float)),
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user