feat: bounty chemistry bonus (#2070)
Re #388 Reviewed-on: #2070 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
bfe2e93c76
commit
099f12a197
@ -1,8 +1,7 @@
|
||||
import { TInventoryDatabaseDocument } from "@/src/models/inventoryModels/inventoryModel";
|
||||
import { config } from "@/src/services/configService";
|
||||
import { addEmailItem, getInventory, updateCurrency } from "@/src/services/inventoryService";
|
||||
import { addEmailItem, getDialogue, getInventory, updateCurrency } from "@/src/services/inventoryService";
|
||||
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||
import { ICompletedDialogue, IDialogueDatabase } from "@/src/types/inventoryTypes/inventoryTypes";
|
||||
import { ICompletedDialogue } from "@/src/types/inventoryTypes/inventoryTypes";
|
||||
import { IInventoryChanges } from "@/src/types/purchaseTypes";
|
||||
import { RequestHandler } from "express";
|
||||
|
||||
@ -107,26 +106,3 @@ interface IOtherDialogueInfo {
|
||||
Tag: string;
|
||||
Value: number;
|
||||
}
|
||||
|
||||
const getDialogue = (inventory: TInventoryDatabaseDocument, dialogueName: string): IDialogueDatabase => {
|
||||
let dialogue = inventory.DialogueHistory!.Dialogues!.find(x => x.DialogueName == 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),
|
||||
QueuedDialogues: [],
|
||||
Gifts: [],
|
||||
Booleans: [],
|
||||
Completed: [],
|
||||
DialogueName: dialogueName
|
||||
}) - 1
|
||||
];
|
||||
}
|
||||
return dialogue;
|
||||
};
|
||||
|
@ -28,7 +28,8 @@ import {
|
||||
ITraits,
|
||||
ICalendarProgress,
|
||||
INemesisWeaponTargetFingerprint,
|
||||
INemesisPetTargetFingerprint
|
||||
INemesisPetTargetFingerprint,
|
||||
IDialogueDatabase
|
||||
} from "@/src/types/inventoryTypes/inventoryTypes";
|
||||
import { IGenericUpdate, IUpdateNodeIntrosResponse } from "../types/genericUpdate";
|
||||
import { IKeyChainRequest, IMissionInventoryUpdateRequest } from "../types/requestTypes";
|
||||
@ -1906,6 +1907,29 @@ export const cleanupInventory = (inventory: TInventoryDatabaseDocument): void =>
|
||||
}
|
||||
};
|
||||
|
||||
export const getDialogue = (inventory: TInventoryDatabaseDocument, dialogueName: string): IDialogueDatabase => {
|
||||
let dialogue = inventory.DialogueHistory!.Dialogues!.find(x => x.DialogueName == 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),
|
||||
QueuedDialogues: [],
|
||||
Gifts: [],
|
||||
Booleans: [],
|
||||
Completed: [],
|
||||
DialogueName: dialogueName
|
||||
}) - 1
|
||||
];
|
||||
}
|
||||
return dialogue;
|
||||
};
|
||||
|
||||
export const getCalendarProgress = (inventory: TInventoryDatabaseDocument): ICalendarProgress => {
|
||||
const currentSeason = getWorldState().KnownCalendarSeasons[0];
|
||||
|
||||
|
@ -35,6 +35,7 @@ import {
|
||||
combineInventoryChanges,
|
||||
generateRewardSeed,
|
||||
getCalendarProgress,
|
||||
getDialogue,
|
||||
giveNemesisPetRecipe,
|
||||
giveNemesisWeaponRecipe,
|
||||
updateCurrency,
|
||||
@ -63,7 +64,15 @@ import {
|
||||
} from "@/src/helpers/nemesisHelpers";
|
||||
import { Loadout } from "../models/inventoryModels/loadoutModel";
|
||||
import { ILoadoutConfigDatabase } from "../types/saveLoadoutTypes";
|
||||
import { getLiteSortie, getSortie, idToBountyCycle, idToDay, idToWeek, pushClassicBounties } from "./worldStateService";
|
||||
import {
|
||||
getLiteSortie,
|
||||
getSortie,
|
||||
getWorldState,
|
||||
idToBountyCycle,
|
||||
idToDay,
|
||||
idToWeek,
|
||||
pushClassicBounties
|
||||
} from "./worldStateService";
|
||||
import { config } from "./configService";
|
||||
import libraryDailyTasks from "@/static/fixed_responses/libraryDailyTasks.json";
|
||||
import { ISyndicateMissionInfo } from "../types/worldStateTypes";
|
||||
@ -1188,8 +1197,9 @@ export const addMissionRewards = async (
|
||||
}
|
||||
|
||||
if (rewardInfo.challengeMissionId) {
|
||||
const [syndicateTag, tierStr] = rewardInfo.challengeMissionId.split("_"); // TODO: third part in HexSyndicate jobs - Chemistry points
|
||||
const [syndicateTag, tierStr, chemistryStr] = rewardInfo.challengeMissionId.split("_");
|
||||
const tier = Number(tierStr);
|
||||
const chemistry = Number(chemistryStr);
|
||||
const isSteelPath = missions?.Tier;
|
||||
if (syndicateTag === "ZarimanSyndicate") {
|
||||
let medallionAmount = tier + 1;
|
||||
@ -1206,6 +1216,23 @@ export const addMissionRewards = async (
|
||||
if (isSteelPath) standingAmount *= 1.5;
|
||||
AffiliationMods.push(addStanding(inventory, syndicateTag, standingAmount));
|
||||
}
|
||||
if (syndicateTag == "HexSyndicate" && chemistry && tier < 6) {
|
||||
const seed = getWorldState().SyndicateMissions.find(x => x.Tag == "HexSyndicate")!.Seed;
|
||||
const { nodes, buddies } = getHexBounties(seed);
|
||||
const buddy = buddies[tier];
|
||||
logger.debug(`Hex seed is ${seed}, giving chemistry for ${buddy}`);
|
||||
if (missions?.Tag != nodes[tier]) {
|
||||
logger.warn(
|
||||
`Uh-oh, tier ${tier} bounty should've been on ${nodes[tier]} but you were just on ${missions?.Tag}`
|
||||
);
|
||||
}
|
||||
const tomorrowAt0Utc = config.noKimCooldowns
|
||||
? Date.now()
|
||||
: (Math.trunc(Date.now() / 86400_000) + 1) * 86400_000;
|
||||
const dialogue = getDialogue(inventory, buddy);
|
||||
dialogue.Chemistry += chemistry;
|
||||
dialogue.BountyChemExpiry = new Date(tomorrowAt0Utc);
|
||||
}
|
||||
if (isSteelPath) {
|
||||
await addItem(inventory, "/Lotus/Types/Items/MiscItems/SteelEssence", 1);
|
||||
MissionRewards.push({
|
||||
@ -1765,3 +1792,55 @@ const libraryPersonalTargetToAvatar: Record<string, string> = {
|
||||
"/Lotus/Types/Game/Library/Targets/Research10Target":
|
||||
"/Lotus/Types/Enemies/Corpus/Spaceman/AIWeek/NullifySpacemanAvatar"
|
||||
};
|
||||
|
||||
const node_excluded_buddies: Record<string, string> = {
|
||||
SolNode856: "/Lotus/Types/Gameplay/1999Wf/Dialogue/ArthurDialogue_rom.dialogue",
|
||||
SolNode852: "/Lotus/Types/Gameplay/1999Wf/Dialogue/LettieDialogue_rom.dialogue",
|
||||
SolNode851: "/Lotus/Types/Gameplay/1999Wf/Dialogue/JabirDialogue_rom.dialogue",
|
||||
SolNode850: "/Lotus/Types/Gameplay/1999Wf/Dialogue/EleanorDialogue_rom.dialogue",
|
||||
SolNode853: "/Lotus/Types/Gameplay/1999Wf/Dialogue/AoiDialogue_rom.dialogue",
|
||||
SolNode854: "/Lotus/Types/Gameplay/1999Wf/Dialogue/QuincyDialogue_rom.dialogue"
|
||||
};
|
||||
|
||||
const getHexBounties = (seed: number): { nodes: string[]; buddies: string[] } => {
|
||||
// We're gonna shuffle these arrays, so they're not truly 'const'.
|
||||
const nodes: string[] = [
|
||||
"SolNode850",
|
||||
"SolNode851",
|
||||
"SolNode852",
|
||||
"SolNode853",
|
||||
"SolNode854",
|
||||
"SolNode856",
|
||||
"SolNode858"
|
||||
];
|
||||
const excludable_nodes: string[] = ["SolNode851", "SolNode852", "SolNode853", "SolNode854"];
|
||||
const buddies: string[] = [
|
||||
"/Lotus/Types/Gameplay/1999Wf/Dialogue/JabirDialogue_rom.dialogue",
|
||||
"/Lotus/Types/Gameplay/1999Wf/Dialogue/AoiDialogue_rom.dialogue",
|
||||
"/Lotus/Types/Gameplay/1999Wf/Dialogue/ArthurDialogue_rom.dialogue",
|
||||
"/Lotus/Types/Gameplay/1999Wf/Dialogue/EleanorDialogue_rom.dialogue",
|
||||
"/Lotus/Types/Gameplay/1999Wf/Dialogue/LettieDialogue_rom.dialogue",
|
||||
"/Lotus/Types/Gameplay/1999Wf/Dialogue/QuincyDialogue_rom.dialogue"
|
||||
];
|
||||
|
||||
const rng = new SRng(seed);
|
||||
rng.shuffleArray(nodes);
|
||||
rng.shuffleArray(excludable_nodes);
|
||||
while (nodes.length > buddies.length) {
|
||||
nodes.splice(
|
||||
nodes.findIndex(x => x == excludable_nodes[0]),
|
||||
1
|
||||
);
|
||||
excludable_nodes.splice(0, 1);
|
||||
}
|
||||
rng.shuffleArray(buddies);
|
||||
for (let i = 0; i != 6; ++i) {
|
||||
if (buddies[i] == node_excluded_buddies[nodes[i]]) {
|
||||
const swapIdx = (i + 1) % buddies.length;
|
||||
const tmp = buddies[swapIdx];
|
||||
buddies[swapIdx] = buddies[i];
|
||||
buddies[i] = tmp;
|
||||
}
|
||||
}
|
||||
return { nodes, buddies };
|
||||
};
|
||||
|
@ -115,4 +115,19 @@ export class SRng {
|
||||
randomReward<T extends { probability: number }>(pool: T[]): T | undefined {
|
||||
return getRewardAtPercentage(pool, this.randomFloat());
|
||||
}
|
||||
|
||||
churnSeed(its: number): void {
|
||||
while (its--) {
|
||||
this.state = (0x5851f42d4c957f2dn * this.state + 0x14057b7ef767814fn) & 0xffffffffffffffffn;
|
||||
}
|
||||
}
|
||||
|
||||
shuffleArray<T>(arr: T[]): void {
|
||||
for (let lastIdx = arr.length - 1; lastIdx >= 1; --lastIdx) {
|
||||
const swapIdx = this.randomInt(0, lastIdx);
|
||||
const tmp = arr[swapIdx];
|
||||
arr[swapIdx] = arr[lastIdx];
|
||||
arr[lastIdx] = tmp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user