Compare commits

..

4 Commits

Author SHA1 Message Date
d62ef9bbf3 fix: don't give level mission credits on free roam missions (#881)
Reviewed-on: http://209.141.38.3/OpenWF/SpaceNinjaServer/pulls/881
Co-authored-by: Ordis <134585663+OrdisPrime@users.noreply.github.com>
Co-committed-by: Ordis <134585663+OrdisPrime@users.noreply.github.com>
2025-02-01 08:20:11 -08:00
5460ccf93d feat: loc-pin saving (#879)
Closes #404

Co-authored-by: Sainan <sainan@calamity.inc>
Reviewed-on: http://209.141.38.3/OpenWF/SpaceNinjaServer/pulls/879
Reviewed-by: Sainan <sainan@noreply.localhost>
Co-authored-by: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com>
Co-committed-by: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com>
2025-02-01 07:41:34 -08:00
53ce6ccce2 fix: subtract standing gained in missions from daily bin (#880)
Fixes #794

Reviewed-on: http://209.141.38.3/OpenWF/SpaceNinjaServer/pulls/880
Co-authored-by: Sainan <sainan@calamity.inc>
Co-committed-by: Sainan <sainan@calamity.inc>
2025-02-01 07:32:56 -08:00
edc3171eee feat: Quests 2 (#878) 2025-02-01 16:31:04 +01:00
16 changed files with 295 additions and 81 deletions

8
package-lock.json generated
View File

@ -12,7 +12,7 @@
"copyfiles": "^2.4.1", "copyfiles": "^2.4.1",
"express": "^5", "express": "^5",
"mongoose": "^8.9.4", "mongoose": "^8.9.4",
"warframe-public-export-plus": "^0.5.24", "warframe-public-export-plus": "^0.5.26",
"warframe-riven-info": "^0.1.2", "warframe-riven-info": "^0.1.2",
"winston": "^3.17.0", "winston": "^3.17.0",
"winston-daily-rotate-file": "^5.0.0" "winston-daily-rotate-file": "^5.0.0"
@ -4092,9 +4092,9 @@
} }
}, },
"node_modules/warframe-public-export-plus": { "node_modules/warframe-public-export-plus": {
"version": "0.5.25", "version": "0.5.26",
"resolved": "https://registry.npmjs.org/warframe-public-export-plus/-/warframe-public-export-plus-0.5.25.tgz", "resolved": "https://registry.npmjs.org/warframe-public-export-plus/-/warframe-public-export-plus-0.5.26.tgz",
"integrity": "sha512-vYEm6q7zECk+2xi9pb6KEksoNNEqSqcrbEtwuEDCySAzZJwbE8iErFtTA2ZAK81LOG42i8yDMevvldyke/Sr3A==" "integrity": "sha512-oRcz14nBHcwx5CgCbxw5bhesiJtVlspuHyqzjOwRu63c2Vs+1TgfqbVGdqc9sm3AZL9eJhtay+khc1h7r3BM/A=="
}, },
"node_modules/warframe-riven-info": { "node_modules/warframe-riven-info": {
"version": "0.1.2", "version": "0.1.2",

View File

@ -16,7 +16,7 @@
"copyfiles": "^2.4.1", "copyfiles": "^2.4.1",
"express": "^5", "express": "^5",
"mongoose": "^8.9.4", "mongoose": "^8.9.4",
"warframe-public-export-plus": "^0.5.24", "warframe-public-export-plus": "^0.5.26",
"warframe-riven-info": "^0.1.2", "warframe-riven-info": "^0.1.2",
"winston": "^3.17.0", "winston": "^3.17.0",
"winston-daily-rotate-file": "^5.0.0" "winston-daily-rotate-file": "^5.0.0"

View File

@ -2,40 +2,21 @@ import { RequestHandler } from "express";
import { isEmptyObject, parseString } from "@/src/helpers/general"; import { isEmptyObject, parseString } from "@/src/helpers/general";
import { getJSONfromString } from "@/src/helpers/stringHelpers"; import { getJSONfromString } from "@/src/helpers/stringHelpers";
import { addKeyChainItems, getInventory } from "@/src/services/inventoryService"; import { addKeyChainItems, getInventory } from "@/src/services/inventoryService";
import { IGroup } from "@/src/types/loginTypes";
import { updateQuestStage } from "@/src/services/questService";
export const giveKeyChainTriggeredItemsController: RequestHandler = async (req, res) => { export const giveKeyChainTriggeredItemsController: RequestHandler = async (req, res) => {
const accountId = parseString(req.query.accountId); const accountId = parseString(req.query.accountId);
const keyChainTriggeredItemsRequest = getJSONfromString<IGiveKeyChainTriggeredItemsRequest>( const keyChainInfo = getJSONfromString<IKeyChainRequest>((req.body as string).toString());
(req.body as string).toString()
);
const inventory = await getInventory(accountId); const inventory = await getInventory(accountId);
const inventoryChanges = await addKeyChainItems(inventory, keyChainTriggeredItemsRequest); const inventoryChanges = await addKeyChainItems(inventory, keyChainInfo);
if (isEmptyObject(inventoryChanges)) { if (isEmptyObject(inventoryChanges)) {
throw new Error("inventory changes was empty after getting keychain items: should not happen"); 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) // items were added: update quest stage's i (item was given)
const quest = inventory.QuestKeys.find(quest => quest.ItemType === keyChainTriggeredItemsRequest.KeyChain); updateQuestStage(inventory, keyChainInfo, { i: true });
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(); await inventory.save();
res.send(inventoryChanges); res.send(inventoryChanges);
@ -50,7 +31,8 @@ export const giveKeyChainTriggeredItemsController: RequestHandler = async (req,
//{"WishlistChanges":["/Lotus/Types/Items/ShipFeatureItems/ArsenalFeatureItem"],"MiscItems":[{"ItemType":"/Lotus/Types/Items/ShipFeatureItems/ArsenalFeatureItem","ItemCount":1}]} //{"WishlistChanges":["/Lotus/Types/Items/ShipFeatureItems/ArsenalFeatureItem"],"MiscItems":[{"ItemType":"/Lotus/Types/Items/ShipFeatureItems/ArsenalFeatureItem","ItemCount":1}]}
}; };
export interface IGiveKeyChainTriggeredItemsRequest { export interface IKeyChainRequest {
KeyChain: string; KeyChain: string;
ChainStage: number; ChainStage: number;
Groups?: IGroup[];
} }

View File

@ -0,0 +1,36 @@
import { IKeyChainRequest } from "@/src/controllers/api/giveKeyChainTriggeredItemsController";
import { IMessage } from "@/src/models/inboxModel";
import { createMessage } from "@/src/services/inboxService";
import { getInventory } from "@/src/services/inventoryService";
import { getKeyChainMessage } from "@/src/services/itemDataService";
import { getAccountIdForRequest } from "@/src/services/loginService";
import { updateQuestStage } from "@/src/services/questService";
import { RequestHandler } from "express";
export const giveKeyChainTriggeredMessageController: RequestHandler = async (req, res) => {
const accountId = await getAccountIdForRequest(req);
const keyChainInfo = JSON.parse((req.body as Buffer).toString()) as IKeyChainRequest;
console.log(keyChainInfo);
const keyChainMessage = getKeyChainMessage(keyChainInfo);
const message = {
sndr: keyChainMessage.sender,
msg: keyChainMessage.body,
sub: keyChainMessage.title,
att: keyChainMessage.attachments.length > 0 ? keyChainMessage.attachments : undefined,
icon: keyChainMessage.icon ?? "",
transmission: keyChainMessage.transmission ?? "",
highPriority: keyChainMessage.highPriority ?? false,
r: false
} satisfies IMessage;
const savedMessages = await createMessage(accountId, [message]);
console.log("savedMessages", savedMessages);
const inventory = await getInventory(accountId, "QuestKeys");
updateQuestStage(inventory, keyChainInfo, { m: true });
await inventory.save();
res.send(1);
};

View File

@ -0,0 +1,45 @@
import { getJSONfromString } from "@/src/helpers/stringHelpers";
import { addItem, getInventory } from "@/src/services/inventoryService";
import { getAccountIdForRequest } from "@/src/services/loginService";
import { IOid } from "@/src/types/commonTypes";
import { RequestHandler } from "express";
export const giveQuestKeyRewardController: RequestHandler = async (req, res) => {
const accountId = await getAccountIdForRequest(req);
const rewardRequest = getJSONfromString<IQuestKeyRewardRequest>((req.body as Buffer).toString());
if (Array.isArray(rewardRequest.reward)) {
throw new Error("Multiple rewards not expected");
}
const reward = rewardRequest.reward;
const inventory = await getInventory(accountId);
const inventoryChanges = await addItem(inventory, reward.ItemType, reward.Amount);
await inventory.save();
res.json(inventoryChanges.InventoryChanges);
//TODO: consider whishlist changes
};
export interface IQuestKeyRewardRequest {
reward: IQuestKeyReward;
}
export interface IQuestKeyReward {
RewardType: string;
CouponType: string;
Icon: string;
ItemType: string;
StoreItemType: string;
ProductCategory: string;
Amount: number;
ScalingMultiplier: number;
Durability: string;
DisplayName: string;
Duration: number;
CouponSku: number;
Syndicate: string;
Milestones: any[];
ChooseSetIndex: number;
NewSystemReward: boolean;
_id: IOid;
}

View File

@ -4,7 +4,8 @@ import { logger } from "@/src/utils/logger";
import { getJSONfromString } from "@/src/helpers/stringHelpers"; import { getJSONfromString } from "@/src/helpers/stringHelpers";
import { updateQuestKey, IUpdateQuestRequest } from "@/src/services/questService"; import { updateQuestKey, IUpdateQuestRequest } from "@/src/services/questService";
import { getQuestCompletionItems } from "@/src/services/itemDataService"; import { getQuestCompletionItems } from "@/src/services/itemDataService";
import { addItem, combineInventoryChanges, getInventory } from "@/src/services/inventoryService"; import { addItems, getInventory } from "@/src/services/inventoryService";
import { IInventoryChanges } from "@/src/types/purchaseTypes";
// eslint-disable-next-line @typescript-eslint/no-misused-promises // eslint-disable-next-line @typescript-eslint/no-misused-promises
export const updateQuestController: RequestHandler = async (req, res) => { export const updateQuestController: RequestHandler = async (req, res) => {
@ -18,24 +19,28 @@ export const updateQuestController: RequestHandler = async (req, res) => {
const inventory = await getInventory(accountId); const inventory = await getInventory(accountId);
const updateQuestResponse: { CustomData?: string; InventoryChanges?: IInventoryChanges; MissionRewards: [] } = {
MissionRewards: []
};
updateQuestKey(inventory, updateQuestRequest.QuestKeys); updateQuestKey(inventory, updateQuestRequest.QuestKeys);
if (updateQuestRequest.QuestKeys[0].Completed) { if (updateQuestRequest.QuestKeys[0].Completed) {
logger.debug(`completed quest ${updateQuestRequest.QuestKeys[0].ItemType} `); logger.debug(`completed quest ${updateQuestRequest.QuestKeys[0].ItemType} `);
const questKeyName = updateQuestRequest.QuestKeys[0].ItemType; const questKeyName = updateQuestRequest.QuestKeys[0].ItemType;
const questCompletionItems = getQuestCompletionItems(questKeyName); const questCompletionItems = getQuestCompletionItems(questKeyName);
logger.debug(`quest completion items`, questCompletionItems);
logger.debug(`quest completion items { ${questCompletionItems.map(item => item.ItemType).join(", ")} }`); const inventoryChanges = await addItems(inventory, questCompletionItems);
inventory.ActiveQuest = "";
const inventoryChanges = {}; updateQuestResponse.InventoryChanges = inventoryChanges;
for (const item of questCompletionItems) {
const inventoryDelta = await addItem(inventory, item.ItemType, item.ItemCount);
combineInventoryChanges(inventoryChanges, inventoryDelta.InventoryChanges);
} }
res.json({ MissionRewards: [], inventoryChanges });
return; //TODO: might need to parse the custom data and add the associated items to inventory
if (updateQuestRequest.QuestKeys[0].CustomData) {
updateQuestResponse.CustomData = updateQuestRequest.QuestKeys[0].CustomData;
} }
await inventory.save(); await inventory.save();
res.send({ MissionRewards: [] }); res.send(updateQuestResponse);
}; };

View File

@ -12,23 +12,26 @@ export interface IMessageClient extends Omit<IMessageDatabase, "_id" | "date" |
messageId: IOid; messageId: IOid;
} }
export interface IMessageDatabase { export interface IMessageDatabase extends IMessage {
ownerId: Types.ObjectId; ownerId: Types.ObjectId;
date: Date; date: Date; //created at
_id: Types.ObjectId; _id: Types.ObjectId;
}
export interface IMessage {
sndr: string; sndr: string;
msg: string; msg: string;
sub: string; sub: string;
icon: string; icon?: string;
highPriority?: boolean; highPriority?: boolean;
lowPrioNewPlayers?: boolean; lowPrioNewPlayers?: boolean;
startDate?: Date; startDate?: Date;
endDate?: Date; endDate?: Date;
r?: boolean;
att?: string[]; att?: string[];
countedAtt?: ITypeCount[]; countedAtt?: ITypeCount[];
transmission?: string; transmission?: string;
arg?: Arg[]; arg?: Arg[];
r?: boolean;
} }
export interface Arg { export interface Arg {

View File

@ -60,7 +60,10 @@ import {
ITraits, ITraits,
IKubrowPetDetailsClient, IKubrowPetDetailsClient,
IKubrowPetEggDatabase, IKubrowPetEggDatabase,
IKubrowPetEggClient IKubrowPetEggClient,
ICustomMarkers,
IMarkerInfo,
IMarker
} from "../../types/inventoryTypes/inventoryTypes"; } from "../../types/inventoryTypes/inventoryTypes";
import { IOid } from "../../types/commonTypes"; import { IOid } from "../../types/commonTypes";
import { import {
@ -479,19 +482,22 @@ const helminthResourceSchema = new Schema<IHelminthResource>(
{ _id: false } { _id: false }
); );
const questProgressSchema = new Schema<IQuestStage>({ const questProgressSchema = new Schema<IQuestStage>(
{
c: Number, c: Number,
i: Boolean, i: Boolean,
m: Boolean, m: Boolean,
b: [] b: []
}); },
{ _id: false }
);
const questKeysSchema = new Schema<IQuestKeyDatabase>( const questKeysSchema = new Schema<IQuestKeyDatabase>(
{ {
Progress: { type: [questProgressSchema], default: undefined }, Progress: { type: [questProgressSchema], default: undefined },
unlock: Boolean, unlock: Boolean,
Completed: Boolean, Completed: Boolean,
//CustomData: Schema.Types.Mixed, CustomData: String,
CompletionDate: Date, CompletionDate: Date,
ItemType: String ItemType: String
}, },
@ -846,6 +852,35 @@ infestedFoundrySchema.set("toJSON", {
} }
}); });
const markerSchema = new Schema<IMarker>(
{
anchorName: String,
color: Number,
label: String,
x: Number,
y: Number,
z: Number,
showInHud: Boolean
},
{ _id: false }
);
const markerInfoSchema = new Schema<IMarkerInfo>(
{
icon: String,
markers: [markerSchema]
},
{ _id: false }
);
const CustomMarkersSchema = new Schema<ICustomMarkers>(
{
tag: String,
markerInfos: [markerInfoSchema]
},
{ _id: false }
);
const inventorySchema = new Schema<IInventoryDatabase, InventoryDocumentProps>( const inventorySchema = new Schema<IInventoryDatabase, InventoryDocumentProps>(
{ {
accountOwnerId: Schema.Types.ObjectId, accountOwnerId: Schema.Types.ObjectId,
@ -1130,6 +1165,9 @@ const inventorySchema = new Schema<IInventoryDatabase, InventoryDocumentProps>(
//https://warframe.fandom.com/wiki/Incarnon //https://warframe.fandom.com/wiki/Incarnon
EvolutionProgress: { type: [evolutionProgressSchema], default: undefined }, EvolutionProgress: { type: [evolutionProgressSchema], default: undefined },
//https://warframe.fandom.com/wiki/Loc-Pin
CustomMarkers: { type: [CustomMarkersSchema], default: undefined },
//Unknown and system //Unknown and system
DuviriInfo: DuviriInfoSchema, DuviriInfo: DuviriInfoSchema,
Mailbox: MailboxSchema, Mailbox: MailboxSchema,

View File

@ -82,6 +82,8 @@ import { updateQuestController } from "@/src/controllers/api/updateQuestControll
import { updateSessionGetController, updateSessionPostController } from "@/src/controllers/api/updateSessionController"; import { updateSessionGetController, updateSessionPostController } from "@/src/controllers/api/updateSessionController";
import { updateThemeController } from "../controllers/api/updateThemeController"; import { updateThemeController } from "../controllers/api/updateThemeController";
import { upgradesController } from "@/src/controllers/api/upgradesController"; import { upgradesController } from "@/src/controllers/api/upgradesController";
import { giveKeyChainTriggeredMessageController } from "@/src/controllers/api/giveKeyChainTriggeredMessageController";
import { giveQuestKeyRewardController } from "@/src/controllers/api/giveQuestKey";
const apiRouter = express.Router(); const apiRouter = express.Router();
@ -138,6 +140,8 @@ apiRouter.post("/getAlliance.php", getAllianceController);
apiRouter.post("/getVoidProjectionRewards.php", getVoidProjectionRewardsController); apiRouter.post("/getVoidProjectionRewards.php", getVoidProjectionRewardsController);
apiRouter.post("/gildWeapon.php", gildWeaponController); apiRouter.post("/gildWeapon.php", gildWeaponController);
apiRouter.post("/giveKeyChainTriggeredItems.php", giveKeyChainTriggeredItemsController); apiRouter.post("/giveKeyChainTriggeredItems.php", giveKeyChainTriggeredItemsController);
apiRouter.post("/giveKeyChainTriggeredMessage.php", giveKeyChainTriggeredMessageController);
apiRouter.post("/giveQuestKeyReward.php", giveQuestKeyRewardController);
apiRouter.post("/guildTech.php", guildTechController); apiRouter.post("/guildTech.php", guildTechController);
apiRouter.post("/hostSession.php", hostSessionController); apiRouter.post("/hostSession.php", hostSessionController);
apiRouter.post("/infestedFoundry.php", infestedFoundryController); apiRouter.post("/infestedFoundry.php", infestedFoundryController);

View File

@ -46,7 +46,6 @@ export const createNewEventMessages = async (req: Request) => {
prev.eventMessageDate > current.eventMessageDate ? prev : current prev.eventMessageDate > current.eventMessageDate ? prev : current
); );
console.log("latestEventMessage", latestEventMessage);
account.LatestEventMessageDate = new Date(latestEventMessage.eventMessageDate); account.LatestEventMessageDate = new Date(latestEventMessage.eventMessageDate);
await account.save(); await account.save();
}; };

View File

@ -41,13 +41,14 @@ import {
ExportRecipes, ExportRecipes,
ExportResources, ExportResources,
ExportSentinels, ExportSentinels,
ExportSyndicates,
ExportUpgrades, ExportUpgrades,
ExportWeapons, ExportWeapons,
TStandingLimitBin TStandingLimitBin
} from "warframe-public-export-plus"; } from "warframe-public-export-plus";
import { createShip } from "./shipService"; import { createShip } from "./shipService";
import { creditBundles, fusionBundles } from "@/src/services/missionInventoryUpdateService"; import { creditBundles, fusionBundles } from "@/src/services/missionInventoryUpdateService";
import { IGiveKeyChainTriggeredItemsRequest } from "@/src/controllers/api/giveKeyChainTriggeredItemsController"; import { IKeyChainRequest } from "@/src/controllers/api/giveKeyChainTriggeredItemsController";
import { toOid } from "../helpers/inventoryHelpers"; import { toOid } from "../helpers/inventoryHelpers";
export const createInventory = async ( export const createInventory = async (
@ -499,11 +500,16 @@ export const addItem = async (
export const addItems = async ( export const addItems = async (
inventory: TInventoryDatabaseDocument, inventory: TInventoryDatabaseDocument,
items: ITypeCount[], items: ITypeCount[] | string[],
inventoryChanges: IInventoryChanges = {} inventoryChanges: IInventoryChanges = {}
): Promise<IInventoryChanges> => { ): Promise<IInventoryChanges> => {
let inventoryDelta;
for (const item of items) { for (const item of items) {
const inventoryDelta = await addItem(inventory, item.ItemType, item.ItemCount); if (typeof item === "string") {
inventoryDelta = await addItem(inventory, item);
} else {
inventoryDelta = await addItem(inventory, item.ItemType, item.ItemCount);
}
combineInventoryChanges(inventoryChanges, inventoryDelta.InventoryChanges); combineInventoryChanges(inventoryChanges, inventoryDelta.InventoryChanges);
} }
return inventoryChanges; return inventoryChanges;
@ -1049,12 +1055,11 @@ export const addBooster = (ItemType: string, time: number, inventory: TInventory
export const updateSyndicate = ( export const updateSyndicate = (
inventory: HydratedDocument<IInventoryDatabase, InventoryDocumentProps>, inventory: HydratedDocument<IInventoryDatabase, InventoryDocumentProps>,
syndicateUpdate: IMissionInventoryUpdateRequest["AffiliationChanges"] syndicateUpdate: IMissionInventoryUpdateRequest["AffiliationChanges"]
) => { ): void => {
syndicateUpdate?.forEach(affiliation => { syndicateUpdate?.forEach(affiliation => {
const syndicate = inventory.Affiliations.find(x => x.Tag == affiliation.Tag); const syndicate = inventory.Affiliations.find(x => x.Tag == affiliation.Tag);
if (syndicate !== undefined) { if (syndicate !== undefined) {
syndicate.Standing = syndicate.Standing += affiliation.Standing;
syndicate.Standing === undefined ? affiliation.Standing : syndicate.Standing + affiliation.Standing;
syndicate.Title = syndicate.Title === undefined ? affiliation.Title : syndicate.Title + affiliation.Title; syndicate.Title = syndicate.Title === undefined ? affiliation.Title : syndicate.Title + affiliation.Title;
} else { } else {
inventory.Affiliations.push({ inventory.Affiliations.push({
@ -1065,8 +1070,8 @@ export const updateSyndicate = (
FreeFavorsUsed: [] FreeFavorsUsed: []
}); });
} }
updateStandingLimit(inventory, ExportSyndicates[affiliation.Tag].dailyLimitBin, affiliation.Standing);
}); });
return { AffiliationMods: [] };
}; };
/** /**
@ -1074,7 +1079,7 @@ export const updateSyndicate = (
*/ */
export const addKeyChainItems = async ( export const addKeyChainItems = async (
inventory: TInventoryDatabaseDocument, inventory: TInventoryDatabaseDocument,
keyChainData: IGiveKeyChainTriggeredItemsRequest keyChainData: IKeyChainRequest
): Promise<IInventoryChanges> => { ): Promise<IInventoryChanges> => {
const keyChainItems = getKeyChainItems(keyChainData); const keyChainItems = getKeyChainItems(keyChainData);
@ -1092,5 +1097,7 @@ export const addKeyChainItems = async (
combineInventoryChanges(inventoryChanges, inventoryChangesDelta.InventoryChanges); combineInventoryChanges(inventoryChanges, inventoryChangesDelta.InventoryChanges);
} }
await addItems(inventory, nonStoreItems);
return inventoryChanges; return inventoryChanges;
}; };

View File

@ -1,4 +1,4 @@
import { IGiveKeyChainTriggeredItemsRequest } from "@/src/controllers/api/giveKeyChainTriggeredItemsController"; import { IKeyChainRequest } from "@/src/controllers/api/giveKeyChainTriggeredItemsController";
import { getIndexAfter } from "@/src/helpers/stringHelpers"; import { getIndexAfter } from "@/src/helpers/stringHelpers";
import { ITypeCount } from "@/src/types/inventoryTypes/inventoryTypes"; import { ITypeCount } from "@/src/types/inventoryTypes/inventoryTypes";
import { logger } from "@/src/utils/logger"; import { logger } from "@/src/utils/logger";
@ -142,7 +142,7 @@ export const getString = (key: string, dict: Record<string, string>): string =>
return dict[key] ?? key; return dict[key] ?? key;
}; };
export const getKeyChainItems = ({ KeyChain, ChainStage }: IGiveKeyChainTriggeredItemsRequest): string[] => { export const getKeyChainItems = ({ KeyChain, ChainStage }: IKeyChainRequest): string[] => {
const chainStages = ExportKeys[KeyChain].chainStages; const chainStages = ExportKeys[KeyChain].chainStages;
if (!chainStages) { if (!chainStages) {
throw new Error(`KeyChain ${KeyChain} does not contain chain stages`); throw new Error(`KeyChain ${KeyChain} does not contain chain stages`);
@ -154,7 +154,9 @@ export const getKeyChainItems = ({ KeyChain, ChainStage }: IGiveKeyChainTriggere
} }
if (keyChainStage.itemsToGiveWhenTriggered.length === 0) { if (keyChainStage.itemsToGiveWhenTriggered.length === 0) {
throw new Error(`No items to give for KeyChain ${KeyChain} at stage ${ChainStage}`); throw new Error(
`client requested key chain items in KeyChain ${KeyChain} at stage ${ChainStage}, but they did not exist`
);
} }
return keyChainStage.itemsToGiveWhenTriggered; return keyChainStage.itemsToGiveWhenTriggered;
@ -194,3 +196,24 @@ export const getQuestCompletionItems = (questKey: string) => {
return items; return items;
}; };
export const getKeyChainMessage = ({ KeyChain, ChainStage }: IKeyChainRequest) => {
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`);
}
const chainStageMessage = keyChainStage.messageToSendWhenTriggered;
if (!chainStageMessage) {
throw new Error(
`client requested key chain message in keychain ${KeyChain} at stage ${ChainStage} but they did not exist`
);
}
return chainStageMessage;
};

View File

@ -155,6 +155,20 @@ export const addMissionInventoryUpdates = (
inventory.PlayerSkills.LPP_DRIFTER += value.LPP_DRIFTER; inventory.PlayerSkills.LPP_DRIFTER += value.LPP_DRIFTER;
break; break;
} }
case "CustomMarkers": {
value.forEach(markers => {
const map = inventory.CustomMarkers
? inventory.CustomMarkers.find(entry => entry.tag == markers.tag)
: undefined;
if (map) {
map.markerInfos = markers.markerInfos;
} else {
inventory.CustomMarkers ??= [];
inventory.CustomMarkers.push(markers);
}
});
break;
}
default: default:
// Equipment XP updates // Equipment XP updates
if (equipmentKeys.includes(key as TEquipmentKey)) { if (equipmentKeys.includes(key as TEquipmentKey)) {
@ -210,11 +224,15 @@ export const addMissionRewards = async (
} }
if (missions) { if (missions) {
const node = getNode(missions.Tag);
if (node.missionIndex !== 28) {
const levelCreditReward = getLevelCreditRewards(missions?.Tag); const levelCreditReward = getLevelCreditRewards(missions?.Tag);
missionCompletionCredits += levelCreditReward; missionCompletionCredits += levelCreditReward;
inventory.RegularCredits += levelCreditReward; inventory.RegularCredits += levelCreditReward;
logger.debug(`levelCreditReward ${levelCreditReward}`); logger.debug(`levelCreditReward ${levelCreditReward}`);
} }
}
//TODO: resolve issue with creditbundles //TODO: resolve issue with creditbundles
for (const reward of MissionRewards) { for (const reward of MissionRewards) {

View File

@ -1,7 +1,18 @@
import { IInventoryDatabase, IQuestKeyDatabase } from "@/src/types/inventoryTypes/inventoryTypes"; import { IKeyChainRequest } from "@/src/controllers/api/giveKeyChainTriggeredItemsController";
import { TInventoryDatabaseDocument } from "@/src/models/inventoryModels/inventoryModel";
import { IInventoryDatabase, IQuestKeyDatabase, IQuestStage } from "@/src/types/inventoryTypes/inventoryTypes";
import { logger } from "@/src/utils/logger"; import { logger } from "@/src/utils/logger";
import { HydratedDocument } from "mongoose"; import { HydratedDocument } from "mongoose";
export interface IUpdateQuestRequest {
QuestKeys: Omit<IQuestKeyDatabase, "CompletionDate">[];
PS: string;
questCompletion: boolean;
PlayerShipEvents: unknown[];
crossPlaySetting: string;
DoQuestReward: boolean;
}
export const updateQuestKey = ( export const updateQuestKey = (
inventory: HydratedDocument<IInventoryDatabase>, inventory: HydratedDocument<IInventoryDatabase>,
questKeyUpdate: IUpdateQuestRequest["QuestKeys"] questKeyUpdate: IUpdateQuestRequest["QuestKeys"]
@ -17,6 +28,7 @@ export const updateQuestKey = (
throw new Error(`quest key ${questKeyUpdate[0].ItemType} not found`); throw new Error(`quest key ${questKeyUpdate[0].ItemType} not found`);
} }
console.log(questKeyUpdate[0]);
inventory.QuestKeys[questKeyIndex] = questKeyUpdate[0]; inventory.QuestKeys[questKeyIndex] = questKeyUpdate[0];
if (questKeyUpdate[0].Completed) { if (questKeyUpdate[0].Completed) {
@ -24,11 +36,30 @@ export const updateQuestKey = (
} }
}; };
export interface IUpdateQuestRequest { export const updateQuestStage = (
QuestKeys: Omit<IQuestKeyDatabase, "CompletionDate">[]; inventory: TInventoryDatabaseDocument,
PS: string; { KeyChain, ChainStage }: IKeyChainRequest,
questCompletion: boolean; questStageUpdate: IQuestStage
PlayerShipEvents: unknown[]; ): void => {
crossPlaySetting: string; const quest = inventory.QuestKeys.find(quest => quest.ItemType === KeyChain);
DoQuestReward: boolean;
} if (!quest) {
throw new Error(`Quest ${KeyChain} not found in QuestKeys`);
}
if (!quest.Progress) {
throw new Error(`Progress should always exist when giving keychain triggered items or messages`);
}
const questStage = quest.Progress[ChainStage];
if (!questStage) {
const questStageIndex = quest.Progress.push(questStageUpdate) - 1;
if (questStageIndex !== ChainStage) {
throw new Error(`Quest stage index mismatch: ${questStageIndex} !== ${ChainStage}`);
}
return;
}
Object.assign(questStage, questStageUpdate);
};

View File

@ -86,7 +86,7 @@ export interface IQuestKeyDatabase {
Progress?: IQuestStage[]; Progress?: IQuestStage[];
unlock?: boolean; unlock?: boolean;
Completed?: boolean; Completed?: boolean;
CustomData?: string; //TODO: check whether this actually exists CustomData?: string;
ItemType: string; ItemType: string;
CompletionDate?: Date; CompletionDate?: Date;
} }
@ -343,6 +343,7 @@ export interface IInventoryClient extends IDailyAffiliations {
LastInventorySync: IOid; LastInventorySync: IOid;
NextRefill: IMongoDate; // Next time argon crystals will have a decay tick NextRefill: IMongoDate; // Next time argon crystals will have a decay tick
FoundToday?: IMiscItem[]; // for Argon Crystals FoundToday?: IMiscItem[]; // for Argon Crystals
CustomMarkers: ICustomMarkers[];
ActiveLandscapeTraps: any[]; ActiveLandscapeTraps: any[];
EvolutionProgress?: IEvolutionProgress[]; EvolutionProgress?: IEvolutionProgress[];
RepVotes: any[]; RepVotes: any[];
@ -1056,3 +1057,23 @@ export interface ICompletedDialogue {
Booleans: string[]; Booleans: string[];
Choices: number[]; Choices: number[];
} }
export interface ICustomMarkers {
tag: string;
markerInfos: IMarkerInfo[];
}
export interface IMarkerInfo {
icon: string;
markers: IMarker[];
}
export interface IMarker {
anchorName: string;
color: number;
label?: string;
x: number;
y: number;
z: number;
showInHud: boolean;
}

View File

@ -11,8 +11,9 @@ import {
TSolarMapRegion, TSolarMapRegion,
TEquipmentKey, TEquipmentKey,
IFusionTreasure, IFusionTreasure,
IQuestKeyClient, ICustomMarkers,
IPlayerSkills IPlayerSkills,
IQuestKeyDatabase
} from "./inventoryTypes/inventoryTypes"; } from "./inventoryTypes/inventoryTypes";
export interface IThemeUpdateRequest { export interface IThemeUpdateRequest {
@ -46,7 +47,7 @@ export type IMissionInventoryUpdateRequest = {
Consumables?: ITypeCount[]; Consumables?: ITypeCount[];
FusionTreasures?: IFusionTreasure[]; FusionTreasures?: IFusionTreasure[];
Recipes?: ITypeCount[]; Recipes?: ITypeCount[];
QuestKeys?: IQuestKeyClient[]; QuestKeys?: Omit<IQuestKeyDatabase, "CompletionDate">[];
RegularCredits?: number; RegularCredits?: number;
MissionFailed: boolean; MissionFailed: boolean;
MissionStatus: IMissionStatus; MissionStatus: IMissionStatus;
@ -75,6 +76,7 @@ export type IMissionInventoryUpdateRequest = {
EvolutionProgress?: IEvolutionProgress[]; EvolutionProgress?: IEvolutionProgress[];
FocusXpIncreases?: number[]; FocusXpIncreases?: number[];
PlayerSkillGains: IPlayerSkills; PlayerSkillGains: IPlayerSkills;
CustomMarkers?: ICustomMarkers[];
} & { } & {
[K in TEquipmentKey]?: IEquipmentClient[]; [K in TEquipmentKey]?: IEquipmentClient[];
}; };