spt_modpack/user/mods/GamblerTrader/src/Gamble.ts
2025-07-18 17:41:15 -05:00

414 lines
No EOL
17 KiB
TypeScript

import { DependencyContainer } from "tsyringe";
import { ILogger } from "@spt/models/spt/utils/ILogger";
import { RandomUtil } from "@spt/utils/RandomUtil";
import { HashUtil } from "@spt/utils/HashUtil";
import { IAddItemDirectRequest } from "@spt/models/eft/inventory/IAddItemsDirectRequest";
import { Item } from "../common/tables/IItem";
import { ItemCreator } from "./itemCreator";
import { Keys } from "./containers/keys";
import { Stims } from "./containers/Stims";
import { Backpacks } from "./containers/Backpacks";
import { Rigs } from "./containers/Rigs";
import { Headsets } from "./containers/Headsets";
import { Ammo } from "./containers/Ammo";
import { Melees } from "./containers/Melees";
import { MysteryContainer } from "./MysteryContainer";
import { Foods } from "./containers/Foods";
export class Gamble {
public newItemsRequest: IAddItemDirectRequest;
public name: string;
private count: number;
private mysteryContainer: MysteryContainer;
private currentID: string;
private currentCaliber: string;
private currentMagazine: string;
private currentMagazineMaxAmmo: number;
private currentWeaponType: string;
private currentHeadsetCompatible: boolean;
private container: DependencyContainer;
private hashUtil: HashUtil;
private logger: ILogger;
private randomUtil: RandomUtil;
private config: any;
constructor(container: DependencyContainer, config: any, logger: ILogger, name :string){
this.name = name.replace('gambling_', '');
this.logger = logger;
this.container = container;
this.config = config;
this.count = 0;
this.randomUtil = container.resolve<RandomUtil>("RandomUtil");
this.hashUtil = container.resolve<HashUtil>("HashUtil");
this.mysteryContainer = new MysteryContainer(config, logger);
this.newItemsRequest = {
itemsWithModsToAdd: [],
foundInRaid: true,
useSortingTable : true
};
}
public newGamble(name: string = this.name, roll: number = this.randomUtil.getFloat(0,100)): []{
//console.log('NEW GAMBLE: Creating ' + name + ' roll = ' + roll)
switch(name){
case 'wallet':
case '100k':
case '500k':
case '1mil':
case 'roubles':
case 'bitcoin':
case 'gpcoin':
case 'keycard':
case 'key':
case 'stim':
case 'medical':
case 'food':
case 'loadout_grenade':
case 'loadout_facecovers':
case 'loadout_food':
case 'loadout_drink':
case 'loadout_light_bleed':
case 'loadout_heavy_bleed':
case 'loadout_stim':
case 'loadout_splint':
case 'loadout_healing':
case 'melee':
case 'headset':
case 'backpack':
case 'rig':
case '7.62x25':
case '9x18':
case '9x19':
case '9x21':
case '.357':
case '.45':
case '4.6x30':
case '5.7x28':
case '5.45x39':
case '5.56x45':
case '.300':
case '6.8x51':
case '7.62x39':
case '7.62x51':
case '7.62x54':
case '.338':
case '9x39':
case '.366':
case '12.7x55':
case '12/70':
case '20/70':
case '23x75':
this.openReward(name, roll);
break;
case 'weapon':
case 'average_weapon':
case 'default_weapon':
case 'premium_weapon':
case 'helmet':
case 'armor':
case 'premium_armor':
this.openPreset(name, roll);
break;
case 'loadout':
this.openLoadoutContainer(name);
break;
case 'premium_loadout':
this.openLoadoutContainer(name, 10.0);
break;
default:
this.logger.error(`[GamblerTrader] This Mystery Container Doesn't exist! Contact Author!`);
}
if (this.mysteryContainer.isAmmo(this.name)) {
this.newItemsRequest.foundInRaid = this.config.container_config["all_ammo_rewards_found_in_raid_status"];
} else {
this.newItemsRequest.foundInRaid = this.config.container_config[this.name + "_rewards_found_in_raid_status"];
}
return this.newItemsRequest;
}
// Opens all rewards from the loadout container
private openLoadoutContainer(name: string = this.name, roll: number = this.randomUtil.getFloat(0,100)){ // 74.3 - 82 is meme // 82 MAX
this.logger.info(`[GamblerTrader][${name}] The container roll is: ${roll}!`);
const rewards = this.mysteryContainer.getGuaranteedRewards(name);
const randomness = this.mysteryContainer.getGuaranteedRandomness(name);
let currentCaliber: string, currentMagazine: string, currentMagazineMaxAmmo: number, currentWeaponType : string;
if (roll > 90) { // _scav
roll = this.randomUtil.getFloat(44.3, 74.3);
//roll = 65;
} else if (roll > 82) { // _decent
roll = this.randomUtil.getFloat(19.3, 44.3);
//roll = 38;
}
for(let i = 0; i < rewards.length; i++) {
const current = rewards[i];
if (this.mysteryContainer.getName(current)) { // Rewards is a container
if(currentWeaponType == '_meme') {
this.newGamble(current, this.randomUtil.getFloat(5,65));
} else if (this.currentWeaponType == '_meta') {
this.newGamble(current, this.randomUtil.getFloat(5,25));
} else if (this.currentWeaponType == '_decent') {
this.newGamble(current, this.randomUtil.getFloat(12,50));
}else if (this.currentWeaponType == '_scav') {
this.newGamble(current, this.randomUtil.getFloat(35,70));
}else{
if (randomness[i]) {
this.newGamble(current);
} else {
this.newGamble(current, roll);
}
}
if(current === 'helmet') {
if (!this.currentHeadsetCompatible) {
i++; // Skip the headset reward
} else if (this.mysteryContainer.items['helmet'].headset_incompatible_helmets.includes(this.currentID)) {
i++; // Skip the headset reward
}
}
if (current === 'headset'){
if (this.currentID == '5c06c6a80db834001b735491') {
this.openReward('headset', roll, '5c06c6a80db834001b735491', false, 1);
continue;
}
}
if (current === 'armor'){
const currentID = this.currentID;
let truth = false;
if (currentID) {
truth = true;
}
if (currentID && this.mysteryContainer.items['armor'].armor_rigs.includes(currentID)) {
i++; // Skip the rig reward
}
}
if(current === 'weapon') { // Ammo and Magazine generation
currentCaliber = this.currentCaliber;
currentMagazine = this.currentMagazine;
currentMagazineMaxAmmo = this.currentMagazineMaxAmmo
currentWeaponType = this.currentWeaponType;
let magazineCount = 3;
const badMagazines = [
'633ec6ee025b096d320a3b15', // RSh-12 12.7x55 5-round cylinder
'5ae0973a5acfc4001562206c', // Mosin Rifle 7.62x54R 5-round magazine
'587df3a12459772c28142567' // SKS 7.62x39 10-round internal box magazine
]
//push magazines and ammo
const caliber = this.mysteryContainer.items['ammo'].BSGCalibers[currentCaliber];
if (caliber != '20x70' && caliber != '23x75' && caliber != '12/70' && caliber != '.357' && !badMagazines.includes(currentMagazine)) {
//console.log('caliber: ' + caliber + ' magazine: ' + currentMagazine)
this.openReward(caliber, roll, currentMagazine, false, 1);
this.openReward(caliber, roll, currentMagazine, false, 1);
}
let tempRoll: number;
let min, max = 0;
// Depending on the ammo type, we want to generate a different rarity of ammo from the temproll
switch(currentWeaponType) {
case '_meme':
min = 5;
max = 30;
break;
case '_decent':
min = 10;
max = 45;
break;
case '_meta':
min = 2;
max = 26;
break;
default:
min = 10;
max = 90
tempRoll = roll;
break;
}
if (caliber == '20x70' || caliber == '23x75' || caliber == '12/70') {
currentMagazineMaxAmmo = 10;
}
let ammoID: string;
tempRoll = this.randomUtil.getFloat(min, max); // random roll for each ammo reward
for(let i = 0; i < magazineCount; i++){
if (!ammoID) {
this.openReward(caliber, tempRoll, 'NaN', true, currentMagazineMaxAmmo);
ammoID = this.currentID;
} else {
this.openReward(caliber, tempRoll, ammoID, true, currentMagazineMaxAmmo);
}
}
}
} else { // Reward is an item
const reward_amount = this.mysteryContainer.getRewardAmount(name, i);
const stackable = this.mysteryContainer.getStackable(name, i);
if(!stackable){
//console.log('OPEN GUARANTEED REWARDS: Item exists and NOT stackable... Adding to newItemsRequest...')
for(let i = 0; i < reward_amount; i++){
this.newItemsRequest.itemsWithModsToAdd[this.count] = [this.newItemFormat(current)];
this.count++;
}
} else {
//console.log('OPEN GUARANTEED REWARDS: Item exists and is stackable... Adding to newItemsRequest...')
this.newItemsRequest.itemsWithModsToAdd[this.count] = [this.newItemFormat(current, reward_amount)];
this.count++;
}
}
}
}
private openReward(name: string = this.name, roll: number = this.randomUtil.getFloat(0,100), id: string = 'NaN', stackable: boolean = false, reward_amount: number = undefined) {
this.logger.info(`[GamblerTrader][${name}] The container roll is: ${roll}!`);
const odds: Array<number> = this.mysteryContainer.getOdds(name);
let guaranteed_rewards = this.mysteryContainer.getGuaranteedRewards(name);
let reward_rolls: Array<number> = this.mysteryContainer.getRewardRolls(name);
let generatedRewards = [];
/* // Not Implemented Yet...
if (guaranteed_rewards) {
this.openGuaranteedRewards(name, roll);
return;
}
*/
// If there is no defined ID, we need to generate a random item with a determined amound of rolls for each rarity (i.e. Medical container)
if (id === "NaN" && reward_rolls) {
const rewards = this.mysteryContainer.getRewards(name);
for(let i = 0; i < rewards.length; i++) {
for(let j = 0; j < reward_rolls[i]; j++) {
const item = this.mysteryContainer.getReward(name, i);
if (item == "NaN") { // Nothing
continue;
}
this.currentID = item;
this.newItemsRequest.itemsWithModsToAdd[this.count] = [this.newItemFormat(item)];
this.count++;
}
}
}
// If there is no defined ID, we need to generate random reward(s) based off property (rolls)
if (id === "NaN" && !reward_rolls) {
for(let i = 0; i < odds.length; i++) {
if(roll <= odds[i]) {
//console.log('WIN! Creating ' + name + ' index = ' + i + ' rewards = ' + this.mysteryContainer.getReward(name, i))
id = this.mysteryContainer.getReward(name, i);
this.currentID = id;
if(!reward_amount){ // determined amount to receive
reward_amount = this.mysteryContainer.getRewardAmount(name, i);
}
if(!stackable){ // Item is not stackable
stackable = this.mysteryContainer.getStackable(name, i);
}
break;
}
}
}
if(this.config.debug) {
this.logger.info("[GamblerTrader] Weapon Mystery Box Information...");
this.logger.info(id);
}
if (id !== "NaN" && !reward_rolls) {
if(!reward_amount){ // ammo has min and max amount instead of a fixed amount
reward_amount = this.mysteryContainer.getRandomAmount(name);
}
if(!stackable){
for(let i = 0; i < reward_amount; i++){
this.newItemsRequest.itemsWithModsToAdd[this.count] = [this.newItemFormat(id)];
this.count++;
}
} else {
this.newItemsRequest.itemsWithModsToAdd[this.count] = [this.newItemFormat(id, reward_amount)];
this.count++;
}
} else {
this.logger.info(`[GamblerTrader][${name}] Case Opened... Received Nothing... Better luck next time :)`);
}
}
private openPreset(name: string = this.name, roll: number = this.randomUtil.getFloat(0,100)){
this.logger.info(`[GamblerTrader][${name}] The container roll is: ${roll}!`);
//console.log('\nopenPreset()');
// ItemCreator stores all preset creation functions
let item = new ItemCreator(this.container);
let preset: Item[] = [];
const odds: Array<number> = this.mysteryContainer.getOdds(name);
for(let i = 0; i < odds.length; i++) {
if(roll <= odds[i]) {
const parent = this.mysteryContainer.getParent(name);
preset = item.createPreset(parent, this.mysteryContainer.getRarity(parent, i));
this.currentID = preset[0]._tpl;
if (name === 'weapon' || name === 'premium_weapon') {
// Store values for possible future use
this.currentCaliber = item.caliber;
this.currentMagazine = item.magazine;
this.currentWeaponType = item.weaponType;
this.currentMagazineMaxAmmo = item.magazineMaxAmmo;
}
if (name === 'helmet') {
this.currentHeadsetCompatible = item.headsetCompatible;
}
break;
}
}
if(this.config.debug) {
this.logger.info("[GamblerTrader] Weapon Mystery Box Information...");
this.logger.info(preset);
}
if (preset.length != 0) {
this.newItemsRequest.itemsWithModsToAdd[this.count] = [...preset];
this.count++;
} else {
this.logger.info(`[GamblerTrader][Weapon] Case Opened... Received Nothing... Better luck next time :)`);
}
}
private newItemFormat(tpl: string, count = undefined) {
const item = {
_id: this.hashUtil.generate(),
_tpl: tpl,
parentId: "hideout",
slotId: "hideout",
upd: {StackObjectsCount: count ? count : 1}
}
return item;
}
}