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: transmutation (#1112)

Closes #1098 Reviewed-on: https://onlyg.it/OpenWF/SpaceNinjaServer/pulls/1112 Co-authored-by: Sainan <sainan@calamity.inc> Co-committed-by: Sainan <sainan@calamity.inc>

901263ad
Sainan <sainan@calamity.inc>
提交于

代码差异

9 个文件 +260 -139
Modified package-lock.json +4 -4
@@ -12,7 +12,7 @@
12 12 "copyfiles": "^2.4.1",
13 13 "express": "^5",
14 14 "mongoose": "^8.11.0",
15 "warframe-public-export-plus": "^0.5.40",
15 "warframe-public-export-plus": "^0.5.41",
16 16 "warframe-riven-info": "^0.1.2",
17 17 "winston": "^3.17.0",
18 18 "winston-daily-rotate-file": "^5.0.0"
@@ -4083,9 +4083,9 @@
4083 4083 }
4084 4084 },
4085 4085 "node_modules/warframe-public-export-plus": {
4086 "version": "0.5.40",
4087 "resolved": "https://registry.npmjs.org/warframe-public-export-plus/-/warframe-public-export-plus-0.5.40.tgz",
4088 "integrity": "sha512-/qr46LE/KqDdEkW4z52EG0vZP0Z8U26FscFJ2G5K5ewbQdlSVxtf5fpOnzRkAO7jWWKfgoqx7l5WUgaLSPDj0g=="
4086 "version": "0.5.41",
4087 "resolved": "https://registry.npmjs.org/warframe-public-export-plus/-/warframe-public-export-plus-0.5.41.tgz",
4088 "integrity": "sha512-qVOUY4UjF1cyBrBbMwD25xHSdSf9q57/CJgjHsfSE7NUu/6pBDSZzwS0iAetAukws/1V2kDvsuy8AGtOec2L1w=="
4089 4089 },
4090 4090 "node_modules/warframe-riven-info": {
4091 4091 "version": "0.1.2",
Modified package.json +1 -1
@@ -17,7 +17,7 @@
17 17 "copyfiles": "^2.4.1",
18 18 "express": "^5",
19 19 "mongoose": "^8.11.0",
20 "warframe-public-export-plus": "^0.5.40",
20 "warframe-public-export-plus": "^0.5.41",
21 21 "warframe-riven-info": "^0.1.2",
22 22 "winston": "^3.17.0",
23 23 "winston-daily-rotate-file": "^5.0.0"
Modified src/controllers/api/activateRandomModController.ts +6 -63
@@ -1,10 +1,9 @@
1 1 import { toOid } from "@/src/helpers/inventoryHelpers";
2 import { IRivenChallenge } from "@/src/helpers/rivenFingerprintHelper";
2 import { createVeiledRivenFingerprint, rivenRawToRealWeighted } from "@/src/helpers/rivenHelper";
3 3 import { getJSONfromString } from "@/src/helpers/stringHelpers";
4 4 import { addMods, getInventory } from "@/src/services/inventoryService";
5 5 import { getAccountIdForRequest } from "@/src/services/loginService";
6 import { getRandomElement, getRandomInt, getRandomReward } from "@/src/services/rngService";
7 import { logger } from "@/src/utils/logger";
6 import { getRandomElement } from "@/src/services/rngService";
8 7 import { RequestHandler } from "express";
9 8 import { ExportUpgrades } from "warframe-public-export-plus";
10 9
@@ -19,40 +18,18 @@ export const activateRandomModController: RequestHandler = async (req, res) => {
19 18 }
20 19 ]);
21 20 const rivenType = getRandomElement(rivenRawToRealWeighted[request.ItemType]);
22 const challenge = getRandomElement(ExportUpgrades[rivenType].availableChallenges!);
23 const fingerprintChallenge: IRivenChallenge = {
24 Type: challenge.fullName,
25 Progress: 0,
26 Required: getRandomInt(challenge.countRange[0], challenge.countRange[1])
27 };
28 if (Math.random() < challenge.complicationChance) {
29 const complications: { type: string; probability: number }[] = [];
30 for (const complication of challenge.complications) {
31 complications.push({
32 type: complication.fullName,
33 probability: complication.weight
34 });
35 }
36 fingerprintChallenge.Complication = getRandomReward(complications)!.type;
37 logger.debug(
38 `riven rolled challenge ${fingerprintChallenge.Type} with complication ${fingerprintChallenge.Complication}`
39 );
40 const complication = challenge.complications.find(x => x.fullName == fingerprintChallenge.Complication)!;
41 fingerprintChallenge.Required *= complication.countMultiplier;
42 } else {
43 logger.debug(`riven rolled challenge ${fingerprintChallenge.Type}`);
44 }
21 const fingerprint = createVeiledRivenFingerprint(ExportUpgrades[rivenType]);
45 22 const upgradeIndex =
46 23 inventory.Upgrades.push({
47 24 ItemType: rivenType,
48 UpgradeFingerprint: JSON.stringify({ challenge: fingerprintChallenge })
25 UpgradeFingerprint: JSON.stringify(fingerprint)
49 26 }) - 1;
50 27 await inventory.save();
51 28 // For some reason, in this response, the UpgradeFingerprint is simply a nested object and not a string
52 29 res.json({
53 30 NewMod: {
54 UpgradeFingerprint: { challenge: fingerprintChallenge },
55 ItemType: inventory.Upgrades[upgradeIndex].ItemType,
31 UpgradeFingerprint: fingerprint,
32 ItemType: rivenType,
56 33 ItemId: toOid(inventory.Upgrades[upgradeIndex]._id)
57 34 }
58 35 });
@@ -61,37 +38,3 @@ export const activateRandomModController: RequestHandler = async (req, res) => {
61 38 interface IActiveRandomModRequest {
62 39 ItemType: string;
63 40 }
64
65 const rivenRawToRealWeighted: Record<string, string[]> = {
66 "/Lotus/Upgrades/Mods/Randomized/RawArchgunRandomMod": [
67 "/Lotus/Upgrades/Mods/Randomized/LotusArchgunRandomModRare"
68 ],
69 "/Lotus/Upgrades/Mods/Randomized/RawMeleeRandomMod": [
70 "/Lotus/Upgrades/Mods/Randomized/PlayerMeleeWeaponRandomModRare"
71 ],
72 "/Lotus/Upgrades/Mods/Randomized/RawModularMeleeRandomMod": [
73 "/Lotus/Upgrades/Mods/Randomized/LotusModularMeleeRandomModRare"
74 ],
75 "/Lotus/Upgrades/Mods/Randomized/RawModularPistolRandomMod": [
76 "/Lotus/Upgrades/Mods/Randomized/LotusModularPistolRandomModRare"
77 ],
78 "/Lotus/Upgrades/Mods/Randomized/RawPistolRandomMod": ["/Lotus/Upgrades/Mods/Randomized/LotusPistolRandomModRare"],
79 "/Lotus/Upgrades/Mods/Randomized/RawRifleRandomMod": ["/Lotus/Upgrades/Mods/Randomized/LotusRifleRandomModRare"],
80 "/Lotus/Upgrades/Mods/Randomized/RawShotgunRandomMod": [
81 "/Lotus/Upgrades/Mods/Randomized/LotusShotgunRandomModRare"
82 ],
83 "/Lotus/Upgrades/Mods/Randomized/RawSentinelWeaponRandomMod": [
84 "/Lotus/Upgrades/Mods/Randomized/LotusRifleRandomModRare",
85 "/Lotus/Upgrades/Mods/Randomized/LotusRifleRandomModRare",
86 "/Lotus/Upgrades/Mods/Randomized/LotusRifleRandomModRare",
87 "/Lotus/Upgrades/Mods/Randomized/LotusRifleRandomModRare",
88 "/Lotus/Upgrades/Mods/Randomized/LotusRifleRandomModRare",
89 "/Lotus/Upgrades/Mods/Randomized/LotusRifleRandomModRare",
90 "/Lotus/Upgrades/Mods/Randomized/LotusRifleRandomModRare",
91 "/Lotus/Upgrades/Mods/Randomized/LotusRifleRandomModRare",
92 "/Lotus/Upgrades/Mods/Randomized/LotusRifleRandomModRare",
93 "/Lotus/Upgrades/Mods/Randomized/LotusShotgunRandomModRare",
94 "/Lotus/Upgrades/Mods/Randomized/LotusPistolRandomModRare",
95 "/Lotus/Upgrades/Mods/Randomized/PlayerMeleeWeaponRandomModRare"
96 ]
97 };
Added src/controllers/api/artifactTransmutationController.ts +124 -0
@@ -0,0 +1,124 @@
1 import { toOid } from "@/src/helpers/inventoryHelpers";
2 import { createVeiledRivenFingerprint, rivenRawToRealWeighted } from "@/src/helpers/rivenHelper";
3 import { addMiscItems, addMods, getInventory } from "@/src/services/inventoryService";
4 import { getAccountIdForRequest } from "@/src/services/loginService";
5 import { getRandomElement, getRandomWeightedReward, getRandomWeightedRewardUc } from "@/src/services/rngService";
6 import { IOid } from "@/src/types/commonTypes";
7 import { RequestHandler } from "express";
8 import { ExportBoosterPacks, ExportUpgrades, TRarity } from "warframe-public-export-plus";
9
10 export const artifactTransmutationController: RequestHandler = async (req, res) => {
11 const accountId = await getAccountIdForRequest(req);
12 const inventory = await getInventory(accountId);
13 const payload = JSON.parse(String(req.body)) as IArtifactTransmutationRequest;
14
15 inventory.RegularCredits -= payload.Cost;
16 inventory.FusionPoints -= payload.FusionPointCost;
17
18 if (payload.RivenTransmute) {
19 addMiscItems(inventory, [
20 {
21 ItemType: "/Lotus/Types/Gameplay/Eidolon/Resources/SentientSecretItem",
22 ItemCount: -1
23 }
24 ]);
25
26 payload.Consumed.forEach(upgrade => {
27 inventory.Upgrades.pull({ _id: upgrade.ItemId.$oid });
28 });
29
30 const rawRivenType = getRandomRawRivenType();
31 const rivenType = getRandomElement(rivenRawToRealWeighted[rawRivenType]);
32 const fingerprint = createVeiledRivenFingerprint(ExportUpgrades[rivenType]);
33
34 const upgradeIndex =
35 inventory.Upgrades.push({
36 ItemType: rivenType,
37 UpgradeFingerprint: JSON.stringify(fingerprint)
38 }) - 1;
39 await inventory.save();
40 res.json({
41 NewMods: [
42 {
43 ItemId: toOid(inventory.Upgrades[upgradeIndex]._id),
44 ItemType: rivenType,
45 UpgradeFingerprint: fingerprint
46 }
47 ]
48 });
49 } else {
50 const counts: Record<TRarity, number> = {
51 COMMON: 0,
52 UNCOMMON: 0,
53 RARE: 0,
54 LEGENDARY: 0
55 };
56 payload.Consumed.forEach(upgrade => {
57 const meta = ExportUpgrades[upgrade.ItemType];
58 counts[meta.rarity] += upgrade.ItemCount;
59 addMods(inventory, [
60 {
61 ItemType: upgrade.ItemType,
62 ItemCount: upgrade.ItemCount * -1
63 }
64 ]);
65 });
66
67 // Based on the table on https://wiki.warframe.com/w/Transmutation
68 const weights: Record<TRarity, number> = {
69 COMMON: counts.COMMON * 95 + counts.UNCOMMON * 15 + counts.RARE * 4,
70 UNCOMMON: counts.COMMON * 4 + counts.UNCOMMON * 80 + counts.RARE * 10,
71 RARE: counts.COMMON * 1 + counts.UNCOMMON * 5 + counts.RARE * 50,
72 LEGENDARY: 0
73 };
74
75 const options: { uniqueName: string; rarity: TRarity }[] = [];
76 Object.entries(ExportUpgrades).forEach(([uniqueName, upgrade]) => {
77 if (upgrade.canBeTransmutation) {
78 options.push({ uniqueName, rarity: upgrade.rarity });
79 }
80 });
81
82 const newModType = getRandomWeightedReward(options, weights)!.uniqueName;
83 addMods(inventory, [
84 {
85 ItemType: newModType,
86 ItemCount: 1
87 }
88 ]);
89
90 await inventory.save();
91 res.json({
92 NewMods: [
93 {
94 ItemType: newModType,
95 ItemCount: 1
96 }
97 ]
98 });
99 }
100 };
101
102 const getRandomRawRivenType = (): string => {
103 const pack = ExportBoosterPacks["/Lotus/Types/BoosterPacks/CalendarRivenPack"];
104 return getRandomWeightedRewardUc(pack.components, pack.rarityWeightsPerRoll[0])!.Item;
105 };
106
107 interface IArtifactTransmutationRequest {
108 Upgrade: IAgnosticUpgradeClient;
109 LevelDiff: number;
110 Consumed: IAgnosticUpgradeClient[];
111 Cost: number;
112 FusionPointCost: number;
113 RivenTransmute?: boolean;
114 }
115
116 interface IAgnosticUpgradeClient {
117 ItemType: string;
118 ItemId: IOid;
119 FromSKU: boolean;
120 UpgradeFingerprint: string;
121 PendingRerollFingerprint: string;
122 ItemCount: number;
123 LastAdded: IOid;
124 }
Modified src/controllers/api/completeRandomModChallengeController.ts +1 -1
@@ -4,7 +4,7 @@ import { addMiscItems, getInventory, updateCurrency } from "@/src/services/inven
4 4 import { IInventoryChanges } from "@/src/types/purchaseTypes";
5 5 import { IMiscItem } from "@/src/types/inventoryTypes/inventoryTypes";
6 6 import { getJSONfromString } from "@/src/helpers/stringHelpers";
7 import { createUnveiledRivenFingerprint } from "@/src/helpers/rivenFingerprintHelper";
7 import { createUnveiledRivenFingerprint } from "@/src/helpers/rivenHelper";
8 8 import { ExportUpgrades } from "warframe-public-export-plus";
9 9
10 10 export const completeRandomModChallengeController: RequestHandler = async (req, res) => {
Modified src/controllers/api/rerollRandomModController.ts +1 -5
@@ -2,11 +2,7 @@ import { RequestHandler } from "express";
2 2 import { getAccountIdForRequest } from "@/src/services/loginService";
3 3 import { addMiscItems, getInventory } from "@/src/services/inventoryService";
4 4 import { getJSONfromString } from "@/src/helpers/stringHelpers";
5 import {
6 createUnveiledRivenFingerprint,
7 randomiseRivenStats,
8 RivenFingerprint
9 } from "@/src/helpers/rivenFingerprintHelper";
5 import { createUnveiledRivenFingerprint, randomiseRivenStats, RivenFingerprint } from "@/src/helpers/rivenHelper";
10 6 import { ExportUpgrades } from "warframe-public-export-plus";
11 7 import { IOid } from "@/src/types/commonTypes";
12 8
Deleted src/helpers/rivenFingerprintHelper.ts +0 -65
@@ -1,65 +0,0 @@
1 import { IUpgrade } from "warframe-public-export-plus";
2 import { getRandomElement, getRandomInt } from "../services/rngService";
3
4 export type RivenFingerprint = IVeiledRivenFingerprint | IUnveiledRivenFingerprint;
5
6 export interface IVeiledRivenFingerprint {
7 challenge: IRivenChallenge;
8 }
9
10 export interface IRivenChallenge {
11 Type: string;
12 Progress: number;
13 Required: number;
14 Complication?: string;
15 }
16
17 export interface IUnveiledRivenFingerprint {
18 compat: string;
19 lim: 0;
20 lvl: number;
21 lvlReq: number;
22 rerolls?: number;
23 pol: string;
24 buffs: IRivenStat[];
25 curses: IRivenStat[];
26 }
27
28 interface IRivenStat {
29 Tag: string;
30 Value: number;
31 }
32
33 export const createUnveiledRivenFingerprint = (meta: IUpgrade): IUnveiledRivenFingerprint => {
34 const fingerprint: IUnveiledRivenFingerprint = {
35 compat: getRandomElement(meta.compatibleItems!),
36 lim: 0,
37 lvl: 0,
38 lvlReq: getRandomInt(8, 16),
39 pol: getRandomElement(["AP_ATTACK", "AP_DEFENSE", "AP_TACTIC"]),
40 buffs: [],
41 curses: []
42 };
43 randomiseRivenStats(meta, fingerprint);
44 return fingerprint;
45 };
46
47 export const randomiseRivenStats = (meta: IUpgrade, fingerprint: IUnveiledRivenFingerprint): void => {
48 fingerprint.buffs = [];
49 const numBuffs = 2 + Math.trunc(Math.random() * 2); // 2 or 3
50 const buffEntries = meta.upgradeEntries!.filter(x => x.canBeBuff);
51 for (let i = 0; i != numBuffs; ++i) {
52 const buffIndex = Math.trunc(Math.random() * buffEntries.length);
53 const entry = buffEntries[buffIndex];
54 fingerprint.buffs.push({ Tag: entry.tag, Value: Math.trunc(Math.random() * 0x40000000) });
55 buffEntries.splice(buffIndex, 1);
56 }
57
58 fingerprint.curses = [];
59 if (Math.random() < 0.5) {
60 const entry = getRandomElement(
61 meta.upgradeEntries!.filter(x => x.canBeCurse && !fingerprint.buffs.find(y => y.Tag == x.tag))
62 );
63 fingerprint.curses.push({ Tag: entry.tag, Value: Math.trunc(Math.random() * 0x40000000) });
64 }
65 };
Added src/helpers/rivenHelper.ts +121 -0
@@ -0,0 +1,121 @@
1 import { IUpgrade } from "warframe-public-export-plus";
2 import { getRandomElement, getRandomInt, getRandomReward } from "../services/rngService";
3
4 export type RivenFingerprint = IVeiledRivenFingerprint | IUnveiledRivenFingerprint;
5
6 export interface IVeiledRivenFingerprint {
7 challenge: IRivenChallenge;
8 }
9
10 export interface IRivenChallenge {
11 Type: string;
12 Progress: number;
13 Required: number;
14 Complication?: string;
15 }
16
17 export interface IUnveiledRivenFingerprint {
18 compat: string;
19 lim: 0;
20 lvl: number;
21 lvlReq: number;
22 rerolls?: number;
23 pol: string;
24 buffs: IRivenStat[];
25 curses: IRivenStat[];
26 }
27
28 interface IRivenStat {
29 Tag: string;
30 Value: number;
31 }
32
33 export const createVeiledRivenFingerprint = (meta: IUpgrade): IVeiledRivenFingerprint => {
34 const challenge = getRandomElement(meta.availableChallenges!);
35 const fingerprintChallenge: IRivenChallenge = {
36 Type: challenge.fullName,
37 Progress: 0,
38 Required: getRandomInt(challenge.countRange[0], challenge.countRange[1])
39 };
40 if (Math.random() < challenge.complicationChance) {
41 const complications: { type: string; probability: number }[] = [];
42 for (const complication of challenge.complications) {
43 complications.push({
44 type: complication.fullName,
45 probability: complication.weight
46 });
47 }
48 fingerprintChallenge.Complication = getRandomReward(complications)!.type;
49 const complication = challenge.complications.find(x => x.fullName == fingerprintChallenge.Complication)!;
50 fingerprintChallenge.Required *= complication.countMultiplier;
51 }
52 return { challenge: fingerprintChallenge };
53 };
54
55 export const createUnveiledRivenFingerprint = (meta: IUpgrade): IUnveiledRivenFingerprint => {
56 const fingerprint: IUnveiledRivenFingerprint = {
57 compat: getRandomElement(meta.compatibleItems!),
58 lim: 0,
59 lvl: 0,
60 lvlReq: getRandomInt(8, 16),
61 pol: getRandomElement(["AP_ATTACK", "AP_DEFENSE", "AP_TACTIC"]),
62 buffs: [],
63 curses: []
64 };
65 randomiseRivenStats(meta, fingerprint);
66 return fingerprint;
67 };
68
69 export const randomiseRivenStats = (meta: IUpgrade, fingerprint: IUnveiledRivenFingerprint): void => {
70 fingerprint.buffs = [];
71 const numBuffs = 2 + Math.trunc(Math.random() * 2); // 2 or 3
72 const buffEntries = meta.upgradeEntries!.filter(x => x.canBeBuff);
73 for (let i = 0; i != numBuffs; ++i) {
74 const buffIndex = Math.trunc(Math.random() * buffEntries.length);
75 const entry = buffEntries[buffIndex];
76 fingerprint.buffs.push({ Tag: entry.tag, Value: Math.trunc(Math.random() * 0x40000000) });
77 buffEntries.splice(buffIndex, 1);
78 }
79
80 fingerprint.curses = [];
81 if (Math.random() < 0.5) {
82 const entry = getRandomElement(
83 meta.upgradeEntries!.filter(x => x.canBeCurse && !fingerprint.buffs.find(y => y.Tag == x.tag))
84 );
85 fingerprint.curses.push({ Tag: entry.tag, Value: Math.trunc(Math.random() * 0x40000000) });
86 }
87 };
88
89 export const rivenRawToRealWeighted: Record<string, string[]> = {
90 "/Lotus/Upgrades/Mods/Randomized/RawArchgunRandomMod": [
91 "/Lotus/Upgrades/Mods/Randomized/LotusArchgunRandomModRare"
92 ],
93 "/Lotus/Upgrades/Mods/Randomized/RawMeleeRandomMod": [
94 "/Lotus/Upgrades/Mods/Randomized/PlayerMeleeWeaponRandomModRare"
95 ],
96 "/Lotus/Upgrades/Mods/Randomized/RawModularMeleeRandomMod": [
97 "/Lotus/Upgrades/Mods/Randomized/LotusModularMeleeRandomModRare"
98 ],
99 "/Lotus/Upgrades/Mods/Randomized/RawModularPistolRandomMod": [
100 "/Lotus/Upgrades/Mods/Randomized/LotusModularPistolRandomModRare"
101 ],
102 "/Lotus/Upgrades/Mods/Randomized/RawPistolRandomMod": ["/Lotus/Upgrades/Mods/Randomized/LotusPistolRandomModRare"],
103 "/Lotus/Upgrades/Mods/Randomized/RawRifleRandomMod": ["/Lotus/Upgrades/Mods/Randomized/LotusRifleRandomModRare"],
104 "/Lotus/Upgrades/Mods/Randomized/RawShotgunRandomMod": [
105 "/Lotus/Upgrades/Mods/Randomized/LotusShotgunRandomModRare"
106 ],
107 "/Lotus/Upgrades/Mods/Randomized/RawSentinelWeaponRandomMod": [
108 "/Lotus/Upgrades/Mods/Randomized/LotusRifleRandomModRare",
109 "/Lotus/Upgrades/Mods/Randomized/LotusRifleRandomModRare",
110 "/Lotus/Upgrades/Mods/Randomized/LotusRifleRandomModRare",
111 "/Lotus/Upgrades/Mods/Randomized/LotusRifleRandomModRare",
112 "/Lotus/Upgrades/Mods/Randomized/LotusRifleRandomModRare",
113 "/Lotus/Upgrades/Mods/Randomized/LotusRifleRandomModRare",
114 "/Lotus/Upgrades/Mods/Randomized/LotusRifleRandomModRare",
115 "/Lotus/Upgrades/Mods/Randomized/LotusRifleRandomModRare",
116 "/Lotus/Upgrades/Mods/Randomized/LotusRifleRandomModRare",
117 "/Lotus/Upgrades/Mods/Randomized/LotusShotgunRandomModRare",
118 "/Lotus/Upgrades/Mods/Randomized/LotusPistolRandomModRare",
119 "/Lotus/Upgrades/Mods/Randomized/PlayerMeleeWeaponRandomModRare"
120 ]
121 };
Modified src/routes/api.ts +2 -0
@@ -7,6 +7,7 @@ import { addFriendImageController } from "@/src/controllers/api/addFriendImageCo
7 7 import { arcaneCommonController } from "@/src/controllers/api/arcaneCommonController";
8 8 import { archonFusionController } from "@/src/controllers/api/archonFusionController";
9 9 import { artifactsController } from "@/src/controllers/api/artifactsController";
10 import { artifactTransmutationController } from "@/src/controllers/api/artifactTransmutationController";
10 11 import { changeDojoRootController } from "@/src/controllers/api/changeDojoRootController";
11 12 import { checkDailyMissionBonusController } from "@/src/controllers/api/checkDailyMissionBonusController";
12 13 import { claimCompletedRecipeController } from "@/src/controllers/api/claimCompletedRecipeController";
@@ -150,6 +151,7 @@ apiRouter.post("/addFriendImage.php", addFriendImageController);
150 151 apiRouter.post("/arcaneCommon.php", arcaneCommonController);
151 152 apiRouter.post("/archonFusion.php", archonFusionController);
152 153 apiRouter.post("/artifacts.php", artifactsController);
154 apiRouter.post("/artifactTransmutation.php", artifactTransmutationController);
153 155 apiRouter.post("/changeDojoRoot.php", changeDojoRootController);
154 156 apiRouter.post("/claimCompletedRecipe.php", claimCompletedRecipeController);
155 157 apiRouter.post("/clearDialogueHistory.php", clearDialogueHistoryController);