feat: baro's void surplus (#2334)
Closes #2284 Reviewed-on: #2334 Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com> Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
This commit is contained in:
		
							parent
							
								
									44a129ab0b
								
							
						
					
					
						commit
						c4c622d82b
					
				@ -11,6 +11,7 @@ export const purchaseController: RequestHandler = async (req, res) => {
 | 
				
			|||||||
    const inventory = await getInventory(accountId);
 | 
					    const inventory = await getInventory(accountId);
 | 
				
			||||||
    const response = await handlePurchase(purchaseRequest, inventory);
 | 
					    const response = await handlePurchase(purchaseRequest, inventory);
 | 
				
			||||||
    await inventory.save();
 | 
					    await inventory.save();
 | 
				
			||||||
 | 
					    //console.log(JSON.stringify(response, null, 2));
 | 
				
			||||||
    res.json(response);
 | 
					    res.json(response);
 | 
				
			||||||
    sendWsBroadcastTo(accountId, { update_inventory: true });
 | 
					    sendWsBroadcastTo(accountId, { update_inventory: true });
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
				
			|||||||
@ -91,7 +91,7 @@ import {
 | 
				
			|||||||
    ICrewMemberSkillEfficiency,
 | 
					    ICrewMemberSkillEfficiency,
 | 
				
			||||||
    ICrewMemberDatabase,
 | 
					    ICrewMemberDatabase,
 | 
				
			||||||
    ICrewMemberClient,
 | 
					    ICrewMemberClient,
 | 
				
			||||||
    ISortieRewardAttenuation,
 | 
					    IRewardAttenuation,
 | 
				
			||||||
    IInvasionProgressDatabase,
 | 
					    IInvasionProgressDatabase,
 | 
				
			||||||
    IInvasionProgressClient,
 | 
					    IInvasionProgressClient,
 | 
				
			||||||
    IAccolades,
 | 
					    IAccolades,
 | 
				
			||||||
@ -1417,10 +1417,10 @@ lastSortieRewardSchema.set("toJSON", {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const sortieRewardAttenutationSchema = new Schema<ISortieRewardAttenuation>(
 | 
					const rewardAttenutationSchema = new Schema<IRewardAttenuation>(
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        Tag: String,
 | 
					        Tag: { type: String, required: true },
 | 
				
			||||||
        Atten: Number
 | 
					        Atten: { type: Number, required: true }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    { _id: false }
 | 
					    { _id: false }
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
@ -1666,7 +1666,7 @@ const inventorySchema = new Schema<IInventoryDatabase, InventoryDocumentProps>(
 | 
				
			|||||||
        CompletedSorties: [String],
 | 
					        CompletedSorties: [String],
 | 
				
			||||||
        LastSortieReward: { type: [lastSortieRewardSchema], default: undefined },
 | 
					        LastSortieReward: { type: [lastSortieRewardSchema], default: undefined },
 | 
				
			||||||
        LastLiteSortieReward: { type: [lastSortieRewardSchema], default: undefined },
 | 
					        LastLiteSortieReward: { type: [lastSortieRewardSchema], default: undefined },
 | 
				
			||||||
        SortieRewardAttenuation: { type: [sortieRewardAttenutationSchema], default: undefined },
 | 
					        SortieRewardAttenuation: { type: [rewardAttenutationSchema], default: undefined },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Resource Extractor Drones
 | 
					        // Resource Extractor Drones
 | 
				
			||||||
        Drones: [droneSchema],
 | 
					        Drones: [droneSchema],
 | 
				
			||||||
@ -1805,7 +1805,9 @@ const inventorySchema = new Schema<IInventoryDatabase, InventoryDocumentProps>(
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        HubNpcCustomizations: { type: [hubNpcCustomizationSchema], default: undefined },
 | 
					        HubNpcCustomizations: { type: [hubNpcCustomizationSchema], default: undefined },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        ClaimedJunctionChallengeRewards: { type: [String], default: undefined }
 | 
					        ClaimedJunctionChallengeRewards: { type: [String], default: undefined },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        SpecialItemRewardAttenuation: { type: [rewardAttenutationSchema], default: undefined }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    { timestamps: { createdAt: "Created", updatedAt: false } }
 | 
					    { timestamps: { createdAt: "Created", updatedAt: false } }
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
 | 
				
			|||||||
@ -296,6 +296,12 @@ export const importInventory = (db: TInventoryDatabaseDocument, client: Partial<
 | 
				
			|||||||
            db[key] = client[key];
 | 
					            db[key] = client[key];
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    // IRewardAtten[]
 | 
				
			||||||
 | 
					    for (const key of ["SortieRewardAttenuation", "SpecialItemRewardAttenuation"] as const) {
 | 
				
			||||||
 | 
					        if (client[key] !== undefined) {
 | 
				
			||||||
 | 
					            db[key] = client[key];
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    if (client.XPInfo !== undefined) {
 | 
					    if (client.XPInfo !== undefined) {
 | 
				
			||||||
        db.XPInfo = client.XPInfo;
 | 
					        db.XPInfo = client.XPInfo;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
				
			|||||||
@ -44,6 +44,7 @@ import {
 | 
				
			|||||||
} from "../types/inventoryTypes/commonInventoryTypes";
 | 
					} from "../types/inventoryTypes/commonInventoryTypes";
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
    ExportArcanes,
 | 
					    ExportArcanes,
 | 
				
			||||||
 | 
					    ExportBoosters,
 | 
				
			||||||
    ExportBundles,
 | 
					    ExportBundles,
 | 
				
			||||||
    ExportChallenges,
 | 
					    ExportChallenges,
 | 
				
			||||||
    ExportCustoms,
 | 
					    ExportCustoms,
 | 
				
			||||||
@ -671,6 +672,17 @@ export const addItem = async (
 | 
				
			|||||||
        return await addEmailItem(inventory, typeName);
 | 
					        return await addEmailItem(inventory, typeName);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Boosters are an odd case. They're only added like this via Baro's Void Surplus afaik.
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        const boosterEntry = Object.entries(ExportBoosters).find(arr => arr[1].typeName == typeName);
 | 
				
			||||||
 | 
					        if (boosterEntry) {
 | 
				
			||||||
 | 
					            addBooster(typeName, quantity, inventory);
 | 
				
			||||||
 | 
					            return {
 | 
				
			||||||
 | 
					                Boosters: [{ ItemType: typeName, ExpiryDate: quantity }]
 | 
				
			||||||
 | 
					            };
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Path-based duck typing
 | 
					    // Path-based duck typing
 | 
				
			||||||
    switch (typeName.substr(1).split("/")[1]) {
 | 
					    switch (typeName.substr(1).split("/")[1]) {
 | 
				
			||||||
        case "Powersuits":
 | 
					        case "Powersuits":
 | 
				
			||||||
@ -1354,7 +1366,7 @@ export const addCustomization = (
 | 
				
			|||||||
    customizationName: string,
 | 
					    customizationName: string,
 | 
				
			||||||
    inventoryChanges: IInventoryChanges = {}
 | 
					    inventoryChanges: IInventoryChanges = {}
 | 
				
			||||||
): IInventoryChanges => {
 | 
					): IInventoryChanges => {
 | 
				
			||||||
    if (!inventory.FlavourItems.find(x => x.ItemType == customizationName)) {
 | 
					    if (!inventory.FlavourItems.some(x => x.ItemType == customizationName)) {
 | 
				
			||||||
        const flavourItemIndex = inventory.FlavourItems.push({ ItemType: customizationName }) - 1;
 | 
					        const flavourItemIndex = inventory.FlavourItems.push({ ItemType: customizationName }) - 1;
 | 
				
			||||||
        // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
 | 
					        // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
 | 
				
			||||||
        inventoryChanges.FlavourItems ??= [];
 | 
					        inventoryChanges.FlavourItems ??= [];
 | 
				
			||||||
@ -1370,7 +1382,7 @@ export const addSkin = (
 | 
				
			|||||||
    typeName: string,
 | 
					    typeName: string,
 | 
				
			||||||
    inventoryChanges: IInventoryChanges = {}
 | 
					    inventoryChanges: IInventoryChanges = {}
 | 
				
			||||||
): IInventoryChanges => {
 | 
					): IInventoryChanges => {
 | 
				
			||||||
    if (inventory.WeaponSkins.find(x => x.ItemType == typeName)) {
 | 
					    if (inventory.WeaponSkins.some(x => x.ItemType == typeName)) {
 | 
				
			||||||
        logger.debug(`refusing to add WeaponSkin ${typeName} because account already owns it`);
 | 
					        logger.debug(`refusing to add WeaponSkin ${typeName} because account already owns it`);
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
        const index = inventory.WeaponSkins.push({ ItemType: typeName, IsNew: true }) - 1;
 | 
					        const index = inventory.WeaponSkins.push({ ItemType: typeName, IsNew: true }) - 1;
 | 
				
			||||||
 | 
				
			|||||||
@ -8,7 +8,7 @@ import {
 | 
				
			|||||||
    updateCurrency,
 | 
					    updateCurrency,
 | 
				
			||||||
    updateSlots
 | 
					    updateSlots
 | 
				
			||||||
} from "@/src/services/inventoryService";
 | 
					} from "@/src/services/inventoryService";
 | 
				
			||||||
import { getRandomWeightedRewardUc } from "@/src/services/rngService";
 | 
					import { getRandomReward, getRandomWeightedRewardUc } from "@/src/services/rngService";
 | 
				
			||||||
import { applyStandingToVendorManifest, getVendorManifestByOid } from "@/src/services/serversideVendorsService";
 | 
					import { applyStandingToVendorManifest, getVendorManifestByOid } from "@/src/services/serversideVendorsService";
 | 
				
			||||||
import { IMiscItem } from "@/src/types/inventoryTypes/inventoryTypes";
 | 
					import { IMiscItem } from "@/src/types/inventoryTypes/inventoryTypes";
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
@ -37,6 +37,7 @@ import { config } from "./configService";
 | 
				
			|||||||
import { TInventoryDatabaseDocument } from "../models/inventoryModels/inventoryModel";
 | 
					import { TInventoryDatabaseDocument } from "../models/inventoryModels/inventoryModel";
 | 
				
			||||||
import { fromStoreItem, toStoreItem } from "./itemDataService";
 | 
					import { fromStoreItem, toStoreItem } from "./itemDataService";
 | 
				
			||||||
import { DailyDeal } from "../models/worldStateModel";
 | 
					import { DailyDeal } from "../models/worldStateModel";
 | 
				
			||||||
 | 
					import { fromMongoDate, toMongoDate } from "../helpers/inventoryHelpers";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const getStoreItemCategory = (storeItem: string): string => {
 | 
					export const getStoreItemCategory = (storeItem: string): string => {
 | 
				
			||||||
    const storeItemString = getSubstringFromKeyword(storeItem, "StoreItems/");
 | 
					    const storeItemString = getSubstringFromKeyword(storeItem, "StoreItems/");
 | 
				
			||||||
@ -53,6 +54,58 @@ export const getStoreItemTypesCategory = (typesItem: string): string => {
 | 
				
			|||||||
    return typeElements[1];
 | 
					    return typeElements[1];
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const tallyVendorPurchase = (
 | 
				
			||||||
 | 
					    inventory: TInventoryDatabaseDocument,
 | 
				
			||||||
 | 
					    inventoryChanges: IInventoryChanges,
 | 
				
			||||||
 | 
					    VendorType: string,
 | 
				
			||||||
 | 
					    ItemId: string,
 | 
				
			||||||
 | 
					    numPurchased: number,
 | 
				
			||||||
 | 
					    Expiry: Date
 | 
				
			||||||
 | 
					): void => {
 | 
				
			||||||
 | 
					    if (!config.noVendorPurchaseLimits) {
 | 
				
			||||||
 | 
					        inventory.RecentVendorPurchases ??= [];
 | 
				
			||||||
 | 
					        let vendorPurchases = inventory.RecentVendorPurchases.find(x => x.VendorType == VendorType);
 | 
				
			||||||
 | 
					        if (!vendorPurchases) {
 | 
				
			||||||
 | 
					            vendorPurchases =
 | 
				
			||||||
 | 
					                inventory.RecentVendorPurchases[
 | 
				
			||||||
 | 
					                    inventory.RecentVendorPurchases.push({
 | 
				
			||||||
 | 
					                        VendorType: VendorType,
 | 
				
			||||||
 | 
					                        PurchaseHistory: []
 | 
				
			||||||
 | 
					                    }) - 1
 | 
				
			||||||
 | 
					                ];
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        let historyEntry = vendorPurchases.PurchaseHistory.find(x => x.ItemId == ItemId);
 | 
				
			||||||
 | 
					        if (historyEntry) {
 | 
				
			||||||
 | 
					            if (Date.now() >= historyEntry.Expiry.getTime()) {
 | 
				
			||||||
 | 
					                historyEntry.NumPurchased = numPurchased;
 | 
				
			||||||
 | 
					                historyEntry.Expiry = Expiry;
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                historyEntry.NumPurchased += numPurchased;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            historyEntry =
 | 
				
			||||||
 | 
					                vendorPurchases.PurchaseHistory[
 | 
				
			||||||
 | 
					                    vendorPurchases.PurchaseHistory.push({
 | 
				
			||||||
 | 
					                        ItemId: ItemId,
 | 
				
			||||||
 | 
					                        NumPurchased: numPurchased,
 | 
				
			||||||
 | 
					                        Expiry: Expiry
 | 
				
			||||||
 | 
					                    }) - 1
 | 
				
			||||||
 | 
					                ];
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        inventoryChanges.NewVendorPurchase = {
 | 
				
			||||||
 | 
					            VendorType: VendorType,
 | 
				
			||||||
 | 
					            PurchaseHistory: [
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    ItemId: ItemId,
 | 
				
			||||||
 | 
					                    NumPurchased: historyEntry.NumPurchased,
 | 
				
			||||||
 | 
					                    Expiry: toMongoDate(Expiry)
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            ]
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					        inventoryChanges.RecentVendorPurchases = inventoryChanges.NewVendorPurchase;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const handlePurchase = async (
 | 
					export const handlePurchase = async (
 | 
				
			||||||
    purchaseRequest: IPurchaseRequest,
 | 
					    purchaseRequest: IPurchaseRequest,
 | 
				
			||||||
    inventory: TInventoryDatabaseDocument
 | 
					    inventory: TInventoryDatabaseDocument
 | 
				
			||||||
@ -99,20 +152,7 @@ export const handlePurchase = async (
 | 
				
			|||||||
            if (offer.LocTagRandSeed !== undefined) {
 | 
					            if (offer.LocTagRandSeed !== undefined) {
 | 
				
			||||||
                seed = BigInt(offer.LocTagRandSeed);
 | 
					                seed = BigInt(offer.LocTagRandSeed);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            if (!config.noVendorPurchaseLimits && ItemId) {
 | 
					            if (ItemId) {
 | 
				
			||||||
                inventory.RecentVendorPurchases ??= [];
 | 
					 | 
				
			||||||
                let vendorPurchases = inventory.RecentVendorPurchases.find(
 | 
					 | 
				
			||||||
                    x => x.VendorType == manifest!.VendorInfo.TypeName
 | 
					 | 
				
			||||||
                );
 | 
					 | 
				
			||||||
                if (!vendorPurchases) {
 | 
					 | 
				
			||||||
                    vendorPurchases =
 | 
					 | 
				
			||||||
                        inventory.RecentVendorPurchases[
 | 
					 | 
				
			||||||
                            inventory.RecentVendorPurchases.push({
 | 
					 | 
				
			||||||
                                VendorType: manifest.VendorInfo.TypeName,
 | 
					 | 
				
			||||||
                                PurchaseHistory: []
 | 
					 | 
				
			||||||
                            }) - 1
 | 
					 | 
				
			||||||
                        ];
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                let expiry = parseInt(offer.Expiry.$date.$numberLong);
 | 
					                let expiry = parseInt(offer.Expiry.$date.$numberLong);
 | 
				
			||||||
                if (purchaseRequest.PurchaseParams.IsWeekly) {
 | 
					                if (purchaseRequest.PurchaseParams.IsWeekly) {
 | 
				
			||||||
                    const EPOCH = 1734307200 * 1000; // Monday
 | 
					                    const EPOCH = 1734307200 * 1000; // Monday
 | 
				
			||||||
@ -120,34 +160,14 @@ export const handlePurchase = async (
 | 
				
			|||||||
                    const weekStart = EPOCH + week * 604800000;
 | 
					                    const weekStart = EPOCH + week * 604800000;
 | 
				
			||||||
                    expiry = weekStart + 604800000;
 | 
					                    expiry = weekStart + 604800000;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                const historyEntry = vendorPurchases.PurchaseHistory.find(x => x.ItemId == ItemId);
 | 
					                tallyVendorPurchase(
 | 
				
			||||||
                let numPurchased = purchaseRequest.PurchaseParams.Quantity;
 | 
					                    inventory,
 | 
				
			||||||
                if (historyEntry) {
 | 
					                    prePurchaseInventoryChanges,
 | 
				
			||||||
                    if (Date.now() >= historyEntry.Expiry.getTime()) {
 | 
					                    manifest.VendorInfo.TypeName,
 | 
				
			||||||
                        historyEntry.NumPurchased = numPurchased;
 | 
					                    ItemId,
 | 
				
			||||||
                        historyEntry.Expiry = new Date(expiry);
 | 
					                    purchaseRequest.PurchaseParams.Quantity,
 | 
				
			||||||
                    } else {
 | 
					                    new Date(expiry)
 | 
				
			||||||
                        numPurchased += historyEntry.NumPurchased;
 | 
					                );
 | 
				
			||||||
                        historyEntry.NumPurchased += purchaseRequest.PurchaseParams.Quantity;
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                } else {
 | 
					 | 
				
			||||||
                    vendorPurchases.PurchaseHistory.push({
 | 
					 | 
				
			||||||
                        ItemId: ItemId,
 | 
					 | 
				
			||||||
                        NumPurchased: purchaseRequest.PurchaseParams.Quantity,
 | 
					 | 
				
			||||||
                        Expiry: new Date(expiry)
 | 
					 | 
				
			||||||
                    });
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                prePurchaseInventoryChanges.NewVendorPurchase = {
 | 
					 | 
				
			||||||
                    VendorType: manifest.VendorInfo.TypeName,
 | 
					 | 
				
			||||||
                    PurchaseHistory: [
 | 
					 | 
				
			||||||
                        {
 | 
					 | 
				
			||||||
                            ItemId: ItemId,
 | 
					 | 
				
			||||||
                            NumPurchased: numPurchased,
 | 
					 | 
				
			||||||
                            Expiry: { $date: { $numberLong: expiry.toString() } }
 | 
					 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
                    ]
 | 
					 | 
				
			||||||
                };
 | 
					 | 
				
			||||||
                prePurchaseInventoryChanges.RecentVendorPurchases = prePurchaseInventoryChanges.NewVendorPurchase;
 | 
					 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            purchaseRequest.PurchaseParams.Quantity *= offer.QuantityMultiplier;
 | 
					            purchaseRequest.PurchaseParams.Quantity *= offer.QuantityMultiplier;
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
@ -193,7 +213,7 @@ export const handlePurchase = async (
 | 
				
			|||||||
                    throw new Error(`vendor purchase should not have an expected price`);
 | 
					                    throw new Error(`vendor purchase should not have an expected price`);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                if (!config.dontSubtractPurchaseItemCost) {
 | 
					                if (offer.PrimePrice && !config.dontSubtractPurchaseItemCost) {
 | 
				
			||||||
                    const invItem: IMiscItem = {
 | 
					                    const invItem: IMiscItem = {
 | 
				
			||||||
                        ItemType: "/Lotus/Types/Items/MiscItems/PrimeBucks",
 | 
					                        ItemType: "/Lotus/Types/Items/MiscItems/PrimeBucks",
 | 
				
			||||||
                        ItemCount: offer.PrimePrice * purchaseRequest.PurchaseParams.Quantity * -1
 | 
					                        ItemCount: offer.PrimePrice * purchaseRequest.PurchaseParams.Quantity * -1
 | 
				
			||||||
@ -202,6 +222,17 @@ export const handlePurchase = async (
 | 
				
			|||||||
                    purchaseResponse.InventoryChanges.MiscItems ??= [];
 | 
					                    purchaseResponse.InventoryChanges.MiscItems ??= [];
 | 
				
			||||||
                    purchaseResponse.InventoryChanges.MiscItems.push(invItem);
 | 
					                    purchaseResponse.InventoryChanges.MiscItems.push(invItem);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if (offer.Limit) {
 | 
				
			||||||
 | 
					                    tallyVendorPurchase(
 | 
				
			||||||
 | 
					                        inventory,
 | 
				
			||||||
 | 
					                        purchaseResponse.InventoryChanges,
 | 
				
			||||||
 | 
					                        "VoidTrader",
 | 
				
			||||||
 | 
					                        offer.ItemType,
 | 
				
			||||||
 | 
					                        purchaseRequest.PurchaseParams.Quantity,
 | 
				
			||||||
 | 
					                        fromMongoDate(worldState.VoidTraders[0].Expiry)
 | 
				
			||||||
 | 
					                    );
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            break;
 | 
					            break;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@ -509,12 +540,57 @@ const handleBoosterPackPurchase = async (
 | 
				
			|||||||
            "attempt to roll over 100 booster packs in a single go. possible but unlikely to be desirable for the user or the server."
 | 
					            "attempt to roll over 100 booster packs in a single go. possible but unlikely to be desirable for the user or the server."
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    const specialItemReward = pack.components.find(x => x.PityIncreaseRate);
 | 
				
			||||||
    for (let i = 0; i != quantity; ++i) {
 | 
					    for (let i = 0; i != quantity; ++i) {
 | 
				
			||||||
        const disallowedItems = new Set();
 | 
					        if (specialItemReward) {
 | 
				
			||||||
        for (let roll = 0; roll != pack.rarityWeightsPerRoll.length; ) {
 | 
					            {
 | 
				
			||||||
            const weights = pack.rarityWeightsPerRoll[roll];
 | 
					                const normalComponents = [];
 | 
				
			||||||
            const result = getRandomWeightedRewardUc(pack.components, weights);
 | 
					                for (const comp of pack.components) {
 | 
				
			||||||
            if (result) {
 | 
					                    if (!comp.PityIncreaseRate) {
 | 
				
			||||||
 | 
					                        const { Probability, ...rest } = comp;
 | 
				
			||||||
 | 
					                        normalComponents.push({
 | 
				
			||||||
 | 
					                            ...rest,
 | 
				
			||||||
 | 
					                            probability: Probability!
 | 
				
			||||||
 | 
					                        });
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                const result = getRandomReward(normalComponents)!;
 | 
				
			||||||
 | 
					                logger.debug(`booster pack rolled`, result);
 | 
				
			||||||
 | 
					                purchaseResponse.BoosterPackItems += toStoreItem(result.Item) + ',{"lvl":0};';
 | 
				
			||||||
 | 
					                combineInventoryChanges(
 | 
				
			||||||
 | 
					                    purchaseResponse.InventoryChanges,
 | 
				
			||||||
 | 
					                    await addItem(inventory, result.Item, result.Amount)
 | 
				
			||||||
 | 
					                );
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (!inventory.WeaponSkins.some(x => x.ItemType == specialItemReward.Item)) {
 | 
				
			||||||
 | 
					                inventory.SpecialItemRewardAttenuation ??= [];
 | 
				
			||||||
 | 
					                let atten = inventory.SpecialItemRewardAttenuation.find(x => x.Tag == specialItemReward.Item);
 | 
				
			||||||
 | 
					                if (!atten) {
 | 
				
			||||||
 | 
					                    atten =
 | 
				
			||||||
 | 
					                        inventory.SpecialItemRewardAttenuation[
 | 
				
			||||||
 | 
					                            inventory.SpecialItemRewardAttenuation.push({
 | 
				
			||||||
 | 
					                                Tag: specialItemReward.Item,
 | 
				
			||||||
 | 
					                                Atten: specialItemReward.Probability!
 | 
				
			||||||
 | 
					                            }) - 1
 | 
				
			||||||
 | 
					                        ];
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                if (Math.random() < atten.Atten) {
 | 
				
			||||||
 | 
					                    purchaseResponse.BoosterPackItems += toStoreItem(specialItemReward.Item) + ',{"lvl":0};';
 | 
				
			||||||
 | 
					                    combineInventoryChanges(
 | 
				
			||||||
 | 
					                        purchaseResponse.InventoryChanges,
 | 
				
			||||||
 | 
					                        await addItem(inventory, specialItemReward.Item)
 | 
				
			||||||
 | 
					                    );
 | 
				
			||||||
 | 
					                    // TOVERIFY: Is the SpecialItemRewardAttenuation entry removed now?
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    atten.Atten += specialItemReward.PityIncreaseRate!;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            const disallowedItems = new Set();
 | 
				
			||||||
 | 
					            for (let roll = 0; roll != pack.rarityWeightsPerRoll.length; ) {
 | 
				
			||||||
 | 
					                const weights = pack.rarityWeightsPerRoll[roll];
 | 
				
			||||||
 | 
					                const result = getRandomWeightedRewardUc(pack.components, weights)!;
 | 
				
			||||||
                logger.debug(`booster pack rolled`, result);
 | 
					                logger.debug(`booster pack rolled`, result);
 | 
				
			||||||
                if (disallowedItems.has(result.Item)) {
 | 
					                if (disallowedItems.has(result.Item)) {
 | 
				
			||||||
                    logger.debug(`oops, can't use that one; trying again`);
 | 
					                    logger.debug(`oops, can't use that one; trying again`);
 | 
				
			||||||
@ -524,9 +600,12 @@ const handleBoosterPackPurchase = async (
 | 
				
			|||||||
                    disallowedItems.add(result.Item);
 | 
					                    disallowedItems.add(result.Item);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                purchaseResponse.BoosterPackItems += toStoreItem(result.Item) + ',{"lvl":0};';
 | 
					                purchaseResponse.BoosterPackItems += toStoreItem(result.Item) + ',{"lvl":0};';
 | 
				
			||||||
                combineInventoryChanges(purchaseResponse.InventoryChanges, await addItem(inventory, result.Item, 1));
 | 
					                combineInventoryChanges(
 | 
				
			||||||
 | 
					                    purchaseResponse.InventoryChanges,
 | 
				
			||||||
 | 
					                    await addItem(inventory, result.Item, result.Amount)
 | 
				
			||||||
 | 
					                );
 | 
				
			||||||
 | 
					                ++roll;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            ++roll;
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return purchaseResponse;
 | 
					    return purchaseResponse;
 | 
				
			||||||
 | 
				
			|||||||
@ -298,7 +298,7 @@ export interface IInventoryClient extends IDailyAffiliations, InventoryClientEqu
 | 
				
			|||||||
    CompletedSorties: string[];
 | 
					    CompletedSorties: string[];
 | 
				
			||||||
    LastSortieReward?: ILastSortieRewardClient[];
 | 
					    LastSortieReward?: ILastSortieRewardClient[];
 | 
				
			||||||
    LastLiteSortieReward?: ILastSortieRewardClient[];
 | 
					    LastLiteSortieReward?: ILastSortieRewardClient[];
 | 
				
			||||||
    SortieRewardAttenuation?: ISortieRewardAttenuation[];
 | 
					    SortieRewardAttenuation?: IRewardAttenuation[];
 | 
				
			||||||
    Drones: IDroneClient[];
 | 
					    Drones: IDroneClient[];
 | 
				
			||||||
    StepSequencers: IStepSequencer[];
 | 
					    StepSequencers: IStepSequencer[];
 | 
				
			||||||
    ActiveAvatarImageType?: string;
 | 
					    ActiveAvatarImageType?: string;
 | 
				
			||||||
@ -383,6 +383,7 @@ export interface IInventoryClient extends IDailyAffiliations, InventoryClientEqu
 | 
				
			|||||||
    HubNpcCustomizations?: IHubNpcCustomization[];
 | 
					    HubNpcCustomizations?: IHubNpcCustomization[];
 | 
				
			||||||
    Ship?: IOrbiter; // U22 and below, response only
 | 
					    Ship?: IOrbiter; // U22 and below, response only
 | 
				
			||||||
    ClaimedJunctionChallengeRewards?: string[]; // U39
 | 
					    ClaimedJunctionChallengeRewards?: string[]; // U39
 | 
				
			||||||
 | 
					    SpecialItemRewardAttenuation?: IRewardAttenuation[]; // Baro's Void Surplus
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface IAffiliation {
 | 
					export interface IAffiliation {
 | 
				
			||||||
@ -785,7 +786,7 @@ export interface ILastSortieRewardDatabase extends Omit<ILastSortieRewardClient,
 | 
				
			|||||||
    SortieId: Types.ObjectId;
 | 
					    SortieId: Types.ObjectId;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface ISortieRewardAttenuation {
 | 
					export interface IRewardAttenuation {
 | 
				
			||||||
    Tag: string;
 | 
					    Tag: string;
 | 
				
			||||||
    Atten: number;
 | 
					    Atten: number;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -160,6 +160,7 @@ export interface IVoidTraderOffer {
 | 
				
			|||||||
    ItemType: string;
 | 
					    ItemType: string;
 | 
				
			||||||
    PrimePrice: number;
 | 
					    PrimePrice: number;
 | 
				
			||||||
    RegularPrice: number;
 | 
					    RegularPrice: number;
 | 
				
			||||||
 | 
					    Limit?: number;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface IVoidStorm {
 | 
					export interface IVoidStorm {
 | 
				
			||||||
 | 
				
			|||||||
@ -1,7 +1,8 @@
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
  "evergreen": [
 | 
					  "evergreen": [
 | 
				
			||||||
    { "ItemType": "/Lotus/StoreItems/Types/Keys/MummyQuestKeyBlueprint", "PrimePrice": 100, "RegularPrice": 25000 },
 | 
					    { "ItemType": "/Lotus/StoreItems/Types/Keys/MummyQuestKeyBlueprint", "PrimePrice": 100, "RegularPrice": 25000 },
 | 
				
			||||||
    { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Effects/FootstepsMaple", "PrimePrice": 15, "RegularPrice": 1000 }
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Effects/FootstepsMaple", "PrimePrice": 15, "RegularPrice": 1000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Types/BoosterPacks/BaroTreasureBox", "PrimePrice": 0, "RegularPrice": 50000, "Limit": 1 }
 | 
				
			||||||
  ],
 | 
					  ],
 | 
				
			||||||
  "armorSets": [
 | 
					  "armorSets": [
 | 
				
			||||||
    [
 | 
					    [
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user