forked from OpenWF/SpaceNinjaServer
feat: conquest progression & rewards (#1791)
Closes #1570 Co-authored-by: Jānis <janisslsm@noreply.localhost> Reviewed-on: OpenWF/SpaceNinjaServer#1791 Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com> Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
This commit is contained in:
parent
ce5b0fc9e2
commit
15aaa28a4f
8
package-lock.json
generated
8
package-lock.json
generated
@ -18,7 +18,7 @@
|
|||||||
"morgan": "^1.10.0",
|
"morgan": "^1.10.0",
|
||||||
"ncp": "^2.0.0",
|
"ncp": "^2.0.0",
|
||||||
"typescript": "^5.5",
|
"typescript": "^5.5",
|
||||||
"warframe-public-export-plus": "^0.5.56",
|
"warframe-public-export-plus": "^0.5.57",
|
||||||
"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"
|
||||||
@ -3789,9 +3789,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/warframe-public-export-plus": {
|
"node_modules/warframe-public-export-plus": {
|
||||||
"version": "0.5.56",
|
"version": "0.5.57",
|
||||||
"resolved": "https://registry.npmjs.org/warframe-public-export-plus/-/warframe-public-export-plus-0.5.56.tgz",
|
"resolved": "https://registry.npmjs.org/warframe-public-export-plus/-/warframe-public-export-plus-0.5.57.tgz",
|
||||||
"integrity": "sha512-px+J7tUm6fkSzwKkvL73ySQReDq9oM1UrHSLM3vbYGBvELM892iBgPYG45okIhScCSdwmmXTiWZTf4x/I4qiNQ=="
|
"integrity": "sha512-CKbg7/2hSDH7I7yYSWwkrP4N2rEAEK1vNEuehj+RD9vMvl1c4u6klHLMwdh+ULxXiW4djWIlNIhs5bi/fm58Mg=="
|
||||||
},
|
},
|
||||||
"node_modules/warframe-riven-info": {
|
"node_modules/warframe-riven-info": {
|
||||||
"version": "0.1.2",
|
"version": "0.1.2",
|
||||||
|
@ -25,7 +25,7 @@
|
|||||||
"morgan": "^1.10.0",
|
"morgan": "^1.10.0",
|
||||||
"ncp": "^2.0.0",
|
"ncp": "^2.0.0",
|
||||||
"typescript": "^5.5",
|
"typescript": "^5.5",
|
||||||
"warframe-public-export-plus": "^0.5.56",
|
"warframe-public-export-plus": "^0.5.57",
|
||||||
"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"
|
||||||
|
@ -21,10 +21,12 @@ export const entratiLabConquestModeController: RequestHandler = async (req, res)
|
|||||||
inventory.EntratiVaultCountResetDate = new Date(weekEnd);
|
inventory.EntratiVaultCountResetDate = new Date(weekEnd);
|
||||||
if (inventory.EntratiLabConquestUnlocked) {
|
if (inventory.EntratiLabConquestUnlocked) {
|
||||||
inventory.EntratiLabConquestUnlocked = 0;
|
inventory.EntratiLabConquestUnlocked = 0;
|
||||||
|
inventory.EntratiLabConquestCacheScoreMission = 0;
|
||||||
inventory.EntratiLabConquestActiveFrameVariants = [];
|
inventory.EntratiLabConquestActiveFrameVariants = [];
|
||||||
}
|
}
|
||||||
if (inventory.EchoesHexConquestUnlocked) {
|
if (inventory.EchoesHexConquestUnlocked) {
|
||||||
inventory.EchoesHexConquestUnlocked = 0;
|
inventory.EchoesHexConquestUnlocked = 0;
|
||||||
|
inventory.EchoesHexConquestCacheScoreMission = 0;
|
||||||
inventory.EchoesHexConquestActiveFrameVariants = [];
|
inventory.EchoesHexConquestActiveFrameVariants = [];
|
||||||
inventory.EchoesHexConquestActiveStickers = [];
|
inventory.EchoesHexConquestActiveStickers = [];
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@ import { addMissionInventoryUpdates, addMissionRewards } from "@/src/services/mi
|
|||||||
import { getInventory } from "@/src/services/inventoryService";
|
import { getInventory } from "@/src/services/inventoryService";
|
||||||
import { getInventoryResponse } from "./inventoryController";
|
import { getInventoryResponse } from "./inventoryController";
|
||||||
import { logger } from "@/src/utils/logger";
|
import { logger } from "@/src/utils/logger";
|
||||||
|
import { IMissionInventoryUpdateResponse } from "@/src/types/missionTypes";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
**** INPUT ****
|
**** INPUT ****
|
||||||
@ -71,8 +72,14 @@ export const missionInventoryUpdateController: RequestHandler = async (req, res)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { MissionRewards, inventoryChanges, credits, AffiliationMods, SyndicateXPItemReward } =
|
const {
|
||||||
await addMissionRewards(inventory, missionReport, firstCompletion);
|
MissionRewards,
|
||||||
|
inventoryChanges,
|
||||||
|
credits,
|
||||||
|
AffiliationMods,
|
||||||
|
SyndicateXPItemReward,
|
||||||
|
ConquestCompletedMissionsCount
|
||||||
|
} = await addMissionRewards(inventory, missionReport, firstCompletion);
|
||||||
|
|
||||||
await inventory.save();
|
await inventory.save();
|
||||||
const inventoryResponse = await getInventoryResponse(inventory, true);
|
const inventoryResponse = await getInventoryResponse(inventory, true);
|
||||||
@ -86,8 +93,9 @@ export const missionInventoryUpdateController: RequestHandler = async (req, res)
|
|||||||
...inventoryUpdates,
|
...inventoryUpdates,
|
||||||
//FusionPoints: inventoryChanges?.FusionPoints, // This in combination with InventoryJson or InventoryChanges seems to just double the number of endo shown, so unsure when this is needed.
|
//FusionPoints: inventoryChanges?.FusionPoints, // This in combination with InventoryJson or InventoryChanges seems to just double the number of endo shown, so unsure when this is needed.
|
||||||
SyndicateXPItemReward,
|
SyndicateXPItemReward,
|
||||||
AffiliationMods
|
AffiliationMods,
|
||||||
});
|
ConquestCompletedMissionsCount
|
||||||
|
} satisfies IMissionInventoryUpdateResponse);
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -43,7 +43,7 @@ import { TInventoryDatabaseDocument } from "@/src/models/inventoryModels/invento
|
|||||||
import { getEntriesUnsafe } from "@/src/utils/ts-utils";
|
import { getEntriesUnsafe } from "@/src/utils/ts-utils";
|
||||||
import { IEquipmentClient } from "@/src/types/inventoryTypes/commonInventoryTypes";
|
import { IEquipmentClient } from "@/src/types/inventoryTypes/commonInventoryTypes";
|
||||||
import { handleStoreItemAcquisition } from "./purchaseService";
|
import { handleStoreItemAcquisition } from "./purchaseService";
|
||||||
import { IMissionReward } from "../types/missionTypes";
|
import { IMissionCredits, IMissionReward } from "../types/missionTypes";
|
||||||
import { crackRelic } from "@/src/helpers/relicHelper";
|
import { crackRelic } from "@/src/helpers/relicHelper";
|
||||||
import { createMessage } from "./inboxService";
|
import { createMessage } from "./inboxService";
|
||||||
import kuriaMessage50 from "@/static/fixed_responses/kuriaMessages/fiftyPercent.json";
|
import kuriaMessage50 from "@/static/fixed_responses/kuriaMessages/fiftyPercent.json";
|
||||||
@ -586,8 +586,140 @@ interface AddMissionRewardsReturnType {
|
|||||||
credits?: IMissionCredits;
|
credits?: IMissionCredits;
|
||||||
AffiliationMods?: IAffiliationMods[];
|
AffiliationMods?: IAffiliationMods[];
|
||||||
SyndicateXPItemReward?: number;
|
SyndicateXPItemReward?: number;
|
||||||
|
ConquestCompletedMissionsCount?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface IConquestReward {
|
||||||
|
at: number;
|
||||||
|
pool: IRngResult[];
|
||||||
|
}
|
||||||
|
|
||||||
|
const labConquestRewards: IConquestReward[] = [
|
||||||
|
{
|
||||||
|
at: 5,
|
||||||
|
pool: ExportRewards[
|
||||||
|
"/Lotus/Types/Game/MissionDecks/EntratiLabConquestRewards/EntratiLabConquestSilverRewards"
|
||||||
|
][0] as IRngResult[]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
at: 10,
|
||||||
|
pool: ExportRewards[
|
||||||
|
"/Lotus/Types/Game/MissionDecks/EntratiLabConquestRewards/EntratiLabConquestSilverRewards"
|
||||||
|
][0] as IRngResult[]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
at: 15,
|
||||||
|
pool: [
|
||||||
|
{
|
||||||
|
type: "/Lotus/StoreItems/Types/Gameplay/EntratiLab/Resources/EntratiLanthornBundle",
|
||||||
|
itemCount: 3,
|
||||||
|
probability: 1
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
at: 20,
|
||||||
|
pool: ExportRewards[
|
||||||
|
"/Lotus/Types/Game/MissionDecks/EntratiLabConquestRewards/EntratiLabConquestGoldRewards"
|
||||||
|
][0] as IRngResult[]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
at: 28,
|
||||||
|
pool: [
|
||||||
|
{
|
||||||
|
type: "/Lotus/StoreItems/Types/Items/MiscItems/DistillPoints",
|
||||||
|
itemCount: 20,
|
||||||
|
probability: 1
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
at: 31,
|
||||||
|
pool: ExportRewards[
|
||||||
|
"/Lotus/Types/Game/MissionDecks/EntratiLabConquestRewards/EntratiLabConquestGoldRewards"
|
||||||
|
][0] as IRngResult[]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
at: 34,
|
||||||
|
pool: ExportRewards[
|
||||||
|
"/Lotus/Types/Game/MissionDecks/EntratiLabConquestRewards/EntratiLabConquestArcaneRewards"
|
||||||
|
][0] as IRngResult[]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
at: 37,
|
||||||
|
pool: [
|
||||||
|
{
|
||||||
|
type: "/Lotus/StoreItems/Types/Items/MiscItems/DistillPoints",
|
||||||
|
itemCount: 50,
|
||||||
|
probability: 1
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
const hexConquestRewards: IConquestReward[] = [
|
||||||
|
{
|
||||||
|
at: 5,
|
||||||
|
pool: ExportRewards[
|
||||||
|
"/Lotus/Types/Game/MissionDecks/1999ConquestRewards/1999ConquestSilverRewards"
|
||||||
|
][0] as IRngResult[]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
at: 10,
|
||||||
|
pool: ExportRewards[
|
||||||
|
"/Lotus/Types/Game/MissionDecks/1999ConquestRewards/1999ConquestSilverRewards"
|
||||||
|
][0] as IRngResult[]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
at: 15,
|
||||||
|
pool: [
|
||||||
|
{
|
||||||
|
type: "/Lotus/StoreItems/Types/BoosterPacks/1999StickersPackEchoesArchimedea",
|
||||||
|
itemCount: 1,
|
||||||
|
probability: 1
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
at: 20,
|
||||||
|
pool: ExportRewards[
|
||||||
|
"/Lotus/Types/Game/MissionDecks/1999ConquestRewards/1999ConquestGoldRewards"
|
||||||
|
][0] as IRngResult[]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
at: 28,
|
||||||
|
pool: [
|
||||||
|
{
|
||||||
|
type: "/Lotus/StoreItems/Types/Items/MiscItems/1999ConquestBucks",
|
||||||
|
itemCount: 6,
|
||||||
|
probability: 1
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
at: 31,
|
||||||
|
pool: ExportRewards[
|
||||||
|
"/Lotus/Types/Game/MissionDecks/1999ConquestRewards/1999ConquestGoldRewards"
|
||||||
|
][0] as IRngResult[]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
at: 34,
|
||||||
|
pool: ExportRewards[
|
||||||
|
"/Lotus/Types/Game/MissionDecks/1999ConquestRewards/1999ConquestArcaneRewards"
|
||||||
|
][0] as IRngResult[]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
at: 37,
|
||||||
|
pool: [
|
||||||
|
{
|
||||||
|
type: "/Lotus/StoreItems/Types/Items/MiscItems/1999ConquestBucks",
|
||||||
|
itemCount: 9,
|
||||||
|
probability: 1
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
//TODO: return type of partial missioninventoryupdate response
|
//TODO: return type of partial missioninventoryupdate response
|
||||||
export const addMissionRewards = async (
|
export const addMissionRewards = async (
|
||||||
inventory: TInventoryDatabaseDocument,
|
inventory: TInventoryDatabaseDocument,
|
||||||
@ -620,6 +752,7 @@ export const addMissionRewards = async (
|
|||||||
const inventoryChanges: IInventoryChanges = {};
|
const inventoryChanges: IInventoryChanges = {};
|
||||||
const AffiliationMods: IAffiliationMods[] = [];
|
const AffiliationMods: IAffiliationMods[] = [];
|
||||||
let SyndicateXPItemReward;
|
let SyndicateXPItemReward;
|
||||||
|
let ConquestCompletedMissionsCount;
|
||||||
|
|
||||||
let missionCompletionCredits = 0;
|
let missionCompletionCredits = 0;
|
||||||
//inventory change is what the client has not rewarded itself, also the client needs to know the credit changes for display
|
//inventory change is what the client has not rewarded itself, also the client needs to know the credit changes for display
|
||||||
@ -691,6 +824,62 @@ export const addMissionRewards = async (
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (rewardInfo.ConquestCompleted !== undefined) {
|
||||||
|
let score = 1;
|
||||||
|
if (rewardInfo.ConquestHardModeActive === 1) score += 3;
|
||||||
|
|
||||||
|
if (rewardInfo.ConquestPersonalModifiersActive !== undefined)
|
||||||
|
score += rewardInfo.ConquestPersonalModifiersActive;
|
||||||
|
if (rewardInfo.ConquestEquipmentSuggestionsFulfilled !== undefined)
|
||||||
|
score += rewardInfo.ConquestEquipmentSuggestionsFulfilled;
|
||||||
|
|
||||||
|
score *= rewardInfo.ConquestCompleted + 1;
|
||||||
|
|
||||||
|
if (rewardInfo.ConquestCompleted == 2 && rewardInfo.ConquestHardModeActive === 1) score += 1;
|
||||||
|
|
||||||
|
logger.debug(`completed conquest mission ${rewardInfo.ConquestCompleted + 1} for a score of ${score}`);
|
||||||
|
|
||||||
|
const conquestType = rewardInfo.ConquestType;
|
||||||
|
const conquestNode =
|
||||||
|
conquestType == "HexConquest" ? "EchoesHexConquestHardModeUnlocked" : "EntratiLabConquestHardModeUnlocked";
|
||||||
|
if (score >= 25 && inventory.NodeIntrosCompleted.indexOf(conquestNode) == -1)
|
||||||
|
inventory.NodeIntrosCompleted.push(conquestNode);
|
||||||
|
|
||||||
|
if (conquestType == "HexConquest") {
|
||||||
|
inventory.EchoesHexConquestCacheScoreMission ??= 0;
|
||||||
|
if (score > inventory.EchoesHexConquestCacheScoreMission) {
|
||||||
|
for (const reward of hexConquestRewards) {
|
||||||
|
if (score >= reward.at && inventory.EchoesHexConquestCacheScoreMission < reward.at) {
|
||||||
|
const rolled = getRandomReward(reward.pool)!;
|
||||||
|
logger.debug(`rolled hex conquest reward for reaching ${reward.at} points`, rolled);
|
||||||
|
MissionRewards.push({
|
||||||
|
StoreItem: rolled.type,
|
||||||
|
ItemCount: rolled.itemCount
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
inventory.EchoesHexConquestCacheScoreMission = score;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
inventory.EntratiLabConquestCacheScoreMission ??= 0;
|
||||||
|
if (score > inventory.EntratiLabConquestCacheScoreMission) {
|
||||||
|
for (const reward of labConquestRewards) {
|
||||||
|
if (score >= reward.at && inventory.EntratiLabConquestCacheScoreMission < reward.at) {
|
||||||
|
const rolled = getRandomReward(reward.pool)!;
|
||||||
|
logger.debug(`rolled lab conquest reward for reaching ${reward.at} points`, rolled);
|
||||||
|
MissionRewards.push({
|
||||||
|
StoreItem: rolled.type,
|
||||||
|
ItemCount: rolled.itemCount
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
inventory.EntratiLabConquestCacheScoreMission = score;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ConquestCompletedMissionsCount = rewardInfo.ConquestCompleted == 2 ? 0 : rewardInfo.ConquestCompleted + 1;
|
||||||
|
}
|
||||||
|
|
||||||
for (const reward of MissionRewards) {
|
for (const reward of MissionRewards) {
|
||||||
const inventoryChange = await handleStoreItemAcquisition(
|
const inventoryChange = await handleStoreItemAcquisition(
|
||||||
reward.StoreItem,
|
reward.StoreItem,
|
||||||
@ -882,16 +1071,16 @@ export const addMissionRewards = async (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return { inventoryChanges, MissionRewards, credits, AffiliationMods, SyndicateXPItemReward };
|
return {
|
||||||
|
inventoryChanges,
|
||||||
|
MissionRewards,
|
||||||
|
credits,
|
||||||
|
AffiliationMods,
|
||||||
|
SyndicateXPItemReward,
|
||||||
|
ConquestCompletedMissionsCount
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
interface IMissionCredits {
|
|
||||||
MissionCredits: number[];
|
|
||||||
CreditBonus: number[];
|
|
||||||
TotalCredits: number[];
|
|
||||||
DailyMissionBonus?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
//creditBonus is not entirely accurate.
|
//creditBonus is not entirely accurate.
|
||||||
//TODO: consider ActiveBoosters
|
//TODO: consider ActiveBoosters
|
||||||
export const addCredits = (
|
export const addCredits = (
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
import { IAffiliationMods, IInventoryChanges } from "./purchaseTypes";
|
||||||
|
|
||||||
export const inventoryFields = ["RawUpgrades", "MiscItems", "Consumables", "Recipes"] as const;
|
export const inventoryFields = ["RawUpgrades", "MiscItems", "Consumables", "Recipes"] as const;
|
||||||
export type IInventoryFieldType = (typeof inventoryFields)[number];
|
export type IInventoryFieldType = (typeof inventoryFields)[number];
|
||||||
|
|
||||||
@ -11,3 +13,20 @@ export interface IMissionReward {
|
|||||||
FromEnemyCache?: boolean;
|
FromEnemyCache?: boolean;
|
||||||
IsStrippedItem?: boolean;
|
IsStrippedItem?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface IMissionCredits {
|
||||||
|
MissionCredits: number[];
|
||||||
|
CreditBonus: number[];
|
||||||
|
TotalCredits: number[];
|
||||||
|
DailyMissionBonus?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IMissionInventoryUpdateResponse extends Partial<IMissionCredits> {
|
||||||
|
ConquestCompletedMissionsCount?: number;
|
||||||
|
InventoryJson?: string;
|
||||||
|
MissionRewards?: IMissionReward[];
|
||||||
|
InventoryChanges?: IInventoryChanges;
|
||||||
|
FusionPoints?: number;
|
||||||
|
SyndicateXPItemReward?: number;
|
||||||
|
AffiliationMods?: IAffiliationMods[];
|
||||||
|
}
|
||||||
|
@ -125,6 +125,7 @@ export type IMissionInventoryUpdateRequest = {
|
|||||||
wagerTier?: number; // the index
|
wagerTier?: number; // the index
|
||||||
creditsFee?: number; // the index
|
creditsFee?: number; // the index
|
||||||
InvasionProgress?: IInvasionProgressClient[];
|
InvasionProgress?: IInvasionProgressClient[];
|
||||||
|
ConquestMissionsCompleted?: number;
|
||||||
} & {
|
} & {
|
||||||
[K in TEquipmentKey]?: IEquipmentClient[];
|
[K in TEquipmentKey]?: IEquipmentClient[];
|
||||||
};
|
};
|
||||||
@ -150,7 +151,12 @@ export interface IRewardInfo {
|
|||||||
PurgatoryRewardQualifications?: string;
|
PurgatoryRewardQualifications?: string;
|
||||||
rewardSeed?: number | bigint;
|
rewardSeed?: number | bigint;
|
||||||
periodicMissionTag?: string;
|
periodicMissionTag?: string;
|
||||||
|
ConquestType?: string;
|
||||||
|
ConquestCompleted?: number;
|
||||||
|
ConquestEquipmentSuggestionsFulfilled?: number;
|
||||||
|
ConquestPersonalModifiersActive?: number;
|
||||||
|
ConquestStickersActive?: number;
|
||||||
|
ConquestHardModeActive?: number;
|
||||||
// for bounties, only EOM_AFK and node are given from above, plus:
|
// for bounties, only EOM_AFK and node are given from above, plus:
|
||||||
JobTier?: number;
|
JobTier?: number;
|
||||||
jobId?: string;
|
jobId?: string;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user