Voici la liste exacte et dans l'ordre de ce qui est fait quand vous cliquez sur ouvrir un booster :
Rate limit et maintenance
Vérifier :
• que l’utilisateur n’a pas dépassé la limite d’ouverture (par exemple 5 ouvertures par minute)
• que le site n’est pas en maintenance
• que l’utilisateur possède au moins un booster disponible (pending_lootboxes > 0)
Nonce anti-replay
Si un nonce est fourni, il est consommé pour empêcher une double ouverture du même booster (protection anti-replay).
Poids de rareté de base
Charger les poids de rareté :
• soit ceux configurés en admin (par exemple : Commun 60, Rare 30, Épique 8, Légendaire 2)
• soit les poids par défaut si rien n’est configuré.
Ces poids servent de base pour tous les tirages.
Amélioration "Rarity Boost"
Si l’utilisateur possède un palier rarity_boost, les poids de rareté sont remplacés par ceux de ce palier.
Le rarity_boost écrase donc les poids de base.
Amélioration "Sacrifice de Cartes"
Si cette amélioration est active, elle applique ses propres deltas de poids (par exemple : diminuer la probabilité des Communes, augmenter celle des Légendaires).
Ces deltas s’ajoutent par-dessus les poids actuels (après le rarity_boost éventuel).
Taille du booster
L’amélioration booster_size détermine le nombre de cartes de base dans le booster (cardCount), par exemple 2 ou 3 cartes.
Super booster
Calcul de la probabilité de super booster :
• baseChance = palier super_booster de l’utilisateur
• extraChance = palier super_booster_chance (branche “chance”, seulement si super_booster est au moins à 1)
• superChance = baseChance + extraChance
Si un super booster admin est en attente (pending_super_boosters), il est déclenché automatiquement (super booster garanti).
Tirage aléatoire :
• si Math.random() * 100 < superChance, alors isSuperBooster = true
• sinon, isSuperBooster = false
Si c’est un super booster, le nombre total de cartes tirées est :
totalCards = cardCount + baseBonusCards + extraCards
Si c’est un super booster garanti admin, il y a au minimum +4 cartes bonus.
Perte de carte (Sacrifice)
Si rarity_sacrifice est actif et que card_loss_pct > 0, on effectue un tirage aléatoire :
• si le tirage est déclenché, on retire 1 carte au total (ou 2 si c’est un super booster)
• on impose un plancher : totalCards ne peut jamais descendre en dessous de 1.
Exclusion des cartes IMP (imprenables)
La fonction getImprenableIds(user) construit la liste des cartes imprenables pour cet utilisateur.
Ces cartes sont exclues du pool de tirage et ne peuvent pas être obtenues dans ce booster.
Réduction de doublons
On calcule un pourcentage de réduction de doublons :
dupReductionPct = palier less_dupes (et éventuellement d’autres sources non liées aux streamers, si tu en ajoutes plus tard).
Ce pourcentage sera utilisé pour favoriser les cartes non possédées.
Tirage des cartes (getRandomCards en mode “scaled”)
On appelle :
getRandomCards(totalCards, null, userWeights, excludeIds, collection, dupReductionPct, scaledMode = true)
Pour chaque carte à tirer :
Choix de la collection (mode scaled)
Pour chaque collection visible, on compte le nombre de cartes manquantes pour l’utilisateur.
Le poids d’une collection est égal au nombre de cartes manquantes dans cette collection.
On effectue un tirage pondéré : une collection est choisie proportionnellement à ce qu’il manque à l’utilisateur.
Si l’utilisateur possède déjà toutes les cartes d’une collection, cette collection est exclue du tirage.
Choix de la rareté
Dans la collection choisie, on effectue un tirage pondéré selon les userWeights finaux (issus des étapes sur les poids de base, le rarity_boost et le sacrifice de cartes).
Par exemple : Commun 56, Rare 30, Épique 10, Légendaire 4.
Réduction des doublons
Dans la rareté choisie :
• nonDupePool = cartes de cette rareté que l’utilisateur ne possède pas encore, en excluant les IMPs et les cartes déjà tirées dans ce même booster
• effectivePct = minimum entre dupReductionPct et le nombre de cartes restantes dans nonDupePool (pour éviter de promettre plus de “non doublons” qu’il n’en existe réellement)
Tirage :
• si Math.random() * 100 < effectivePct, on pioche dans nonDupePool (carte non possédée garantie)
• sinon, on pioche de manière totalement aléatoire dans tout le pool de la rareté (doublons possibles).
Décrément du booster
À la fin du tirage, on décrémente le stock de boosters de l’utilisateur :
pending_lootboxes--
(ou décrément du booster de collection admin, si un booster spécifique était utilisé).
Mise à jour de la collection
Pour chaque carte tirée :
• user.collection[id]++
• si c’est la première fois que l’utilisateur obtient cette carte, on incrémente aussi unique_cards_max.
Statistiques et succès
On met à jour les statistiques :
• boosters_opened++
• cards_obtained += totalCards
On vérifie ensuite les succès et succès secrets (par exemple : obtenir 2 légendaires ou plus dans un booster, obtenir 15 fois la même commune, etc.), puis on appelle la logique de checkAchievements.
Réponse envoyée au client
La réponse renvoie :
• la liste des cartes tirées
• la liste des IDs des cartes obtenues pour la première fois
• le nombre de boosters restants
• l’information indiquant si le booster était un super booster ou non.