forked from OpenWF/SpaceNinjaServer
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,
|
IPersonalTechProjectDatabase,
|
||||||
IPersonalTechProjectClient,
|
IPersonalTechProjectClient,
|
||||||
ILastSortieRewardDatabase,
|
ILastSortieRewardDatabase,
|
||||||
ILastSortieRewardClient
|
ILastSortieRewardClient,
|
||||||
|
ICrewMemberSkill,
|
||||||
|
ICrewMemberSkillEfficiency,
|
||||||
|
ICrewMemberDatabase,
|
||||||
|
ICrewMemberClient
|
||||||
} from "../../types/inventoryTypes/inventoryTypes";
|
} from "../../types/inventoryTypes/inventoryTypes";
|
||||||
import { IOid } from "../../types/commonTypes";
|
import { IOid } from "../../types/commonTypes";
|
||||||
import {
|
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>(
|
const slotsBinSchema = new Schema<ISlots>(
|
||||||
{
|
{
|
||||||
Slots: Number,
|
Slots: Number,
|
||||||
@ -1363,7 +1416,7 @@ const inventorySchema = new Schema<IInventoryDatabase, InventoryDocumentProps>(
|
|||||||
CrewShipSalvagedWeaponSkins: [upgradeSchema],
|
CrewShipSalvagedWeaponSkins: [upgradeSchema],
|
||||||
|
|
||||||
//RailJack Crew
|
//RailJack Crew
|
||||||
CrewMembers: [Schema.Types.Mixed],
|
CrewMembers: [crewMemberSchema],
|
||||||
|
|
||||||
//Complete Mission\Quests
|
//Complete Mission\Quests
|
||||||
Missions: [missionSchema],
|
Missions: [missionSchema],
|
||||||
@ -1645,6 +1698,7 @@ export type InventoryDocumentProps = {
|
|||||||
CrewShipWeaponSkins: Types.DocumentArray<IUpgradeDatabase>;
|
CrewShipWeaponSkins: Types.DocumentArray<IUpgradeDatabase>;
|
||||||
CrewShipSalvagedWeaponSkins: Types.DocumentArray<IUpgradeDatabase>;
|
CrewShipSalvagedWeaponSkins: Types.DocumentArray<IUpgradeDatabase>;
|
||||||
PersonalTechProjects: Types.DocumentArray<IPersonalTechProjectDatabase>;
|
PersonalTechProjects: Types.DocumentArray<IPersonalTechProjectDatabase>;
|
||||||
|
CrewMembers: Types.DocumentArray<ICrewMemberDatabase>;
|
||||||
} & { [K in TEquipmentKey]: Types.DocumentArray<IEquipmentDatabase> };
|
} & { [K in TEquipmentKey]: Types.DocumentArray<IEquipmentDatabase> };
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
|
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
|
||||||
|
@ -22,7 +22,8 @@ import {
|
|||||||
IDroneClient,
|
IDroneClient,
|
||||||
IUpgradeClient,
|
IUpgradeClient,
|
||||||
TPartialStartingGear,
|
TPartialStartingGear,
|
||||||
ILoreFragmentScan
|
ILoreFragmentScan,
|
||||||
|
ICrewMemberClient
|
||||||
} from "@/src/types/inventoryTypes/inventoryTypes";
|
} from "@/src/types/inventoryTypes/inventoryTypes";
|
||||||
import { IGenericUpdate, IUpdateNodeIntrosResponse } from "../types/genericUpdate";
|
import { IGenericUpdate, IUpdateNodeIntrosResponse } from "../types/genericUpdate";
|
||||||
import { IKeyChainRequest, IMissionInventoryUpdateRequest } from "../types/requestTypes";
|
import { IKeyChainRequest, IMissionInventoryUpdateRequest } from "../types/requestTypes";
|
||||||
@ -713,6 +714,15 @@ export const addItem = async (
|
|||||||
return {
|
return {
|
||||||
MiscItems: miscItemChanges
|
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") {
|
} else if (typeName == "/Lotus/Types/Game/CrewShip/RailJack/DefaultHarness") {
|
||||||
return addCrewShipHarness(inventory, typeName);
|
return addCrewShipHarness(inventory, typeName);
|
||||||
}
|
}
|
||||||
@ -1212,6 +1222,78 @@ const addDrone = (
|
|||||||
return inventoryChanges;
|
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 (
|
export const addEmailItem = async (
|
||||||
inventory: TInventoryDatabaseDocument,
|
inventory: TInventoryDatabaseDocument,
|
||||||
typeName: string,
|
typeName: string,
|
||||||
|
@ -141,7 +141,8 @@ export const handlePurchase = async (
|
|||||||
inventory,
|
inventory,
|
||||||
purchaseRequest.PurchaseParams.Quantity,
|
purchaseRequest.PurchaseParams.Quantity,
|
||||||
undefined,
|
undefined,
|
||||||
undefined,
|
false,
|
||||||
|
purchaseRequest.PurchaseParams.UsePremium,
|
||||||
seed
|
seed
|
||||||
);
|
);
|
||||||
combineInventoryChanges(purchaseResponse.InventoryChanges, prePurchaseInventoryChanges);
|
combineInventoryChanges(purchaseResponse.InventoryChanges, prePurchaseInventoryChanges);
|
||||||
@ -331,6 +332,7 @@ export const handleStoreItemAcquisition = async (
|
|||||||
quantity: number = 1,
|
quantity: number = 1,
|
||||||
durability: TRarity = "COMMON",
|
durability: TRarity = "COMMON",
|
||||||
ignorePurchaseQuantity: boolean = false,
|
ignorePurchaseQuantity: boolean = false,
|
||||||
|
premiumPurchase: boolean = true,
|
||||||
seed?: bigint
|
seed?: bigint
|
||||||
): Promise<IPurchaseResponse> => {
|
): Promise<IPurchaseResponse> => {
|
||||||
let purchaseResponse = {
|
let purchaseResponse = {
|
||||||
@ -352,11 +354,20 @@ export const handleStoreItemAcquisition = async (
|
|||||||
}
|
}
|
||||||
switch (storeCategory) {
|
switch (storeCategory) {
|
||||||
default: {
|
default: {
|
||||||
purchaseResponse = { InventoryChanges: await addItem(inventory, internalName, quantity, true, seed) };
|
purchaseResponse = {
|
||||||
|
InventoryChanges: await addItem(inventory, internalName, quantity, premiumPurchase, seed)
|
||||||
|
};
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "Types":
|
case "Types":
|
||||||
purchaseResponse = await handleTypesPurchase(internalName, inventory, quantity, ignorePurchaseQuantity);
|
purchaseResponse = await handleTypesPurchase(
|
||||||
|
internalName,
|
||||||
|
inventory,
|
||||||
|
quantity,
|
||||||
|
ignorePurchaseQuantity,
|
||||||
|
premiumPurchase,
|
||||||
|
seed
|
||||||
|
);
|
||||||
break;
|
break;
|
||||||
case "Boosters":
|
case "Boosters":
|
||||||
purchaseResponse = handleBoostersPurchase(storeItemName, inventory, durability);
|
purchaseResponse = handleBoostersPurchase(storeItemName, inventory, durability);
|
||||||
@ -478,13 +489,15 @@ const handleTypesPurchase = async (
|
|||||||
typesName: string,
|
typesName: string,
|
||||||
inventory: TInventoryDatabaseDocument,
|
inventory: TInventoryDatabaseDocument,
|
||||||
quantity: number,
|
quantity: number,
|
||||||
ignorePurchaseQuantity: boolean
|
ignorePurchaseQuantity: boolean,
|
||||||
|
premiumPurchase: boolean = true,
|
||||||
|
seed?: bigint
|
||||||
): Promise<IPurchaseResponse> => {
|
): Promise<IPurchaseResponse> => {
|
||||||
const typeCategory = getStoreItemTypesCategory(typesName);
|
const typeCategory = getStoreItemTypesCategory(typesName);
|
||||||
logger.debug(`type category ${typeCategory}`);
|
logger.debug(`type category ${typeCategory}`);
|
||||||
switch (typeCategory) {
|
switch (typeCategory) {
|
||||||
default:
|
default:
|
||||||
return { InventoryChanges: await addItem(inventory, typesName, quantity) };
|
return { InventoryChanges: await addItem(inventory, typesName, quantity, premiumPurchase, seed) };
|
||||||
case "BoosterPacks":
|
case "BoosterPacks":
|
||||||
return handleBoosterPackPurchase(typesName, inventory, quantity);
|
return handleBoosterPackPurchase(typesName, inventory, quantity);
|
||||||
case "SlotItems":
|
case "SlotItems":
|
||||||
|
@ -49,6 +49,7 @@ export interface IInventoryDatabase
|
|||||||
| "PersonalTechProjects"
|
| "PersonalTechProjects"
|
||||||
| "LastSortieReward"
|
| "LastSortieReward"
|
||||||
| "LastLiteSortieReward"
|
| "LastLiteSortieReward"
|
||||||
|
| "CrewMembers"
|
||||||
| TEquipmentKey
|
| TEquipmentKey
|
||||||
>,
|
>,
|
||||||
InventoryDatabaseEquipment {
|
InventoryDatabaseEquipment {
|
||||||
@ -83,6 +84,7 @@ export interface IInventoryDatabase
|
|||||||
PersonalTechProjects: IPersonalTechProjectDatabase[];
|
PersonalTechProjects: IPersonalTechProjectDatabase[];
|
||||||
LastSortieReward?: ILastSortieRewardDatabase[];
|
LastSortieReward?: ILastSortieRewardDatabase[];
|
||||||
LastLiteSortieReward?: ILastSortieRewardDatabase[];
|
LastLiteSortieReward?: ILastSortieRewardDatabase[];
|
||||||
|
CrewMembers: ICrewMemberDatabase[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IQuestKeyDatabase {
|
export interface IQuestKeyDatabase {
|
||||||
@ -324,7 +326,7 @@ export interface IInventoryClient extends IDailyAffiliations, InventoryClientEqu
|
|||||||
InfestedFoundry?: IInfestedFoundryClient;
|
InfestedFoundry?: IInfestedFoundryClient;
|
||||||
BlessingCooldown?: IMongoDate;
|
BlessingCooldown?: IMongoDate;
|
||||||
CrewShipRawSalvage: ITypeCount[];
|
CrewShipRawSalvage: ITypeCount[];
|
||||||
CrewMembers: ICrewMember[];
|
CrewMembers: ICrewMemberClient[];
|
||||||
LotusCustomization: ILotusCustomization;
|
LotusCustomization: ILotusCustomization;
|
||||||
UseAdultOperatorLoadout?: boolean;
|
UseAdultOperatorLoadout?: boolean;
|
||||||
NemesisAbandonedRewards: string[];
|
NemesisAbandonedRewards: string[];
|
||||||
@ -461,13 +463,24 @@ export interface ICompletedJob {
|
|||||||
StageCompletions: number[];
|
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;
|
ItemType: string;
|
||||||
NemesisFingerprint: number;
|
NemesisFingerprint: bigint;
|
||||||
Seed: number;
|
Seed: bigint;
|
||||||
HireDate: IMongoDate;
|
AssignedRole?: number;
|
||||||
AssignedRole: number;
|
SkillEfficiency: ICrewMemberSkillEfficiency;
|
||||||
SkillEfficiency: ISkillEfficiency;
|
|
||||||
WeaponConfigIdx: number;
|
WeaponConfigIdx: number;
|
||||||
WeaponId: IOid;
|
WeaponId: IOid;
|
||||||
XP: number;
|
XP: number;
|
||||||
@ -477,16 +490,9 @@ export interface ICrewMember {
|
|||||||
ItemId: IOid;
|
ItemId: IOid;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ISkillEfficiency {
|
export interface ICrewMemberDatabase extends Omit<ICrewMemberClient, "WeaponId" | "ItemId"> {
|
||||||
PILOTING: ICombat;
|
WeaponId: Types.ObjectId;
|
||||||
GUNNERY: ICombat;
|
_id: Types.ObjectId;
|
||||||
ENGINEERING: ICombat;
|
|
||||||
COMBAT: ICombat;
|
|
||||||
SURVIVABILITY: ICombat;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ICombat {
|
|
||||||
Assigned: number;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum InventorySlot {
|
export enum InventorySlot {
|
||||||
|
@ -6,7 +6,8 @@ import {
|
|||||||
INemesisClient,
|
INemesisClient,
|
||||||
ITypeCount,
|
ITypeCount,
|
||||||
IRecentVendorPurchaseClient,
|
IRecentVendorPurchaseClient,
|
||||||
TEquipmentKey
|
TEquipmentKey,
|
||||||
|
ICrewMemberClient
|
||||||
} from "./inventoryTypes/inventoryTypes";
|
} from "./inventoryTypes/inventoryTypes";
|
||||||
|
|
||||||
export interface IPurchaseRequest {
|
export interface IPurchaseRequest {
|
||||||
@ -47,6 +48,7 @@ export type IInventoryChanges = {
|
|||||||
Nemesis?: Partial<INemesisClient>;
|
Nemesis?: Partial<INemesisClient>;
|
||||||
NewVendorPurchase?: IRecentVendorPurchaseClient; // >= 38.5.0
|
NewVendorPurchase?: IRecentVendorPurchaseClient; // >= 38.5.0
|
||||||
RecentVendorPurchases?: IRecentVendorPurchaseClient; // < 38.5.0
|
RecentVendorPurchases?: IRecentVendorPurchaseClient; // < 38.5.0
|
||||||
|
CrewMembers?: ICrewMemberClient[];
|
||||||
} & Record<
|
} & Record<
|
||||||
Exclude<
|
Exclude<
|
||||||
string,
|
string,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user