XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet

XFESpaceNinjaServer

A simple server for a small space ninja game

公开
关注 0 Fork 0 Star 0
返回提交历史

XFEstudio/XFESpaceNinjaServer

feat: acquisition of CrewMembers (#1705)

Reviewed-on: https://onlyg.it/OpenWF/SpaceNinjaServer/pulls/1705 Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com> Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>

196182f9
Sainan <63328889+Sainan@users.noreply.github.com>
提交于

代码差异

5 个文件 +183 -26
Modified src/models/inventoryModels/inventoryModel.ts +56 -2
@@ -88,7 +88,11 @@ import {
88 88 IPersonalTechProjectDatabase,
89 89 IPersonalTechProjectClient,
90 90 ILastSortieRewardDatabase,
91 ILastSortieRewardClient
91 ILastSortieRewardClient,
92 ICrewMemberSkill,
93 ICrewMemberSkillEfficiency,
94 ICrewMemberDatabase,
95 ICrewMemberClient
92 96 } from "../../types/inventoryTypes/inventoryTypes";
93 97 import { IOid } from "../../types/commonTypes";
94 98 import {
@@ -294,6 +298,55 @@ upgradeSchema.set("toJSON", {
294 298 }
295 299 });
296 300
301 const crewMemberSkillSchema = new Schema<ICrewMemberSkill>(
302 {
303 Assigned: Number
304 },
305 { _id: false }
306 );
307
308 const crewMemberSkillEfficiencySchema = new Schema<ICrewMemberSkillEfficiency>(
309 {
310 PILOTING: crewMemberSkillSchema,
311 GUNNERY: crewMemberSkillSchema,
312 ENGINEERING: crewMemberSkillSchema,
313 COMBAT: crewMemberSkillSchema,
314 SURVIVABILITY: crewMemberSkillSchema
315 },
316 { _id: false }
317 );
318
319 const crewMemberSchema = new Schema<ICrewMemberDatabase>(
320 {
321 ItemType: { type: String, required: true },
322 NemesisFingerprint: { type: BigInt, default: 0n },
323 Seed: { type: BigInt, default: 0n },
324 AssignedRole: Number,
325 SkillEfficiency: crewMemberSkillEfficiencySchema,
326 WeaponConfigIdx: Number,
327 WeaponId: { type: Schema.Types.ObjectId, default: "000000000000000000000000" },
328 XP: { type: Number, default: 0 },
329 PowersuitType: { type: String, required: true },
330 Configs: [ItemConfigSchema],
331 SecondInCommand: { type: Boolean, default: false }
332 },
333 { id: false }
334 );
335
336 crewMemberSchema.set("toJSON", {
337 virtuals: true,
338 transform(_doc, obj) {
339 const db = obj as ICrewMemberDatabase;
340 const client = obj as ICrewMemberClient;
341
342 client.WeaponId = toOid(db.WeaponId);
343 client.ItemId = toOid(db._id);
344
345 delete obj._id;
346 delete obj.__v;
347 }
348 });
349
297 350 const slotsBinSchema = new Schema<ISlots>(
298 351 {
299 352 Slots: Number,
@@ -1363,7 +1416,7 @@ const inventorySchema = new Schema<IInventoryDatabase, InventoryDocumentProps>(
1363 1416 CrewShipSalvagedWeaponSkins: [upgradeSchema],
1364 1417
1365 1418 //RailJack Crew
1366 CrewMembers: [Schema.Types.Mixed],
1419 CrewMembers: [crewMemberSchema],
1367 1420
1368 1421 //Complete Mission\Quests
1369 1422 Missions: [missionSchema],
@@ -1645,6 +1698,7 @@ export type InventoryDocumentProps = {
1645 1698 CrewShipWeaponSkins: Types.DocumentArray<IUpgradeDatabase>;
1646 1699 CrewShipSalvagedWeaponSkins: Types.DocumentArray<IUpgradeDatabase>;
1647 1700 PersonalTechProjects: Types.DocumentArray<IPersonalTechProjectDatabase>;
1701 CrewMembers: Types.DocumentArray<ICrewMemberDatabase>;
1648 1702 } & { [K in TEquipmentKey]: Types.DocumentArray<IEquipmentDatabase> };
1649 1703
1650 1704 // eslint-disable-next-line @typescript-eslint/no-empty-object-type
Modified src/services/inventoryService.ts +83 -1
@@ -22,7 +22,8 @@ import {
22 22 IDroneClient,
23 23 IUpgradeClient,
24 24 TPartialStartingGear,
25 ILoreFragmentScan
25 ILoreFragmentScan,
26 ICrewMemberClient
26 27 } from "@/src/types/inventoryTypes/inventoryTypes";
27 28 import { IGenericUpdate, IUpdateNodeIntrosResponse } from "../types/genericUpdate";
28 29 import { IKeyChainRequest, IMissionInventoryUpdateRequest } from "../types/requestTypes";
@@ -713,6 +714,15 @@ export const addItem = async (
713 714 return {
714 715 MiscItems: miscItemChanges
715 716 };
717 } else if (typeName.startsWith("/Lotus/Types/Game/CrewShip/CrewMember/")) {
718 if (!seed) {
719 throw new Error(`Expected crew member to have a seed`);
720 }
721 seed |= 0x33b81en << 32n;
722 return {
723 ...addCrewMember(inventory, typeName, seed),
724 ...occupySlot(inventory, InventorySlot.CREWMEMBERS, premiumPurchase)
725 };
716 726 } else if (typeName == "/Lotus/Types/Game/CrewShip/RailJack/DefaultHarness") {
717 727 return addCrewShipHarness(inventory, typeName);
718 728 }
@@ -1212,6 +1222,78 @@ const addDrone = (
1212 1222 return inventoryChanges;
1213 1223 };
1214 1224
1225 /*const getCrewMemberSkills = (seed: bigint, skillPointsToAssign: number): Record<string, number> => {
1226 const rng = new SRng(seed);
1227
1228 const skills = ["PILOTING", "GUNNERY", "ENGINEERING", "COMBAT", "SURVIVABILITY"];
1229 for (let i = 1; i != 5; ++i) {
1230 const swapIndex = rng.randomInt(0, i);
1231 if (swapIndex != i) {
1232 const tmp = skills[i];
1233 skills[i] = skills[swapIndex];
1234 skills[swapIndex] = tmp;
1235 }
1236 }
1237
1238 rng.randomFloat(); // unused afaict
1239
1240 const skillAssignments = [0, 0, 0, 0, 0];
1241 for (let skill = 0; skillPointsToAssign; skill = (skill + 1) % 5) {
1242 const maxIncrease = Math.min(5 - skillAssignments[skill], skillPointsToAssign);
1243 const increase = rng.randomInt(0, maxIncrease);
1244 skillAssignments[skill] += increase;
1245 skillPointsToAssign -= increase;
1246 }
1247
1248 skillAssignments.sort((a, b) => b - a);
1249
1250 const combined: Record<string, number> = {};
1251 for (let i = 0; i != 5; ++i) {
1252 combined[skills[i]] = skillAssignments[i];
1253 }
1254 return combined;
1255 };*/
1256
1257 const addCrewMember = (
1258 inventory: TInventoryDatabaseDocument,
1259 itemType: string,
1260 seed: bigint,
1261 inventoryChanges: IInventoryChanges = {}
1262 ): IInventoryChanges => {
1263 // SkillEfficiency is additional to the base stats, so we don't need to compute this
1264 //const skillPointsToAssign = itemType.endsWith("Strong") ? 12 : itemType.indexOf("Medium") != -1 ? 10 : 8;
1265 //const skills = getCrewMemberSkills(seed, skillPointsToAssign);
1266
1267 // Arbiters = male
1268 // CephalonSuda = female
1269 // NewLoka = female
1270 // Perrin = male
1271 // RedVeil = male
1272 // SteelMeridian = female
1273 const powersuitType =
1274 itemType.indexOf("Arbiters") != -1 || itemType.indexOf("Perrin") != -1 || itemType.indexOf("RedVeil") != -1
1275 ? "/Lotus/Powersuits/NpcPowersuits/CrewMemberMaleSuit"
1276 : "/Lotus/Powersuits/NpcPowersuits/CrewMemberFemaleSuit";
1277
1278 const index =
1279 inventory.CrewMembers.push({
1280 ItemType: itemType,
1281 NemesisFingerprint: 0n,
1282 Seed: seed,
1283 SkillEfficiency: {
1284 PILOTING: { Assigned: 0 },
1285 GUNNERY: { Assigned: 0 },
1286 ENGINEERING: { Assigned: 0 },
1287 COMBAT: { Assigned: 0 },
1288 SURVIVABILITY: { Assigned: 0 }
1289 },
1290 PowersuitType: powersuitType
1291 }) - 1;
1292 inventoryChanges.CrewMembers ??= [];
1293 inventoryChanges.CrewMembers.push(inventory.CrewMembers[index].toJSON<ICrewMemberClient>());
1294 return inventoryChanges;
1295 };
1296
1215 1297 export const addEmailItem = async (
1216 1298 inventory: TInventoryDatabaseDocument,
1217 1299 typeName: string,
Modified src/services/purchaseService.ts +18 -5
@@ -141,7 +141,8 @@ export const handlePurchase = async (
141 141 inventory,
142 142 purchaseRequest.PurchaseParams.Quantity,
143 143 undefined,
144 undefined,
144 false,
145 purchaseRequest.PurchaseParams.UsePremium,
145 146 seed
146 147 );
147 148 combineInventoryChanges(purchaseResponse.InventoryChanges, prePurchaseInventoryChanges);
@@ -331,6 +332,7 @@ export const handleStoreItemAcquisition = async (
331 332 quantity: number = 1,
332 333 durability: TRarity = "COMMON",
333 334 ignorePurchaseQuantity: boolean = false,
335 premiumPurchase: boolean = true,
334 336 seed?: bigint
335 337 ): Promise<IPurchaseResponse> => {
336 338 let purchaseResponse = {
@@ -352,11 +354,20 @@ export const handleStoreItemAcquisition = async (
352 354 }
353 355 switch (storeCategory) {
354 356 default: {
355 purchaseResponse = { InventoryChanges: await addItem(inventory, internalName, quantity, true, seed) };
357 purchaseResponse = {
358 InventoryChanges: await addItem(inventory, internalName, quantity, premiumPurchase, seed)
359 };
356 360 break;
357 361 }
358 362 case "Types":
359 purchaseResponse = await handleTypesPurchase(internalName, inventory, quantity, ignorePurchaseQuantity);
363 purchaseResponse = await handleTypesPurchase(
364 internalName,
365 inventory,
366 quantity,
367 ignorePurchaseQuantity,
368 premiumPurchase,
369 seed
370 );
360 371 break;
361 372 case "Boosters":
362 373 purchaseResponse = handleBoostersPurchase(storeItemName, inventory, durability);
@@ -478,13 +489,15 @@ const handleTypesPurchase = async (
478 489 typesName: string,
479 490 inventory: TInventoryDatabaseDocument,
480 491 quantity: number,
481 ignorePurchaseQuantity: boolean
492 ignorePurchaseQuantity: boolean,
493 premiumPurchase: boolean = true,
494 seed?: bigint
482 495 ): Promise<IPurchaseResponse> => {
483 496 const typeCategory = getStoreItemTypesCategory(typesName);
484 497 logger.debug(`type category ${typeCategory}`);
485 498 switch (typeCategory) {
486 499 default:
487 return { InventoryChanges: await addItem(inventory, typesName, quantity) };
500 return { InventoryChanges: await addItem(inventory, typesName, quantity, premiumPurchase, seed) };
488 501 case "BoosterPacks":
489 502 return handleBoosterPackPurchase(typesName, inventory, quantity);
490 503 case "SlotItems":
Modified src/types/inventoryTypes/inventoryTypes.ts +23 -17
@@ -49,6 +49,7 @@ export interface IInventoryDatabase
49 49 | "PersonalTechProjects"
50 50 | "LastSortieReward"
51 51 | "LastLiteSortieReward"
52 | "CrewMembers"
52 53 | TEquipmentKey
53 54 >,
54 55 InventoryDatabaseEquipment {
@@ -83,6 +84,7 @@ export interface IInventoryDatabase
83 84 PersonalTechProjects: IPersonalTechProjectDatabase[];
84 85 LastSortieReward?: ILastSortieRewardDatabase[];
85 86 LastLiteSortieReward?: ILastSortieRewardDatabase[];
87 CrewMembers: ICrewMemberDatabase[];
86 88 }
87 89
88 90 export interface IQuestKeyDatabase {
@@ -324,7 +326,7 @@ export interface IInventoryClient extends IDailyAffiliations, InventoryClientEqu
324 326 InfestedFoundry?: IInfestedFoundryClient;
325 327 BlessingCooldown?: IMongoDate;
326 328 CrewShipRawSalvage: ITypeCount[];
327 CrewMembers: ICrewMember[];
329 CrewMembers: ICrewMemberClient[];
328 330 LotusCustomization: ILotusCustomization;
329 331 UseAdultOperatorLoadout?: boolean;
330 332 NemesisAbandonedRewards: string[];
@@ -461,13 +463,24 @@ export interface ICompletedJob {
461 463 StageCompletions: number[];
462 464 }
463 465
464 export interface ICrewMember {
466 export interface ICrewMemberSkill {
467 Assigned: number;
468 }
469
470 export interface ICrewMemberSkillEfficiency {
471 PILOTING: ICrewMemberSkill;
472 GUNNERY: ICrewMemberSkill;
473 ENGINEERING: ICrewMemberSkill;
474 COMBAT: ICrewMemberSkill;
475 SURVIVABILITY: ICrewMemberSkill;
476 }
477
478 export interface ICrewMemberClient {
465 479 ItemType: string;
466 NemesisFingerprint: number;
467 Seed: number;
468 HireDate: IMongoDate;
469 AssignedRole: number;
470 SkillEfficiency: ISkillEfficiency;
480 NemesisFingerprint: bigint;
481 Seed: bigint;
482 AssignedRole?: number;
483 SkillEfficiency: ICrewMemberSkillEfficiency;
471 484 WeaponConfigIdx: number;
472 485 WeaponId: IOid;
473 486 XP: number;
@@ -477,16 +490,9 @@ export interface ICrewMember {
477 490 ItemId: IOid;
478 491 }
479 492
480 export interface ISkillEfficiency {
481 PILOTING: ICombat;
482 GUNNERY: ICombat;
483 ENGINEERING: ICombat;
484 COMBAT: ICombat;
485 SURVIVABILITY: ICombat;
486 }
487
488 export interface ICombat {
489 Assigned: number;
493 export interface ICrewMemberDatabase extends Omit<ICrewMemberClient, "WeaponId" | "ItemId"> {
494 WeaponId: Types.ObjectId;
495 _id: Types.ObjectId;
490 496 }
491 497
492 498 export enum InventorySlot {
Modified src/types/purchaseTypes.ts +3 -1
@@ -6,7 +6,8 @@ import {
6 6 INemesisClient,
7 7 ITypeCount,
8 8 IRecentVendorPurchaseClient,
9 TEquipmentKey
9 TEquipmentKey,
10 ICrewMemberClient
10 11 } from "./inventoryTypes/inventoryTypes";
11 12
12 13 export interface IPurchaseRequest {
@@ -47,6 +48,7 @@ export type IInventoryChanges = {
47 48 Nemesis?: Partial<INemesisClient>;
48 49 NewVendorPurchase?: IRecentVendorPurchaseClient; // >= 38.5.0
49 50 RecentVendorPurchases?: IRecentVendorPurchaseClient; // < 38.5.0
51 CrewMembers?: ICrewMemberClient[];
50 52 } & Record<
51 53 Exclude<
52 54 string,