From fd7f4c9e92c6a421453fd60e08588ccd8bf07b75 Mon Sep 17 00:00:00 2001 From: Sainan <63328889+Sainan@users.noreply.github.com> Date: Fri, 25 Apr 2025 11:53:34 -0700 Subject: [PATCH] feat: calendar progress (#1830) Closes #1775 Reviewed-on: https://onlyg.it/OpenWF/SpaceNinjaServer/pulls/1830 Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com> Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com> --- .../api/completeCalendarEventController.ts | 41 ++++++++++++++ src/models/inventoryModels/inventoryModel.ts | 12 ++--- src/routes/api.ts | 2 + src/services/inventoryService.ts | 54 +++++++++++++------ src/services/missionInventoryUpdateService.ts | 10 ++++ src/services/worldStateService.ts | 2 +- src/types/inventoryTypes/inventoryTypes.ts | 9 ++-- src/types/requestTypes.ts | 1 + src/types/worldStateTypes.ts | 2 +- 9 files changed, 104 insertions(+), 29 deletions(-) create mode 100644 src/controllers/api/completeCalendarEventController.ts diff --git a/src/controllers/api/completeCalendarEventController.ts b/src/controllers/api/completeCalendarEventController.ts new file mode 100644 index 00000000..20c8abb3 --- /dev/null +++ b/src/controllers/api/completeCalendarEventController.ts @@ -0,0 +1,41 @@ +import { getCalendarProgress, getInventory } from "@/src/services/inventoryService"; +import { getAccountIdForRequest } from "@/src/services/loginService"; +import { handleStoreItemAcquisition } from "@/src/services/purchaseService"; +import { getWorldState } from "@/src/services/worldStateService"; +import { IInventoryChanges } from "@/src/types/purchaseTypes"; +import { RequestHandler } from "express"; + +// GET request; query parameters: CompletedEventIdx=0&Iteration=4&Version=19&Season=CST_SUMMER +export const completeCalendarEventController: RequestHandler = async (req, res) => { + const accountId = await getAccountIdForRequest(req); + const inventory = await getInventory(accountId); + const calendarProgress = getCalendarProgress(inventory); + const currentSeason = getWorldState().KnownCalendarSeasons[0]; + let inventoryChanges: IInventoryChanges = {}; + let dayIndex = 0; + for (const day of currentSeason.Days) { + if (day.events.length == 0 || day.events[0].type != "CET_CHALLENGE") { + if (dayIndex == calendarProgress.SeasonProgress.LastCompletedDayIdx) { + if (day.events.length != 0) { + const selection = day.events[parseInt(req.query.CompletedEventIdx as string)]; + if (selection.type == "CET_REWARD") { + inventoryChanges = (await handleStoreItemAcquisition(selection.reward!, inventory)) + .InventoryChanges; + } else if (selection.type == "CET_UPGRADE") { + calendarProgress.YearProgress.Upgrades.push(selection.upgrade!); + } else if (selection.type != "CET_PLOT") { + throw new Error(`unexpected selection type: ${selection.type}`); + } + } + break; + } + ++dayIndex; + } + } + calendarProgress.SeasonProgress.LastCompletedDayIdx++; + await inventory.save(); + res.json({ + InventoryChanges: inventoryChanges, + CalendarProgress: inventory.CalendarProgress + }); +}; diff --git a/src/models/inventoryModels/inventoryModel.ts b/src/models/inventoryModels/inventoryModel.ts index 6b73186f..2e09caf8 100644 --- a/src/models/inventoryModels/inventoryModel.ts +++ b/src/models/inventoryModels/inventoryModel.ts @@ -1125,15 +1125,15 @@ const CustomMarkersSchema = new Schema( const calenderProgressSchema = new Schema( { Version: { type: Number, default: 19 }, - Iteration: { type: Number, default: 2 }, + Iteration: { type: Number, required: true }, YearProgress: { - Upgrades: { type: [] } + Upgrades: { type: [String], default: [] } }, SeasonProgress: { - SeasonType: String, - LastCompletedDayIdx: { type: Number, default: -1 }, - LastCompletedChallengeDayIdx: { type: Number, default: -1 }, - ActivatedChallenges: [] + SeasonType: { type: String, required: true }, + LastCompletedDayIdx: { type: Number, default: 0 }, + LastCompletedChallengeDayIdx: { type: Number, default: 0 }, + ActivatedChallenges: { type: [String], default: [] } } }, { _id: false } diff --git a/src/routes/api.ts b/src/routes/api.ts index c3dc2e50..b11af3e4 100644 --- a/src/routes/api.ts +++ b/src/routes/api.ts @@ -19,6 +19,7 @@ import { claimCompletedRecipeController } from "@/src/controllers/api/claimCompl import { claimLibraryDailyTaskRewardController } from "@/src/controllers/api/claimLibraryDailyTaskRewardController"; import { clearDialogueHistoryController } from "@/src/controllers/api/clearDialogueHistoryController"; import { clearNewEpisodeRewardController } from "@/src/controllers/api/clearNewEpisodeRewardController"; +import { completeCalendarEventController } from "@/src/controllers/api/completeCalendarEventController"; import { completeRandomModChallengeController } from "@/src/controllers/api/completeRandomModChallengeController"; import { confirmAllianceInvitationController } from "@/src/controllers/api/confirmAllianceInvitationController"; import { confirmGuildInvitationGetController, confirmGuildInvitationPostController } from "@/src/controllers/api/confirmGuildInvitationController"; @@ -158,6 +159,7 @@ apiRouter.get("/changeDojoRoot.php", changeDojoRootController); apiRouter.get("/changeGuildRank.php", changeGuildRankController); apiRouter.get("/checkDailyMissionBonus.php", checkDailyMissionBonusController); apiRouter.get("/claimLibraryDailyTaskReward.php", claimLibraryDailyTaskRewardController); +apiRouter.get("/completeCalendarEvent.php", completeCalendarEventController); apiRouter.get("/confirmAllianceInvitation.php", confirmAllianceInvitationController); apiRouter.get("/confirmGuildInvitation.php", confirmGuildInvitationGetController); apiRouter.get("/credits.php", creditsController); diff --git a/src/services/inventoryService.ts b/src/services/inventoryService.ts index a9176e0b..94554299 100644 --- a/src/services/inventoryService.ts +++ b/src/services/inventoryService.ts @@ -18,7 +18,6 @@ import { IKubrowPetEggDatabase, IKubrowPetEggClient, ILibraryDailyTaskInfo, - ICalendarProgress, IDroneClient, IUpgradeClient, TPartialStartingGear, @@ -26,7 +25,8 @@ import { ICrewMemberClient, Status, IKubrowPetDetailsDatabase, - ITraits + ITraits, + ICalendarProgress } from "@/src/types/inventoryTypes/inventoryTypes"; import { IGenericUpdate, IUpdateNodeIntrosResponse } from "../types/genericUpdate"; import { IKeyChainRequest, IMissionInventoryUpdateRequest } from "../types/requestTypes"; @@ -78,6 +78,7 @@ import libraryDailyTasks from "@/static/fixed_responses/libraryDailyTasks.json"; import { getRandomElement, getRandomInt, getRandomWeightedReward, SRng } from "./rngService"; import { createMessage } from "./inboxService"; import { getMaxStanding } from "@/src/helpers/syndicateStandingHelper"; +import { getWorldState } from "./worldStateService"; export const createInventory = async ( accountOwnerId: Types.ObjectId, @@ -91,7 +92,6 @@ export const createInventory = async ( }); inventory.LibraryAvailableDailyTaskInfo = createLibraryDailyTask(); - inventory.CalendarProgress = createCalendar(); inventory.RewardSeed = generateRewardSeed(); inventory.DuviriInfo = { Seed: generateRewardSeed(), @@ -1756,20 +1756,6 @@ export const createLibraryDailyTask = (): ILibraryDailyTaskInfo => { }; }; -const createCalendar = (): ICalendarProgress => { - return { - Version: 19, - Iteration: 2, - YearProgress: { Upgrades: [] }, - SeasonProgress: { - SeasonType: "CST_SPRING", - LastCompletedDayIdx: -1, - LastCompletedChallengeDayIdx: -1, - ActivatedChallenges: [] - } - }; -}; - export const setupKahlSyndicate = (inventory: TInventoryDatabaseDocument): void => { inventory.Affiliations.push({ Title: 1, @@ -1806,3 +1792,37 @@ export const cleanupInventory = (inventory: TInventoryDatabaseDocument): void => LibrarySyndicate.FreeFavorsEarned = undefined; } }; + +export const getCalendarProgress = (inventory: TInventoryDatabaseDocument): ICalendarProgress => { + const currentSeason = getWorldState().KnownCalendarSeasons[0]; + + if (!inventory.CalendarProgress) { + inventory.CalendarProgress = { + Version: 19, + Iteration: currentSeason.YearIteration, + YearProgress: { + Upgrades: [] + }, + SeasonProgress: { + SeasonType: currentSeason.Season, + LastCompletedDayIdx: 0, + LastCompletedChallengeDayIdx: 0, + ActivatedChallenges: [] + } + }; + } + + const yearRolledOver = inventory.CalendarProgress.Iteration != currentSeason.YearIteration; + if (yearRolledOver) { + inventory.CalendarProgress.Iteration = currentSeason.YearIteration; + inventory.CalendarProgress.YearProgress.Upgrades = []; + } + if (yearRolledOver || inventory.CalendarProgress.SeasonProgress.SeasonType != currentSeason.Season) { + inventory.CalendarProgress.SeasonProgress.SeasonType = currentSeason.Season; + inventory.CalendarProgress.SeasonProgress.LastCompletedDayIdx = -1; + inventory.CalendarProgress.SeasonProgress.LastCompletedChallengeDayIdx = -1; + inventory.CalendarProgress.SeasonProgress.ActivatedChallenges = []; + } + + return inventory.CalendarProgress; +}; diff --git a/src/services/missionInventoryUpdateService.ts b/src/services/missionInventoryUpdateService.ts index 625a7fd0..cb55fc69 100644 --- a/src/services/missionInventoryUpdateService.ts +++ b/src/services/missionInventoryUpdateService.ts @@ -33,6 +33,7 @@ import { addStanding, combineInventoryChanges, generateRewardSeed, + getCalendarProgress, updateCurrency, updateSyndicate } from "@/src/services/inventoryService"; @@ -560,6 +561,15 @@ export const addMissionInventoryUpdates = async ( } break; } + case "CalendarProgress": { + const calendarProgress = getCalendarProgress(inventory); + for (const progress of value) { + const challengeName = progress.challenge.substring(progress.challenge.lastIndexOf("/") + 1); + calendarProgress.SeasonProgress.LastCompletedChallengeDayIdx++; + calendarProgress.SeasonProgress.ActivatedChallenges.push(challengeName); + } + break; + } default: // Equipment XP updates if (equipmentKeys.includes(key as TEquipmentKey)) { diff --git a/src/services/worldStateService.ts b/src/services/worldStateService.ts index e1af4369..4619d27a 100644 --- a/src/services/worldStateService.ts +++ b/src/services/worldStateService.ts @@ -683,7 +683,7 @@ const getCalendarSeason = (week: number): ICalendarSeason => { Activation: { $date: { $numberLong: weekStart.toString() } }, Expiry: { $date: { $numberLong: weekEnd.toString() } }, Days: eventDays, - Season: ["CST_WINTER", "CST_SPRING", "CST_SUMMER", "CST_FALL"][seasonIndex], + Season: (["CST_WINTER", "CST_SPRING", "CST_SUMMER", "CST_FALL"] as const)[seasonIndex], YearIteration: Math.trunc(week / 4), Version: 19, UpgradeAvaliabilityRequirements: ["/Lotus/Upgrades/Calendar/1999UpgradeApplicationRequirement"] diff --git a/src/types/inventoryTypes/inventoryTypes.ts b/src/types/inventoryTypes/inventoryTypes.ts index 8ddfbf42..66ce5b3c 100644 --- a/src/types/inventoryTypes/inventoryTypes.ts +++ b/src/types/inventoryTypes/inventoryTypes.ts @@ -353,7 +353,7 @@ export interface IInventoryClient extends IDailyAffiliations, InventoryClientEqu DeathSquadable: boolean; EndlessXP?: IEndlessXpProgress[]; DialogueHistory?: IDialogueHistoryClient; - CalendarProgress: ICalendarProgress; + CalendarProgress?: ICalendarProgress; SongChallenges?: ISongChallenge[]; EntratiVaultCountLastPeriod?: number; EntratiVaultCountResetDate?: IMongoDate; @@ -1193,17 +1193,18 @@ export interface IMarker { z: number; showInHud: boolean; } + export interface ISeasonProgress { - SeasonType: "CST_UNDEFINED" | "CST_WINTER" | "CST_SPRING" | "CST_SUMMER" | "CST_FALL"; + SeasonType: "CST_WINTER" | "CST_SPRING" | "CST_SUMMER" | "CST_FALL"; LastCompletedDayIdx: number; LastCompletedChallengeDayIdx: number; - ActivatedChallenges: unknown[]; + ActivatedChallenges: string[]; } export interface ICalendarProgress { Version: number; Iteration: number; - YearProgress: { Upgrades: unknown[] }; + YearProgress: { Upgrades: string[] }; SeasonProgress: ISeasonProgress; } diff --git a/src/types/requestTypes.ts b/src/types/requestTypes.ts index 74764bc1..d5b04058 100644 --- a/src/types/requestTypes.ts +++ b/src/types/requestTypes.ts @@ -44,6 +44,7 @@ export type IMissionInventoryUpdateRequest = { SyndicateId?: string; SortieId?: string; + CalendarProgress?: { challenge: string }[]; SeasonChallengeCompletions?: ISeasonChallenge[]; AffiliationChanges?: IAffiliationChange[]; crossPlaySetting?: string; diff --git a/src/types/worldStateTypes.ts b/src/types/worldStateTypes.ts index 835792b8..303c3c33 100644 --- a/src/types/worldStateTypes.ts +++ b/src/types/worldStateTypes.ts @@ -133,7 +133,7 @@ export interface ISeasonChallenge { export interface ICalendarSeason { Activation: IMongoDate; Expiry: IMongoDate; - Season: string; // "CST_UNDEFINED" | "CST_WINTER" | "CST_SPRING" | "CST_SUMMER" | "CST_FALL" + Season: "CST_WINTER" | "CST_SPRING" | "CST_SUMMER" | "CST_FALL"; Days: ICalendarDay[]; YearIteration: number; Version: number;