feat: bounty rewards #1549

Merged
Sainan merged 3 commits from bounty-rewards into main 2025-04-11 06:54:36 -07:00
4 changed files with 34 additions and 8 deletions

View File

@ -55,7 +55,8 @@ export const missionInventoryUpdateController: RequestHandler = async (req, res)
const inventory = await getInventory(accountId); const inventory = await getInventory(accountId);
const inventoryUpdates = await addMissionInventoryUpdates(inventory, missionReport); const inventoryUpdates = await addMissionInventoryUpdates(inventory, missionReport);
if (missionReport.MissionStatus !== "GS_SUCCESS") { // skip mission rewards if not GS_SUCCESS and not a bounty (by presence of jobId, as there's a reward every stage but only the last stage has GS_SUCCESS)
if (missionReport.MissionStatus !== "GS_SUCCESS" && !missionReport.RewardInfo?.jobId) {
await inventory.save(); await inventory.save();
const inventoryResponse = await getInventoryResponse(inventory, true); const inventoryResponse = await getInventoryResponse(inventory, true);
res.json({ res.json({

View File

@ -50,6 +50,7 @@ import conservationAnimals from "@/static/fixed_responses/conservationAnimals.js
import { getInfNodes } from "@/src/helpers/nemesisHelpers"; import { getInfNodes } from "@/src/helpers/nemesisHelpers";
import { Loadout } from "../models/inventoryModels/loadoutModel"; import { Loadout } from "../models/inventoryModels/loadoutModel";
import { ILoadoutConfigDatabase } from "../types/saveLoadoutTypes"; import { ILoadoutConfigDatabase } from "../types/saveLoadoutTypes";
import { getWorldState } from "./worldStateService";
const getRotations = (rotationCount: number, tierOverride: number | undefined): number[] => { const getRotations = (rotationCount: number, tierOverride: number | undefined): number[] => {
if (rotationCount === 0) return [0]; if (rotationCount === 0) return [0];
@ -806,13 +807,23 @@ function getRandomMissionDrops(RewardInfo: IRewardInfo, tierOverride: number | u
const drops: IMissionReward[] = []; const drops: IMissionReward[] = [];
if (RewardInfo.node in ExportRegions) { if (RewardInfo.node in ExportRegions) {
const region = ExportRegions[RewardInfo.node]; const region = ExportRegions[RewardInfo.node];
const rewardManifests: string[] = let rewardManifests: string[] =
RewardInfo.periodicMissionTag == "EliteAlert" || RewardInfo.periodicMissionTag == "EliteAlertB" RewardInfo.periodicMissionTag == "EliteAlert" || RewardInfo.periodicMissionTag == "EliteAlertB"
? ["/Lotus/Types/Game/MissionDecks/EliteAlertMissionRewards/EliteAlertMissionRewards"] ? ["/Lotus/Types/Game/MissionDecks/EliteAlertMissionRewards/EliteAlertMissionRewards"]
: region.rewardManifests; : region.rewardManifests;
let rotations: number[] = []; let rotations: number[] = [];
if (RewardInfo.VaultsCracked) { if (RewardInfo.jobId) {
if (RewardInfo.JobTier! >= 0) {
const id = RewardInfo.jobId.split("_")[3];
const syndicateInfo = getWorldState().SyndicateMissions.find(x => x._id.$oid == id);
if (syndicateInfo) {
const jobInfo = syndicateInfo.Jobs![RewardInfo.JobTier!];
rewardManifests = [jobInfo.rewards];
rotations = [RewardInfo.JobStage!];
}
}
} else if (RewardInfo.VaultsCracked) {
// For Spy missions, e.g. 3 vaults cracked = A, B, C // For Spy missions, e.g. 3 vaults cracked = A, B, C
for (let i = 0; i != RewardInfo.VaultsCracked; ++i) { for (let i = 0; i != RewardInfo.VaultsCracked; ++i) {
rotations.push(i); rotations.push(i);
@ -821,6 +832,9 @@ function getRandomMissionDrops(RewardInfo: IRewardInfo, tierOverride: number | u
const rotationCount = RewardInfo.rewardQualifications?.length || 0; const rotationCount = RewardInfo.rewardQualifications?.length || 0;
rotations = getRotations(rotationCount, tierOverride); rotations = getRotations(rotationCount, tierOverride);
} }
if (rewardManifests.length != 0) {
logger.debug(`generating random mission rewards`, { rewardManifests, rotations });
}
rewardManifests rewardManifests
.map(name => ExportRewards[name]) .map(name => ExportRewards[name])
.forEach(table => { .forEach(table => {

View File

@ -145,10 +145,10 @@ export interface IRewardInfo {
periodicMissionTag?: string; periodicMissionTag?: string;
// 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?: string; JobTier?: number;
jobId?: string; jobId?: string;
JobStage?: string; JobStage?: number;
Q?: boolean; // always false? Q?: boolean; // likely indicates that the bonus objective for this stage was completed
CheckpointCounter?: number; // starts at 1, is incremented with each job stage upload, and does not reset when starting a new job CheckpointCounter?: number; // starts at 1, is incremented with each job stage upload, and does not reset when starting a new job
} }

View File

@ -5,7 +5,7 @@ export interface IWorldState {
BuildLabel: string; BuildLabel: string;
Time: number; Time: number;
Goals: IGoal[]; Goals: IGoal[];
SyndicateMissions: ISyndicateMission[]; SyndicateMissions: ISyndicateMissionInfo[];
GlobalUpgrades: IGlobalUpgrade[]; GlobalUpgrades: IGlobalUpgrade[];
Sorties: ISortie[]; Sorties: ISortie[];
LiteSorties: ILiteSortie[]; LiteSorties: ILiteSortie[];
@ -39,13 +39,24 @@ export interface IGoal {
Node: string; Node: string;
} }
export interface ISyndicateMission { export interface ISyndicateMissionInfo {
_id: IOid; _id: IOid;
Activation: IMongoDate; Activation: IMongoDate;
Expiry: IMongoDate; Expiry: IMongoDate;
Tag: string; Tag: string;
Seed: number; Seed: number;
Nodes: string[]; Nodes: string[];
Jobs?: {
jobType?: string;
rewards: string;
masteryReq: number;
minEnemyLevel: number;
maxEnemyLevel: number;
xpAmounts: number[];
endless?: boolean;
locationTag?: string;
isVault?: boolean;
}[];
} }
export interface IGlobalUpgrade { export interface IGlobalUpgrade {