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

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
Sainan <63328889+Sainan@users.noreply.github.com>
提交于

代码差异

4 个文件 +123 -29
Modified src/controllers/api/saveDialogueController.ts +2 -26
@@ -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 };
Modified src/services/inventoryService.ts +25 -1
@@ -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
Modified src/services/missionInventoryUpdateService.ts +81 -2
@@ -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 };
Modified src/services/rngService.ts +15 -0
@@ -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 }