XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet

XFESpaceNinjaServer

A simple server for a small space ninja game

公开
关注 0 Fork 0 Star 0
返回提交历史

XFEstudio/XFESpaceNinjaServer

chore: simplify bounty reward logic (#3230)

Reviewed-on: https://onlyg.it/OpenWF/SpaceNinjaServer/pulls/3230 Reviewed-by: Sainan <63328889+sainan@users.noreply.github.com> Co-authored-by: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com> Co-committed-by: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com>

1c70ea89
AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com>
提交于

代码差异

1 个文件 +209 -264
Modified src/services/missionInventoryUpdateService.ts +209 -264
@@ -84,7 +84,7 @@ import {
84 84 } from "./worldStateService.ts";
85 85 import { config } from "./configService.ts";
86 86 import libraryDailyTasks from "../../static/fixed_responses/libraryDailyTasks.json" with { type: "json" };
87 import type { IGoal, ISyndicateMissionInfo } from "../types/worldStateTypes.ts";
87 import type { IGoal, ISyndicateJob, ISyndicateMissionInfo } from "../types/worldStateTypes.ts";
88 88 import { fromOid, version_compare } from "../helpers/inventoryHelpers.ts";
89 89 import type { TAccountDocument } from "./loginService.ts";
90 90 import type { ITypeCount } from "../types/commonTypes.ts";
@@ -1488,99 +1488,18 @@ export const addMissionRewards = async (
1488 1488 }
1489 1489
1490 1490 if (rewardInfo.JobStage != undefined && rewardInfo.jobId) {
1491 // eslint-disable-next-line @typescript-eslint/no-unused-vars
1492 const [jobType, unkIndex, hubNode, syndicateMissionId] = rewardInfo.jobId.split("_");
1493 const syndicateMissions: ISyndicateMissionInfo[] = [];
1494 if (syndicateMissionId) {
1495 pushClassicBounties(syndicateMissions, idToBountyCycle(syndicateMissionId), account.BuildLabel);
1496 }
1497 let syndicateEntry: ISyndicateMissionInfo | IGoal | undefined = syndicateMissions.find(
1498 m => m._id.$oid === syndicateMissionId
1499 );
1500 if (
1501 [
1502 "/Lotus/Types/Gameplay/Eidolon/Jobs/Events/InfestedPlainsBounty",
1503 "/Lotus/Types/Gameplay/Eidolon/Jobs/Events/GhoulAlertBounty"
1504 ].some(prefix => jobType.startsWith(prefix))
1505 ) {
1506 const { Goals } = getWorldState(account.BuildLabel);
1507 syndicateEntry = Goals.find(m => m._id.$oid === syndicateMissionId);
1508 if (syndicateEntry) syndicateEntry.Tag = syndicateEntry.JobAffiliationTag!;
1509 }
1510 const specialCase = [
1511 {
1512 endings: ["Heists/HeistProfitTakerBountyOne"],
1513 stage: 2,
1514 amount: 1000,
1515 tag: "SolarisSyndicate"
1516 },
1517 {
1518 endings: [
1519 "Heists/HeistProfitTakerBountyTwo",
1520 "Heists/HeistProfitTakerBountyThree",
1521 "Heists/HeistProfitTakerBountyFour",
1522 "Heists/HeistExploiterBountyOne"
1523 ],
1524 amount: 1000,
1525 tag: "SolarisSyndicate"
1526 },
1527 { endings: ["Hunts/AllTeralystsHunt"], stage: 2, amount: 5000, tag: "CetusSyndicate" },
1528 {
1529 endings: ["Hunts/TeralystHunt"],
1530 amount: 1000,
1531 tag: "CetusSyndicate"
1532 },
1533 { endings: ["Jobs/NewbieJob"], amount: 200, tag: "CetusSyndicate" }
1534 ];
1535 const match = specialCase.find(rule => rule.endings.some(e => jobType.endsWith(e)));
1536 if (match) {
1537 const specialCaseReward = match.stage === undefined || rewardInfo.JobStage === match.stage ? match : null;
1538 if (specialCaseReward) {
1539 addStanding(inventory, match.tag, Math.floor(match.amount / (rewardInfo.Q ? 0.8 : 1)), AffiliationMods);
1540 }
1541 } else if (syndicateEntry && syndicateEntry.Jobs) {
1542 let currentJob = syndicateEntry.Jobs[rewardInfo.JobTier!];
1543 if (
1544 [
1545 "/Lotus/Types/Gameplay/Eidolon/Jobs/Events/InfestedPlainsBounty",
1546 "/Lotus/Types/Gameplay/Eidolon/Jobs/Events/GhoulAlertBounty"
1547 ].some(prefix => jobType.startsWith(prefix))
1548 ) {
1549 currentJob = syndicateEntry.Jobs.find(j => j.jobType === jobType)!;
1550 }
1551 if (syndicateEntry.Tag === "EntratiSyndicate") {
1552 if (
1553 [
1554 "DeimosRuinsExterminateBounty",
1555 "DeimosRuinsEscortBounty",
1556 "DeimosRuinsMistBounty",
1557 "DeimosRuinsPurifyBounty",
1558 "DeimosRuinsSacBounty",
1559 "VaultBounty"
1560 ].some(ending => jobType.endsWith(ending))
1561 ) {
1562 const vault = syndicateEntry.Jobs.find(j => j.locationTag == rewardInfo.jobId!.split("_").at(-1));
1563 if (vault) {
1564 currentJob = vault;
1565 if (jobType.endsWith("VaultBounty")) {
1566 currentJob.xpAmounts[rewardInfo.JobTier!] = currentJob.xpAmounts.reduce((s, a) => s + a, 0);
1567 }
1568 }
1569 }
1491 const result = getSyndicateJob(rewardInfo, account.BuildLabel);
1492 if (result) {
1493 const currentJob = result.currentJob;
1494 const syndicateTag = result.syndicateTag;
1495 if (syndicateTag === "EntratiSyndicate") {
1570 1496 let medallionAmount = Math.floor(currentJob.xpAmounts[rewardInfo.JobStage] / (rewardInfo.Q ? 0.8 : 1));
1571 if (
1572 ["DeimosEndlessAreaDefenseBounty", "DeimosEndlessExcavateBounty", "DeimosEndlessPurifyBounty"].some(
1573 ending => jobType.endsWith(ending)
1574 )
1575 ) {
1576 const endlessJob = syndicateEntry.Jobs.find(j => j.endless);
1577 if (endlessJob) {
1578 const index = rewardInfo.JobStage % endlessJob.xpAmounts.length;
1579 const excess = Math.floor(rewardInfo.JobStage / (endlessJob.xpAmounts.length - 1));
1580 medallionAmount = Math.floor(endlessJob.xpAmounts[index] * (1 + 0.15000001 * excess));
1581 }
1497 if (currentJob.endless) {
1498 const index = rewardInfo.JobStage % currentJob.xpAmounts.length;
1499 const excess = Math.floor(rewardInfo.JobStage / (currentJob.xpAmounts.length - 1));
1500 medallionAmount = Math.floor(currentJob.xpAmounts[index] * (1 + 0.15000001 * excess));
1582 1501 }
1583 if (typeof medallionAmount === "number" && !isNaN(medallionAmount)) {
1502 if (!isNaN(medallionAmount)) {
1584 1503 MissionRewards.push({
1585 1504 StoreItem: "/Lotus/StoreItems/Types/Items/Deimos/EntratiFragmentUncommonB",
1586 1505 ItemCount: medallionAmount
@@ -1591,17 +1510,19 @@ export const addMissionRewards = async (
1591 1510 );
1592 1511 } else {
1593 1512 logger.warning(
1594 `${jobType} tried to give ${medallionAmount} medallions for the ${rewardInfo.JobStage} stage of the ${rewardInfo.JobTier} tier bounty`
1513 `${currentJob.jobType} tried to give ${medallionAmount} medallions for the ${rewardInfo.JobStage} stage of the ${rewardInfo.JobTier} tier bounty`
1595 1514 );
1596 1515 logger.warning(`currentJob`, { currentJob: currentJob });
1597 1516 }
1598 } else if (!jobType.startsWith("/Lotus/Types/Gameplay/NokkoColony/Jobs")) {
1599 addStanding(
1600 inventory,
1601 syndicateEntry.Tag,
1602 Math.floor(currentJob.xpAmounts[rewardInfo.JobStage] / (rewardInfo.Q ? 0.8 : 1)),
1603 AffiliationMods
1604 );
1517 } else if (!currentJob.jobType!.startsWith("/Lotus/Types/Gameplay/NokkoColony/Jobs")) {
1518 const xpAmount =
1519 rewardInfo.JobStage < currentJob.xpAmounts.length
1520 ? currentJob.xpAmounts[rewardInfo.JobStage]
1521 : currentJob.xpAmounts[0];
1522 if (rewardInfo.JobStage >= currentJob.xpAmounts.length) {
1523 logger.debug(`xpAmount for stage ${rewardInfo.JobStage} is out of bounds, fallback to ${xpAmount}`);
1524 }
1525 addStanding(inventory, syndicateTag, Math.floor(xpAmount / (rewardInfo.Q ? 0.8 : 1)), AffiliationMods);
1605 1526 }
1606 1527 }
1607 1528 }
@@ -2047,178 +1968,49 @@ function getRandomMissionDrops(
2047 1968 }
2048 1969
2049 1970 let rotations: number[] = [];
2050 if (RewardInfo.jobId) {
2051 if (RewardInfo.JobStage! >= 0) {
2052 // eslint-disable-next-line @typescript-eslint/no-unused-vars
2053 const [jobType, unkIndex, hubNode, syndicateMissionId] = RewardInfo.jobId.split("_");
2054 let isEndlessJob = false;
2055 if (syndicateMissionId) {
2056 const syndicateMissions: ISyndicateMissionInfo[] = [];
2057 if (syndicateMissionId) {
2058 pushClassicBounties(syndicateMissions, idToBountyCycle(syndicateMissionId), account.BuildLabel);
2059 }
2060 let syndicateEntry: ISyndicateMissionInfo | IGoal | undefined = syndicateMissions.find(
2061 m => m._id.$oid === syndicateMissionId
2062 );
2063 if (
2064 [
2065 "/Lotus/Types/Gameplay/Eidolon/Jobs/Events/InfestedPlainsBounty",
2066 "/Lotus/Types/Gameplay/Eidolon/Jobs/Events/GhoulAlertBounty"
2067 ].some(prefix => jobType.startsWith(prefix))
2068 ) {
2069 const { Goals } = getWorldState(account.BuildLabel);
2070 syndicateEntry = Goals.find(m => m._id.$oid === syndicateMissionId);
2071 if (syndicateEntry) syndicateEntry.Tag = syndicateEntry.JobAffiliationTag!;
1971 if (RewardInfo.jobId && RewardInfo.JobStage! >= 0) {
1972 const result = getSyndicateJob(RewardInfo, account.BuildLabel);
1973
1974 if (result) {
1975 const currentJob = result.currentJob;
1976 const jobType = currentJob.jobType!;
1977 if (currentJob.rewards != "") rewardManifests = [currentJob.rewards];
1978 if (currentJob.xpAmounts.length > 1) {
1979 const curentStage = RewardInfo.JobStage! + 1;
1980 const totalStage = currentJob.xpAmounts.length;
1981 let tableIndex = 1; // Stage 2, Stage 3 of 4, and Stage 3 of 5
1982
1983 if (curentStage == 1) {
1984 tableIndex = 0;
1985 } else if (curentStage == totalStage) {
1986 tableIndex = 3;
1987 } else if (totalStage == 5 && curentStage == 4) {
1988 tableIndex = 2;
2072 1989 }
2073 if (syndicateEntry && syndicateEntry.Jobs) {
2074 let job;
2075 if (RewardInfo.JobTier !== undefined && RewardInfo.JobTier >= 0) {
2076 job = syndicateEntry.Jobs[RewardInfo.JobTier];
2077 }
2078
2079 if (syndicateEntry.Tag === "EntratiSyndicate") {
2080 if (
2081 [
2082 "DeimosRuinsExterminateBounty",
2083 "DeimosRuinsEscortBounty",
2084 "DeimosRuinsMistBounty",
2085 "DeimosRuinsPurifyBounty",
2086 "DeimosRuinsSacBounty",
2087 "VaultBounty"
2088 ].some(ending => jobType.endsWith(ending))
2089 ) {
2090 const vault = syndicateEntry.Jobs.find(
2091 j => j.locationTag === RewardInfo.jobId!.split("_").at(-1)
2092 );
2093 if (vault) {
2094 job = vault;
2095 if (jobType.endsWith("VaultBounty")) {
2096 job.rewards = job.rewards.replace(
2097 "/Lotus/Types/Game/MissionDecks/",
2098 "/Supplementals/"
2099 );
2100 job.xpAmounts = [job.xpAmounts.reduce((partialSum, a) => partialSum + a, 0)];
2101 }
2102 }
2103 }
2104 if (
2105 [
2106 "DeimosEndlessAreaDefenseBounty",
2107 "DeimosEndlessExcavateBounty",
2108 "DeimosEndlessPurifyBounty"
2109 ].some(ending => jobType.endsWith(ending))
2110 ) {
2111 const endlessJob = syndicateEntry.Jobs.find(j => j.endless);
2112 if (endlessJob) {
2113 isEndlessJob = true;
2114 job = endlessJob;
2115 const excess = Math.floor(RewardInfo.JobStage! / (job.xpAmounts.length - 1));
2116
2117 const rotationIndexes = [0, 0, 1, 2];
2118 const rotationIndex = rotationIndexes[excess % rotationIndexes.length];
2119 const dropTable = [
2120 "/Lotus/Types/Game/MissionDecks/DeimosMissionRewards/TierBTableARewards",
2121 "/Lotus/Types/Game/MissionDecks/DeimosMissionRewards/TierBTableBRewards",
2122 "/Lotus/Types/Game/MissionDecks/DeimosMissionRewards/TierBTableCRewards"
2123 ];
2124 job.rewards = dropTable[rotationIndex];
2125 }
2126 }
2127 } else if (syndicateEntry.Tag === "SolarisSyndicate") {
2128 if (jobType.endsWith("Heists/HeistProfitTakerBountyOne") && RewardInfo.JobStage == 2) {
2129 job = {
2130 rewards:
2131 "/Lotus/Types/Game/MissionDecks/HeistJobMissionRewards/HeistTierATableARewards",
2132 masteryReq: 0,
2133 minEnemyLevel: 40,
2134 maxEnemyLevel: 60,
2135 xpAmounts: [1000]
2136 };
2137 RewardInfo.Q = false; // Just in case
2138 } else if (jobType.startsWith("/Lotus/Types/Gameplay/NokkoColony/Jobs/NokkoJob")) {
2139 job = {
2140 rewards: jobType
2141 .replace("SteelPath", "Steel")
2142 .replace(
2143 "/Lotus/Types/Gameplay/NokkoColony/Jobs/NokkoJob",
2144 "/Lotus/Types/Game/MissionDecks/NokkoColonyRewards/NokkoColonyRewards"
2145 ),
2146 masteryReq: 0,
2147 minEnemyLevel: 30,
2148 maxEnemyLevel: 40,
2149 xpAmounts: [0, 0, 0, 0, 0]
2150 };
2151 } else {
2152 const tierMap = {
2153 Two: "B",
2154 Three: "C",
2155 Four: "D"
2156 };
2157 1990
2158 for (const [key, tier] of Object.entries(tierMap)) {
2159 if (jobType.endsWith(`Heists/HeistProfitTakerBounty${key}`)) {
2160 job = {
2161 rewards: `/Lotus/Types/Game/MissionDecks/HeistJobMissionRewards/HeistTier${tier}TableARewards`,
2162 masteryReq: 0,
2163 minEnemyLevel: 40,
2164 maxEnemyLevel: 60,
2165 xpAmounts: [1000]
2166 };
2167 RewardInfo.Q = false; // Just in case
2168 break;
2169 }
2170 }
2171 }
2172 }
2173 if (
2174 [
2175 "/Lotus/Types/Gameplay/Eidolon/Jobs/Events/InfestedPlainsBounty",
2176 "/Lotus/Types/Gameplay/Eidolon/Jobs/Events/GhoulAlertBounty"
2177 ].some(prefix => jobType.startsWith(prefix))
2178 ) {
2179 job = syndicateEntry.Jobs.find(j => j.jobType === jobType)!;
2180 }
2181 if (job) {
2182 rewardManifests = [job.rewards];
2183 if (job.xpAmounts.length > 1) {
2184 const curentStage = RewardInfo.JobStage! + 1;
2185 const totalStage = job.xpAmounts.length;
2186 let tableIndex = 1; // Stage 2, Stage 3 of 4, and Stage 3 of 5
2187
2188 if (curentStage == 1) {
2189 tableIndex = 0;
2190 } else if (curentStage == totalStage) {
2191 tableIndex = 3;
2192 } else if (totalStage == 5 && curentStage == 4) {
2193 tableIndex = 2;
2194 }
2195 if (jobType.startsWith("/Lotus/Types/Gameplay/NokkoColony/Jobs/NokkoJob")) {
2196 if (RewardInfo.JobStage === job.xpAmounts.length - 1) {
2197 rotations = [0, 1, 2];
2198 } else {
2199 rewardManifests = [];
2200 }
2201 } else {
2202 rotations = [tableIndex];
2203 }
2204 } else {
2205 rotations = [0];
2206 }
2207 if (
2208 RewardInfo.Q &&
2209 (RewardInfo.JobStage === job.xpAmounts.length - 1 || jobType.endsWith("VaultBounty")) &&
2210 !jobType.startsWith("/Lotus/Types/Gameplay/NokkoColony/Jobs/NokkoJob") &&
2211 !isEndlessJob
2212 ) {
2213 rotations.push(ExportRewards[job.rewards].length - 1);
2214 }
1991 if (jobType.startsWith("/Lotus/Types/Gameplay/NokkoColony/Jobs/NokkoJob")) {
1992 if (RewardInfo.JobStage === currentJob.xpAmounts.length - 1) {
1993 rotations = [0, 1, 2];
1994 } else {
1995 rewardManifests = [];
2215 1996 }
1997 } else {
1998 rotations = [tableIndex];
2216 1999 }
2000 } else {
2001 rotations = [0];
2217 2002 }
2218 2003 if (jobType == "/Lotus/Types/Gameplay/Eidolon/Jobs/NewbieJob") {
2219 rewardManifests = ["/Lotus/Types/Game/MissionDecks/EidolonJobMissionRewards/TierATableARewards"];
2220 2004 rotations = [3];
2221 2005 if (RewardInfo.Q) rotations.push(3);
2006 } else if (
2007 RewardInfo.Q &&
2008 (RewardInfo.JobStage === currentJob.xpAmounts.length - 1 || jobType.endsWith("VaultBounty")) &&
2009 !jobType.startsWith("/Lotus/Types/Gameplay/NokkoColony/Jobs/NokkoJob") &&
2010 !jobType.startsWith("/Lotus/Types/Gameplay/Venus/Jobs/Heists/") &&
2011 !currentJob.endless
2012 ) {
2013 rotations.push(ExportRewards[currentJob.rewards].length - 1);
2222 2014 }
2223 2015 }
2224 2016 } else if (RewardInfo.challengeMissionId) {
@@ -2545,6 +2337,159 @@ export const handleConservation = (
2545 2337 }
2546 2338 };
2547 2339
2340 const getSyndicateJob = (
2341 rewardInfo: IRewardInfo,
2342 buildLabel?: string
2343 ): { currentJob: ISyndicateJob; syndicateTag: string } | undefined => {
2344 if (!rewardInfo.jobId) return;
2345
2346 const parts = rewardInfo.jobId.split("_");
2347 const jobType = parts[0];
2348 const syndicateMissionId = parts.find(p => p.length === 24);
2349 let syndicateTag = parts.find(p => p.endsWith("Syndicate"));
2350
2351 const isGoalJob = [
2352 "/Lotus/Types/Gameplay/Eidolon/Jobs/Events/InfestedPlainsBounty",
2353 "/Lotus/Types/Gameplay/Eidolon/Jobs/Events/GhoulAlertBounty"
2354 ].some(prefix => jobType.startsWith(prefix));
2355
2356 let syndicateEntry: ISyndicateMissionInfo | IGoal | undefined;
2357 if (isGoalJob) {
2358 syndicateEntry = getWorldState(buildLabel).Goals.find(g => g._id.$oid === syndicateMissionId);
2359 if (syndicateEntry) syndicateTag = syndicateEntry.JobAffiliationTag!;
2360 } else if (syndicateMissionId) {
2361 const syndicateMissions: ISyndicateMissionInfo[] = [];
2362 pushClassicBounties(syndicateMissions, idToBountyCycle(syndicateMissionId), buildLabel);
2363 syndicateEntry = syndicateMissions.find(m => m._id.$oid == syndicateMissionId);
2364 if (syndicateEntry) syndicateTag = syndicateEntry.Tag;
2365 }
2366
2367 if (jobType.endsWith("Hunts/AllTeralystsHunt") && rewardInfo.JobStage == 2) {
2368 return {
2369 currentJob: {
2370 jobType,
2371 rewards: "",
2372 minEnemyLevel: 20,
2373 maxEnemyLevel: 40,
2374 xpAmounts: [5000]
2375 },
2376 syndicateTag: "CetusSyndicate"
2377 };
2378 } else if (jobType.endsWith("Hunts/TeralystHunt")) {
2379 return {
2380 currentJob: {
2381 jobType,
2382 rewards: "",
2383 minEnemyLevel: 20,
2384 maxEnemyLevel: 40,
2385 xpAmounts: [1000]
2386 },
2387 syndicateTag: "CetusSyndicate"
2388 };
2389 } else if (jobType.endsWith("Jobs/NewbieJob")) {
2390 return {
2391 currentJob: {
2392 jobType,
2393 rewards: "/Lotus/Types/Game/MissionDecks/EidolonJobMissionRewards/TierATableARewards",
2394 minEnemyLevel: 3,
2395 maxEnemyLevel: 5,
2396 xpAmounts: [200]
2397 },
2398 syndicateTag: "CetusSyndicate"
2399 };
2400 }
2401
2402 if (!syndicateTag) return;
2403
2404 if (syndicateTag == "SolarisSyndicate") {
2405 if (jobType.startsWith("/Lotus/Types/Gameplay/NokkoColony/Jobs/NokkoJob")) {
2406 return {
2407 currentJob: {
2408 jobType,
2409 rewards: jobType
2410 .replace("SteelPath", "Steel")
2411 .replace(
2412 "/Lotus/Types/Gameplay/NokkoColony/Jobs/NokkoJob",
2413 "/Lotus/Types/Game/MissionDecks/NokkoColonyRewards/NokkoColonyRewards"
2414 ),
2415 masteryReq: 0,
2416 minEnemyLevel: 30,
2417 maxEnemyLevel: 40,
2418 xpAmounts: [0, 0, 0, 0, 0]
2419 },
2420 syndicateTag
2421 };
2422 } else if (jobType == "/Lotus/Types/Gameplay/Venus/Jobs/Heists/ExploiterHunt") {
2423 return {
2424 currentJob: {
2425 jobType,
2426 rewards: "",
2427 masteryReq: 0,
2428 minEnemyLevel: 40,
2429 maxEnemyLevel: 60,
2430 xpAmounts: [1000]
2431 },
2432 syndicateTag
2433 };
2434 }
2435
2436 const tierMap: Record<string, string> = { One: "A", Two: "B", Three: "C", Four: "D" };
2437 for (const [key, tier] of Object.entries(tierMap)) {
2438 if (key == "One" && rewardInfo.JobStage != 2) continue;
2439 if (!jobType.endsWith(`Heists/HeistProfitTakerBounty${key}`)) continue;
2440
2441 return {
2442 currentJob: {
2443 jobType,
2444 rewards: `/Lotus/Types/Game/MissionDecks/HeistJobMissionRewards/HeistTier${tier}TableARewards`,
2445 masteryReq: 0,
2446 minEnemyLevel: 40,
2447 maxEnemyLevel: 60,
2448 xpAmounts: [1000]
2449 },
2450 syndicateTag
2451 };
2452 }
2453 }
2454
2455 if (!syndicateEntry?.Jobs) return;
2456
2457 let currentJob: ISyndicateJob | undefined =
2458 rewardInfo.JobTier !== undefined && !isGoalJob
2459 ? syndicateEntry.Jobs[rewardInfo.JobTier]
2460 : syndicateEntry.Jobs.find(j => j.jobType == jobType);
2461
2462 if (syndicateTag == "EntratiSyndicate") {
2463 if (jobType.includes("DeimosEndless")) {
2464 const endlessJob = syndicateEntry.Jobs.find(j => j.endless);
2465 if (endlessJob) {
2466 currentJob = endlessJob;
2467 const excess = Math.floor(rewardInfo.JobStage! / (currentJob.xpAmounts.length - 1));
2468 const rotationIndex = [0, 0, 1, 2][excess % 4];
2469 const dropTable = [
2470 "/Lotus/Types/Game/MissionDecks/DeimosMissionRewards/TierBTableARewards",
2471 "/Lotus/Types/Game/MissionDecks/DeimosMissionRewards/TierBTableBRewards",
2472 "/Lotus/Types/Game/MissionDecks/DeimosMissionRewards/TierBTableCRewards"
2473 ];
2474 currentJob.rewards = dropTable[rotationIndex];
2475 }
2476 } else if (jobType.endsWith("VaultBounty")) {
2477 const chamberTag = [...parts].reverse().find(p => p.startsWith("Chamber"));
2478 const vault = syndicateEntry.Jobs.find(j => j.locationTag == chamberTag);
2479 if (vault) {
2480 currentJob = vault;
2481 currentJob.jobType = jobType;
2482 currentJob.rewards = currentJob.rewards.replace("/Lotus/Types/Game/MissionDecks/", "/Supplementals/");
2483 currentJob.xpAmounts = [currentJob.xpAmounts.reduce((sum, a) => sum + a, 0)];
2484 }
2485 }
2486 }
2487
2488 if (!currentJob) return;
2489
2490 return { currentJob, syndicateTag };
2491 };
2492
2548 2493 const corruptedMods = [
2549 2494 "/Lotus/StoreItems/Upgrades/Mods/Melee/DualStat/CorruptedHeavyDamageChargeSpeedMod", // Corrupt Charge
2550 2495 "/Lotus/StoreItems/Upgrades/Mods/Pistol/DualStat/CorruptedCritDamagePistol", // Hollow Point