XFEstudio/XFESpaceNinjaServer
feat: loadouts in U7-U13 (#3062)
This gets saving and loading loadouts working in U13 and below, there may be some cases that cause some appearance options to get reset when swapping between builds (namely anything in the skins field likes to get reset for some reason if set on a newer build like U13 and going back to U7-U8). U14-U15 need their own implementation for loading loadouts to work in them (I haven't found where the client gets the loadout data from yet), so loadouts remain unsupported in those builds for now. Also seems to fix loadouts in U16-U19.5 (by extension of now using legacy oid in places where it's needed), and fixes appearance options in U16. Reviewed-on: https://onlyg.it/OpenWF/SpaceNinjaServer/pulls/3062 Reviewed-by: Sainan <63328889+sainan@users.noreply.github.com> Co-authored-by: VoltPrime <subsonicjackal@gmail.com> Co-committed-by: VoltPrime <subsonicjackal@gmail.com>
332791bc
代码差异
@@ -5,9 +5,9 @@ import { Inventory } from "../../models/inventoryModels/inventoryModel.ts";
import { config } from "../../services/configService.ts";
import allDialogue from "../../../static/fixed_responses/allDialogue.json" with { type: "json" };
import allPopups from "../../../static/fixed_responses/allPopups.json" with { type: "json" };
import type { ILoadoutDatabase } from "../../types/saveLoadoutTypes.ts";
import type { ILoadoutConfigClientLegacy, ILoadoutDatabase, ILoadOutPresets } from "../../types/saveLoadoutTypes.ts";
import type { IInventoryClient, IShipInventory, IUpgradeClient } from "../../types/inventoryTypes/inventoryTypes.ts";
import { equipmentKeys } from "../../types/inventoryTypes/inventoryTypes.ts";
import { equipmentKeys, loadoutKeysLegacy } from "../../types/inventoryTypes/inventoryTypes.ts";
import type { IPolarity } from "../../types/inventoryTypes/commonInventoryTypes.ts";
import { ArtifactPolarity } from "../../types/inventoryTypes/commonInventoryTypes.ts";
import type { ICountedItem } from "warframe-public-export-plus";
@@ -30,7 +30,14 @@ import { getNemesisManifest } from "../../helpers/nemesisHelpers.ts";
import { getPersonalRooms } from "../../services/personalRoomsService.ts";
import type { IPersonalRoomsClient } from "../../types/personalRoomsTypes.ts";
import { Ship } from "../../models/shipModel.ts";
import { toLegacyOid, toOid, toOid2, version_compare } from "../../helpers/inventoryHelpers.ts";
import {
convertIColorToLegacyColors,
convertIColorToLegacyColorsWithAtt,
toLegacyOid,
toOid,
toOid2,
version_compare
} from "../../helpers/inventoryHelpers.ts";
import { Inbox } from "../../models/inboxModel.ts";
import { unixTimesInMs } from "../../constants/timeConstants.ts";
import { DailyDeal } from "../../models/worldStateModel.ts";
@@ -480,6 +487,22 @@ export const getInventoryResponse = async (
for (const category of equipmentKeys) {
for (const item of inventoryResponse[category]) {
toLegacyOid(item.ItemId);
if (version_compare(buildLabel, "2015.05.14.16.29") < 0) {
// Appearance config format is different for versions before U16.5
for (const config of item.Configs) {
if (version_compare(buildLabel, "2015.03.21.08.17") < 0) {
config.Customization = {
Colors: convertIColorToLegacyColorsWithAtt(config.pricol, config.attcol),
Skins: config.Skins ?? []
};
} else if (version_compare(buildLabel, "2015.03.21.08.17") >= 0) {
config.Colors = convertIColorToLegacyColorsWithAtt(
config.pricol,
config.attcol
);
}
}
}
}
}
@@ -588,6 +611,52 @@ export const getInventoryResponse = async (
if (inventoryResponse.GuildId) {
toLegacyOid(inventoryResponse.GuildId);
}
for (const item of inventoryResponse.CurrentLoadOutIds) {
toLegacyOid(item);
}
if (
version_compare(buildLabel, "2015.03.19.00.00") <= 0 &&
inventoryResponse.CurrentLoadOutIds.length > 0 &&
inventoryResponse.LoadOutPresets.NORMAL.length > 0
) {
if (version_compare(buildLabel, "2014.07.21.18.38") >= 0) {
// U14-U15 expect a different response than any other version, where the client is pulling loadout data from has not been found yet
// As such, loading loadouts is currently unsupported for these versions
logger.warn("Loadouts are currently unsupported in U14-U15, loadouts will be undefined");
} else {
// U13 and below
inventoryResponse.CurrentLoadout = mapLegacyLoadoutConfig(
inventory,
inventoryResponse.LoadOutPresets,
buildLabel
);
}
}
for (const category of loadoutKeysLegacy) {
for (const item of inventoryResponse.LoadOutPresets[category]) {
toLegacyOid(item.ItemId);
if (item.s) {
if (item.s.ItemId) {
toLegacyOid(item.s.ItemId);
}
}
if (item.l) {
if (item.l.ItemId) {
toLegacyOid(item.l.ItemId);
}
}
if (item.p) {
if (item.p.ItemId) {
toLegacyOid(item.p.ItemId);
}
}
if (item.m) {
if (item.m.ItemId) {
toLegacyOid(item.m.ItemId);
}
}
}
}
}
}
}
@@ -596,6 +665,220 @@ export const getInventoryResponse = async (
return inventoryResponse;
};
const mapLegacyLoadoutConfig = (
inventory: TInventoryDatabaseDocument,
loadoutPresets: ILoadOutPresets,
buildLabel: string
): ILoadoutConfigClientLegacy | undefined => {
// Loadout config mapping for U15 and below
const normPreset = loadoutPresets.NORMAL.find(x => x.ItemId.$oid == inventory.CurrentLoadOutIds[0].toString());
if (normPreset) {
const s = normPreset.s?.ItemId?.$oid ? inventory.Suits.id(normPreset.s.ItemId.$oid) : null;
const p = normPreset.p?.ItemId?.$oid ? inventory.Pistols.id(normPreset.p.ItemId.$oid) : null;
const l = normPreset.l?.ItemId?.$oid ? inventory.LongGuns.id(normPreset.l.ItemId.$oid) : null;
const m = normPreset.m?.ItemId?.$oid ? inventory.Melee.id(normPreset.m.ItemId.$oid) : null;
const loadoutConfig = {
ItemId: { $id: version_compare(buildLabel, "2014.07.21.18.38") < 0 ? "Current" : normPreset.ItemId.$oid },
Name: normPreset.n ?? "Default Loadout",
Presets: [
{
ItemId: { $id: s?._id.toString() ?? "ffffffffffffffffffffffff" },
ModSlot: version_compare(buildLabel, "2013.09.13.00.00") < 0 ? 0 : (normPreset.s?.mod ?? 0),
CustSlot: normPreset.s?.cus ?? 0,
Customization: {
Emblem: "",
Colors: convertIColorToLegacyColors(s?.Configs[normPreset.s?.cus ?? 0].pricol),
Skins: s?.Configs[normPreset.s?.cus ?? 0].Skins ?? []
}
},
{
ItemId: { $id: p?._id.toString() ?? "ffffffffffffffffffffffff" },
ModSlot: version_compare(buildLabel, "2013.09.13.00.00") < 0 ? 0 : (normPreset.p?.mod ?? 0),
CustSlot: normPreset.p?.cus ?? 0,
Customization: {
Emblem: "",
Colors: convertIColorToLegacyColors(p?.Configs[normPreset.p?.cus ?? 0].pricol),
Skins: p?.Configs[normPreset.p?.cus ?? 0].Skins ?? []
}
},
{
ItemId: { $id: l?._id.toString() ?? "ffffffffffffffffffffffff" },
ModSlot: version_compare(buildLabel, "2013.09.13.00.00") < 0 ? 0 : (normPreset.l?.mod ?? 0),
CustSlot: normPreset.l?.cus ?? 0,
Customization: {
Emblem: "",
Colors: convertIColorToLegacyColors(l?.Configs[normPreset.l?.cus ?? 0].pricol),
Skins: l?.Configs[normPreset.l?.cus ?? 0].Skins ?? []
}
},
{
ItemId: { $id: m?._id.toString() ?? "ffffffffffffffffffffffff" },
ModSlot: version_compare(buildLabel, "2013.09.13.00.00") < 0 ? 0 : (normPreset.m?.mod ?? 0),
CustSlot: normPreset.m?.cus ?? 0,
Customization: {
Emblem: "",
Colors: convertIColorToLegacyColors(m?.Configs[normPreset.m?.cus ?? 0].pricol),
Skins: m?.Configs[normPreset.m?.cus ?? 0].Skins ?? []
}
}
]
};
if (version_compare(buildLabel, "2013.03.18.00.00") >= 0) {
if (inventory.CurrentLoadOutIds.length > 1 && loadoutPresets.SENTINEL.length > 0) {
const compPreset = loadoutPresets.SENTINEL.find(
x => x.ItemId.$oid == inventory.CurrentLoadOutIds[1].toString()
);
if (compPreset) {
const s = compPreset.s?.ItemId?.$oid ? inventory.Sentinels.id(compPreset.s.ItemId.$oid) : null;
const l = compPreset.l?.ItemId?.$oid
? inventory.SentinelWeapons.id(compPreset.l.ItemId.$oid)
: null;
loadoutConfig.Presets.push(
{
ItemId: { $id: s?._id.toString() ?? "ffffffffffffffffffffffff" },
ModSlot: version_compare(buildLabel, "2013.09.13.00.00") < 0 ? 0 : (compPreset.s?.mod ?? 0),
CustSlot: compPreset.s?.cus ?? 0,
Customization: {
Emblem: "",
Colors: convertIColorToLegacyColors(s?.Configs[compPreset.s?.cus ?? 0].pricol),
Skins: s?.Configs[compPreset.s?.cus ?? 0].Skins ?? []
}
},
{
ItemId: { $id: l?._id.toString() ?? "ffffffffffffffffffffffff" },
ModSlot: version_compare(buildLabel, "2013.09.13.00.00") < 0 ? 0 : (compPreset.l?.mod ?? 0),
CustSlot: compPreset.l?.cus ?? 0,
Customization: {
Emblem: "",
Colors: convertIColorToLegacyColors(l?.Configs[compPreset.l?.cus ?? 0].pricol),
Skins: l?.Configs[compPreset.l?.cus ?? 0].Skins ?? []
}
}
);
}
} else {
logger.warn(
`Could not find SENTINEL loadout with id ${inventory.CurrentLoadOutIds[1].toString()}, this part of the loadout will be empty`
);
loadoutConfig.Presets.push(
{
ItemId: { $id: "ffffffffffffffffffffffff" },
ModSlot: 0,
CustSlot: 0,
Customization: {
Emblem: "",
Colors: [],
Skins: []
}
},
{
ItemId: { $id: "ffffffffffffffffffffffff" },
ModSlot: 0,
CustSlot: 0,
Customization: {
Emblem: "",
Colors: [],
Skins: []
}
}
);
}
}
if (version_compare(buildLabel, "2014.10.24.08.24") >= 0) {
if (inventory.CurrentLoadOutIds.length > 2 && loadoutPresets.ARCHWING.length > 0) {
const archPreset = loadoutPresets.ARCHWING.find(
x => x.ItemId.$oid == inventory.CurrentLoadOutIds[2].toString()
);
if (archPreset) {
const s = archPreset.s?.ItemId?.$oid ? inventory.SpaceSuits.id(archPreset.s.ItemId.$oid) : null;
const l = archPreset.l?.ItemId?.$oid ? inventory.SpaceGuns.id(archPreset.l.ItemId.$oid) : null;
const m = archPreset.m?.ItemId?.$oid ? inventory.SpaceMelee.id(archPreset.m.ItemId.$oid) : null;
loadoutConfig.Presets.push(
{
ItemId: { $id: s?._id.toString() ?? "ffffffffffffffffffffffff" },
ModSlot: archPreset.s?.mod ?? 0,
CustSlot: archPreset.s?.cus ?? 0,
Customization: {
Emblem: "",
Colors: convertIColorToLegacyColors(s?.Configs[archPreset.s?.cus ?? 0].pricol),
Skins: s?.Configs[0].Skins ?? []
}
},
{
ItemId: { $id: l?._id.toString() ?? "ffffffffffffffffffffffff" },
ModSlot: archPreset.l?.mod ?? 0,
CustSlot: archPreset.l?.cus ?? 0,
Customization: {
Emblem: "",
Colors: convertIColorToLegacyColors(l?.Configs[archPreset.l?.cus ?? 0].pricol),
Skins: l?.Configs[0].Skins ?? []
}
},
{
ItemId: { $id: m?._id.toString() ?? "ffffffffffffffffffffffff" },
ModSlot: archPreset.m?.mod ?? 0,
CustSlot: archPreset.m?.cus ?? 0,
Customization: {
Emblem: "",
Colors: convertIColorToLegacyColors(m?.Configs[archPreset.m?.cus ?? 0].pricol),
Skins: m?.Configs[0].Skins ?? []
}
}
);
}
} else {
logger.warn(
`Could not find ARCHWING loadout with id ${inventory.CurrentLoadOutIds[2].toString()}, this part of the loadout will be empty`
);
loadoutConfig.Presets.push(
{
ItemId: { $id: "ffffffffffffffffffffffff" },
ModSlot: 0,
CustSlot: 0,
Customization: {
Emblem: "",
Colors: [],
Skins: []
}
},
{
ItemId: { $id: "ffffffffffffffffffffffff" },
ModSlot: 0,
CustSlot: 0,
Customization: {
Emblem: "",
Colors: [],
Skins: []
}
},
{
ItemId: { $id: "ffffffffffffffffffffffff" },
ModSlot: 0,
CustSlot: 0,
Customization: {
Emblem: "",
Colors: [],
Skins: []
}
}
);
}
}
return loadoutConfig;
}
logger.error(
`Could not find NORMAL loadout with id ${inventory.CurrentLoadOutIds[0].toString()}, entire loadout will be undefined`
);
return undefined;
};
const getExpRequiredForMr = (rank: number): number => {
if (rank <= 30) {
return 2500 * rank * rank;
@@ -2,6 +2,7 @@ import type { IMongoDate, IOid, IOidWithLegacySupport } from "../types/commonTyp
import { Types } from "mongoose";
import type { TRarity } from "warframe-public-export-plus";
import type { IFusionTreasure } from "../types/inventoryTypes/inventoryTypes.ts";
import type { IColor } from "../types/inventoryTypes/commonInventoryTypes.ts";
export const version_compare = (a: string, b: string): number => {
const a_digits = a
@@ -70,6 +71,38 @@ export const parseFusionTreasure = (name: string, count: number): IFusionTreasur
};
};
export const convertIColorToLegacyColors = (colors: IColor | undefined): number[] => {
const convertedColors = [colors?.t0 ?? -1, colors?.t1 ?? -1, colors?.t2 ?? -1, colors?.t3 ?? -1, colors?.en ?? -1];
return convertedColors;
};
export const convertIColorToLegacyColorsWithAtt = (
pricol: IColor | undefined,
attcol: IColor | undefined
): number[] => {
const convertedColors = [
pricol?.t0 ?? -1,
pricol?.t1 ?? -1,
pricol?.t2 ?? -1,
pricol?.t3 ?? -1,
pricol?.en ?? -1,
attcol?.t0 ?? -1,
attcol?.t1 ?? -1,
attcol?.t2 ?? -1,
attcol?.t3 ?? -1,
attcol?.en ?? -1
];
return convertedColors;
};
export const convertLegacyColorsToIColor = (colors: number[] | undefined): IColor => {
if (colors) {
return { t0: colors[0], t1: colors[1], t2: colors[2], t3: colors[3], en: colors[4] };
} else {
return {};
}
};
export type TTraitsPool = Record<
"Colors" | "EyeColors" | "FurPatterns" | "BodyTypes" | "Heads" | "Tails",
{ type: string; rarity: TRarity }[]
@@ -1,21 +1,31 @@
import type {
IItemEntry,
ILoadoutClient,
ILoadoutConfigClientLegacy,
ILoadoutEntry,
ILoadoutPresetClientLegacy,
IOperatorConfigEntry,
ISaveLoadoutRequestNoUpgradeVer
} from "../types/saveLoadoutTypes.ts";
import { Loadout } from "../models/inventoryModels/loadoutModel.ts";
import { addMods, getInventory } from "./inventoryService.ts";
import type { IOid } from "../types/commonTypes.ts";
import type { IOidWithLegacySupport } from "../types/commonTypes.ts";
import { Types } from "mongoose";
import { isEmptyObject } from "../helpers/general.ts";
import { version_compare } from "../helpers/inventoryHelpers.ts";
import {
convertLegacyColorsToIColor,
fromDbOid,
fromOid,
toObjectId,
version_compare
} from "../helpers/inventoryHelpers.ts";
import { logger } from "../utils/logger.ts";
import type { TEquipmentKey } from "../types/inventoryTypes/inventoryTypes.ts";
import { equipmentKeys } from "../types/inventoryTypes/inventoryTypes.ts";
import type { IItemConfig, IItemConfigDatabase } from "../types/inventoryTypes/commonInventoryTypes.ts";
import { importCrewShipMembers, importCrewShipWeapon, importLoadOutConfig } from "./importService.ts";
import type { IEquipmentDatabase, IEquipmentSelectionDatabase } from "../types/equipmentTypes.ts";
import type { TInventoryDatabaseDocument } from "../models/inventoryModels/inventoryModel.ts";
//TODO: setup default items on account creation or like originally in giveStartingItems.php
@@ -65,68 +75,125 @@ export const handleInventoryItemConfigChange = async (
break;
}
case "LoadOuts": {
logger.debug("loadout received");
const loadout = await Loadout.findOne({ loadoutOwnerId: accountId });
if (!loadout) {
throw new Error("loadout not found");
}
if (
buildLabel &&
version_compare(buildLabel, "2014.04.10.17.47") >= 0 &&
version_compare(buildLabel, "2015.03.19.00.00") < 0
) {
// U14-U15
// const configs = equipment as {
// [key: string]: ILoadoutConfigClientLegacy;
// };
let newLoadoutId: Types.ObjectId | undefined;
for (const [_loadoutSlot, _loadout] of Object.entries(equipment)) {
const loadoutSlot = _loadoutSlot as keyof ILoadoutClient;
const newLoadout = _loadout as ILoadoutEntry;
// logger.debug("legacy loadout received (U14-U15 format)", configs);
// empty loadout slot like: "NORMAL": {}
if (isEmptyObject(newLoadout)) {
continue;
// for (const key in configs) {
// const x = configs[key];
// await saveLegacyLoadoutPreset(inventory, x.Presets, x.Name, buildLabel);
// }
logger.warn("Loadouts are currently unsupported in U14-U15, only saving mod/appearance configs");
break;
} else {
logger.debug("loadout received");
const loadout = await Loadout.findOne({ loadoutOwnerId: accountId });
if (!loadout) {
throw new Error("loadout not found");
}
// all non-empty entries are one loadout slot
for (const [loadoutId, loadoutConfig] of Object.entries(newLoadout)) {
if (loadoutConfig.Remove) {
loadout[loadoutSlot].pull({ _id: loadoutId });
let newLoadoutId: Types.ObjectId | undefined;
for (const [_loadoutSlot, _loadout] of Object.entries(equipment)) {
const loadoutSlot = _loadoutSlot as keyof ILoadoutClient;
const newLoadout = _loadout as ILoadoutEntry;
// empty loadout slot like: "NORMAL": {}
if (isEmptyObject(newLoadout)) {
continue;
}
const oldLoadoutConfig = loadout[loadoutSlot].id(loadoutId);
// all non-empty entries are one loadout slot
for (const [loadoutId, loadoutConfig] of Object.entries(newLoadout)) {
if (loadoutConfig.Remove) {
loadout[loadoutSlot].pull({ _id: loadoutId });
continue;
}
const loadoutConfigDatabase = importLoadOutConfig(loadoutConfig);
const oldLoadoutConfig = loadout[loadoutSlot].id(loadoutId);
// if no config with this id exists, create a new one
if (!oldLoadoutConfig) {
//save the new object id and assign it for every ffff return at the end
if (loadoutConfigDatabase._id.toString() === "ffffffffffffffffffffffff") {
if (!newLoadoutId) {
newLoadoutId = new Types.ObjectId();
const loadoutConfigDatabase = importLoadOutConfig(loadoutConfig);
// if no config with this id exists, create a new one
if (!oldLoadoutConfig) {
//save the new object id and assign it for every ffff return at the end
if (loadoutConfigDatabase._id.toString() === "ffffffffffffffffffffffff") {
if (!newLoadoutId) {
newLoadoutId = new Types.ObjectId();
}
loadoutConfigDatabase._id = newLoadoutId;
loadout[loadoutSlot].push(loadoutConfigDatabase);
continue;
}
loadoutConfigDatabase._id = newLoadoutId;
loadout[loadoutSlot].push(loadoutConfigDatabase);
continue;
}
loadout[loadoutSlot].push(loadoutConfigDatabase);
continue;
}
const loadoutIndex = loadout[loadoutSlot].indexOf(oldLoadoutConfig);
if (loadoutIndex === -1) {
throw new Error("loadout index not found");
}
const loadoutIndex = loadout[loadoutSlot].indexOf(oldLoadoutConfig);
if (loadoutIndex === -1) {
throw new Error("loadout index not found");
loadout[loadoutSlot][loadoutIndex].overwrite(loadoutConfigDatabase);
}
}
await loadout.save();
loadout[loadoutSlot][loadoutIndex].overwrite(loadoutConfigDatabase);
//only return an id if a new loadout was added
if (newLoadoutId) {
return newLoadoutId.toString();
}
}
await loadout.save();
//only return an id if a new loadout was added
if (newLoadoutId) {
return newLoadoutId.toString();
break;
}
case "LoadOut": {
// U10-U13
const config = equipment as ILoadoutConfigClientLegacy;
logger.debug("legacy loadout received (U10-U13 format)", config);
await saveLegacyLoadoutPreset(inventory, config.Presets, config.Name, buildLabel);
break;
}
case "Presets": {
// U8 and below
const presets = equipment as ILoadoutPresetClientLegacy[];
logger.debug("legacy loadout received (U8 format)", presets);
await saveLegacyLoadoutPreset(inventory, presets, undefined, buildLabel);
break;
}
case "CurrentLoadout": {
// U14-U15
const id = equipment as string;
if (inventory.CurrentLoadOutIds[0]) {
inventory.CurrentLoadOutIds[0] = toObjectId(id);
}
if (inventory.CurrentLoadOutIds[1]) {
inventory.CurrentLoadOutIds[1] = toObjectId(id);
}
if (inventory.CurrentLoadOutIds[2]) {
inventory.CurrentLoadOutIds[2] = toObjectId(id);
}
break;
}
case "CurrentLoadOutIds": {
const loadoutIds = equipment as IOid[]; // TODO: Check for more than just an array of oids, I think i remember one instance
inventory.CurrentLoadOutIds = loadoutIds;
const loadoutIds = equipment as IOidWithLegacySupport[]; // TODO: Check for more than just an array of oids, I think i remember one instance
const ids: Types.ObjectId[] = [];
loadoutIds.forEach(x => {
ids.push(toObjectId(fromOid(x)));
});
inventory.CurrentLoadOutIds = ids;
break;
}
case "EquippedGear":
@@ -199,32 +266,47 @@ export const handleInventoryItemConfigChange = async (
for (const [configId, config] of Object.entries(itemConfigEntries)) {
if (/^[0-9]+$/.test(configId)) {
const c = config as IItemConfig;
if (buildLabel && version_compare(buildLabel, "2014.04.10.17.47") < 0) {
if (c.Upgrades) {
// U10-U11 store mods in the item config as $id instead of a string, need to convert that here
const convertedUpgrades: string[] = [];
c.Upgrades.forEach(upgrade => {
const upgradeId = upgrade as { $id: string };
const rawUpgrade = inventory.RawUpgrades.id(upgradeId.$id);
if (rawUpgrade) {
const newId = new Types.ObjectId();
convertedUpgrades.push(newId.toString());
addMods(inventory, [
{
if (buildLabel && version_compare(buildLabel, "2015.03.21.08.17") <= 0) {
const legacyColors = c.Customization?.Colors ?? c.Colors;
if (legacyColors) {
if (legacyColors.length == 10) {
c.pricol = convertLegacyColorsToIColor(legacyColors.splice(0, 5));
c.attcol = convertLegacyColorsToIColor(legacyColors);
} else {
c.pricol = convertLegacyColorsToIColor(legacyColors);
}
}
const legacySkins = c.Customization?.Skins;
if (legacySkins) {
c.Skins = legacySkins;
}
if (version_compare(buildLabel, "2014.04.10.17.47") < 0) {
if (c.Upgrades) {
// U10-U11 store mods in the item config as $id instead of a string, need to convert that here
const convertedUpgrades: string[] = [];
c.Upgrades.forEach(upgrade => {
const upgradeId = upgrade as { $id: string };
const rawUpgrade = inventory.RawUpgrades.id(upgradeId.$id);
if (rawUpgrade) {
const newId = new Types.ObjectId();
convertedUpgrades.push(newId.toString());
addMods(inventory, [
{
ItemType: rawUpgrade.ItemType,
ItemCount: -1
}
]);
inventory.Upgrades.push({
UpgradeFingerprint: `{"lvl":0}`,
ItemType: rawUpgrade.ItemType,
ItemCount: -1
}
]);
inventory.Upgrades.push({
UpgradeFingerprint: `{"lvl":0}`,
ItemType: rawUpgrade.ItemType,
_id: newId
});
} else {
convertedUpgrades.push(upgradeId.$id);
}
});
c.Upgrades = convertedUpgrades;
_id: newId
});
} else {
convertedUpgrades.push(upgradeId.$id);
}
});
c.Upgrades = convertedUpgrades;
}
}
}
inventoryItem.Configs[parseInt(configId)] = c as IItemConfigDatabase;
@@ -264,3 +346,207 @@ export const handleInventoryItemConfigChange = async (
}
await inventory.save();
};
const saveLegacyLoadoutPreset = async (
inventory: TInventoryDatabaseDocument,
presets: ILoadoutPresetClientLegacy[],
name: string | undefined,
buildLabel: string | undefined
): Promise<void> => {
const loadout = await Loadout.findOne({ loadoutOwnerId: inventory.accountOwnerId });
if (!loadout) {
throw new Error("loadout not found");
}
const currentLoadouts = inventory.CurrentLoadOutIds as Types.ObjectId[];
const s =
fromOid(presets[0].ItemId) != "ffffffffffffffffffffffff"
? configureLegacyEquipmentSelection(
presets[0],
buildLabel,
inventory.Suits.id(fromOid(presets[0].ItemId)),
loadout.NORMAL.id(currentLoadouts[0])?.s?.cus
)
: undefined;
const p =
fromOid(presets[1].ItemId) != "ffffffffffffffffffffffff"
? configureLegacyEquipmentSelection(
presets[1],
buildLabel,
inventory.Pistols.id(fromOid(presets[1].ItemId)),
loadout.NORMAL.id(currentLoadouts[0])?.p?.cus
)
: undefined;
const l =
fromOid(presets[2].ItemId) != "ffffffffffffffffffffffff"
? configureLegacyEquipmentSelection(
presets[2],
buildLabel,
inventory.LongGuns.id(fromOid(presets[2].ItemId)),
loadout.NORMAL.id(currentLoadouts[0])?.l?.cus
)
: undefined;
const m =
fromOid(presets[3].ItemId) != "ffffffffffffffffffffffff"
? configureLegacyEquipmentSelection(
presets[3],
buildLabel,
inventory.Melee.id(fromOid(presets[3].ItemId)),
loadout.NORMAL.id(currentLoadouts[0])?.m?.cus
)
: undefined;
if (loadout.NORMAL.length == 0) {
const loadoutId = new Types.ObjectId("000000000000000000000000");
loadout.NORMAL.push({
n: "Default Loadout",
s: s,
l: l,
p: p,
m: m,
_id: loadoutId
});
if (currentLoadouts.length == 0) {
currentLoadouts.push(loadoutId);
} else {
currentLoadouts[0] = loadoutId;
}
} else {
const loadoutId = fromDbOid(currentLoadouts[0]);
const preset = loadout.NORMAL.id(loadoutId);
if (preset) {
preset.n = name ?? preset.n;
preset.s = s;
preset.p = p;
preset.l = l;
preset.m = m;
} else {
logger.warn(
`Could not find NORMAL loadout with id ${loadoutId.toString()}, equipment selection will not be saved`
);
}
}
if (presets.length >= 6) {
const s =
fromOid(presets[4].ItemId) != "ffffffffffffffffffffffff"
? configureLegacyEquipmentSelection(
presets[4],
buildLabel,
inventory.Sentinels.id(fromOid(presets[4].ItemId)),
loadout.SENTINEL.id(currentLoadouts[1])?.s?.cus
)
: undefined;
const l =
fromOid(presets[5].ItemId) != "ffffffffffffffffffffffff"
? configureLegacyEquipmentSelection(
presets[5],
buildLabel,
inventory.SentinelWeapons.id(fromOid(presets[5].ItemId)),
loadout.SENTINEL.id(currentLoadouts[1])?.l?.cus
)
: undefined;
if (loadout.SENTINEL.length == 0) {
const loadoutId = new Types.ObjectId("000000000000000000000000");
loadout.SENTINEL.push({
n: "Default Loadout",
s: s,
l: l,
_id: loadoutId
});
if (currentLoadouts.length < 2) {
currentLoadouts.push(loadoutId);
} else {
currentLoadouts[1] = loadoutId;
}
} else {
const loadoutId = fromDbOid(currentLoadouts[1]);
const preset = loadout.SENTINEL.id(loadoutId);
if (preset) {
preset.n = name ?? preset.n;
preset.s = s;
preset.l = l;
} else {
logger.warn(
`Could not find SENTINEL loadout with id ${loadoutId.toString()}, equipment selection will not be saved`
);
}
}
}
if (presets.length == 9) {
const s =
fromOid(presets[6].ItemId) != "ffffffffffffffffffffffff"
? configureLegacyEquipmentSelection(presets[6], buildLabel, null, 0)
: undefined;
const l =
fromOid(presets[7].ItemId) != "ffffffffffffffffffffffff"
? configureLegacyEquipmentSelection(presets[7], buildLabel, null, 0)
: undefined;
const m =
fromOid(presets[8].ItemId) != "ffffffffffffffffffffffff"
? configureLegacyEquipmentSelection(presets[8], buildLabel, null, 0)
: undefined;
if (loadout.ARCHWING.length == 0) {
const loadoutId = new Types.ObjectId("000000000000000000000000");
loadout.ARCHWING.push({
n: "Default Loadout",
s: s,
l: l,
m: m,
_id: loadoutId
});
if (currentLoadouts.length < 3) {
currentLoadouts.push(loadoutId);
} else {
currentLoadouts[2] = loadoutId;
}
} else {
const loadoutId = fromDbOid(currentLoadouts[2]);
const preset = loadout.ARCHWING.id(loadoutId);
if (preset) {
preset.n = name ?? preset.n;
preset.s = s;
preset.l = l;
preset.m = m;
} else {
logger.warn(
`Could not find ARCHWING loadout with id ${loadoutId.toString()}, equipment selection will not be saved`
);
}
}
}
await loadout.save();
};
const configureLegacyEquipmentSelection = (
preset: ILoadoutPresetClientLegacy,
buildLabel: string | undefined,
item: IEquipmentDatabase | null,
appearanceConfig: number | undefined
): IEquipmentSelectionDatabase | undefined => {
if (preset.ItemId.$id) {
const slotEntry = {
ItemId: toObjectId(preset.ItemId.$id),
mod: preset.ModSlot ?? 0,
cus: preset.CustSlot ?? 0
};
if (item && buildLabel && version_compare(buildLabel, "2013.09.13.00.00") < 0) {
// Specific code path for U8 and below for applying cosmetics
const config = item.Configs[appearanceConfig ?? 0];
if (item.Configs[appearanceConfig ?? 0]) {
config.pricol = convertLegacyColorsToIColor(preset.Customization?.Colors);
config.Skins = preset.Customization?.Skins;
}
}
return slotEntry;
} else {
return undefined;
}
};
@@ -54,6 +54,15 @@ export interface IItemConfig {
AbilityOverride?: IAbilityOverride;
PvpUpgrades?: string[];
ugly?: boolean;
Colors?: number[]; // U16.0
Customization?: IItemConfigCustomizationsLegacy; // U10-U15
}
export interface IItemConfigCustomizationsLegacy {
CustomEmblems?: { EmblemId: string }[];
Emblem?: string;
Colors: number[];
Skins: string[];
}
export interface IItemConfigDatabase extends Omit<IItemConfig, "Upgrades"> {
@@ -13,7 +13,7 @@ import type { IFingerprintStat, RivenFingerprint } from "../../helpers/rivenHelp
import type { IOrbiterClient } from "../personalRoomsTypes.ts";
import type { ICountedStoreItem } from "warframe-public-export-plus";
import type { IEquipmentClient, IEquipmentDatabase, ITraits } from "../equipmentTypes.ts";
import type { ILoadOutPresets } from "../saveLoadoutTypes.ts";
import type { ILoadoutConfigClientLegacy, ILoadOutPresets } from "../saveLoadoutTypes.ts";
import type { CalendarSeasonType } from "../worldStateTypes.ts";
export type InventoryDatabaseEquipment = {
@@ -195,6 +195,8 @@ export const equipmentKeys = [
"CrewShipSalvagedWeapons"
] as const;
export const loadoutKeysLegacy = ["NORMAL", "NORMAL_PVP", "LUNARO", "ARCHWING", "SENTINEL", "OPERATOR"] as const;
export type TEquipmentKey = (typeof equipmentKeys)[number];
export interface IDuviriInfo {
@@ -301,6 +303,7 @@ export interface IInventoryClient extends IDailyAffiliations, InventoryClientEqu
FlavourItems: IFlavourItem[];
LoadOutPresets: ILoadOutPresets;
CurrentLoadOutIds: IOid[];
CurrentLoadout?: ILoadoutConfigClientLegacy; // U8-13
Missions: IMission[];
RandomUpgradesIdentified?: number;
LastRegionPlayed: TSolarMapRegion;
@@ -1,8 +1,9 @@
import type { IOid } from "./commonTypes.ts";
import type { IOid, IOidWithLegacySupport } from "./commonTypes.ts";
import type {
ICrewShipCustomization,
IFlavourItem,
IItemConfig,
IItemConfigCustomizationsLegacy,
ILotusCustomization,
IOperatorConfigClient
} from "./inventoryTypes/commonInventoryTypes.ts";
@@ -15,7 +16,9 @@ import type {
} from "./equipmentTypes.ts";
export interface ISaveLoadoutRequest {
LoadOuts: ILoadoutClient;
LoadOuts: ILoadoutClient | ILoadoutConfigClientLegacy;
LoadOut?: ILoadoutPresetClientLegacy[]; // U10-U13
Presets?: ILoadoutPresetClientLegacy[]; // U8
LongGuns: IItemEntry;
OperatorAmps: IItemEntry;
Pistols: IItemEntry;
@@ -42,6 +45,7 @@ export interface ISaveLoadoutRequest {
OperatorLoadOuts: IOperatorConfigEntry;
KahlLoadOuts: IOperatorConfigEntry;
CrewShips: IItemEntry;
CurrentLoadout: string; // U14-U15
CurrentLoadOutIds: IOid[];
ValidNewLoadoutId: string;
ActiveCrewShip: IOid;
@@ -148,3 +152,15 @@ export interface ILoadoutConfigDatabase
h?: IEquipmentSelectionDatabase;
a?: IEquipmentSelectionDatabase;
}
export interface ILoadoutConfigClientLegacy {
ItemId: IOidWithLegacySupport;
Name: string;
Presets: ILoadoutPresetClientLegacy[];
}
export interface ILoadoutPresetClientLegacy {
ItemId: IOidWithLegacySupport;
ModSlot?: number;
CustSlot?: number;
Customization?: IItemConfigCustomizationsLegacy;
}