From 7efcdfd9a3232d001a261091d36ae0c735a8790d Mon Sep 17 00:00:00 2001 From: nrbdev Date: Thu, 20 Feb 2025 05:45:33 -0500 Subject: [PATCH] feat: implement giveStartingGear --- src/controllers/api/giveStartingGear.ts | 207 ++++++++++++++++++++++++ src/routes/api.ts | 2 + 2 files changed, 209 insertions(+) create mode 100644 src/controllers/api/giveStartingGear.ts diff --git a/src/controllers/api/giveStartingGear.ts b/src/controllers/api/giveStartingGear.ts new file mode 100644 index 00000000..cbc7ebae --- /dev/null +++ b/src/controllers/api/giveStartingGear.ts @@ -0,0 +1,207 @@ +import { config } from "@/src/services/configService"; +import { toOid } from "@/src/helpers/inventoryHelpers"; +import { IOid } from "@/src/types/commonTypes"; +import { Types } from "mongoose"; +import { RequestHandler } from "express"; +import { IEquipmentClient } from "@/src/types/inventoryTypes/commonInventoryTypes"; +import { + IDailyAffiliations, + IInventoryClient, + IPlayerSkills, + ITypeCount +} from "@/src/types/inventoryTypes/inventoryTypes"; +import { addConsumables, addKeyChainItems, addMods, getInventory } from "@/src/services/inventoryService"; +import { getJSONfromString } from "@/src/helpers/stringHelpers"; +import { getAccountIdForRequest } from "@/src/services/loginService"; + +export const giveStartingGearController: RequestHandler = async (req, res) => { + const accountId = await getAccountIdForRequest(req); + const inventory = await getInventory(accountId); + + const dataJSON = getJSONfromString(String(req.body)); + + await addKeyChainItems(inventory, { + KeyChain: "/Lotus/Types/Keys/VorsPrize/VorsPrizeQuestKeyChain", + ChainStage: 0 + }); + + for (const key of Object.keys(dataJSON) as Array) { + switch (key) { + // This is the only gear we receive as the rest will come from future quests + case "LongGuns": + case "Pistols": + case "Melee": + case "Suits": + // Filter out already owned items (shouldnt happen but this was mostly for testing) + const existingItems = new Set(inventory[key].map(item => item.ItemType)); + + // Adding items to inventory + inventory[key].push( + ...dataJSON[key] + .filter(item => !existingItems.has(item.ItemType)) + .map(x => ({ + _id: new Types.ObjectId(), + ItemType: x.ItemType, + XP: x.XP, + Configs: [{}, {}, {}] + })) + ); + } + } + + addConsumables(inventory, [{ ItemCount: 1, ItemType: "/Lotus/Types/Restoratives/LisetAutoHack" }]); + if (!config.unlockAllFlavourItems) { + inventory.FlavourItems.push([ + { ItemType: "/Lotus/Types/StoreItems/AvatarImages/AvatarImageItem1" }, + { ItemType: "/Lotus/Types/StoreItems/AvatarImages/AvatarImageItem2" }, + { ItemType: "/Lotus/Types/StoreItems/AvatarImages/AvatarImageItem3" }, + { ItemType: "/Lotus/Types/StoreItems/AvatarImages/AvatarImageItem4" } + ]); + } + addMods(inventory, [ + { + ItemCount: 1, + ItemType: "/Lotus/Upgrades/Mods/Warframe/AvatarShieldMaxMod", + LastAdded: toOid(new Types.ObjectId()) + } + ]); + + inventory.DrifterMelee.push({ + ItemType: "/Lotus/Types/Friendly/PlayerControllable/Weapons/DuviriDualSwords", + _id: new Types.ObjectId() + }); + inventory.PlayedParkourTutorial = true; + inventory.ReceivedStartingGear = true; + inventory.TrainingDate = new Date(); + inventory.QuestKeys.push({ + Progress: [{ i: true }], + unlock: false, + ItemType: "/Lotus/Types/Keys/VorsPrize/VorsPrizeQuestKeyChain" + }); + await inventory.save(); + + res.json({}); +}; + +// Alot of stuff is not received in the request, instead of being redundant I will just omit an already created type (pain) +interface IStartingGearClient + extends Omit< + IInventoryClient, + | keyof IDailyAffiliations + | "Missions" + | "RandomUpgradesIdentified" + | "LastRegionPlayed" + | "TradesRemaining" + | "DailyFocus" + | "GiftsRemaining" + | "HasOwnedVoidProjectionsPreviously" + | "ChallengesFixVersion" + | "ChallengeProgress" + | "ReceivedStartingGear" + | "PendingRecipes" + | "PendingTrades" + | "DeathMarks" + | "WebFlags" + | "CompletedAlerts" + | "TauntHistory" + | "StoryModeChoice" + | "PeriodicMissionCompletions" + | "ActiveDojoColorResearch" + | "SentientSpawnChanceBoosters" + | "SupportedSyndicate" + | "Affiliations" + | "QualifyingInvasions" + | "FactionScores" + | "ArchwingEnabled" + | "PendingSpectreLoadouts" + | "SpectreLoadouts" + | "CompletedSyndicates" + | "FocusXP" + | "Alignment" + | "CompletedSorties" + | "LastSortieReward" + | "ActiveAvatarImageType" + | "DiscoveredMarkers" + | "CompletedJobs" + | "FocusAbility" + | "HasContributedToDojo" + | "HWIDProtectEnabled" + | "AlignmentReplay" + | "PersonalGoalProgress" + | "ThemeStyle" + | "ThemeBackground" + | "ThemeSounds" + | "BountyScore" + | "ChallengeInstanceStates" + | "LoginMilestoneRewards" + | "NodeIntrosCompleted" + | "GuildId" + | "CompletedJobChains" + | "SeasonChallengeHistory" + | "EquippedInstrument" + | "InvasionChainProgress" + | "NemesisHistory" + | "LastNemesisAllySpawnTime" + | "Settings" + | "PersonalTechProjects" + | "PlayerSkills" + | "TradeBannedUntil" + | "PlayedParkourTutorial" + | "SubscribedToEmailsPersonalized" + | "BlessingCooldown" + | "NemesisAbandonedRewards" + | "LastInventorySync" + | "NextRefill" + | "CustomMarkers" + | "ActiveLandscapeTraps" + | "EvolutionProgress" + | "RepVotes" + | "UsedDailyDeals" + | "LibraryPersonalTarget" + | "LibraryPersonalProgress" + | "CollectibleSeries" + | "LibraryAvailableDailyTaskInfo" + | "HasResetAccount" + | "PendingCoupon" + | "Harvestable" + | "DeathSquadable" + | "EndlessXP" + | "DialogueHistory" + > { + LongGuns: IStartingGearItem[]; + Melee: IStartingGearItem[]; + Pistols: IStartingGearItem[]; + Suits: IStartingGearItem[]; + XPLost?: unknown[]; + CrewShipFusionPoints?: number; + PlayerSkillGains?: IPlayerSkills[]; + // Lot's of unknown but these never receive data (at least not in this request) + StrippedItems?: unknown[]; + BonusMiscItems?: unknown[]; + EmailItems: ITypeCount[]; + OneTimePurchases?: unknown[]; + Rating?: number; + WishlistChanges?: unknown[]; + RecentVendorPurchases: (string | number)[]; + RemovedIdItems?: { ItemId: number }[]; + SongChallenges?: unknown[]; +} + +interface IStartingGearItem + extends Omit< + IEquipmentClient, + | "_id" + | "InfestationDate" + | "InfestationDays" + | "InfestationType" + | "UnlockLevel" + | "Weapon" + | "Customization" + | "RailjackImage" + | "CrewMembers" + | "Details" + > { + // Warframe sends an ItemId instead _id, it will be converted to _id before being pushed to the inventory + ItemId: IOid; + Favorite?: boolean; +} diff --git a/src/routes/api.ts b/src/routes/api.ts index 7ad9e5a6..ef353ebf 100644 --- a/src/routes/api.ts +++ b/src/routes/api.ts @@ -35,6 +35,7 @@ import { gildWeaponController } from "@/src/controllers/api/gildWeaponController import { giveKeyChainTriggeredItemsController } from "@/src/controllers/api/giveKeyChainTriggeredItemsController"; import { giveKeyChainTriggeredMessageController } from "@/src/controllers/api/giveKeyChainTriggeredMessageController"; import { giveQuestKeyRewardController } from "@/src/controllers/api/giveQuestKey"; +import { giveStartingGearController } from "../controllers/api/giveStartingGear"; import { guildTechController } from "../controllers/api/guildTechController"; import { hostSessionController } from "@/src/controllers/api/hostSessionController"; import { hubController } from "@/src/controllers/api/hubController"; @@ -146,6 +147,7 @@ apiRouter.post("/gildWeapon.php", gildWeaponController); apiRouter.post("/giveKeyChainTriggeredItems.php", giveKeyChainTriggeredItemsController); apiRouter.post("/giveKeyChainTriggeredMessage.php", giveKeyChainTriggeredMessageController); apiRouter.post("/giveQuestKeyReward.php", giveQuestKeyRewardController); +apiRouter.post("/giveStartingGear.php", giveStartingGearController); apiRouter.post("/guildTech.php", guildTechController); apiRouter.post("/hostSession.php", hostSessionController); apiRouter.post("/infestedFoundry.php", infestedFoundryController);