返回提交历史
Modified
src/models/inventoryModels/inventoryModel.ts
+15
-1
Modified
src/services/importService.ts
+16
-0
Modified
src/services/inventoryService.ts
+151
-0
Modified
src/services/saveLoadoutService.ts
+36
-0
Modified
src/types/inventoryTypes/inventoryTypes.ts
+23
-2
Modified
src/types/saveLoadoutTypes.ts
+4
-0
XFEstudio/SpaceNinjaServer
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
代码差异
6 个文件
+245
-3
@@ -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>;
@@ -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
}
@@ -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);
@@ -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)) {
@@ -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;
@@ -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">;