返回提交历史
Modified
src/controllers/api/saveDialogueController.ts
+2
-26
Modified
src/services/inventoryService.ts
+25
-1
Modified
src/services/missionInventoryUpdateService.ts
+81
-2
Modified
src/services/rngService.ts
+15
-0
XFEstudio/SpaceNinjaServer
feat: bounty chemistry bonus (#2070)
Re #388 Reviewed-on: https://onlyg.it/OpenWF/SpaceNinjaServer/pulls/2070 Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com> Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
099f12a1
代码差异
4 个文件
+123
-29
@@ -1,8 +1,7 @@
1
import { TInventoryDatabaseDocument } from "@/src/models/inventoryModels/inventoryModel";
2
1
import { config } from "@/src/services/configService";
3
import { addEmailItem, getInventory, updateCurrency } from "@/src/services/inventoryService";
2
import { addEmailItem, getDialogue, getInventory, updateCurrency } from "@/src/services/inventoryService";
4
3
import { getAccountIdForRequest } from "@/src/services/loginService";
5
import { ICompletedDialogue, IDialogueDatabase } from "@/src/types/inventoryTypes/inventoryTypes";
4
import { ICompletedDialogue } from "@/src/types/inventoryTypes/inventoryTypes";
6
5
import { IInventoryChanges } from "@/src/types/purchaseTypes";
7
6
import { RequestHandler } from "express";
8
7
@@ -107,26 +106,3 @@ interface IOtherDialogueInfo {
107
106
Tag: string;
108
107
Value: number;
109
108
}
110
111
const getDialogue = (inventory: TInventoryDatabaseDocument, dialogueName: string): IDialogueDatabase => {
112
let dialogue = inventory.DialogueHistory!.Dialogues!.find(x => x.DialogueName == dialogueName);
113
if (!dialogue) {
114
dialogue =
115
inventory.DialogueHistory!.Dialogues![
116
inventory.DialogueHistory!.Dialogues!.push({
117
Rank: 0,
118
Chemistry: 0,
119
AvailableDate: new Date(0),
120
AvailableGiftDate: new Date(0),
121
RankUpExpiry: new Date(0),
122
BountyChemExpiry: new Date(0),
123
QueuedDialogues: [],
124
Gifts: [],
125
Booleans: [],
126
Completed: [],
127
DialogueName: dialogueName
128
}) - 1
129
];
130
}
131
return dialogue;
132
};
@@ -28,7 +28,8 @@ import {
28
28
ITraits,
29
29
ICalendarProgress,
30
30
INemesisWeaponTargetFingerprint,
31
INemesisPetTargetFingerprint
31
INemesisPetTargetFingerprint,
32
IDialogueDatabase
32
33
} from "@/src/types/inventoryTypes/inventoryTypes";
33
34
import { IGenericUpdate, IUpdateNodeIntrosResponse } from "../types/genericUpdate";
34
35
import { IKeyChainRequest, IMissionInventoryUpdateRequest } from "../types/requestTypes";
@@ -1906,6 +1907,29 @@ export const cleanupInventory = (inventory: TInventoryDatabaseDocument): void =>
1906
1907
}
1907
1908
};
1908
1909
1910
export const getDialogue = (inventory: TInventoryDatabaseDocument, dialogueName: string): IDialogueDatabase => {
1911
let dialogue = inventory.DialogueHistory!.Dialogues!.find(x => x.DialogueName == dialogueName);
1912
if (!dialogue) {
1913
dialogue =
1914
inventory.DialogueHistory!.Dialogues![
1915
inventory.DialogueHistory!.Dialogues!.push({
1916
Rank: 0,
1917
Chemistry: 0,
1918
AvailableDate: new Date(0),
1919
AvailableGiftDate: new Date(0),
1920
RankUpExpiry: new Date(0),
1921
BountyChemExpiry: new Date(0),
1922
QueuedDialogues: [],
1923
Gifts: [],
1924
Booleans: [],
1925
Completed: [],
1926
DialogueName: dialogueName
1927
}) - 1
1928
];
1929
}
1930
return dialogue;
1931
};
1932
1909
1933
export const getCalendarProgress = (inventory: TInventoryDatabaseDocument): ICalendarProgress => {
1910
1934
const currentSeason = getWorldState().KnownCalendarSeasons[0];
1911
1935
@@ -35,6 +35,7 @@ import {
35
35
combineInventoryChanges,
36
36
generateRewardSeed,
37
37
getCalendarProgress,
38
getDialogue,
38
39
giveNemesisPetRecipe,
39
40
giveNemesisWeaponRecipe,
40
41
updateCurrency,
@@ -63,7 +64,15 @@ import {
63
64
} from "@/src/helpers/nemesisHelpers";
64
65
import { Loadout } from "../models/inventoryModels/loadoutModel";
65
66
import { ILoadoutConfigDatabase } from "../types/saveLoadoutTypes";
66
import { getLiteSortie, getSortie, idToBountyCycle, idToDay, idToWeek, pushClassicBounties } from "./worldStateService";
67
import {
68
getLiteSortie,
69
getSortie,
70
getWorldState,
71
idToBountyCycle,
72
idToDay,
73
idToWeek,
74
pushClassicBounties
75
} from "./worldStateService";
67
76
import { config } from "./configService";
68
77
import libraryDailyTasks from "@/static/fixed_responses/libraryDailyTasks.json";
69
78
import { ISyndicateMissionInfo } from "../types/worldStateTypes";
@@ -1188,8 +1197,9 @@ export const addMissionRewards = async (
1188
1197
}
1189
1198
1190
1199
if (rewardInfo.challengeMissionId) {
1191
const [syndicateTag, tierStr] = rewardInfo.challengeMissionId.split("_"); // TODO: third part in HexSyndicate jobs - Chemistry points
1200
const [syndicateTag, tierStr, chemistryStr] = rewardInfo.challengeMissionId.split("_");
1192
1201
const tier = Number(tierStr);
1202
const chemistry = Number(chemistryStr);
1193
1203
const isSteelPath = missions?.Tier;
1194
1204
if (syndicateTag === "ZarimanSyndicate") {
1195
1205
let medallionAmount = tier + 1;
@@ -1206,6 +1216,23 @@ export const addMissionRewards = async (
1206
1216
if (isSteelPath) standingAmount *= 1.5;
1207
1217
AffiliationMods.push(addStanding(inventory, syndicateTag, standingAmount));
1208
1218
}
1219
if (syndicateTag == "HexSyndicate" && chemistry && tier < 6) {
1220
const seed = getWorldState().SyndicateMissions.find(x => x.Tag == "HexSyndicate")!.Seed;
1221
const { nodes, buddies } = getHexBounties(seed);
1222
const buddy = buddies[tier];
1223
logger.debug(`Hex seed is ${seed}, giving chemistry for ${buddy}`);
1224
if (missions?.Tag != nodes[tier]) {
1225
logger.warn(
1226
`Uh-oh, tier ${tier} bounty should've been on ${nodes[tier]} but you were just on ${missions?.Tag}`
1227
);
1228
}
1229
const tomorrowAt0Utc = config.noKimCooldowns
1230
? Date.now()
1231
: (Math.trunc(Date.now() / 86400_000) + 1) * 86400_000;
1232
const dialogue = getDialogue(inventory, buddy);
1233
dialogue.Chemistry += chemistry;
1234
dialogue.BountyChemExpiry = new Date(tomorrowAt0Utc);
1235
}
1209
1236
if (isSteelPath) {
1210
1237
await addItem(inventory, "/Lotus/Types/Items/MiscItems/SteelEssence", 1);
1211
1238
MissionRewards.push({
@@ -1765,3 +1792,55 @@ const libraryPersonalTargetToAvatar: Record<string, string> = {
1765
1792
"/Lotus/Types/Game/Library/Targets/Research10Target":
1766
1793
"/Lotus/Types/Enemies/Corpus/Spaceman/AIWeek/NullifySpacemanAvatar"
1767
1794
};
1795
1796
const node_excluded_buddies: Record<string, string> = {
1797
SolNode856: "/Lotus/Types/Gameplay/1999Wf/Dialogue/ArthurDialogue_rom.dialogue",
1798
SolNode852: "/Lotus/Types/Gameplay/1999Wf/Dialogue/LettieDialogue_rom.dialogue",
1799
SolNode851: "/Lotus/Types/Gameplay/1999Wf/Dialogue/JabirDialogue_rom.dialogue",
1800
SolNode850: "/Lotus/Types/Gameplay/1999Wf/Dialogue/EleanorDialogue_rom.dialogue",
1801
SolNode853: "/Lotus/Types/Gameplay/1999Wf/Dialogue/AoiDialogue_rom.dialogue",
1802
SolNode854: "/Lotus/Types/Gameplay/1999Wf/Dialogue/QuincyDialogue_rom.dialogue"
1803
};
1804
1805
const getHexBounties = (seed: number): { nodes: string[]; buddies: string[] } => {
1806
// We're gonna shuffle these arrays, so they're not truly 'const'.
1807
const nodes: string[] = [
1808
"SolNode850",
1809
"SolNode851",
1810
"SolNode852",
1811
"SolNode853",
1812
"SolNode854",
1813
"SolNode856",
1814
"SolNode858"
1815
];
1816
const excludable_nodes: string[] = ["SolNode851", "SolNode852", "SolNode853", "SolNode854"];
1817
const buddies: string[] = [
1818
"/Lotus/Types/Gameplay/1999Wf/Dialogue/JabirDialogue_rom.dialogue",
1819
"/Lotus/Types/Gameplay/1999Wf/Dialogue/AoiDialogue_rom.dialogue",
1820
"/Lotus/Types/Gameplay/1999Wf/Dialogue/ArthurDialogue_rom.dialogue",
1821
"/Lotus/Types/Gameplay/1999Wf/Dialogue/EleanorDialogue_rom.dialogue",
1822
"/Lotus/Types/Gameplay/1999Wf/Dialogue/LettieDialogue_rom.dialogue",
1823
"/Lotus/Types/Gameplay/1999Wf/Dialogue/QuincyDialogue_rom.dialogue"
1824
];
1825
1826
const rng = new SRng(seed);
1827
rng.shuffleArray(nodes);
1828
rng.shuffleArray(excludable_nodes);
1829
while (nodes.length > buddies.length) {
1830
nodes.splice(
1831
nodes.findIndex(x => x == excludable_nodes[0]),
1832
1
1833
);
1834
excludable_nodes.splice(0, 1);
1835
}
1836
rng.shuffleArray(buddies);
1837
for (let i = 0; i != 6; ++i) {
1838
if (buddies[i] == node_excluded_buddies[nodes[i]]) {
1839
const swapIdx = (i + 1) % buddies.length;
1840
const tmp = buddies[swapIdx];
1841
buddies[swapIdx] = buddies[i];
1842
buddies[i] = tmp;
1843
}
1844
}
1845
return { nodes, buddies };
1846
};
@@ -115,4 +115,19 @@ export class SRng {
115
115
randomReward<T extends { probability: number }>(pool: T[]): T | undefined {
116
116
return getRewardAtPercentage(pool, this.randomFloat());
117
117
}
118
119
churnSeed(its: number): void {
120
while (its--) {
121
this.state = (0x5851f42d4c957f2dn * this.state + 0x14057b7ef767814fn) & 0xffffffffffffffffn;
122
}
123
}
124
125
shuffleArray<T>(arr: T[]): void {
126
for (let lastIdx = arr.length - 1; lastIdx >= 1; --lastIdx) {
127
const swapIdx = this.randomInt(0, lastIdx);
128
const tmp = arr[swapIdx];
129
arr[swapIdx] = arr[lastIdx];
130
arr[lastIdx] = tmp;
131
}
132
}
118
133
}