forked from OpenWF/SpaceNinjaServer
feat: Quests1 (#852)
This commit is contained in:
parent
8858b15693
commit
ef2708b510
1649
package-lock.json
generated
1649
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
56
src/controllers/api/giveKeyChainTriggeredItemsController.ts
Normal file
56
src/controllers/api/giveKeyChainTriggeredItemsController.ts
Normal file
@ -0,0 +1,56 @@
|
||||
import { RequestHandler } from "express";
|
||||
import { isEmptyObject, parseString } from "@/src/helpers/general";
|
||||
import { getJSONfromString } from "@/src/helpers/stringHelpers";
|
||||
import { addKeyChainItems, getInventory } from "@/src/services/inventoryService";
|
||||
|
||||
export const giveKeyChainTriggeredItemsController: RequestHandler = async (req, res) => {
|
||||
const accountId = parseString(req.query.accountId);
|
||||
const keyChainTriggeredItemsRequest = getJSONfromString(
|
||||
(req.body as string).toString()
|
||||
) as IGiveKeyChainTriggeredItemsRequest;
|
||||
|
||||
const inventory = await getInventory(accountId);
|
||||
const inventoryChanges = await addKeyChainItems(inventory, keyChainTriggeredItemsRequest);
|
||||
|
||||
if (isEmptyObject(inventoryChanges)) {
|
||||
throw new Error("inventory changes was empty after getting keychain items: should not happen");
|
||||
}
|
||||
// items were added: update quest stage's i (item was given)
|
||||
const quest = inventory.QuestKeys.find(quest => quest.ItemType === keyChainTriggeredItemsRequest.KeyChain);
|
||||
|
||||
if (!quest) {
|
||||
throw new Error(`Quest ${keyChainTriggeredItemsRequest.KeyChain} not found in QuestKeys`);
|
||||
}
|
||||
if (!quest.Progress) {
|
||||
throw new Error(`Progress should always exist when giving keychain triggered items`);
|
||||
}
|
||||
|
||||
const questStage = quest.Progress[keyChainTriggeredItemsRequest.ChainStage];
|
||||
if (questStage) {
|
||||
questStage.i = true;
|
||||
} else {
|
||||
const questStageIndex = quest.Progress.push({ i: true }) - 1;
|
||||
if (questStageIndex !== keyChainTriggeredItemsRequest.ChainStage) {
|
||||
throw new Error(
|
||||
`Quest stage index mismatch: ${questStageIndex} !== ${keyChainTriggeredItemsRequest.ChainStage}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
await inventory.save();
|
||||
res.send(inventoryChanges);
|
||||
|
||||
//TODO: Check whether Wishlist is used to track items which should exist uniquely in the inventory
|
||||
/*
|
||||
some items are added or removed (not sure) to the wishlist, in that case a
|
||||
WishlistChanges: ["/Lotus/Types/Items/ShipFeatureItems/ArsenalFeatureItem"],
|
||||
is added to the response, need to determine for which items this is the case and what purpose this has.
|
||||
*/
|
||||
//{"KeyChain":"/Lotus/Types/Keys/VorsPrize/VorsPrizeQuestKeyChain","ChainStage":0}
|
||||
//{"WishlistChanges":["/Lotus/Types/Items/ShipFeatureItems/ArsenalFeatureItem"],"MiscItems":[{"ItemType":"/Lotus/Types/Items/ShipFeatureItems/ArsenalFeatureItem","ItemCount":1}]}
|
||||
};
|
||||
|
||||
export interface IGiveKeyChainTriggeredItemsRequest {
|
||||
KeyChain: string;
|
||||
ChainStage: number;
|
||||
}
|
@ -22,8 +22,6 @@ export const inventorySlotsController: RequestHandler = async (req, res) => {
|
||||
const accountId = await getAccountIdForRequest(req);
|
||||
//const body = JSON.parse(req.body as string) as IInventorySlotsRequest;
|
||||
|
||||
//console.log(body);
|
||||
|
||||
//TODO: check which slot was purchased because pvpBonus is also possible
|
||||
|
||||
const inventory = await getInventory(accountId);
|
||||
@ -31,7 +29,5 @@ export const inventorySlotsController: RequestHandler = async (req, res) => {
|
||||
updateSlots(inventory, InventorySlot.PVE_LOADOUTS, 1, 1);
|
||||
await inventory.save();
|
||||
|
||||
//console.log({ InventoryChanges: currencyChanges }, " added loadout changes:");
|
||||
|
||||
res.json({ InventoryChanges: currencyChanges });
|
||||
};
|
||||
|
@ -1,10 +1,13 @@
|
||||
import { RequestHandler } from "express";
|
||||
import { missionInventoryUpdate } from "@/src/services/inventoryService";
|
||||
import { combineRewardAndLootInventory, getRewards } from "@/src/services/missionInventoryUpdateService";
|
||||
import { getJSONfromString } from "@/src/helpers/stringHelpers";
|
||||
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||
import { IMissionInventoryUpdateRequest } from "@/src/types/requestTypes";
|
||||
import { logger } from "@/src/utils/logger";
|
||||
import {
|
||||
addMissionInventoryUpdates,
|
||||
addMissionRewards,
|
||||
calculateFinalCredits
|
||||
} from "@/src/services/missionInventoryUpdateService";
|
||||
import { getInventory } from "@/src/services/inventoryService";
|
||||
/*
|
||||
**** INPUT ****
|
||||
- [ ] crossPlaySetting
|
||||
@ -30,13 +33,13 @@ import { logger } from "@/src/utils/logger";
|
||||
- [ ] hosts
|
||||
- [x] ChallengeProgress
|
||||
- [ ] SeasonChallengeHistory
|
||||
- [ ] PS (Passive anti-cheat data which includes your username, module list, process list, and system name.)
|
||||
- [ ] PS (anticheat data)
|
||||
- [ ] ActiveDojoColorResearch
|
||||
- [x] RewardInfo
|
||||
- [ ] ReceivedCeremonyMsg
|
||||
- [ ] LastCeremonyResetDate
|
||||
- [ ] MissionPTS (Used to validate the mission/alive time above.)
|
||||
- [ ] RepHash (A hash from the replication manager/RepMgr Unknown what it does.)
|
||||
- [ ] RepHash
|
||||
- [ ] EndOfMatchUpload
|
||||
- [ ] ObjectiveReached
|
||||
- [ ] FpsAvg
|
||||
@ -45,34 +48,52 @@ import { logger } from "@/src/utils/logger";
|
||||
- [ ] FpsSamples
|
||||
*/
|
||||
|
||||
const missionInventoryUpdateController: RequestHandler = async (req, res): Promise<void> => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
||||
export const missionInventoryUpdateController: RequestHandler = async (req, res): Promise<void> => {
|
||||
const accountId = await getAccountIdForRequest(req);
|
||||
|
||||
try {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-call
|
||||
const lootInventory = getJSONfromString(req.body.toString()) as IMissionInventoryUpdateRequest;
|
||||
const missionReport = getJSONfromString((req.body as string).toString()) as IMissionInventoryUpdateRequest;
|
||||
|
||||
logger.debug("missionInventoryUpdate with lootInventory =", lootInventory);
|
||||
|
||||
const { InventoryChanges, MissionRewards } = getRewards(lootInventory);
|
||||
|
||||
const { combinedInventoryChanges, TotalCredits, CreditsBonus, MissionCredits, FusionPoints } =
|
||||
combineRewardAndLootInventory(InventoryChanges, lootInventory);
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const InventoryJson = JSON.stringify(await missionInventoryUpdate(combinedInventoryChanges, accountId));
|
||||
res.json({
|
||||
// InventoryJson, // this part will reset game data and missions will be locked
|
||||
MissionRewards,
|
||||
InventoryChanges,
|
||||
TotalCredits,
|
||||
CreditsBonus,
|
||||
MissionCredits,
|
||||
FusionPoints
|
||||
});
|
||||
} catch (err) {
|
||||
console.error("Error parsing JSON data:", err);
|
||||
if (missionReport.MissionStatus !== "GS_SUCCESS") {
|
||||
console.log(`Mission failed: ${missionReport.RewardInfo?.node}`);
|
||||
//todo: return expected response for failed mission
|
||||
res.json([]);
|
||||
//duvirisadjob does not provide missionStatus
|
||||
}
|
||||
|
||||
const inventory = await getInventory(accountId);
|
||||
|
||||
const missionRewardsResults = await addMissionRewards(inventory, missionReport);
|
||||
|
||||
if (!missionRewardsResults) {
|
||||
console.error("Failed to add mission rewards");
|
||||
res.status(500).json({ error: "Failed to add mission rewards" });
|
||||
return;
|
||||
}
|
||||
|
||||
const { MissionRewards, inventoryChanges, missionCompletionCredits } = missionRewardsResults;
|
||||
|
||||
const inventoryUpdates = addMissionInventoryUpdates(inventory, missionReport);
|
||||
|
||||
//todo ? can go after not awaiting
|
||||
//creditBonus is not correct for mirage mission 3
|
||||
const credits = calculateFinalCredits(inventory, {
|
||||
missionCompletionCredits,
|
||||
missionDropCredits: missionReport.RegularCredits ?? 0,
|
||||
rngRewardCredits: inventoryChanges.RegularCredits as number
|
||||
});
|
||||
|
||||
const InventoryJson = JSON.stringify((await inventory.save()).toJSON());
|
||||
|
||||
//TODO: figure out when to send inventory. it is needed for many cases.
|
||||
res.json({
|
||||
InventoryJson,
|
||||
InventoryChanges: inventoryChanges,
|
||||
MissionRewards,
|
||||
...credits,
|
||||
...inventoryUpdates,
|
||||
FusionPoints: inventoryChanges.FusionPoints
|
||||
});
|
||||
};
|
||||
|
||||
/*
|
||||
@ -85,5 +106,3 @@ const missionInventoryUpdateController: RequestHandler = async (req, res): Promi
|
||||
- [x] InventoryChanges
|
||||
- [x] FusionPoints
|
||||
*/
|
||||
|
||||
export { missionInventoryUpdateController };
|
||||
|
12
src/controllers/api/unlockShipFeatureController.ts
Normal file
12
src/controllers/api/unlockShipFeatureController.ts
Normal file
@ -0,0 +1,12 @@
|
||||
import { RequestHandler } from "express";
|
||||
import { updateShipFeature } from "@/src/services/personalRoomsService";
|
||||
import { IUnlockShipFeatureRequest } from "@/src/types/requestTypes";
|
||||
import { parseString } from "@/src/helpers/general";
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
||||
export const unlockShipFeatureController: RequestHandler = async (req, res) => {
|
||||
const accountId = parseString(req.query.accountId);
|
||||
const shipFeatureRequest = JSON.parse((req.body as string).toString()) as IUnlockShipFeatureRequest;
|
||||
await updateShipFeature(accountId, shipFeatureRequest.Feature);
|
||||
res.send([]);
|
||||
};
|
41
src/controllers/api/updateQuestController.ts
Normal file
41
src/controllers/api/updateQuestController.ts
Normal file
@ -0,0 +1,41 @@
|
||||
import { RequestHandler } from "express";
|
||||
import { parseString } from "@/src/helpers/general";
|
||||
import { logger } from "@/src/utils/logger";
|
||||
import { getJSONfromString } from "@/src/helpers/stringHelpers";
|
||||
import { updateQuestKey, IUpdateQuestRequest } from "@/src/services/questService";
|
||||
import { getQuestCompletionItems } from "@/src/services/itemDataService";
|
||||
import { addItem, combineInventoryChanges, getInventory } from "@/src/services/inventoryService";
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
||||
export const updateQuestController: RequestHandler = async (req, res) => {
|
||||
const accountId = parseString(req.query.accountId);
|
||||
const updateQuestRequest = getJSONfromString((req.body as string).toString()) as IUpdateQuestRequest;
|
||||
|
||||
// updates should be made only to one quest key per request
|
||||
if (updateQuestRequest.QuestKeys.length > 1) {
|
||||
throw new Error(`quest keys array should only have 1 item, but has ${updateQuestRequest.QuestKeys.length}`);
|
||||
}
|
||||
|
||||
const inventory = await getInventory(accountId);
|
||||
|
||||
updateQuestKey(inventory, updateQuestRequest.QuestKeys);
|
||||
|
||||
if (updateQuestRequest.QuestKeys[0].Completed) {
|
||||
logger.debug(`completed quest ${updateQuestRequest.QuestKeys[0].ItemType} `);
|
||||
const questKeyName = updateQuestRequest.QuestKeys[0].ItemType;
|
||||
const questCompletionItems = getQuestCompletionItems(questKeyName);
|
||||
|
||||
logger.debug(`quest completion items { ${questCompletionItems.map(item => item.ItemType).join(", ")} }`);
|
||||
|
||||
const inventoryChanges = {};
|
||||
for (const item of questCompletionItems) {
|
||||
const inventoryDelta = await addItem(inventory, item.ItemType, item.ItemCount);
|
||||
combineInventoryChanges(inventoryChanges, inventoryDelta.InventoryChanges);
|
||||
}
|
||||
res.json({ MissionRewards: [], inventoryChanges });
|
||||
return;
|
||||
}
|
||||
|
||||
await inventory.save();
|
||||
res.send({ MissionRewards: [] });
|
||||
};
|
@ -26,9 +26,9 @@ import {
|
||||
IInfestedFoundryDatabase,
|
||||
IHelminthResource,
|
||||
IConsumedSuit,
|
||||
IQuestProgress,
|
||||
IQuestStage,
|
||||
IQuestKeyDatabase,
|
||||
IQuestKeyResponse,
|
||||
IQuestKeyClient,
|
||||
IFusionTreasure,
|
||||
ISpectreLoadout,
|
||||
IWeaponSkinDatabase,
|
||||
@ -518,7 +518,7 @@ infestedFoundrySchema.set("toJSON", {
|
||||
}
|
||||
});
|
||||
|
||||
const questProgressSchema = new Schema<IQuestProgress>({
|
||||
const questProgressSchema = new Schema<IQuestStage>({
|
||||
c: Number,
|
||||
i: Boolean,
|
||||
m: Boolean,
|
||||
@ -527,7 +527,7 @@ const questProgressSchema = new Schema<IQuestProgress>({
|
||||
|
||||
const questKeysSchema = new Schema<IQuestKeyDatabase>(
|
||||
{
|
||||
Progress: [questProgressSchema],
|
||||
Progress: { type: [questProgressSchema], default: undefined },
|
||||
unlock: Boolean,
|
||||
Completed: Boolean,
|
||||
//CustomData: Schema.Types.Mixed,
|
||||
@ -544,7 +544,7 @@ questKeysSchema.set("toJSON", {
|
||||
const questKeysDatabase = ret as IQuestKeyDatabase;
|
||||
|
||||
if (questKeysDatabase.CompletionDate) {
|
||||
(questKeysDatabase as IQuestKeyResponse).CompletionDate = toMongoDate(questKeysDatabase.CompletionDate);
|
||||
(questKeysDatabase as IQuestKeyClient).CompletionDate = toMongoDate(questKeysDatabase.CompletionDate);
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -941,6 +941,7 @@ const inventorySchema = new Schema<IInventoryDatabase, InventoryDocumentProps>(
|
||||
//Complete Mission\Quests
|
||||
Missions: [Schema.Types.Mixed],
|
||||
QuestKeys: [questKeysSchema],
|
||||
ActiveQuest: { type: String, default: "/Lotus/Types/Keys/VorsPrize/VorsPrizeQuestKeyChain" }, //TODO: check after mission starting gear
|
||||
//item like DojoKey or Boss missions key
|
||||
LevelKeys: [Schema.Types.Mixed],
|
||||
//Active quests
|
||||
@ -1164,7 +1165,7 @@ inventorySchema.set("toJSON", {
|
||||
});
|
||||
|
||||
// type overwrites for subdocuments/subdocument arrays
|
||||
type InventoryDocumentProps = {
|
||||
export type InventoryDocumentProps = {
|
||||
Suits: Types.DocumentArray<IEquipmentDatabase>;
|
||||
LongGuns: Types.DocumentArray<IEquipmentDatabase>;
|
||||
Pistols: Types.DocumentArray<IEquipmentDatabase>;
|
||||
|
@ -70,7 +70,7 @@ const apartmentSchema = new Schema<IApartment>(
|
||||
{
|
||||
Rooms: [roomSchema],
|
||||
FavouriteLoadouts: [Schema.Types.Mixed],
|
||||
Gardening: gardeningSchema
|
||||
Gardening: gardeningSchema // TODO: ensure this is correct
|
||||
},
|
||||
{ _id: false }
|
||||
);
|
||||
@ -96,7 +96,7 @@ const orbiterSchema = new Schema<IOrbiter>(
|
||||
{ _id: false }
|
||||
);
|
||||
const orbiterDefault: IOrbiter = {
|
||||
Features: [],
|
||||
Features: ["/Lotus/Types/Items/ShipFeatureItems/EarthNavigationFeatureItem"], //TODO: potentially remove after missionstarting gear
|
||||
Rooms: [
|
||||
{ Name: "AlchemyRoom", MaxCapacity: 1600 },
|
||||
{ Name: "BridgeRoom", MaxCapacity: 1600 },
|
||||
|
@ -78,6 +78,9 @@ import { updateChallengeProgressController } from "@/src/controllers/api/updateC
|
||||
import { updateSessionGetController, updateSessionPostController } from "@/src/controllers/api/updateSessionController";
|
||||
import { updateThemeController } from "../controllers/api/updateThemeController";
|
||||
import { upgradesController } from "@/src/controllers/api/upgradesController";
|
||||
import { updateQuestController } from "@/src/controllers/api/updateQuestController";
|
||||
import { giveKeyChainTriggeredItemsController } from "@/src/controllers/api/giveKeyChainTriggeredItemsController";
|
||||
import { unlockShipFeatureController } from "@/src/controllers/api/unlockShipFeatureController";
|
||||
|
||||
const apiRouter = express.Router();
|
||||
|
||||
@ -132,6 +135,7 @@ apiRouter.post("/genericUpdate.php", genericUpdateController);
|
||||
apiRouter.post("/getAlliance.php", getAllianceController);
|
||||
apiRouter.post("/getVoidProjectionRewards.php", getVoidProjectionRewardsController);
|
||||
apiRouter.post("/gildWeapon.php", gildWeaponController);
|
||||
apiRouter.post("/giveKeyChainTriggeredItems.php", giveKeyChainTriggeredItemsController);
|
||||
apiRouter.post("/guildTech.php", guildTechController);
|
||||
apiRouter.post("/hostSession.php", hostSessionController);
|
||||
apiRouter.post("/infestedFoundry.php", infestedFoundryController);
|
||||
@ -161,10 +165,12 @@ apiRouter.post("/syndicateSacrifice.php", syndicateSacrificeController);
|
||||
apiRouter.post("/syndicateStandingBonus.php", syndicateStandingBonusController);
|
||||
apiRouter.post("/tauntHistory.php", tauntHistoryController);
|
||||
apiRouter.post("/trainingResult.php", trainingResultController);
|
||||
apiRouter.post("/unlockShipFeature.php", unlockShipFeatureController);
|
||||
apiRouter.post("/updateChallengeProgress.php", updateChallengeProgressController);
|
||||
apiRouter.post("/updateNodeIntros.php", genericUpdateController);
|
||||
apiRouter.post("/updateSession.php", updateSessionPostController);
|
||||
apiRouter.post("/updateTheme.php", updateThemeController);
|
||||
apiRouter.post("/updateQuest.php", updateQuestController);
|
||||
apiRouter.post("/upgrades.php", upgradesController);
|
||||
|
||||
export { apiRouter };
|
||||
|
@ -1,6 +1,10 @@
|
||||
import { Inventory, TInventoryDatabaseDocument } from "@/src/models/inventoryModels/inventoryModel";
|
||||
import {
|
||||
Inventory,
|
||||
InventoryDocumentProps,
|
||||
TInventoryDatabaseDocument
|
||||
} from "@/src/models/inventoryModels/inventoryModel";
|
||||
import { config } from "@/src/services/configService";
|
||||
import { Types } from "mongoose";
|
||||
import { HydratedDocument, Types } from "mongoose";
|
||||
import { SlotNames, IInventoryChanges, IBinChanges, ICurrencyChanges } from "@/src/types/purchaseTypes";
|
||||
import {
|
||||
IChallengeProgress,
|
||||
@ -14,9 +18,9 @@ import {
|
||||
InventorySlot,
|
||||
IWeaponSkinClient,
|
||||
TEquipmentKey,
|
||||
equipmentKeys,
|
||||
IFusionTreasure,
|
||||
IDailyAffiliations
|
||||
IDailyAffiliations,
|
||||
IInventoryDatabase
|
||||
} from "@/src/types/inventoryTypes/inventoryTypes";
|
||||
import { IGenericUpdate } from "../types/genericUpdate";
|
||||
import {
|
||||
@ -25,7 +29,7 @@ import {
|
||||
IUpdateChallengeProgressRequest
|
||||
} from "../types/requestTypes";
|
||||
import { logger } from "@/src/utils/logger";
|
||||
import { getWeaponType, getExalted } from "@/src/services/itemDataService";
|
||||
import { getWeaponType, getExalted, getKeyChainItems } from "@/src/services/itemDataService";
|
||||
import { IEquipmentClient, IItemConfig } from "../types/inventoryTypes/commonInventoryTypes";
|
||||
import {
|
||||
ExportArcanes,
|
||||
@ -39,6 +43,8 @@ import {
|
||||
TStandingLimitBin
|
||||
} from "warframe-public-export-plus";
|
||||
import { createShip } from "./shipService";
|
||||
import { creditBundles, fusionBundles } from "@/src/services/missionInventoryUpdateService";
|
||||
import { IGiveKeyChainTriggeredItemsRequest } from "@/src/controllers/api/giveKeyChainTriggeredItemsController";
|
||||
|
||||
export const createInventory = async (
|
||||
accountOwnerId: Types.ObjectId,
|
||||
@ -64,31 +70,32 @@ export const createInventory = async (
|
||||
{ ItemCount: 1, ItemType: "/Lotus/Types/StoreItems/AvatarImages/AvatarImageItem2" },
|
||||
{ ItemCount: 1, ItemType: "/Lotus/Types/StoreItems/AvatarImages/AvatarImageItem3" },
|
||||
{ ItemCount: 1, ItemType: "/Lotus/Types/StoreItems/AvatarImages/AvatarImageItem4" },
|
||||
{ ItemCount: 1, ItemType: "/Lotus/Types/Restoratives/LisetAutoHack" },
|
||||
|
||||
// Vor's Prize rewards
|
||||
{ ItemCount: 1, ItemType: "/Lotus/Upgrades/Mods/Warframe/AvatarHealthMaxMod" },
|
||||
{ ItemCount: 1, ItemType: "/Lotus/Upgrades/Mods/Warframe/AvatarShieldMaxMod" },
|
||||
{ ItemCount: 1, ItemType: "/Lotus/Upgrades/Mods/Warframe/AvatarAbilityRangeMod" },
|
||||
{ ItemCount: 1, ItemType: "/Lotus/Upgrades/Mods/Warframe/AvatarAbilityStrengthMod" },
|
||||
{ ItemCount: 1, ItemType: "/Lotus/Upgrades/Mods/Warframe/AvatarAbilityDurationMod" },
|
||||
{ ItemCount: 1, ItemType: "/Lotus/Upgrades/Mods/Warframe/AvatarPickupBonusMod" },
|
||||
{ ItemCount: 1, ItemType: "/Lotus/Upgrades/Mods/Warframe/AvatarPowerMaxMod" },
|
||||
{ ItemCount: 1, ItemType: "/Lotus/Upgrades/Mods/Warframe/AvatarEnemyRadarMod" },
|
||||
{ ItemCount: 1, ItemType: "/Lotus/Upgrades/Mods/Melee/WeaponFireRateMod" },
|
||||
{ ItemCount: 1, ItemType: "/Lotus/Upgrades/Mods/Melee/WeaponMeleeDamageMod" },
|
||||
{ ItemCount: 1, ItemType: "/Lotus/Upgrades/Mods/Rifle/WeaponFactionDamageCorpus" },
|
||||
{ ItemCount: 1, ItemType: "/Lotus/Upgrades/Mods/Rifle/WeaponFactionDamageGrineer" },
|
||||
{ ItemCount: 1, ItemType: "/Lotus/Upgrades/Mods/Rifle/WeaponDamageAmountMod" },
|
||||
{ ItemCount: 1, ItemType: "/Lotus/Upgrades/Mods/Pistol/WeaponFireDamageMod" },
|
||||
{ ItemCount: 1, ItemType: "/Lotus/Upgrades/Mods/Pistol/WeaponElectricityDamageMod" },
|
||||
{ ItemCount: 1, ItemType: "/Lotus/Upgrades/Mods/Pistol/WeaponDamageAmountMod" },
|
||||
{ ItemCount: 1, ItemType: "/Lotus/Types/Recipes/Weapons/BurstonRifleBlueprint" },
|
||||
{ ItemCount: 1, ItemType: "/Lotus/Types/Items/MiscItems/Morphic" },
|
||||
{ ItemCount: 400, ItemType: "/Lotus/Types/Items/MiscItems/PolymerBundle" },
|
||||
{ ItemCount: 150, ItemType: "/Lotus/Types/Items/MiscItems/AlloyPlate" }
|
||||
{ ItemCount: 1, ItemType: "/Lotus/Types/Restoratives/LisetAutoHack" }
|
||||
];
|
||||
|
||||
// const vorsPrizeRewards = [
|
||||
// // Vor's Prize rewards
|
||||
// { ItemCount: 1, ItemType: "/Lotus/Upgrades/Mods/Warframe/AvatarHealthMaxMod" },
|
||||
// { ItemCount: 1, ItemType: "/Lotus/Upgrades/Mods/Warframe/AvatarShieldMaxMod" },
|
||||
// { ItemCount: 1, ItemType: "/Lotus/Upgrades/Mods/Warframe/AvatarAbilityRangeMod" },
|
||||
// { ItemCount: 1, ItemType: "/Lotus/Upgrades/Mods/Warframe/AvatarAbilityStrengthMod" },
|
||||
// { ItemCount: 1, ItemType: "/Lotus/Upgrades/Mods/Warframe/AvatarAbilityDurationMod" },
|
||||
// { ItemCount: 1, ItemType: "/Lotus/Upgrades/Mods/Warframe/AvatarPickupBonusMod" },
|
||||
// { ItemCount: 1, ItemType: "/Lotus/Upgrades/Mods/Warframe/AvatarPowerMaxMod" },
|
||||
// { ItemCount: 1, ItemType: "/Lotus/Upgrades/Mods/Warframe/AvatarEnemyRadarMod" },
|
||||
// { ItemCount: 1, ItemType: "/Lotus/Upgrades/Mods/Melee/WeaponFireRateMod" },
|
||||
// { ItemCount: 1, ItemType: "/Lotus/Upgrades/Mods/Melee/WeaponMeleeDamageMod" },
|
||||
// { ItemCount: 1, ItemType: "/Lotus/Upgrades/Mods/Rifle/WeaponFactionDamageCorpus" },
|
||||
// { ItemCount: 1, ItemType: "/Lotus/Upgrades/Mods/Rifle/WeaponFactionDamageGrineer" },
|
||||
// { ItemCount: 1, ItemType: "/Lotus/Upgrades/Mods/Rifle/WeaponDamageAmountMod" },
|
||||
// { ItemCount: 1, ItemType: "/Lotus/Upgrades/Mods/Pistol/WeaponFireDamageMod" },
|
||||
// { ItemCount: 1, ItemType: "/Lotus/Upgrades/Mods/Pistol/WeaponElectricityDamageMod" },
|
||||
// { ItemCount: 1, ItemType: "/Lotus/Upgrades/Mods/Pistol/WeaponDamageAmountMod" },
|
||||
// { ItemCount: 1, ItemType: "/Lotus/Types/Recipes/Weapons/BurstonRifleBlueprint" },
|
||||
// { ItemCount: 1, ItemType: "/Lotus/Types/Items/MiscItems/Morphic" },
|
||||
// { ItemCount: 400, ItemType: "/Lotus/Types/Items/MiscItems/PolymerBundle" },
|
||||
// { ItemCount: 150, ItemType: "/Lotus/Types/Items/MiscItems/AlloyPlate" }
|
||||
// ];
|
||||
for (const equipment of defaultEquipment) {
|
||||
await addItem(inventory, equipment.ItemType, equipment.ItemCount);
|
||||
}
|
||||
@ -109,7 +116,6 @@ export const createInventory = async (
|
||||
});
|
||||
|
||||
inventory.QuestKeys.push({
|
||||
Completed: true,
|
||||
ItemType: "/Lotus/Types/Keys/VorsPrize/VorsPrizeQuestKeyChain"
|
||||
});
|
||||
|
||||
@ -132,13 +138,19 @@ export const createInventory = async (
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Combines two inventory changes objects into one.
|
||||
*
|
||||
* @param InventoryChanges - will hold the combined changes
|
||||
* @param delta - inventory changes to be added
|
||||
*/
|
||||
export const combineInventoryChanges = (InventoryChanges: IInventoryChanges, delta: IInventoryChanges): void => {
|
||||
for (const key in delta) {
|
||||
if (!(key in InventoryChanges)) {
|
||||
InventoryChanges[key] = delta[key];
|
||||
} else if (Array.isArray(delta[key])) {
|
||||
const left = InventoryChanges[key] as object[];
|
||||
const right: object[] = delta[key];
|
||||
const right: object[] | string[] = delta[key];
|
||||
for (const item of right) {
|
||||
left.push(item);
|
||||
}
|
||||
@ -154,8 +166,10 @@ export const combineInventoryChanges = (InventoryChanges: IInventoryChanges, del
|
||||
left.Extra ??= 0;
|
||||
left.Extra += right.Extra;
|
||||
}
|
||||
} else if (typeof delta[key] === "number") {
|
||||
(InventoryChanges[key] as number) += delta[key];
|
||||
} else {
|
||||
logger.warn(`inventory change not merged: ${key}`);
|
||||
throw new Error(`inventory change not merged: unhandled type for inventory key ${key}`);
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -274,6 +288,24 @@ export const addItem = async (
|
||||
}
|
||||
};
|
||||
}
|
||||
if (typeName in creditBundles) {
|
||||
const creditsTotal = creditBundles[typeName] * quantity;
|
||||
inventory.RegularCredits += creditsTotal;
|
||||
return {
|
||||
InventoryChanges: {
|
||||
RegularCredits: creditsTotal
|
||||
}
|
||||
};
|
||||
}
|
||||
if (typeName in fusionBundles) {
|
||||
const fusionPointsTotal = fusionBundles[typeName] * quantity;
|
||||
inventory.FusionPoints += fusionPointsTotal;
|
||||
return {
|
||||
InventoryChanges: {
|
||||
FusionPoints: fusionPointsTotal
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Path-based duck typing
|
||||
switch (typeName.substr(1).split("/")[1]) {
|
||||
@ -364,7 +396,7 @@ export const addItem = async (
|
||||
}
|
||||
}
|
||||
}
|
||||
case "Game":
|
||||
case "Game": {
|
||||
if (typeName.substr(1).split("/")[3] == "Projections") {
|
||||
// Void Relics, e.g. /Lotus/Types/Game/Projections/T2VoidProjectionGaussPrimeDBronze
|
||||
const miscItemChanges = [
|
||||
@ -382,6 +414,40 @@ export const addItem = async (
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "Keys": {
|
||||
inventory.QuestKeys.push({ ItemType: typeName });
|
||||
return {
|
||||
InventoryChanges: {
|
||||
QuestKeys: [
|
||||
{
|
||||
ItemType: typeName
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
}
|
||||
case "NeutralCreatures": {
|
||||
const horseIndex = inventory.Horses.push({ ItemType: typeName });
|
||||
return {
|
||||
InventoryChanges: {
|
||||
Horses: inventory.Horses[horseIndex - 1].toJSON()
|
||||
}
|
||||
};
|
||||
}
|
||||
case "Recipes": {
|
||||
inventory.MiscItems.push({ ItemType: typeName, ItemCount: quantity });
|
||||
return {
|
||||
InventoryChanges: {
|
||||
MiscItems: [
|
||||
{
|
||||
ItemType: typeName,
|
||||
ItemCount: quantity
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
const errorMessage = `unable to add item: ${typeName}`;
|
||||
@ -684,7 +750,8 @@ const addCrewShip = (
|
||||
return inventoryChanges;
|
||||
};
|
||||
|
||||
const addGearExpByCategory = (
|
||||
//TODO: wrong id is not erroring
|
||||
export const addGearExpByCategory = (
|
||||
inventory: TInventoryDatabaseDocument,
|
||||
gearArray: IEquipmentClient[] | undefined,
|
||||
categoryName: TEquipmentKey
|
||||
@ -870,7 +937,7 @@ export const addChallenges = (
|
||||
});
|
||||
};
|
||||
|
||||
const addMissionComplete = (inventory: TInventoryDatabaseDocument, { Tag, Completes }: IMission): void => {
|
||||
export const addMissionComplete = (inventory: TInventoryDatabaseDocument, { Tag, Completes }: IMission): void => {
|
||||
const { Missions } = inventory;
|
||||
const itemIndex = Missions.findIndex(item => item.Tag === Tag);
|
||||
|
||||
@ -882,83 +949,6 @@ const addMissionComplete = (inventory: TInventoryDatabaseDocument, { Tag, Comple
|
||||
}
|
||||
};
|
||||
|
||||
export const missionInventoryUpdate = async (data: IMissionInventoryUpdateRequest, accountId: string) => {
|
||||
const {
|
||||
RawUpgrades,
|
||||
MiscItems,
|
||||
RegularCredits,
|
||||
ChallengeProgress,
|
||||
FusionPoints,
|
||||
Consumables,
|
||||
Recipes,
|
||||
Missions,
|
||||
FusionTreasures
|
||||
} = data;
|
||||
const inventory = await getInventory(accountId);
|
||||
|
||||
// credits
|
||||
inventory.RegularCredits += RegularCredits || 0;
|
||||
|
||||
// endo
|
||||
inventory.FusionPoints += FusionPoints || 0;
|
||||
|
||||
// syndicate
|
||||
data.AffiliationChanges?.forEach(affiliation => {
|
||||
const syndicate = inventory.Affiliations.find(x => x.Tag == affiliation.Tag);
|
||||
if (syndicate !== undefined) {
|
||||
syndicate.Standing =
|
||||
syndicate.Standing === undefined ? affiliation.Standing : syndicate.Standing + affiliation.Standing;
|
||||
syndicate.Title = syndicate.Title === undefined ? affiliation.Title : syndicate.Title + affiliation.Title;
|
||||
} else {
|
||||
inventory.Affiliations.push({
|
||||
Standing: affiliation.Standing,
|
||||
Title: affiliation.Title,
|
||||
Tag: affiliation.Tag,
|
||||
FreeFavorsEarned: [],
|
||||
FreeFavorsUsed: []
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Gear XP
|
||||
equipmentKeys.forEach(key => addGearExpByCategory(inventory, data[key], key));
|
||||
|
||||
// Incarnon Challenges
|
||||
if (data.EvolutionProgress) {
|
||||
for (const evoProgress of data.EvolutionProgress) {
|
||||
const entry = inventory.EvolutionProgress
|
||||
? inventory.EvolutionProgress.find(entry => entry.ItemType == evoProgress.ItemType)
|
||||
: undefined;
|
||||
if (entry) {
|
||||
entry.Progress = evoProgress.Progress;
|
||||
entry.Rank = evoProgress.Rank;
|
||||
} else {
|
||||
inventory.EvolutionProgress ??= [];
|
||||
inventory.EvolutionProgress.push(evoProgress);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// LastRegionPlayed
|
||||
if (data.LastRegionPlayed) {
|
||||
inventory.LastRegionPlayed = data.LastRegionPlayed;
|
||||
}
|
||||
|
||||
// other
|
||||
addMods(inventory, RawUpgrades);
|
||||
addMiscItems(inventory, MiscItems);
|
||||
addConsumables(inventory, Consumables);
|
||||
addRecipes(inventory, Recipes);
|
||||
addChallenges(inventory, ChallengeProgress);
|
||||
addFusionTreasures(inventory, FusionTreasures);
|
||||
if (Missions) {
|
||||
addMissionComplete(inventory, Missions);
|
||||
}
|
||||
|
||||
const changedInventory = await inventory.save();
|
||||
return changedInventory.toJSON();
|
||||
};
|
||||
|
||||
export const addBooster = async (ItemType: string, time: number, accountId: string): Promise<void> => {
|
||||
const currentTime = Math.floor(Date.now() / 1000) - 129600; // Value is wrong without 129600. Figure out why, please. :)
|
||||
|
||||
@ -977,3 +967,52 @@ export const addBooster = async (ItemType: string, time: number, accountId: stri
|
||||
|
||||
await inventory.save();
|
||||
};
|
||||
|
||||
export const updateSyndicate = (
|
||||
inventory: HydratedDocument<IInventoryDatabase, InventoryDocumentProps>,
|
||||
syndicateUpdate: IMissionInventoryUpdateRequest["AffiliationChanges"]
|
||||
) => {
|
||||
syndicateUpdate?.forEach(affiliation => {
|
||||
const syndicate = inventory.Affiliations.find(x => x.Tag == affiliation.Tag);
|
||||
if (syndicate !== undefined) {
|
||||
syndicate.Standing =
|
||||
syndicate.Standing === undefined ? affiliation.Standing : syndicate.Standing + affiliation.Standing;
|
||||
syndicate.Title = syndicate.Title === undefined ? affiliation.Title : syndicate.Title + affiliation.Title;
|
||||
} else {
|
||||
inventory.Affiliations.push({
|
||||
Standing: affiliation.Standing,
|
||||
Title: affiliation.Title,
|
||||
Tag: affiliation.Tag,
|
||||
FreeFavorsEarned: [],
|
||||
FreeFavorsUsed: []
|
||||
});
|
||||
}
|
||||
});
|
||||
return { AffiliationMods: [] };
|
||||
};
|
||||
|
||||
/**
|
||||
* @returns object with inventory keys of changes or empty object when no items were added
|
||||
*/
|
||||
export const addKeyChainItems = async (
|
||||
inventory: TInventoryDatabaseDocument,
|
||||
keyChainData: IGiveKeyChainTriggeredItemsRequest
|
||||
): Promise<IInventoryChanges> => {
|
||||
const keyChainItems = getKeyChainItems(keyChainData);
|
||||
|
||||
logger.debug(
|
||||
`adding key chain items ${keyChainItems.join()} for ${keyChainData.KeyChain} at stage ${keyChainData.ChainStage}`
|
||||
);
|
||||
|
||||
const nonStoreItems = keyChainItems.map(item => item.replace("StoreItems/", ""));
|
||||
|
||||
//TODO: inventoryChanges is not typed correctly
|
||||
const inventoryChanges = {};
|
||||
|
||||
for (const item of nonStoreItems) {
|
||||
const inventoryChangesDelta = await addItem(inventory, item);
|
||||
combineInventoryChanges(inventoryChanges, inventoryChangesDelta.InventoryChanges);
|
||||
}
|
||||
|
||||
return inventoryChanges;
|
||||
};
|
||||
|
@ -1,4 +1,7 @@
|
||||
import { IGiveKeyChainTriggeredItemsRequest } from "@/src/controllers/api/giveKeyChainTriggeredItemsController";
|
||||
import { getIndexAfter } from "@/src/helpers/stringHelpers";
|
||||
import { ITypeCount } from "@/src/types/inventoryTypes/inventoryTypes";
|
||||
import { logger } from "@/src/utils/logger";
|
||||
import {
|
||||
dict_de,
|
||||
dict_en,
|
||||
@ -20,13 +23,16 @@ import {
|
||||
ExportGear,
|
||||
ExportKeys,
|
||||
ExportRecipes,
|
||||
ExportRegions,
|
||||
ExportResources,
|
||||
ExportSentinels,
|
||||
ExportWarframes,
|
||||
ExportWeapons,
|
||||
IPowersuit,
|
||||
IRecipe
|
||||
IRecipe,
|
||||
IRegion
|
||||
} from "warframe-public-export-plus";
|
||||
import questCompletionItems from "@/static/fixed_responses/questCompletionRewards.json";
|
||||
|
||||
export type WeaponTypeInternal =
|
||||
| "LongGuns"
|
||||
@ -150,3 +156,56 @@ export const getDict = (lang: string): Record<string, string> => {
|
||||
export const getString = (key: string, dict: Record<string, string>): string => {
|
||||
return dict[key] ?? key;
|
||||
};
|
||||
|
||||
export const getKeyChainItems = ({ KeyChain, ChainStage }: IGiveKeyChainTriggeredItemsRequest): string[] => {
|
||||
const chainStages = ExportKeys[KeyChain].chainStages;
|
||||
if (!chainStages) {
|
||||
throw new Error(`KeyChain ${KeyChain} does not contain chain stages`);
|
||||
}
|
||||
|
||||
const keyChainStage = chainStages[ChainStage];
|
||||
if (!keyChainStage) {
|
||||
throw new Error(`KeyChainStage ${ChainStage} not found`);
|
||||
}
|
||||
|
||||
if (keyChainStage.itemsToGiveWhenTriggered.length === 0) {
|
||||
throw new Error(`No items to give for KeyChain ${KeyChain} at stage ${ChainStage}`);
|
||||
}
|
||||
|
||||
return keyChainStage.itemsToGiveWhenTriggered;
|
||||
};
|
||||
|
||||
export const getLevelKeyRewards = (levelKey: string) => {
|
||||
const levelKeyData = ExportKeys[levelKey];
|
||||
if (!levelKeyData) {
|
||||
const error = `LevelKey ${levelKey} not found`;
|
||||
logger.error(error);
|
||||
throw new Error(error);
|
||||
}
|
||||
|
||||
if (!levelKeyData.rewards) {
|
||||
const error = `LevelKey ${levelKey} does not contain rewards`;
|
||||
logger.error(error);
|
||||
throw new Error(error);
|
||||
}
|
||||
|
||||
return levelKeyData.rewards;
|
||||
};
|
||||
|
||||
export const getNode = (nodeName: string): IRegion => {
|
||||
const node = ExportRegions[nodeName];
|
||||
if (!node) {
|
||||
throw new Error(`Node ${nodeName} not found`);
|
||||
}
|
||||
|
||||
return node;
|
||||
};
|
||||
|
||||
export const getQuestCompletionItems = (questKey: string) => {
|
||||
const items = (questCompletionItems as unknown as Record<string, ITypeCount[] | undefined>)[questKey];
|
||||
if (!items) {
|
||||
throw new Error(`Quest ${questKey} not found in questCompletionItems`);
|
||||
}
|
||||
|
||||
return items;
|
||||
};
|
||||
|
@ -45,16 +45,16 @@ export const createPersonalRooms = async (accountId: Types.ObjectId, shipId: Typ
|
||||
activeShipId: shipId
|
||||
});
|
||||
if (config.skipTutorial) {
|
||||
// Vor's Prize rewards
|
||||
const defaultFeatures = [
|
||||
"/Lotus/Types/Items/ShipFeatureItems/EarthNavigationFeatureItem",
|
||||
"/Lotus/Types/Items/ShipFeatureItems/MercuryNavigationFeatureItem",
|
||||
"/Lotus/Types/Items/ShipFeatureItems/ArsenalFeatureItem",
|
||||
"/Lotus/Types/Items/ShipFeatureItems/SocialMenuFeatureItem",
|
||||
"/Lotus/Types/Items/ShipFeatureItems/FoundryFeatureItem",
|
||||
"/Lotus/Types/Items/ShipFeatureItems/ModsFeatureItem"
|
||||
];
|
||||
personalRooms.Ship.Features.push(...defaultFeatures);
|
||||
// // Vor's Prize rewards
|
||||
// const defaultFeatures = [
|
||||
// "/Lotus/Types/Items/ShipFeatureItems/EarthNavigationFeatureItem",
|
||||
// "/Lotus/Types/Items/ShipFeatureItems/MercuryNavigationFeatureItem",
|
||||
// "/Lotus/Types/Items/ShipFeatureItems/ArsenalFeatureItem",
|
||||
// "/Lotus/Types/Items/ShipFeatureItems/SocialMenuFeatureItem",
|
||||
// "/Lotus/Types/Items/ShipFeatureItems/FoundryFeatureItem",
|
||||
// "/Lotus/Types/Items/ShipFeatureItems/ModsFeatureItem"
|
||||
// ];
|
||||
// personalRooms.Ship.Features.push(...defaultFeatures);
|
||||
}
|
||||
await personalRooms.save();
|
||||
};
|
||||
|
@ -1,30 +1,280 @@
|
||||
import { IMissionRewardResponse, IInventoryFieldType, inventoryFields } from "@/src/types/missionTypes";
|
||||
|
||||
import {
|
||||
ExportRegions,
|
||||
ExportRewards,
|
||||
ExportUpgrades,
|
||||
ExportGear,
|
||||
ExportRecipes,
|
||||
ExportRelics,
|
||||
ExportResources,
|
||||
IReward
|
||||
} from "warframe-public-export-plus";
|
||||
import { IMissionInventoryUpdateRequest } from "../types/requestTypes";
|
||||
import { ExportRegions, ExportRewards, IReward } from "warframe-public-export-plus";
|
||||
import { IMissionInventoryUpdateRequest, IRewardInfo } from "../types/requestTypes";
|
||||
import { logger } from "@/src/utils/logger";
|
||||
import { IRngResult, getRandomReward } from "@/src/services/rngService";
|
||||
import { IInventoryDatabase } from "@/src/types/inventoryTypes/inventoryTypes";
|
||||
import {
|
||||
addChallenges,
|
||||
addConsumables,
|
||||
addFusionTreasures,
|
||||
addGearExpByCategory,
|
||||
addItem,
|
||||
addMiscItems,
|
||||
addMissionComplete,
|
||||
addMods,
|
||||
addRecipes,
|
||||
combineInventoryChanges,
|
||||
updateSyndicate
|
||||
} from "@/src/services/inventoryService";
|
||||
import { updateQuestKey } from "@/src/services/questService";
|
||||
import { HydratedDocument } from "mongoose";
|
||||
import { IInventoryChanges } from "@/src/types/purchaseTypes";
|
||||
import { getLevelKeyRewards, getNode } from "@/src/services/itemDataService";
|
||||
import { InventoryDocumentProps, TInventoryDatabaseDocument } from "@/src/models/inventoryModels/inventoryModel";
|
||||
|
||||
// need reverse engineer rewardSeed, otherwise ingame displayed rotation reward will be different than added to db or displayed on mission end
|
||||
const getRewards = ({
|
||||
RewardInfo
|
||||
}: IMissionInventoryUpdateRequest): {
|
||||
InventoryChanges: IMissionInventoryUpdateRequest;
|
||||
MissionRewards: IMissionRewardResponse[];
|
||||
} => {
|
||||
if (!RewardInfo) {
|
||||
return { InventoryChanges: {}, MissionRewards: [] };
|
||||
const getRotations = (rotationCount: number): number[] => {
|
||||
if (rotationCount === 0) return [0];
|
||||
|
||||
const rotationPattern = [0, 0, 1, 2]; // A, A, B, C
|
||||
const rotatedValues = [];
|
||||
|
||||
for (let i = 0; i < rotationCount; i++) {
|
||||
rotatedValues.push(rotationPattern[i % rotationPattern.length]);
|
||||
}
|
||||
|
||||
return rotatedValues;
|
||||
};
|
||||
|
||||
const getRandomRewardByChance = (pool: IReward[]): IRngResult | undefined => {
|
||||
return getRandomReward(pool as IRngResult[]);
|
||||
};
|
||||
|
||||
export const creditBundles: Record<string, number> = {
|
||||
"/Lotus/Types/PickUps/Credits/1500Credits": 1500,
|
||||
"/Lotus/Types/PickUps/Credits/2000Credits": 2000,
|
||||
"/Lotus/Types/PickUps/Credits/2500Credits": 2500,
|
||||
"/Lotus/Types/PickUps/Credits/3000Credits": 3000,
|
||||
"/Lotus/Types/PickUps/Credits/4000Credits": 4000,
|
||||
"/Lotus/Types/PickUps/Credits/5000Credits": 5000,
|
||||
"/Lotus/Types/PickUps/Credits/7500Credits": 7500,
|
||||
"/Lotus/Types/PickUps/Credits/10000Credits": 10000,
|
||||
"/Lotus/Types/PickUps/Credits/5000Hollars": 5000,
|
||||
"/Lotus/Types/PickUps/Credits/7500Hollars": 7500,
|
||||
"/Lotus/Types/PickUps/Credits/10000Hollars": 10000,
|
||||
"/Lotus/Types/PickUps/Credits/CorpusArenaCreditRewards/CorpusArenaRewardOneHard": 105000,
|
||||
"/Lotus/Types/PickUps/Credits/CorpusArenaCreditRewards/CorpusArenaRewardTwoHard": 175000,
|
||||
"/Lotus/Types/PickUps/Credits/CorpusArenaCreditRewards/CorpusArenaRewardThreeHard": 250000,
|
||||
"/Lotus/Types/StoreItems/CreditBundles/Zariman/TableACreditsCommon": 15000,
|
||||
"/Lotus/Types/StoreItems/CreditBundles/Zariman/TableACreditsUncommon": 30000
|
||||
};
|
||||
|
||||
export const fusionBundles: Record<string, number> = {
|
||||
"/Lotus/Upgrades/Mods/FusionBundles/CommonFusionBundle": 15,
|
||||
"/Lotus/Upgrades/Mods/FusionBundles/UncommonFusionBundle": 50,
|
||||
"/Lotus/Upgrades/Mods/FusionBundles/RareFusionBundle": 80
|
||||
};
|
||||
|
||||
type Entries<T, K extends keyof T = keyof T> = (K extends unknown ? [K, T[K]] : never)[];
|
||||
|
||||
function getEntriesUnsafe<T extends object>(object: T): Entries<T> {
|
||||
return Object.entries(object) as Entries<T>;
|
||||
}
|
||||
|
||||
//type TMissionInventoryUpdateKeys = keyof IMissionInventoryUpdateRequest;
|
||||
//const ignoredInventoryUpdateKeys = ["FpsAvg", "FpsMax", "FpsMin", "FpsSamples"] satisfies TMissionInventoryUpdateKeys[]; // for keys with no meaning for this server
|
||||
//type TignoredInventoryUpdateKeys = (typeof ignoredInventoryUpdateKeys)[number];
|
||||
//const knownUnhandledKeys: readonly string[] = ["test"] as const; // for unimplemented but important keys
|
||||
|
||||
export const addMissionInventoryUpdates = (
|
||||
inventory: HydratedDocument<IInventoryDatabase, InventoryDocumentProps>,
|
||||
inventoryUpdates: IMissionInventoryUpdateRequest
|
||||
) => {
|
||||
//TODO: type this properly
|
||||
const inventoryChanges: Partial<IInventoryDatabase> = {};
|
||||
if (inventoryUpdates.MissionFailed === true) {
|
||||
return;
|
||||
}
|
||||
for (const [key, value] of getEntriesUnsafe(inventoryUpdates)) {
|
||||
if (value === undefined) {
|
||||
logger.error(`Inventory update key ${key} has no value `);
|
||||
continue;
|
||||
}
|
||||
switch (key) {
|
||||
case "RegularCredits":
|
||||
inventory.RegularCredits += value;
|
||||
break;
|
||||
case "QuestKeys":
|
||||
updateQuestKey(inventory, value);
|
||||
break;
|
||||
case "AffiliationChanges":
|
||||
updateSyndicate(inventory, value);
|
||||
break;
|
||||
// Incarnon Challenges
|
||||
case "EvolutionProgress": {
|
||||
for (const evoProgress of value) {
|
||||
const entry = inventory.EvolutionProgress
|
||||
? inventory.EvolutionProgress.find(entry => entry.ItemType == evoProgress.ItemType)
|
||||
: undefined;
|
||||
if (entry) {
|
||||
entry.Progress = evoProgress.Progress;
|
||||
entry.Rank = evoProgress.Rank;
|
||||
} else {
|
||||
inventory.EvolutionProgress ??= [];
|
||||
inventory.EvolutionProgress.push(evoProgress);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "Missions":
|
||||
addMissionComplete(inventory, value);
|
||||
break;
|
||||
case "LastRegionPlayed":
|
||||
inventory.LastRegionPlayed = value;
|
||||
break;
|
||||
case "RawUpgrades":
|
||||
addMods(inventory, value);
|
||||
break;
|
||||
case "MiscItems":
|
||||
addMiscItems(inventory, value);
|
||||
break;
|
||||
case "Consumables":
|
||||
addConsumables(inventory, value);
|
||||
break;
|
||||
case "Recipes":
|
||||
addRecipes(inventory, value);
|
||||
break;
|
||||
case "ChallengeProgress":
|
||||
addChallenges(inventory, value);
|
||||
break;
|
||||
case "FusionTreasures":
|
||||
addFusionTreasures(inventory, value);
|
||||
break;
|
||||
case "FusionBundles": {
|
||||
let fusionPoints = 0;
|
||||
for (const fusionBundle of value) {
|
||||
const fusionPointsTotal = fusionBundles[fusionBundle.ItemType] * fusionBundle.ItemCount;
|
||||
inventory.FusionPoints += fusionPointsTotal;
|
||||
fusionPoints += fusionPointsTotal;
|
||||
}
|
||||
inventoryChanges.FusionPoints = fusionPoints;
|
||||
break;
|
||||
}
|
||||
// Equipment XP updates
|
||||
case "Suits":
|
||||
case "LongGuns":
|
||||
case "Pistols":
|
||||
case "Melee":
|
||||
case "SpecialItems":
|
||||
case "Sentinels":
|
||||
case "SentinelWeapons":
|
||||
case "SpaceSuits":
|
||||
case "SpaceGuns":
|
||||
case "SpaceMelee":
|
||||
case "Hoverboards":
|
||||
case "OperatorAmps":
|
||||
case "MoaPets":
|
||||
addGearExpByCategory(inventory, value, key);
|
||||
break;
|
||||
default:
|
||||
// if (
|
||||
// (ignoredInventoryUpdateKeys as readonly string[]).includes(key) ||
|
||||
// knownUnhandledKeys.includes(key)
|
||||
// ) {
|
||||
// continue;
|
||||
// }
|
||||
// logger.error(`Unhandled inventory update key: ${key}`);
|
||||
}
|
||||
}
|
||||
|
||||
return inventoryChanges;
|
||||
};
|
||||
|
||||
//TODO: return type of partial missioninventoryupdate response
|
||||
export const addMissionRewards = async (
|
||||
inventory: TInventoryDatabaseDocument,
|
||||
{ RewardInfo: rewardInfo, LevelKeyName: levelKeyName, Missions: missions }: IMissionInventoryUpdateRequest
|
||||
) => {
|
||||
if (!rewardInfo) {
|
||||
logger.error("no reward info provided");
|
||||
return;
|
||||
}
|
||||
|
||||
//TODO: check double reward merging
|
||||
const MissionRewards = getRandomMissionDrops(rewardInfo).map(drop => {
|
||||
return { StoreItem: drop.type, ItemCount: drop.itemCount };
|
||||
});
|
||||
console.log("random mission drops:", MissionRewards);
|
||||
const inventoryChanges: IInventoryChanges = {};
|
||||
|
||||
let missionCompletionCredits = 0;
|
||||
//inventory change is what the client has not rewarded itself, credit updates seem to be taken from totalCredits
|
||||
if (levelKeyName) {
|
||||
const fixedLevelRewards = getLevelKeyRewards(levelKeyName);
|
||||
//logger.debug(`fixedLevelRewards ${fixedLevelRewards}`);
|
||||
for (const reward of fixedLevelRewards) {
|
||||
if (reward.rewardType == "RT_CREDITS") {
|
||||
inventory.RegularCredits += reward.amount;
|
||||
missionCompletionCredits += reward.amount;
|
||||
continue;
|
||||
}
|
||||
MissionRewards.push({
|
||||
StoreItem: reward.itemType,
|
||||
ItemCount: reward.rewardType === "RT_RESOURCE" ? reward.amount : 1
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (missions) {
|
||||
const levelCreditReward = getLevelCreditRewards(missions?.Tag);
|
||||
missionCompletionCredits += levelCreditReward;
|
||||
inventory.RegularCredits += levelCreditReward;
|
||||
logger.debug(`levelCreditReward ${levelCreditReward}`);
|
||||
}
|
||||
|
||||
//TODO: resolve issue with creditbundles
|
||||
for (const reward of MissionRewards) {
|
||||
//TODO: additem should take in storeItems
|
||||
const inventoryChange = await addItem(inventory, reward.StoreItem.replace("StoreItems/", ""), reward.ItemCount);
|
||||
//TODO: combineInventoryChanges improve type safety, merging 2 of the same item?
|
||||
//TODO: check for the case when two of the same item are added, combineInventoryChanges should merge them
|
||||
//TODO: some conditional types to rule out binchanges?
|
||||
combineInventoryChanges(inventoryChanges, inventoryChange.InventoryChanges);
|
||||
}
|
||||
|
||||
return { inventoryChanges, MissionRewards, missionCompletionCredits };
|
||||
};
|
||||
|
||||
//might not be faithful to original
|
||||
//TODO: consider ActiveBoosters
|
||||
export const calculateFinalCredits = (
|
||||
inventory: HydratedDocument<IInventoryDatabase>,
|
||||
{
|
||||
missionDropCredits,
|
||||
missionCompletionCredits,
|
||||
rngRewardCredits = 0
|
||||
}: { missionDropCredits: number; missionCompletionCredits: number; rngRewardCredits: number }
|
||||
) => {
|
||||
const hasDailyCreditBonus = true;
|
||||
const totalCredits = missionDropCredits + missionCompletionCredits + rngRewardCredits;
|
||||
|
||||
const finalCredits = {
|
||||
MissionCredits: [missionDropCredits, missionDropCredits],
|
||||
CreditBonus: [missionCompletionCredits, missionCompletionCredits],
|
||||
TotalCredits: [totalCredits, totalCredits]
|
||||
};
|
||||
|
||||
if (hasDailyCreditBonus) {
|
||||
inventory.RegularCredits += totalCredits;
|
||||
finalCredits.CreditBonus[1] *= 2;
|
||||
finalCredits.MissionCredits[1] *= 2;
|
||||
finalCredits.TotalCredits[1] *= 2;
|
||||
}
|
||||
|
||||
if (!hasDailyCreditBonus) {
|
||||
return finalCredits;
|
||||
}
|
||||
return { ...finalCredits, DailyMissionBonus: true };
|
||||
};
|
||||
|
||||
function getLevelCreditRewards(nodeName: string): number {
|
||||
const minEnemyLevel = getNode(nodeName).minEnemyLevel;
|
||||
|
||||
return 1000 + (minEnemyLevel - 1) * 100;
|
||||
|
||||
//TODO: get dark sektor fixed credit rewards and railjack bonus
|
||||
}
|
||||
|
||||
function getRandomMissionDrops(RewardInfo: IRewardInfo): IRngResult[] {
|
||||
const drops: IRngResult[] = [];
|
||||
if (RewardInfo.node in ExportRegions) {
|
||||
const region = ExportRegions[RewardInfo.node];
|
||||
@ -53,161 +303,16 @@ const getRewards = ({
|
||||
});
|
||||
|
||||
if (region.cacheRewardManifest && RewardInfo.EnemyCachesFound) {
|
||||
console.log("cache rewards", RewardInfo.EnemyCachesFound);
|
||||
const deck = ExportRewards[region.cacheRewardManifest];
|
||||
for (let rotation = 0; rotation != RewardInfo.EnemyCachesFound; ++rotation) {
|
||||
const drop = getRandomRewardByChance(deck[rotation]);
|
||||
if (drop) {
|
||||
console.log("cache drop", drop);
|
||||
drops.push(drop);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
logger.debug("Mission rewards:", drops);
|
||||
return formatRewardsToInventoryType(drops);
|
||||
};
|
||||
|
||||
const combineRewardAndLootInventory = (
|
||||
rewardInventory: IMissionInventoryUpdateRequest,
|
||||
lootInventory: IMissionInventoryUpdateRequest
|
||||
) => {
|
||||
const missionCredits = lootInventory.RegularCredits || 0;
|
||||
const creditsBonus = rewardInventory.RegularCredits || 0;
|
||||
const totalCredits = missionCredits + creditsBonus;
|
||||
let FusionPoints = rewardInventory.FusionPoints || 0;
|
||||
|
||||
// Discharge Endo picked up during the mission
|
||||
if (lootInventory.FusionBundles) {
|
||||
for (const fusionBundle of lootInventory.FusionBundles) {
|
||||
if (fusionBundle.ItemType in fusionBundles) {
|
||||
FusionPoints += fusionBundles[fusionBundle.ItemType] * fusionBundle.ItemCount;
|
||||
} else {
|
||||
logger.error(`unknown fusion bundle: ${fusionBundle.ItemType}`);
|
||||
}
|
||||
}
|
||||
lootInventory.FusionBundles = undefined;
|
||||
}
|
||||
|
||||
lootInventory.RegularCredits = totalCredits;
|
||||
lootInventory.FusionPoints = FusionPoints;
|
||||
inventoryFields.forEach((field: IInventoryFieldType) => {
|
||||
if (rewardInventory[field] && !lootInventory[field]) {
|
||||
lootInventory[field] = [];
|
||||
}
|
||||
rewardInventory[field]?.forEach(item => lootInventory[field]!.push(item));
|
||||
});
|
||||
|
||||
return {
|
||||
combinedInventoryChanges: lootInventory,
|
||||
TotalCredits: [totalCredits, totalCredits],
|
||||
CreditsBonus: [creditsBonus, creditsBonus],
|
||||
MissionCredits: [missionCredits, missionCredits],
|
||||
FusionPoints: FusionPoints
|
||||
};
|
||||
};
|
||||
|
||||
const getRotations = (rotationCount: number): number[] => {
|
||||
if (rotationCount === 0) return [0];
|
||||
|
||||
const rotationPattern = [0, 0, 1, 2]; // A, A, B, C
|
||||
const rotatedValues = [];
|
||||
|
||||
for (let i = 0; i < rotationCount; i++) {
|
||||
rotatedValues.push(rotationPattern[i % rotationPattern.length]);
|
||||
}
|
||||
|
||||
return rotatedValues;
|
||||
};
|
||||
|
||||
const getRandomRewardByChance = (pool: IReward[]): IRngResult | undefined => {
|
||||
return getRandomReward(pool as IRngResult[]);
|
||||
};
|
||||
|
||||
const creditBundles: Record<string, number> = {
|
||||
"/Lotus/StoreItems/Types/PickUps/Credits/1500Credits": 1500,
|
||||
"/Lotus/StoreItems/Types/PickUps/Credits/2000Credits": 2000,
|
||||
"/Lotus/StoreItems/Types/PickUps/Credits/2500Credits": 2500,
|
||||
"/Lotus/StoreItems/Types/PickUps/Credits/3000Credits": 3000,
|
||||
"/Lotus/StoreItems/Types/PickUps/Credits/4000Credits": 4000,
|
||||
"/Lotus/StoreItems/Types/PickUps/Credits/5000Credits": 5000,
|
||||
"/Lotus/StoreItems/Types/PickUps/Credits/7500Credits": 7500,
|
||||
"/Lotus/StoreItems/Types/PickUps/Credits/10000Credits": 10000,
|
||||
"/Lotus/StoreItems/Types/StoreItems/CreditBundles/Zariman/TableACreditsCommon": 15000,
|
||||
"/Lotus/StoreItems/Types/StoreItems/CreditBundles/Zariman/TableACreditsUncommon": 30000,
|
||||
"/Lotus/StoreItems/Types/PickUps/Credits/CorpusArenaCreditRewards/CorpusArenaRewardOneHard": 105000,
|
||||
"/Lotus/StoreItems/Types/PickUps/Credits/CorpusArenaCreditRewards/CorpusArenaRewardTwoHard": 175000,
|
||||
"/Lotus/StoreItems/Types/PickUps/Credits/CorpusArenaCreditRewards/CorpusArenaRewardThreeHard": 250000
|
||||
};
|
||||
|
||||
const fusionBundles: Record<string, number> = {
|
||||
"/Lotus/Upgrades/Mods/FusionBundles/CommonFusionBundle": 15,
|
||||
"/Lotus/Upgrades/Mods/FusionBundles/UncommonFusionBundle": 50,
|
||||
"/Lotus/Upgrades/Mods/FusionBundles/RareFusionBundle": 80
|
||||
};
|
||||
|
||||
const formatRewardsToInventoryType = (
|
||||
rewards: IRngResult[]
|
||||
): { InventoryChanges: IMissionInventoryUpdateRequest; MissionRewards: IMissionRewardResponse[] } => {
|
||||
const InventoryChanges: IMissionInventoryUpdateRequest = {};
|
||||
const MissionRewards: IMissionRewardResponse[] = [];
|
||||
for (const reward of rewards) {
|
||||
if (reward.type in creditBundles) {
|
||||
InventoryChanges.RegularCredits ??= 0;
|
||||
InventoryChanges.RegularCredits += creditBundles[reward.type] * reward.itemCount;
|
||||
} else {
|
||||
const type = reward.type.replace("/Lotus/StoreItems/", "/Lotus/");
|
||||
if (type in fusionBundles) {
|
||||
InventoryChanges.FusionPoints ??= 0;
|
||||
InventoryChanges.FusionPoints += fusionBundles[type] * reward.itemCount;
|
||||
} else if (type in ExportUpgrades) {
|
||||
addRewardResponse(InventoryChanges, MissionRewards, type, reward.itemCount, "RawUpgrades");
|
||||
} else if (type in ExportGear) {
|
||||
addRewardResponse(InventoryChanges, MissionRewards, type, reward.itemCount, "Consumables");
|
||||
} else if (type in ExportRecipes) {
|
||||
addRewardResponse(InventoryChanges, MissionRewards, type, reward.itemCount, "Recipes");
|
||||
} else if (
|
||||
type in ExportRelics ||
|
||||
(type in ExportResources && ExportResources[type].productCategory == "MiscItems")
|
||||
) {
|
||||
addRewardResponse(InventoryChanges, MissionRewards, type, reward.itemCount, "MiscItems");
|
||||
} else {
|
||||
logger.error(`rolled reward ${reward.itemCount}X ${reward.type} but unsure how to give it`);
|
||||
}
|
||||
}
|
||||
}
|
||||
return { InventoryChanges, MissionRewards };
|
||||
};
|
||||
|
||||
const addRewardResponse = (
|
||||
InventoryChanges: IMissionInventoryUpdateRequest,
|
||||
MissionRewards: IMissionRewardResponse[],
|
||||
ItemType: string,
|
||||
ItemCount: number,
|
||||
InventoryCategory: IInventoryFieldType
|
||||
) => {
|
||||
if (!ItemType) return;
|
||||
|
||||
if (!InventoryChanges[InventoryCategory]) {
|
||||
InventoryChanges[InventoryCategory] = [];
|
||||
}
|
||||
|
||||
const existReward = InventoryChanges[InventoryCategory].find(item => item.ItemType === ItemType);
|
||||
if (existReward) {
|
||||
existReward.ItemCount += ItemCount;
|
||||
const missionReward = MissionRewards.find(missionReward => missionReward.TypeName === ItemType);
|
||||
if (missionReward) {
|
||||
missionReward.ItemCount += ItemCount;
|
||||
}
|
||||
} else {
|
||||
InventoryChanges[InventoryCategory].push({ ItemType, ItemCount });
|
||||
MissionRewards.push({
|
||||
ItemCount,
|
||||
TweetText: ItemType, // ensure if/how this even still used, or if it's needed at all
|
||||
ProductCategory: InventoryCategory,
|
||||
StoreItem: ItemType.replace("/Lotus/", "/Lotus/StoreItems/"),
|
||||
TypeName: ItemType
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export { getRewards, combineRewardAndLootInventory };
|
||||
return drops;
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { PersonalRooms } from "@/src/models/personalRoomsModel";
|
||||
import { addItem, getInventory } from "@/src/services/inventoryService";
|
||||
|
||||
export const getPersonalRooms = async (accountId: string) => {
|
||||
const personalRooms = await PersonalRooms.findOne({ personalRoomsOwnerId: accountId });
|
||||
@ -8,3 +9,18 @@ export const getPersonalRooms = async (accountId: string) => {
|
||||
}
|
||||
return personalRooms;
|
||||
};
|
||||
|
||||
export const updateShipFeature = async (accountId: string, shipFeature: string) => {
|
||||
const personalRooms = await getPersonalRooms(accountId);
|
||||
|
||||
if (personalRooms.Ship.Features.includes(shipFeature)) {
|
||||
throw new Error(`ship feature ${shipFeature} already unlocked`);
|
||||
}
|
||||
|
||||
personalRooms.Ship.Features.push(shipFeature);
|
||||
await personalRooms.save();
|
||||
|
||||
const inventory = await getInventory(accountId);
|
||||
await addItem(inventory, shipFeature, -1);
|
||||
await inventory.save();
|
||||
};
|
||||
|
34
src/services/questService.ts
Normal file
34
src/services/questService.ts
Normal file
@ -0,0 +1,34 @@
|
||||
import { IInventoryDatabase, IQuestKeyDatabase } from "@/src/types/inventoryTypes/inventoryTypes";
|
||||
import { logger } from "@/src/utils/logger";
|
||||
import { HydratedDocument } from "mongoose";
|
||||
|
||||
export const updateQuestKey = (
|
||||
inventory: HydratedDocument<IInventoryDatabase>,
|
||||
questKeyUpdate: IUpdateQuestRequest["QuestKeys"]
|
||||
): void => {
|
||||
if (questKeyUpdate.length > 1) {
|
||||
logger.error(`more than 1 quest key not supported`);
|
||||
throw new Error("more than 1 quest key not supported");
|
||||
}
|
||||
|
||||
const questKeyIndex = inventory.QuestKeys.findIndex(questKey => questKey.ItemType === questKeyUpdate[0].ItemType);
|
||||
|
||||
if (questKeyIndex === -1) {
|
||||
throw new Error(`quest key ${questKeyUpdate[0].ItemType} not found`);
|
||||
}
|
||||
|
||||
inventory.QuestKeys[questKeyIndex] = questKeyUpdate[0];
|
||||
|
||||
if (questKeyUpdate[0].Completed) {
|
||||
inventory.QuestKeys[questKeyIndex].CompletionDate = new Date();
|
||||
}
|
||||
};
|
||||
|
||||
export interface IUpdateQuestRequest {
|
||||
QuestKeys: Omit<IQuestKeyDatabase, "CompletionDate">[];
|
||||
PS: string;
|
||||
questCompletion: boolean;
|
||||
PlayerShipEvents: unknown[];
|
||||
crossPlaySetting: string;
|
||||
DoQuestReward: boolean;
|
||||
}
|
@ -43,6 +43,7 @@ export interface IInventoryDatabase
|
||||
GuildId?: Types.ObjectId; // GuildId changed from ?IOid to ?Types.ObjectId
|
||||
PendingRecipes: IPendingRecipe[];
|
||||
QuestKeys: IQuestKeyDatabase[];
|
||||
ActiveQuest: string;
|
||||
BlessingCooldown: Date;
|
||||
Ships: Types.ObjectId[];
|
||||
WeaponSkins: IWeaponSkinDatabase[];
|
||||
@ -71,7 +72,7 @@ export interface IInventoryDatabase
|
||||
}
|
||||
|
||||
export interface IQuestKeyDatabase {
|
||||
Progress?: IQuestProgress[];
|
||||
Progress?: IQuestStage[];
|
||||
unlock?: boolean;
|
||||
Completed?: boolean;
|
||||
CustomData?: string; //TODO: check whether this actually exists
|
||||
@ -205,7 +206,7 @@ export interface IInventoryClient extends IDailyAffiliations {
|
||||
RawUpgrades: IRawUpgrade[];
|
||||
ReceivedStartingGear: boolean;
|
||||
Ships: IShipInventory[];
|
||||
QuestKeys: IQuestKeyResponse[];
|
||||
QuestKeys: IQuestKeyClient[];
|
||||
FlavourItems: IFlavourItem[];
|
||||
Scoops: IEquipmentDatabase[];
|
||||
TrainingRetriesLeft: number;
|
||||
@ -889,14 +890,14 @@ export interface IPlayerSkills {
|
||||
LPS_DRIFT_ENDURANCE: number;
|
||||
}
|
||||
|
||||
export interface IQuestKeyResponse extends Omit<IQuestKeyDatabase, "CompletionDate"> {
|
||||
export interface IQuestKeyClient extends Omit<IQuestKeyDatabase, "CompletionDate"> {
|
||||
CompletionDate?: IMongoDate;
|
||||
}
|
||||
|
||||
export interface IQuestProgress {
|
||||
c: number;
|
||||
i: boolean;
|
||||
m: boolean;
|
||||
export interface IQuestStage {
|
||||
c?: number;
|
||||
i?: boolean;
|
||||
m?: boolean;
|
||||
b?: any[];
|
||||
}
|
||||
|
||||
|
@ -1,11 +1,11 @@
|
||||
export const inventoryFields = ["RawUpgrades", "MiscItems", "Consumables", "Recipes"] as const;
|
||||
export type IInventoryFieldType = (typeof inventoryFields)[number];
|
||||
|
||||
export interface IMissionRewardResponse {
|
||||
StoreItem?: string;
|
||||
TypeName: string;
|
||||
export interface IMissionReward {
|
||||
StoreItem: string;
|
||||
TypeName?: string;
|
||||
UpgradeLevel?: number;
|
||||
ItemCount: number;
|
||||
TweetText: string;
|
||||
ProductCategory: string;
|
||||
TweetText?: string;
|
||||
ProductCategory?: string;
|
||||
}
|
||||
|
@ -3,16 +3,15 @@ import { ArtifactPolarity, IPolarity, IEquipmentClient } from "@/src/types/inven
|
||||
import {
|
||||
IBooster,
|
||||
IChallengeProgress,
|
||||
IConsumable,
|
||||
IEvolutionProgress,
|
||||
IMiscItem,
|
||||
ITypeCount,
|
||||
IMission,
|
||||
IRawUpgrade,
|
||||
ISeasonChallenge,
|
||||
TSolarMapRegion,
|
||||
TEquipmentKey,
|
||||
IFusionTreasure
|
||||
IFusionTreasure,
|
||||
IQuestKeyClient
|
||||
} from "./inventoryTypes/inventoryTypes";
|
||||
|
||||
export interface IThemeUpdateRequest {
|
||||
@ -33,10 +32,13 @@ export interface IUpdateChallengeProgressRequest {
|
||||
SeasonChallengeCompletions: ISeasonChallenge[];
|
||||
}
|
||||
|
||||
export interface IMissionInventoryUpdateRequest {
|
||||
rewardsMultiplier?: number;
|
||||
ActiveBoosters?: IBooster[];
|
||||
export type IMissionInventoryUpdateRequest = {
|
||||
AffiliationChanges?: IAffiliationChange[];
|
||||
crossPlaySetting?: string;
|
||||
rewardsMultiplier?: number;
|
||||
GoalTag: string;
|
||||
LevelKeyName: string;
|
||||
ActiveBoosters?: IBooster[];
|
||||
Suits?: IEquipmentClient[];
|
||||
LongGuns?: IEquipmentClient[];
|
||||
Pistols?: IEquipmentClient[];
|
||||
@ -52,21 +54,40 @@ export interface IMissionInventoryUpdateRequest {
|
||||
MoaPets?: IEquipmentClient[];
|
||||
FusionBundles?: ITypeCount[];
|
||||
RawUpgrades?: IRawUpgrade[];
|
||||
MiscItems?: IMiscItem[];
|
||||
Consumables?: IConsumable[];
|
||||
MiscItems?: ITypeCount[];
|
||||
Consumables?: ITypeCount[];
|
||||
FusionTreasures?: IFusionTreasure[];
|
||||
Recipes?: IConsumable[];
|
||||
Recipes?: ITypeCount[];
|
||||
QuestKeys?: IQuestKeyClient[];
|
||||
RegularCredits?: number;
|
||||
ChallengeProgress?: IChallengeProgress[];
|
||||
RewardInfo?: IMissionInventoryUpdateRequestRewardInfo;
|
||||
MissionFailed: boolean;
|
||||
MissionStatus: IMissionStatus;
|
||||
AliveTime: number;
|
||||
MissionTime: number;
|
||||
Missions?: IMission;
|
||||
EvolutionProgress?: IEvolutionProgress[];
|
||||
LastRegionPlayed?: TSolarMapRegion;
|
||||
GameModeId: number;
|
||||
hosts: string[];
|
||||
currentClients: unknown[];
|
||||
ChallengeProgress: IChallengeProgress[];
|
||||
PS: string;
|
||||
ActiveDojoColorResearch: string;
|
||||
RewardInfo?: IRewardInfo;
|
||||
ReceivedCeremonyMsg: boolean;
|
||||
LastCeremonyResetDate: number;
|
||||
MissionPTS: number;
|
||||
RepHash: string;
|
||||
EndOfMatchUpload: boolean;
|
||||
ObjectiveReached: boolean;
|
||||
sharedSessionId: string;
|
||||
FpsAvg: number;
|
||||
FpsMin: number;
|
||||
FpsMax: number;
|
||||
FpsSamples: number;
|
||||
EvolutionProgress?: IEvolutionProgress[];
|
||||
};
|
||||
|
||||
FusionPoints?: number; // Not a part of the request, but we put it in this struct as an intermediate storage.
|
||||
}
|
||||
|
||||
export interface IMissionInventoryUpdateRequestRewardInfo {
|
||||
export interface IRewardInfo {
|
||||
node: string;
|
||||
VaultsCracked?: number; // for Spy missions
|
||||
rewardTier?: number;
|
||||
@ -82,15 +103,15 @@ export interface IMissionInventoryUpdateRequestRewardInfo {
|
||||
rewardSeed?: number;
|
||||
}
|
||||
|
||||
export type IMissionStatus = "GS_SUCCESS" | "GS_FAILURE" | "GS_DUMPED" | "GS_QUIT" | "GS_INTERRUPTED";
|
||||
|
||||
export interface IInventorySlotsRequest {
|
||||
Bin: "PveBonusLoadoutBin";
|
||||
}
|
||||
|
||||
export interface IUpdateGlyphRequest {
|
||||
AvatarImageType: string;
|
||||
AvatarImage: string;
|
||||
}
|
||||
|
||||
export interface IUpgradesRequest {
|
||||
ItemCategory: TEquipmentKey;
|
||||
ItemId: IOid;
|
||||
@ -98,7 +119,6 @@ export interface IUpgradesRequest {
|
||||
UpgradeVersion: number;
|
||||
Operations: IUpgradeOperation[];
|
||||
}
|
||||
|
||||
export interface IUpgradeOperation {
|
||||
OperationType: string;
|
||||
UpgradeRequirement: string; // uniqueName of item being consumed
|
||||
@ -106,3 +126,8 @@ export interface IUpgradeOperation {
|
||||
PolarizeValue: ArtifactPolarity;
|
||||
PolarityRemap: IPolarity[];
|
||||
}
|
||||
export interface IUnlockShipFeatureRequest {
|
||||
Feature: string;
|
||||
KeyChain: string;
|
||||
ChainStage: number;
|
||||
}
|
||||
|
13
static/fixed_responses/questCompletionRewards.json
Normal file
13
static/fixed_responses/questCompletionRewards.json
Normal file
@ -0,0 +1,13 @@
|
||||
{
|
||||
"/Lotus/Types/Keys/VorsPrize/VorsPrizeQuestKeyChain": [
|
||||
{
|
||||
"ItemType": "/Lotus/Types/Keys/DuviriQuest/DuviriQuestKeyChain",
|
||||
"ItemCount": 1
|
||||
},
|
||||
{
|
||||
"ItemType": "/Lotus/Types/NeutralCreatures/ErsatzHorse/ErsatzHorsePowerSuit",
|
||||
"ItemCount": 1
|
||||
}
|
||||
],
|
||||
"/Lotus/Types/Keys/InfestedMicroplanetQuest/InfestedMicroplanetQuestKeyChain": [{ "ItemType": "/Lotus/Types/Recipes/WarframeRecipes/BrokenFrameBlueprint", "ItemCount": 1 }]
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user