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: operator/drifter cosmetic persistence, tauron strike support (#3131)

Closes #3130 Closes #3153 Co-authored-by: sunless-sky <sunless.skies0@gmail.com> Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com> Reviewed-on: https://onlyg.it/OpenWF/SpaceNinjaServer/pulls/3131 Reviewed-by: Sainan <63328889+sainan@users.noreply.github.com> Co-authored-by: sunless-sky <sunless-sky@noreply.localhost> Co-committed-by: sunless-sky <sunless-sky@noreply.localhost>

e79d59c1
sunless-sky <sunless-sky@noreply.localhost>
提交于

代码差异

6 个文件 +245 -3
Modified src/models/inventoryModels/inventoryModel.ts +15 -1
@@ -95,7 +95,8 @@ import type {
95 95 IDialogueResetDateDatabase,
96 96 IDescentLevelReward,
97 97 IDescentCategoryRewardClient,
98 IDescentCategoryRewardDatabase
98 IDescentCategoryRewardDatabase,
99 IFocusLoadoutDatabase
99 100 } from "../../types/inventoryTypes/inventoryTypes.ts";
100 101 import { equipmentKeys } from "../../types/inventoryTypes/inventoryTypes.ts";
101 102 import type { IOid, ITypeCount } from "../../types/commonTypes.ts";
@@ -159,6 +160,14 @@ const focusUpgradeSchema = new Schema<IFocusUpgrade>(
159 160 { _id: false }
160 161 );
161 162
163 const focusLoadoutSchema = new Schema<IFocusLoadoutDatabase>(
164 {
165 Preset: EquipmentSelectionSchema,
166 FocusAbility: String
167 },
168 { _id: false }
169 );
170
162 171 const polaritySchema = new Schema<IPolarity>(
163 172 {
164 173 Slot: Number,
@@ -214,6 +223,7 @@ const ItemConfigSchema = new Schema<IItemConfig>(
214 223 eyecol: colorSchema,
215 224 facial: colorSchema,
216 225 syancol: colorSchema,
226 cloth: colorSchema,
217 227 Upgrades: [String],
218 228 Songs: {
219 229 type: [
@@ -1624,8 +1634,10 @@ const inventorySchema = new Schema<IInventoryDatabase, InventoryDocumentProps>(
1624 1634 FocusAbility: String,
1625 1635 //The treeways of the Focus school.(Active and passive Ability)
1626 1636 FocusUpgrades: [focusUpgradeSchema],
1637 FocusLoadouts: { type: [focusLoadoutSchema], default: [] },
1627 1638 //Focus 2.0 Pool
1628 1639 FocusCapacity: Number,
1640 OneTimePurchases: { type: [String], default: [] },
1629 1641
1630 1642 //Achievement
1631 1643 ChallengeProgress: [challengeProgressSchema],
@@ -1664,6 +1676,7 @@ const inventorySchema = new Schema<IInventoryDatabase, InventoryDocumentProps>(
1664 1676 OperatorLoadOuts: [operatorConfigSchema],
1665 1677 //Drifter
1666 1678 AdultOperatorLoadOuts: [operatorConfigSchema],
1679 OperatorSuits: { type: [EquipmentSchema], default: [] },
1667 1680 OperatorCustomizationSlotPurchases: Number,
1668 1681 // Kahl
1669 1682 KahlLoadOuts: [operatorConfigSchema],
@@ -1996,6 +2009,7 @@ export type InventoryDocumentProps = {
1996 2009 OperatorLoadOuts: Types.DocumentArray<IOperatorConfigDatabase>;
1997 2010 AdultOperatorLoadOuts: Types.DocumentArray<IOperatorConfigDatabase>;
1998 2011 KahlLoadOuts: Types.DocumentArray<IOperatorConfigDatabase>;
2012 OperatorSuits: Types.DocumentArray<IEquipmentDatabase>;
1999 2013 PendingRecipes: Types.DocumentArray<IPendingRecipeDatabase>;
2000 2014 WeaponSkins: Types.DocumentArray<IWeaponSkinDatabase>;
2001 2015 QuestKeys: Types.DocumentArray<IQuestKeyDatabase>;
Modified src/services/importService.ts +16 -0
@@ -13,6 +13,8 @@ import type {
13 13 IDialogueHistoryDatabase,
14 14 IInfestedFoundryClient,
15 15 IInfestedFoundryDatabase,
16 IFocusLoadoutClient,
17 IFocusLoadoutDatabase,
16 18 IInventoryClient,
17 19 INemesisClient,
18 20 INemesisDatabase,
@@ -151,6 +153,13 @@ const convertEquipmentSelection = (es: IEquipmentSelectionClient): IEquipmentSel
151 153 };
152 154 };
153 155
156 const convertFocusLoadout = (client: IFocusLoadoutClient): IFocusLoadoutDatabase => {
157 return {
158 ...client,
159 Preset: convertEquipmentSelection(client.Preset)
160 };
161 };
162
154 163 const convertCrewShipWeaponEmplacements = (
155 164 obj: ICrewShipWeaponEmplacementsClient
156 165 ): ICrewShipWeaponEmplacementsDatabase => {
@@ -261,6 +270,9 @@ export const importInventory = (db: TInventoryDatabaseDocument, client: Partial<
261 270 replaceArray<IEquipmentDatabase>(db[key], client[key].map(convertEquipment));
262 271 }
263 272 }
273 if (client.OperatorSuits !== undefined) {
274 replaceArray<IEquipmentDatabase>(db.OperatorSuits, client.OperatorSuits.map(convertEquipment));
275 }
264 276 if (client.WeaponSkins !== undefined) {
265 277 replaceArray<IWeaponSkinDatabase>(db.WeaponSkins, client.WeaponSkins.map(convertWeaponSkin));
266 278 }
@@ -368,6 +380,7 @@ export const importInventory = (db: TInventoryDatabaseDocument, client: Partial<
368 380 "DeathMarks",
369 381 "Wishlist",
370 382 "NemesisAbandonedRewards",
383 //"OneTimePurchases", // TODO: Import Antiques
371 384 "EntratiLabConquestActiveFrameVariants",
372 385 "EchoesHexConquestActiveFrameVariants",
373 386 "EchoesHexConquestActiveStickers"
@@ -410,6 +423,9 @@ export const importInventory = (db: TInventoryDatabaseDocument, client: Partial<
410 423 if (client.FocusUpgrades !== undefined) {
411 424 db.FocusUpgrades = client.FocusUpgrades;
412 425 }
426 if (client.FocusLoadouts !== undefined) {
427 db.FocusLoadouts = client.FocusLoadouts.map(convertFocusLoadout);
428 }
413 429 if (client.EvolutionProgress !== undefined) {
414 430 db.EvolutionProgress = client.EvolutionProgress;
415 431 }
Modified src/services/inventoryService.ts +151 -0
@@ -97,6 +97,131 @@ import { skinLookupTable } from "../helpers/skinLookupTable.ts";
97 97 import type { TLoadoutDatabaseDocument } from "../models/inventoryModels/loadoutModel.ts";
98 98 import gameToBuildVersion from "../constants/gameToBuildVersion.ts";
99 99
100 type OperatorAntiqueMeta = {
101 focusAbility: string;
102 ultimateUpgrades: string[];
103 };
104
105 const operatorAntiqueMeta: Partial<Record<string, OperatorAntiqueMeta>> = {
106 "/Lotus/Weapons/Operator/Antiques/ZenurikGrimoire/ZanurikGrimoireAntique": {
107 focusAbility: "/Lotus/Upgrades/Focus/Power/PowerFocusAbility",
108 ultimateUpgrades: [
109 "/Lotus/Upgrades/Focus/Power/Ultimate/PowerUltimateUpgrade",
110 "/Lotus/Upgrades/Focus/Power/Ultimate/PowerUltimateBlindUpgrade",
111 "/Lotus/Upgrades/Focus/Power/Ultimate/PowerUltimateBuffUpgrade",
112 "/Lotus/Upgrades/Focus/Power/Ultimate/PowerUltimateChainUpgrade",
113 "/Lotus/Upgrades/Focus/Power/Ultimate/PowerUltimateNumberUpgrade"
114 ]
115 },
116 "/Lotus/Weapons/Operator/Antiques/NaramonBlade/NaramonBladeAntique": {
117 focusAbility: "/Lotus/Upgrades/Focus/Tactic/TacticFocusAbility",
118 ultimateUpgrades: [
119 "/Lotus/Upgrades/Focus/Tactic/Active/TacticUltimateUpgrade",
120 "/Lotus/Upgrades/Focus/Tactic/Active/TacticUltimateDoubleUpgrade",
121 "/Lotus/Upgrades/Focus/Tactic/Active/TacticUltimateNumberUpgrade",
122 "/Lotus/Upgrades/Focus/Tactic/Active/TacticUltimateRangeUpgrade",
123 "/Lotus/Upgrades/Focus/Tactic/Active/TacticUltimateSizeUpgrade"
124 ]
125 },
126 "/Lotus/Weapons/Operator/Antiques/MaduraiBow/MaduraiBowAntique": {
127 focusAbility: "/Lotus/Upgrades/Focus/Attack/AttackFocusAbility",
128 ultimateUpgrades: [
129 "/Lotus/Upgrades/Focus/Attack/Ultimate/AttackUltimateUpgrade",
130 "/Lotus/Upgrades/Focus/Attack/Ultimate/AttackUltimateCooldownUpgrade",
131 "/Lotus/Upgrades/Focus/Attack/Ultimate/AttackUltimateDoubleUpgrade",
132 "/Lotus/Upgrades/Focus/Attack/Ultimate/AttackUltimateRadiusUpgrade",
133 "/Lotus/Upgrades/Focus/Attack/Ultimate/AttackUltimateStrengthUpgrade"
134 ]
135 },
136 "/Lotus/Weapons/Operator/Antiques/UnairuHammer/UniaruHammerAntique": {
137 focusAbility: "/Lotus/Upgrades/Focus/Ward/WardFocusAbility",
138 ultimateUpgrades: [
139 "/Lotus/Upgrades/Focus/Ward/Ultimate/WardUltimateUpgrade",
140 "/Lotus/Upgrades/Focus/Ward/Ultimate/WardUltimateArmorUpgrade",
141 "/Lotus/Upgrades/Focus/Ward/Ultimate/WardUltimateChanceUpgrade",
142 "/Lotus/Upgrades/Focus/Ward/Ultimate/WardUltimateRadiusUpgrade",
143 "/Lotus/Upgrades/Focus/Ward/Ultimate/WardUltimateSlamUpgrade"
144 ]
145 },
146 "/Lotus/Weapons/Operator/Antiques/VazarinStaff/VazarinStaffAntique": {
147 focusAbility: "/Lotus/Upgrades/Focus/Defense/DefenseFocusAbility",
148 ultimateUpgrades: [
149 "/Lotus/Upgrades/Focus/Defense/Ultimate/DefenseUltimateUpgrade",
150 "/Lotus/Upgrades/Focus/Defense/Ultimate/DefenseUltimateDurationUpgrade",
151 "/Lotus/Upgrades/Focus/Defense/Ultimate/DefenseUltimateElementalUpgrade",
152 "/Lotus/Upgrades/Focus/Defense/Ultimate/DefenseUltimateHealUpgrade",
153 "/Lotus/Upgrades/Focus/Defense/Ultimate/DefenseUltimateProcVulnUpgrade"
154 ]
155 }
156 };
157
158 const ensureOperatorSuitsRemasterData = (inventory: TInventoryDatabaseDocument): boolean => {
159 if (inventory.OperatorSuits.length != 0) {
160 return false;
161 }
162 inventory.OperatorSuits.push(
163 { ItemType: "/Lotus/Powersuits/Operator/ChildOperatorSuitRemaster", Configs: [], UpgradeVer: 101 },
164 { ItemType: "/Lotus/Powersuits/Operator/AdultOperatorSuitRemaster", Configs: [], UpgradeVer: 101 }
165 );
166 return true;
167 };
168
169 const setupAntique = (
170 inventory: TInventoryDatabaseDocument,
171 weaponType: string,
172 weaponId: Types.ObjectId,
173 inventoryChanges?: IInventoryChanges
174 ): void => {
175 const meta = operatorAntiqueMeta[weaponType];
176 if (!meta) {
177 throw new Error(`unknown antique: ${weaponType}`);
178 }
179
180 const focusUpgradesAdded: object[] = [];
181 const focusLoadoutsAdded: object[] = [];
182
183 inventory.OneTimePurchases ??= [];
184 if (!inventory.OneTimePurchases.includes(weaponType)) {
185 inventory.OneTimePurchases.push(weaponType);
186 }
187
188 if (!inventory.FocusUpgrades.some(x => x.ItemType === meta.focusAbility)) {
189 inventory.FocusUpgrades.push({ ItemType: meta.focusAbility });
190 focusUpgradesAdded.push({ ItemType: meta.focusAbility });
191 }
192 for (const upgradeType of meta.ultimateUpgrades) {
193 if (!inventory.FocusUpgrades.some(x => x.ItemType === upgradeType)) {
194 inventory.FocusUpgrades.push({ ItemType: upgradeType, Level: 0 });
195 focusUpgradesAdded.push({ ItemType: upgradeType, Level: 0 });
196 }
197 }
198
199 inventory.FocusLoadouts ??= [];
200 if (!inventory.FocusLoadouts.some(x => x.FocusAbility === meta.focusAbility)) {
201 inventory.FocusLoadouts.push({
202 FocusAbility: meta.focusAbility,
203 Preset: { ItemId: weaponId, mod: 0, cus: 0 }
204 });
205 focusLoadoutsAdded.push({
206 FocusAbility: meta.focusAbility,
207 Preset: { ItemId: toOid(weaponId), mod: 0, cus: 0 }
208 });
209 }
210
211 if (inventoryChanges) {
212 if (focusUpgradesAdded.length) {
213 const key = "FocusUpgrades";
214 const existing = inventoryChanges[key] as object[] | undefined;
215 inventoryChanges[key] = existing ? existing.concat(focusUpgradesAdded) : focusUpgradesAdded;
216 }
217 if (focusLoadoutsAdded.length) {
218 const key = "FocusLoadouts";
219 const existing = inventoryChanges[key] as object[] | undefined;
220 inventoryChanges[key] = existing ? existing.concat(focusLoadoutsAdded) : focusLoadoutsAdded;
221 }
222 }
223 };
224
100 225 export const createInventory = async (
101 226 accountOwnerId: Types.ObjectId,
102 227 loadout: TLoadoutDatabaseDocument,
@@ -115,6 +240,7 @@ export const createInventory = async (
115 240 Seed: generateRewardSeed(),
116 241 NumCompletions: 0
117 242 };
243 ensureOperatorSuitsRemasterData(inventory);
118 244 await addItem(inventory, "/Lotus/Types/Friendly/PlayerControllable/Weapons/DuviriDualSwords");
119 245
120 246 if (config.skipTutorial) {
@@ -310,6 +436,7 @@ export const productCategoryToInventoryBin = (productCategory: string): Inventor
310 436 case "SpaceMelee":
311 437 return InventorySlot.SPACEWEAPONS;
312 438 case "OperatorAmps":
439 case "Antiques":
313 440 return InventorySlot.AMPS;
314 441 case "CrewShipWeapons":
315 442 case "CrewShipWeaponSkins":
@@ -1012,6 +1139,26 @@ export const addItem = async (
1012 1139 )
1013 1140 };
1014 1141 }
1142 case "Antiques": {
1143 if (quantity != 1) {
1144 throw new Error(`unexpected acquisition quantity of Antiques: got ${quantity}, expected 1`);
1145 }
1146 const priorLen = inventory.Antiques.length;
1147 const inventoryChanges = addEquipment(inventory, "Antiques", typeName, {
1148 UpgradeVer: 101,
1149 Configs: [{}, {}, {}]
1150 });
1151 const added = inventory.Antiques[priorLen];
1152 setupAntique(inventory, typeName, added._id, inventoryChanges);
1153 return {
1154 ...inventoryChanges,
1155 ...occupySlot(
1156 inventory,
1157 productCategoryToInventoryBin(productCategory) ?? InventorySlot.AMPS,
1158 premiumPurchase
1159 )
1160 };
1161 }
1015 1162 }
1016 1163 break;
1017 1164 }
@@ -2446,6 +2593,10 @@ export const setupKahlSyndicate = (inventory: TInventoryDatabaseDocument): void
2446 2593 export const cleanupInventory = async (inventory: TInventoryDatabaseDocument): Promise<void> => {
2447 2594 inventory.CurrentLoadOutIds = inventory.CurrentLoadOutIds.map(fromDbOid);
2448 2595
2596 if (ensureOperatorSuitsRemasterData(inventory)) {
2597 logger.debug(`added missing Operator suits for Operator remaster`);
2598 }
2599
2449 2600 let index = inventory.MiscItems.findIndex(x => x.ItemType == "");
2450 2601 if (index != -1) {
2451 2602 inventory.MiscItems.splice(index, 1);
Modified src/services/saveLoadoutService.ts +36 -0
@@ -27,6 +27,7 @@ import { importCrewShipMembers, importCrewShipWeapon, importLoadOutConfig } from
27 27 import type { IEquipmentDatabase, IEquipmentSelectionDatabase } from "../types/equipmentTypes.ts";
28 28 import type { TInventoryDatabaseDocument } from "../models/inventoryModels/inventoryModel.ts";
29 29 import gameToBuildVersion from "../constants/gameToBuildVersion.ts";
30 import type { IFocusLoadoutClient } from "../types/inventoryTypes/inventoryTypes.ts";
30 31
31 32 //TODO: setup default items on account creation or like originally in giveStartingItems.php
32 33
@@ -206,6 +207,41 @@ export const handleInventoryItemConfigChange = async (
206 207 inventory.UseAdultOperatorLoadout = equipment as boolean;
207 208 break;
208 209 }
210 case "FocusLoadouts": {
211 const raw = equipment as unknown;
212 const focusLoadouts: unknown[] = Array.isArray(raw)
213 ? raw
214 : raw && typeof raw === "object"
215 ? "Preset" in raw || "FocusAbility" in raw
216 ? [raw]
217 : Object.values(raw)
218 : [];
219 const converted = focusLoadouts
220 .filter((fl): fl is IFocusLoadoutClient => {
221 if (!fl || typeof fl !== "object") return false;
222 const obj = fl as Partial<IFocusLoadoutClient>;
223 return (
224 typeof obj.FocusAbility === "string" &&
225 !!obj.Preset &&
226 typeof obj.Preset === "object" &&
227 !Array.isArray(obj.Preset)
228 );
229 })
230 .map(fl => ({
231 FocusAbility: fl.FocusAbility,
232 Preset: {
233 ...fl.Preset,
234 ItemId:
235 fl.Preset.ItemId && typeof fl.Preset.ItemId === "object"
236 ? toObjectId(fromOid(fl.Preset.ItemId))
237 : undefined
238 } satisfies IEquipmentSelectionDatabase
239 }));
240 if (converted.length || Array.isArray(raw)) {
241 inventory.FocusLoadouts = converted;
242 }
243 break;
244 }
209 245 case "WeaponSkins": {
210 246 const itemEntries = equipment as IItemEntry;
211 247 for (const [itemId, itemConfigEntries] of Object.entries(itemEntries)) {
Modified src/types/inventoryTypes/inventoryTypes.ts +23 -2
@@ -12,7 +12,13 @@ import type {
12 12 import type { IFingerprintStat } from "../../helpers/rivenHelper.ts";
13 13 import type { IOrbiterClient } from "../personalRoomsTypes.ts";
14 14 import type { ICountedStoreItem } from "warframe-public-export-plus";
15 import type { IEquipmentClient, IEquipmentDatabase, ITraits } from "../equipmentTypes.ts";
15 import type {
16 IEquipmentClient,
17 IEquipmentDatabase,
18 IEquipmentSelectionClient,
19 IEquipmentSelectionDatabase,
20 ITraits
21 } from "../equipmentTypes.ts";
16 22 import type { ILoadoutConfigClientLegacy, ILoadOutPresets } from "../saveLoadoutTypes.ts";
17 23 import type { CalendarSeasonType } from "../worldStateTypes.ts";
18 24 import type { SlotNames } from "../purchaseTypes.ts";
@@ -114,6 +120,7 @@ export interface IInventoryDatabase
114 120 | "DescentRewards"
115 121 | "PersonalGoalProgress"
116 122 | "CurrentLoadOutIds"
123 | "FocusLoadouts"
117 124 | TEquipmentKey
118 125 >,
119 126 InventoryDatabaseEquipment,
@@ -160,6 +167,7 @@ export interface IInventoryDatabase
160 167 DescentRewards?: IDescentCategoryRewardDatabase[];
161 168 PersonalGoalProgress?: IGoalProgressDatabase[];
162 169 MissionRelicRewards?: ITypeCount[];
170 FocusLoadouts?: IFocusLoadoutDatabase[];
163 171 }
164 172
165 173 export interface IQuestKeyDatabase {
@@ -184,6 +192,7 @@ export const equipmentKeys = [
184 192 "SpaceMelee",
185 193 "Hoverboards",
186 194 "OperatorAmps",
195 "Antiques",
187 196 "MoaPets",
188 197 "Scoops",
189 198 "Horses",
@@ -196,7 +205,8 @@ export const equipmentKeys = [
196 205 "CrewShipHarnesses",
197 206 "KubrowPets",
198 207 "CrewShipWeapons",
199 "CrewShipSalvagedWeapons"
208 "CrewShipSalvagedWeapons",
209 "OperatorSuits"
200 210 ] as const;
201 211
202 212 export const loadoutKeysLegacy = ["NORMAL", "NORMAL_PVP", "LUNARO", "ARCHWING", "SENTINEL", "OPERATOR"] as const;
@@ -208,6 +218,15 @@ export interface IDuviriInfo {
208 218 NumCompletions: number;
209 219 }
210 220
221 export interface IFocusLoadoutClient {
222 Preset: IEquipmentSelectionClient;
223 FocusAbility: string;
224 }
225
226 export interface IFocusLoadoutDatabase extends Omit<IFocusLoadoutClient, "Preset"> {
227 Preset: IEquipmentSelectionDatabase;
228 }
229
211 230 export interface IMailboxClient {
212 231 LastInboxId: IOid;
213 232 }
@@ -271,6 +290,8 @@ export interface IInventoryClient extends IDailyAffiliations, InventoryClientEqu
271 290 AdultOperatorLoadOuts: IOperatorConfigClient[];
272 291 OperatorLoadOuts: IOperatorConfigClient[];
273 292 KahlLoadOuts: IOperatorConfigClient[];
293 FocusLoadouts?: IFocusLoadoutClient[];
294 OneTimePurchases?: string[];
274 295
275 296 DuviriInfo?: IDuviriInfo;
276 297 Mailbox?: IMailboxClient;
Modified src/types/saveLoadoutTypes.ts +4 -0
@@ -14,6 +14,7 @@ import type {
14 14 IEquipmentSelectionClient,
15 15 IEquipmentSelectionDatabase
16 16 } from "./equipmentTypes.ts";
17 import type { IFocusLoadoutClient } from "./inventoryTypes/inventoryTypes.ts";
17 18
18 19 export interface ISaveLoadoutRequest {
19 20 LoadOuts: ILoadoutClient | ILoadoutConfigClientLegacy;
@@ -21,6 +22,8 @@ export interface ISaveLoadoutRequest {
21 22 Presets?: ILoadoutPresetClientLegacy[]; // U8
22 23 LongGuns: IItemEntry;
23 24 OperatorAmps: IItemEntry;
25 Antiques: IItemEntry;
26 OperatorSuits: IItemEntry;
24 27 Pistols: IItemEntry;
25 28 Suits: IItemEntry;
26 29 Melee: IItemEntry;
@@ -54,6 +57,7 @@ export interface ISaveLoadoutRequest {
54 57 UseAdultOperatorLoadout: boolean;
55 58 WeaponSkins: IItemEntry;
56 59 LotusCustomization: ILotusCustomization;
60 FocusLoadouts?: IFocusLoadoutClient[];
57 61 }
58 62
59 63 export type ISaveLoadoutRequestNoUpgradeVer = Omit<ISaveLoadoutRequest, "UpgradeVer">;