feat: acquisition of CrewMembers (#1705)
Reviewed-on: OpenWF/SpaceNinjaServer#1705 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
							
								
									379f57be2c
								
							
						
					
					
						commit
						196182f9a8
					
				@ -88,7 +88,11 @@ import {
 | 
			
		||||
    IPersonalTechProjectDatabase,
 | 
			
		||||
    IPersonalTechProjectClient,
 | 
			
		||||
    ILastSortieRewardDatabase,
 | 
			
		||||
    ILastSortieRewardClient
 | 
			
		||||
    ILastSortieRewardClient,
 | 
			
		||||
    ICrewMemberSkill,
 | 
			
		||||
    ICrewMemberSkillEfficiency,
 | 
			
		||||
    ICrewMemberDatabase,
 | 
			
		||||
    ICrewMemberClient
 | 
			
		||||
} from "../../types/inventoryTypes/inventoryTypes";
 | 
			
		||||
import { IOid } from "../../types/commonTypes";
 | 
			
		||||
import {
 | 
			
		||||
@ -294,6 +298,55 @@ upgradeSchema.set("toJSON", {
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const crewMemberSkillSchema = new Schema<ICrewMemberSkill>(
 | 
			
		||||
    {
 | 
			
		||||
        Assigned: Number
 | 
			
		||||
    },
 | 
			
		||||
    { _id: false }
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
const crewMemberSkillEfficiencySchema = new Schema<ICrewMemberSkillEfficiency>(
 | 
			
		||||
    {
 | 
			
		||||
        PILOTING: crewMemberSkillSchema,
 | 
			
		||||
        GUNNERY: crewMemberSkillSchema,
 | 
			
		||||
        ENGINEERING: crewMemberSkillSchema,
 | 
			
		||||
        COMBAT: crewMemberSkillSchema,
 | 
			
		||||
        SURVIVABILITY: crewMemberSkillSchema
 | 
			
		||||
    },
 | 
			
		||||
    { _id: false }
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
const crewMemberSchema = new Schema<ICrewMemberDatabase>(
 | 
			
		||||
    {
 | 
			
		||||
        ItemType: { type: String, required: true },
 | 
			
		||||
        NemesisFingerprint: { type: BigInt, default: 0n },
 | 
			
		||||
        Seed: { type: BigInt, default: 0n },
 | 
			
		||||
        AssignedRole: Number,
 | 
			
		||||
        SkillEfficiency: crewMemberSkillEfficiencySchema,
 | 
			
		||||
        WeaponConfigIdx: Number,
 | 
			
		||||
        WeaponId: { type: Schema.Types.ObjectId, default: "000000000000000000000000" },
 | 
			
		||||
        XP: { type: Number, default: 0 },
 | 
			
		||||
        PowersuitType: { type: String, required: true },
 | 
			
		||||
        Configs: [ItemConfigSchema],
 | 
			
		||||
        SecondInCommand: { type: Boolean, default: false }
 | 
			
		||||
    },
 | 
			
		||||
    { id: false }
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
crewMemberSchema.set("toJSON", {
 | 
			
		||||
    virtuals: true,
 | 
			
		||||
    transform(_doc, obj) {
 | 
			
		||||
        const db = obj as ICrewMemberDatabase;
 | 
			
		||||
        const client = obj as ICrewMemberClient;
 | 
			
		||||
 | 
			
		||||
        client.WeaponId = toOid(db.WeaponId);
 | 
			
		||||
        client.ItemId = toOid(db._id);
 | 
			
		||||
 | 
			
		||||
        delete obj._id;
 | 
			
		||||
        delete obj.__v;
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const slotsBinSchema = new Schema<ISlots>(
 | 
			
		||||
    {
 | 
			
		||||
        Slots: Number,
 | 
			
		||||
@ -1363,7 +1416,7 @@ const inventorySchema = new Schema<IInventoryDatabase, InventoryDocumentProps>(
 | 
			
		||||
        CrewShipSalvagedWeaponSkins: [upgradeSchema],
 | 
			
		||||
 | 
			
		||||
        //RailJack Crew
 | 
			
		||||
        CrewMembers: [Schema.Types.Mixed],
 | 
			
		||||
        CrewMembers: [crewMemberSchema],
 | 
			
		||||
 | 
			
		||||
        //Complete Mission\Quests
 | 
			
		||||
        Missions: [missionSchema],
 | 
			
		||||
@ -1645,6 +1698,7 @@ export type InventoryDocumentProps = {
 | 
			
		||||
    CrewShipWeaponSkins: Types.DocumentArray<IUpgradeDatabase>;
 | 
			
		||||
    CrewShipSalvagedWeaponSkins: Types.DocumentArray<IUpgradeDatabase>;
 | 
			
		||||
    PersonalTechProjects: Types.DocumentArray<IPersonalTechProjectDatabase>;
 | 
			
		||||
    CrewMembers: Types.DocumentArray<ICrewMemberDatabase>;
 | 
			
		||||
} & { [K in TEquipmentKey]: Types.DocumentArray<IEquipmentDatabase> };
 | 
			
		||||
 | 
			
		||||
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
 | 
			
		||||
 | 
			
		||||
@ -22,7 +22,8 @@ import {
 | 
			
		||||
    IDroneClient,
 | 
			
		||||
    IUpgradeClient,
 | 
			
		||||
    TPartialStartingGear,
 | 
			
		||||
    ILoreFragmentScan
 | 
			
		||||
    ILoreFragmentScan,
 | 
			
		||||
    ICrewMemberClient
 | 
			
		||||
} from "@/src/types/inventoryTypes/inventoryTypes";
 | 
			
		||||
import { IGenericUpdate, IUpdateNodeIntrosResponse } from "../types/genericUpdate";
 | 
			
		||||
import { IKeyChainRequest, IMissionInventoryUpdateRequest } from "../types/requestTypes";
 | 
			
		||||
@ -713,6 +714,15 @@ export const addItem = async (
 | 
			
		||||
                        return {
 | 
			
		||||
                            MiscItems: miscItemChanges
 | 
			
		||||
                        };
 | 
			
		||||
                    } else if (typeName.startsWith("/Lotus/Types/Game/CrewShip/CrewMember/")) {
 | 
			
		||||
                        if (!seed) {
 | 
			
		||||
                            throw new Error(`Expected crew member to have a seed`);
 | 
			
		||||
                        }
 | 
			
		||||
                        seed |= 0x33b81en << 32n;
 | 
			
		||||
                        return {
 | 
			
		||||
                            ...addCrewMember(inventory, typeName, seed),
 | 
			
		||||
                            ...occupySlot(inventory, InventorySlot.CREWMEMBERS, premiumPurchase)
 | 
			
		||||
                        };
 | 
			
		||||
                    } else if (typeName == "/Lotus/Types/Game/CrewShip/RailJack/DefaultHarness") {
 | 
			
		||||
                        return addCrewShipHarness(inventory, typeName);
 | 
			
		||||
                    }
 | 
			
		||||
@ -1212,6 +1222,78 @@ const addDrone = (
 | 
			
		||||
    return inventoryChanges;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/*const getCrewMemberSkills = (seed: bigint, skillPointsToAssign: number): Record<string, number> => {
 | 
			
		||||
    const rng = new SRng(seed);
 | 
			
		||||
 | 
			
		||||
    const skills = ["PILOTING", "GUNNERY", "ENGINEERING", "COMBAT", "SURVIVABILITY"];
 | 
			
		||||
    for (let i = 1; i != 5; ++i) {
 | 
			
		||||
        const swapIndex = rng.randomInt(0, i);
 | 
			
		||||
        if (swapIndex != i) {
 | 
			
		||||
            const tmp = skills[i];
 | 
			
		||||
            skills[i] = skills[swapIndex];
 | 
			
		||||
            skills[swapIndex] = tmp;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    rng.randomFloat(); // unused afaict
 | 
			
		||||
 | 
			
		||||
    const skillAssignments = [0, 0, 0, 0, 0];
 | 
			
		||||
    for (let skill = 0; skillPointsToAssign; skill = (skill + 1) % 5) {
 | 
			
		||||
        const maxIncrease = Math.min(5 - skillAssignments[skill], skillPointsToAssign);
 | 
			
		||||
        const increase = rng.randomInt(0, maxIncrease);
 | 
			
		||||
        skillAssignments[skill] += increase;
 | 
			
		||||
        skillPointsToAssign -= increase;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    skillAssignments.sort((a, b) => b - a);
 | 
			
		||||
 | 
			
		||||
    const combined: Record<string, number> = {};
 | 
			
		||||
    for (let i = 0; i != 5; ++i) {
 | 
			
		||||
        combined[skills[i]] = skillAssignments[i];
 | 
			
		||||
    }
 | 
			
		||||
    return combined;
 | 
			
		||||
};*/
 | 
			
		||||
 | 
			
		||||
const addCrewMember = (
 | 
			
		||||
    inventory: TInventoryDatabaseDocument,
 | 
			
		||||
    itemType: string,
 | 
			
		||||
    seed: bigint,
 | 
			
		||||
    inventoryChanges: IInventoryChanges = {}
 | 
			
		||||
): IInventoryChanges => {
 | 
			
		||||
    // SkillEfficiency is additional to the base stats, so we don't need to compute this
 | 
			
		||||
    //const skillPointsToAssign = itemType.endsWith("Strong") ? 12 : itemType.indexOf("Medium") != -1 ? 10 : 8;
 | 
			
		||||
    //const skills = getCrewMemberSkills(seed, skillPointsToAssign);
 | 
			
		||||
 | 
			
		||||
    // Arbiters = male
 | 
			
		||||
    // CephalonSuda = female
 | 
			
		||||
    // NewLoka = female
 | 
			
		||||
    // Perrin = male
 | 
			
		||||
    // RedVeil = male
 | 
			
		||||
    // SteelMeridian = female
 | 
			
		||||
    const powersuitType =
 | 
			
		||||
        itemType.indexOf("Arbiters") != -1 || itemType.indexOf("Perrin") != -1 || itemType.indexOf("RedVeil") != -1
 | 
			
		||||
            ? "/Lotus/Powersuits/NpcPowersuits/CrewMemberMaleSuit"
 | 
			
		||||
            : "/Lotus/Powersuits/NpcPowersuits/CrewMemberFemaleSuit";
 | 
			
		||||
 | 
			
		||||
    const index =
 | 
			
		||||
        inventory.CrewMembers.push({
 | 
			
		||||
            ItemType: itemType,
 | 
			
		||||
            NemesisFingerprint: 0n,
 | 
			
		||||
            Seed: seed,
 | 
			
		||||
            SkillEfficiency: {
 | 
			
		||||
                PILOTING: { Assigned: 0 },
 | 
			
		||||
                GUNNERY: { Assigned: 0 },
 | 
			
		||||
                ENGINEERING: { Assigned: 0 },
 | 
			
		||||
                COMBAT: { Assigned: 0 },
 | 
			
		||||
                SURVIVABILITY: { Assigned: 0 }
 | 
			
		||||
            },
 | 
			
		||||
            PowersuitType: powersuitType
 | 
			
		||||
        }) - 1;
 | 
			
		||||
    inventoryChanges.CrewMembers ??= [];
 | 
			
		||||
    inventoryChanges.CrewMembers.push(inventory.CrewMembers[index].toJSON<ICrewMemberClient>());
 | 
			
		||||
    return inventoryChanges;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const addEmailItem = async (
 | 
			
		||||
    inventory: TInventoryDatabaseDocument,
 | 
			
		||||
    typeName: string,
 | 
			
		||||
 | 
			
		||||
@ -141,7 +141,8 @@ export const handlePurchase = async (
 | 
			
		||||
        inventory,
 | 
			
		||||
        purchaseRequest.PurchaseParams.Quantity,
 | 
			
		||||
        undefined,
 | 
			
		||||
        undefined,
 | 
			
		||||
        false,
 | 
			
		||||
        purchaseRequest.PurchaseParams.UsePremium,
 | 
			
		||||
        seed
 | 
			
		||||
    );
 | 
			
		||||
    combineInventoryChanges(purchaseResponse.InventoryChanges, prePurchaseInventoryChanges);
 | 
			
		||||
@ -331,6 +332,7 @@ export const handleStoreItemAcquisition = async (
 | 
			
		||||
    quantity: number = 1,
 | 
			
		||||
    durability: TRarity = "COMMON",
 | 
			
		||||
    ignorePurchaseQuantity: boolean = false,
 | 
			
		||||
    premiumPurchase: boolean = true,
 | 
			
		||||
    seed?: bigint
 | 
			
		||||
): Promise<IPurchaseResponse> => {
 | 
			
		||||
    let purchaseResponse = {
 | 
			
		||||
@ -352,11 +354,20 @@ export const handleStoreItemAcquisition = async (
 | 
			
		||||
        }
 | 
			
		||||
        switch (storeCategory) {
 | 
			
		||||
            default: {
 | 
			
		||||
                purchaseResponse = { InventoryChanges: await addItem(inventory, internalName, quantity, true, seed) };
 | 
			
		||||
                purchaseResponse = {
 | 
			
		||||
                    InventoryChanges: await addItem(inventory, internalName, quantity, premiumPurchase, seed)
 | 
			
		||||
                };
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
            case "Types":
 | 
			
		||||
                purchaseResponse = await handleTypesPurchase(internalName, inventory, quantity, ignorePurchaseQuantity);
 | 
			
		||||
                purchaseResponse = await handleTypesPurchase(
 | 
			
		||||
                    internalName,
 | 
			
		||||
                    inventory,
 | 
			
		||||
                    quantity,
 | 
			
		||||
                    ignorePurchaseQuantity,
 | 
			
		||||
                    premiumPurchase,
 | 
			
		||||
                    seed
 | 
			
		||||
                );
 | 
			
		||||
                break;
 | 
			
		||||
            case "Boosters":
 | 
			
		||||
                purchaseResponse = handleBoostersPurchase(storeItemName, inventory, durability);
 | 
			
		||||
@ -478,13 +489,15 @@ const handleTypesPurchase = async (
 | 
			
		||||
    typesName: string,
 | 
			
		||||
    inventory: TInventoryDatabaseDocument,
 | 
			
		||||
    quantity: number,
 | 
			
		||||
    ignorePurchaseQuantity: boolean
 | 
			
		||||
    ignorePurchaseQuantity: boolean,
 | 
			
		||||
    premiumPurchase: boolean = true,
 | 
			
		||||
    seed?: bigint
 | 
			
		||||
): Promise<IPurchaseResponse> => {
 | 
			
		||||
    const typeCategory = getStoreItemTypesCategory(typesName);
 | 
			
		||||
    logger.debug(`type category ${typeCategory}`);
 | 
			
		||||
    switch (typeCategory) {
 | 
			
		||||
        default:
 | 
			
		||||
            return { InventoryChanges: await addItem(inventory, typesName, quantity) };
 | 
			
		||||
            return { InventoryChanges: await addItem(inventory, typesName, quantity, premiumPurchase, seed) };
 | 
			
		||||
        case "BoosterPacks":
 | 
			
		||||
            return handleBoosterPackPurchase(typesName, inventory, quantity);
 | 
			
		||||
        case "SlotItems":
 | 
			
		||||
 | 
			
		||||
@ -49,6 +49,7 @@ export interface IInventoryDatabase
 | 
			
		||||
            | "PersonalTechProjects"
 | 
			
		||||
            | "LastSortieReward"
 | 
			
		||||
            | "LastLiteSortieReward"
 | 
			
		||||
            | "CrewMembers"
 | 
			
		||||
            | TEquipmentKey
 | 
			
		||||
        >,
 | 
			
		||||
        InventoryDatabaseEquipment {
 | 
			
		||||
@ -83,6 +84,7 @@ export interface IInventoryDatabase
 | 
			
		||||
    PersonalTechProjects: IPersonalTechProjectDatabase[];
 | 
			
		||||
    LastSortieReward?: ILastSortieRewardDatabase[];
 | 
			
		||||
    LastLiteSortieReward?: ILastSortieRewardDatabase[];
 | 
			
		||||
    CrewMembers: ICrewMemberDatabase[];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface IQuestKeyDatabase {
 | 
			
		||||
@ -324,7 +326,7 @@ export interface IInventoryClient extends IDailyAffiliations, InventoryClientEqu
 | 
			
		||||
    InfestedFoundry?: IInfestedFoundryClient;
 | 
			
		||||
    BlessingCooldown?: IMongoDate;
 | 
			
		||||
    CrewShipRawSalvage: ITypeCount[];
 | 
			
		||||
    CrewMembers: ICrewMember[];
 | 
			
		||||
    CrewMembers: ICrewMemberClient[];
 | 
			
		||||
    LotusCustomization: ILotusCustomization;
 | 
			
		||||
    UseAdultOperatorLoadout?: boolean;
 | 
			
		||||
    NemesisAbandonedRewards: string[];
 | 
			
		||||
@ -461,13 +463,24 @@ export interface ICompletedJob {
 | 
			
		||||
    StageCompletions: number[];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface ICrewMember {
 | 
			
		||||
export interface ICrewMemberSkill {
 | 
			
		||||
    Assigned: number;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface ICrewMemberSkillEfficiency {
 | 
			
		||||
    PILOTING: ICrewMemberSkill;
 | 
			
		||||
    GUNNERY: ICrewMemberSkill;
 | 
			
		||||
    ENGINEERING: ICrewMemberSkill;
 | 
			
		||||
    COMBAT: ICrewMemberSkill;
 | 
			
		||||
    SURVIVABILITY: ICrewMemberSkill;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface ICrewMemberClient {
 | 
			
		||||
    ItemType: string;
 | 
			
		||||
    NemesisFingerprint: number;
 | 
			
		||||
    Seed: number;
 | 
			
		||||
    HireDate: IMongoDate;
 | 
			
		||||
    AssignedRole: number;
 | 
			
		||||
    SkillEfficiency: ISkillEfficiency;
 | 
			
		||||
    NemesisFingerprint: bigint;
 | 
			
		||||
    Seed: bigint;
 | 
			
		||||
    AssignedRole?: number;
 | 
			
		||||
    SkillEfficiency: ICrewMemberSkillEfficiency;
 | 
			
		||||
    WeaponConfigIdx: number;
 | 
			
		||||
    WeaponId: IOid;
 | 
			
		||||
    XP: number;
 | 
			
		||||
@ -477,16 +490,9 @@ export interface ICrewMember {
 | 
			
		||||
    ItemId: IOid;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface ISkillEfficiency {
 | 
			
		||||
    PILOTING: ICombat;
 | 
			
		||||
    GUNNERY: ICombat;
 | 
			
		||||
    ENGINEERING: ICombat;
 | 
			
		||||
    COMBAT: ICombat;
 | 
			
		||||
    SURVIVABILITY: ICombat;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface ICombat {
 | 
			
		||||
    Assigned: number;
 | 
			
		||||
export interface ICrewMemberDatabase extends Omit<ICrewMemberClient, "WeaponId" | "ItemId"> {
 | 
			
		||||
    WeaponId: Types.ObjectId;
 | 
			
		||||
    _id: Types.ObjectId;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export enum InventorySlot {
 | 
			
		||||
 | 
			
		||||
@ -6,7 +6,8 @@ import {
 | 
			
		||||
    INemesisClient,
 | 
			
		||||
    ITypeCount,
 | 
			
		||||
    IRecentVendorPurchaseClient,
 | 
			
		||||
    TEquipmentKey
 | 
			
		||||
    TEquipmentKey,
 | 
			
		||||
    ICrewMemberClient
 | 
			
		||||
} from "./inventoryTypes/inventoryTypes";
 | 
			
		||||
 | 
			
		||||
export interface IPurchaseRequest {
 | 
			
		||||
@ -47,6 +48,7 @@ export type IInventoryChanges = {
 | 
			
		||||
    Nemesis?: Partial<INemesisClient>;
 | 
			
		||||
    NewVendorPurchase?: IRecentVendorPurchaseClient; // >= 38.5.0
 | 
			
		||||
    RecentVendorPurchases?: IRecentVendorPurchaseClient; // < 38.5.0
 | 
			
		||||
    CrewMembers?: ICrewMemberClient[];
 | 
			
		||||
} & Record<
 | 
			
		||||
        Exclude<
 | 
			
		||||
            string,
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user