diff --git a/src/controllers/api/clearDialogueHistoryController.ts b/src/controllers/api/clearDialogueHistoryController.ts new file mode 100644 index 00000000..96c8e1c6 --- /dev/null +++ b/src/controllers/api/clearDialogueHistoryController.ts @@ -0,0 +1,23 @@ +import { getInventory } from "@/src/services/inventoryService"; +import { getAccountIdForRequest } from "@/src/services/loginService"; +import { RequestHandler } from "express"; + +export const clearDialogueHistoryController: RequestHandler = async (req, res) => { + const accountId = await getAccountIdForRequest(req); + const inventory = await getInventory(accountId); + const request = JSON.parse(String(req.body)) as IClearDialogueRequest; + if (inventory.DialogueHistory && inventory.DialogueHistory.Dialogues) { + for (const dialogueName of request.Dialogues) { + const index = inventory.DialogueHistory.Dialogues.findIndex(x => x.DialogueName == dialogueName); + if (index != -1) { + inventory.DialogueHistory.Dialogues.splice(index, 1); + } + } + } + await inventory.save(); + res.end(); +}; + +interface IClearDialogueRequest { + Dialogues: string[]; +} diff --git a/src/controllers/api/saveDialogueController.ts b/src/controllers/api/saveDialogueController.ts new file mode 100644 index 00000000..332cb235 --- /dev/null +++ b/src/controllers/api/saveDialogueController.ts @@ -0,0 +1,85 @@ +import { getInventory } from "@/src/services/inventoryService"; +import { getAccountIdForRequest } from "@/src/services/loginService"; +import { ICompletedDialogue } from "@/src/types/inventoryTypes/inventoryTypes"; +import { logger } from "@/src/utils/logger"; +import { RequestHandler } from "express"; + +export const saveDialogueController: RequestHandler = async (req, res) => { + const accountId = await getAccountIdForRequest(req); + const request = JSON.parse(String(req.body)) as SaveDialogueRequest; + if ("YearIteration" in request) { + const inventory = await getInventory(accountId); + if (inventory.DialogueHistory) { + inventory.DialogueHistory.YearIteration = request.YearIteration; + } else { + inventory.DialogueHistory = { YearIteration: request.YearIteration }; + } + await inventory.save(); + res.end(); + } else { + const inventory = await getInventory(accountId); + if (!inventory.DialogueHistory) { + throw new Error("bad inventory state"); + } + if (request.QueuedDialogues.length != 0 || request.OtherDialogueInfos.length != 0) { + logger.error(`saveDialogue request not fully handled: ${String(req.body)}`); + } + inventory.DialogueHistory.Dialogues ??= []; + let dialogue = inventory.DialogueHistory.Dialogues.find(x => x.DialogueName == request.DialogueName); + if (!dialogue) { + dialogue = + inventory.DialogueHistory.Dialogues[ + inventory.DialogueHistory.Dialogues.push({ + Rank: 0, + Chemistry: 0, + AvailableDate: new Date(0), + AvailableGiftDate: new Date(0), + RankUpExpiry: new Date(0), + BountyChemExpiry: new Date(0), + Gifts: [], + Booleans: [], + Completed: [], + DialogueName: request.DialogueName + }) - 1 + ]; + } + dialogue.Rank = request.Rank; + dialogue.Chemistry = request.Chemistry; + //dialogue.QueuedDialogues = request.QueuedDialogues; + for (const bool of request.Booleans) { + dialogue.Booleans.push(bool); + } + for (const bool of request.ResetBooleans) { + const index = dialogue.Booleans.findIndex(x => x == bool); + if (index != -1) { + dialogue.Booleans.splice(index, 1); + } + } + dialogue.Completed.push(request.Data); + const tomorrowAt0Utc = (Math.trunc(Date.now() / (86400 * 1000)) + 1) * 86400 * 1000; + dialogue.AvailableDate = new Date(tomorrowAt0Utc); + await inventory.save(); + res.json({ + InventoryChanges: [], + AvailableDate: { $date: { $numberLong: tomorrowAt0Utc.toString() } } + }); + } +}; + +type SaveDialogueRequest = SaveYearIterationRequest | SaveCompletedDialogueRequest; + +interface SaveYearIterationRequest { + YearIteration: number; +} + +interface SaveCompletedDialogueRequest { + DialogueName: string; + Rank: number; + Chemistry: number; + CompletionType: number; + QueuedDialogues: string[]; // unsure + Booleans: string[]; + ResetBooleans: string[]; + Data: ICompletedDialogue; + OtherDialogueInfos: string[]; // unsure +} diff --git a/src/models/inventoryModels/inventoryModel.ts b/src/models/inventoryModels/inventoryModel.ts index 65a5d3d2..82fefab4 100644 --- a/src/models/inventoryModels/inventoryModel.ts +++ b/src/models/inventoryModels/inventoryModel.ts @@ -47,7 +47,12 @@ import { ICrewShipPilotWeapon, IShipExterior, IHelminthFoodRecord, - ICrewShipMembersDatabase + ICrewShipMembersDatabase, + IDialogueHistoryDatabase, + IDialogueDatabase, + IDialogueGift, + ICompletedDialogue, + IDialogueClient } from "../../types/inventoryTypes/inventoryTypes"; import { IOid } from "../../types/commonTypes"; import { @@ -710,6 +715,60 @@ crewShipSchema.set("toJSON", { } }); +const dialogueGiftSchema = new Schema( + { + Item: String, + GiftedQuantity: Number + }, + { _id: false } +); + +const completedDialogueSchema = new Schema( + { + Id: { type: String, required: true }, + Booleans: { type: [String], required: true }, + Choices: { type: [Number], required: true } + }, + { _id: false } +); + +const dialogueSchema = new Schema( + { + Rank: Number, + Chemistry: Number, + AvailableDate: Date, + AvailableGiftDate: Date, + RankUpExpiry: Date, + BountyChemExpiry: Date, + //QueuedDialogues: ??? + Gifts: { type: [dialogueGiftSchema], default: [] }, + Booleans: { type: [String], default: [] }, + Completed: { type: [completedDialogueSchema], default: [] }, + DialogueName: String + }, + { _id: false } +); +dialogueSchema.set("toJSON", { + virtuals: true, + transform(_doc, ret) { + const db = ret as IDialogueDatabase; + const client = ret as IDialogueClient; + + client.AvailableDate = toMongoDate(db.AvailableDate); + client.AvailableGiftDate = toMongoDate(db.AvailableGiftDate); + client.RankUpExpiry = toMongoDate(db.RankUpExpiry); + client.BountyChemExpiry = toMongoDate(db.BountyChemExpiry); + } +}); + +const dialogueHistorySchema = new Schema( + { + YearIteration: { type: Number, required: true }, + Dialogues: { type: [dialogueSchema], required: false } + }, + { _id: false } +); + const inventorySchema = new Schema( { accountOwnerId: Schema.Types.ObjectId, @@ -1069,7 +1128,9 @@ const inventorySchema = new Schema( //Grustag three DeathSquadable: Boolean, - EndlessXP: { type: [endlessXpProgressSchema], default: undefined } + EndlessXP: { type: [endlessXpProgressSchema], default: undefined }, + + DialogueHistory: dialogueHistorySchema }, { timestamps: { createdAt: "Created" } } ); diff --git a/src/routes/api.ts b/src/routes/api.ts index c6b644ec..a164b455 100644 --- a/src/routes/api.ts +++ b/src/routes/api.ts @@ -6,6 +6,7 @@ import { archonFusionController } from "@/src/controllers/api/archonFusionContro import { artifactsController } from "../controllers/api/artifactsController"; import { checkDailyMissionBonusController } from "@/src/controllers/api/checkDailyMissionBonusController"; import { claimCompletedRecipeController } from "@/src/controllers/api/claimCompletedRecipeController"; +import { clearDialogueHistoryController } from "@/src/controllers/api/clearDialogueHistoryController"; import { createGuildController } from "@/src/controllers/api/createGuildController"; import { creditsController } from "@/src/controllers/api/creditsController"; import { deleteSessionController } from "@/src/controllers/api/deleteSessionController"; @@ -52,6 +53,7 @@ import { projectionManagerController } from "../controllers/api/projectionManage import { purchaseController } from "@/src/controllers/api/purchaseController"; import { queueDojoComponentDestructionController } from "@/src/controllers/api/queueDojoComponentDestructionController"; import { rerollRandomModController } from "@/src/controllers/api/rerollRandomModController"; +import { saveDialogueController } from "@/src/controllers/api/saveDialogueController"; import { saveLoadoutController } from "@/src/controllers/api/saveLoadout"; import { sellController } from "@/src/controllers/api/sellController"; import { setActiveQuestController } from "@/src/controllers/api/setActiveQuestController"; @@ -118,6 +120,7 @@ apiRouter.post("/arcaneCommon.php", arcaneCommonController); apiRouter.post("/archonFusion.php", archonFusionController); apiRouter.post("/artifacts.php", artifactsController); apiRouter.post("/claimCompletedRecipe.php", claimCompletedRecipeController); +apiRouter.post("/clearDialogueHistory.php", clearDialogueHistoryController); apiRouter.post("/createGuild.php", createGuildController); apiRouter.post("/endlessXp.php", endlessXpController); apiRouter.post("/evolveWeapon.php", evolveWeaponController); @@ -142,6 +145,7 @@ apiRouter.post("/playerSkills.php", playerSkillsController); apiRouter.post("/projectionManager.php", projectionManagerController); apiRouter.post("/purchase.php", purchaseController); apiRouter.post("/rerollRandomMod.php", rerollRandomModController); +apiRouter.post("/saveDialogue.php", saveDialogueController); apiRouter.post("/saveLoadout.php", saveLoadoutController); apiRouter.post("/sell.php", sellController); apiRouter.post("/setEquippedInstrument.php", setEquippedInstrumentController); diff --git a/src/types/inventoryTypes/inventoryTypes.ts b/src/types/inventoryTypes/inventoryTypes.ts index de789419..23d06673 100644 --- a/src/types/inventoryTypes/inventoryTypes.ts +++ b/src/types/inventoryTypes/inventoryTypes.ts @@ -306,6 +306,7 @@ export interface IInventoryResponse extends IDailyAffiliations { Harvestable: boolean; DeathSquadable: boolean; EndlessXP?: IEndlessXpProgress[]; + DialogueHistory?: IDialogueHistoryDatabase; } export interface IAffiliation { @@ -948,3 +949,46 @@ export interface IEndlessXpProgress { Category: TEndlessXpCategory; Choices: string[]; } + +export interface IDialogueHistoryClient { + YearIteration: number; + Dialogues?: IDialogueClient[]; +} + +export interface IDialogueHistoryDatabase { + YearIteration: number; + Dialogues?: IDialogueDatabase[]; +} + +export interface IDialogueClient { + Rank: number; + Chemistry: number; + AvailableDate: IMongoDate; + AvailableGiftDate: IMongoDate; + RankUpExpiry: IMongoDate; + BountyChemExpiry: IMongoDate; + //QueuedDialogues: any[]; + Gifts: IDialogueGift[]; + Booleans: string[]; + Completed: ICompletedDialogue[]; + DialogueName: string; +} + +export interface IDialogueDatabase + extends Omit { + AvailableDate: Date; + AvailableGiftDate: Date; + RankUpExpiry: Date; + BountyChemExpiry: Date; +} + +export interface IDialogueGift { + Item: string; + GiftedQuantity: number; +} + +export interface ICompletedDialogue { + Id: string; + Booleans: string[]; + Choices: number[]; +}