返回提交历史
Added
src/controllers/api/asyncMissionAssistController.ts
+136
-0
Modified
src/routes/api.ts
+2
-0
Modified
src/services/loginService.ts
+1
-1
Modified
src/types/inventoryTypes/inventoryTypes.ts
+10
-1
XFEstudio/XFESpaceNinjaServer
feat: asyncMissionAssist (#3814)
Closes #3188 Reviewed-on: https://onlyg.it/OpenWF/SpaceNinjaServer/pulls/3814 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>
f0cc75aa
代码差异
4 个文件
+149
-2
@@ -0,0 +1,136 @@
1
import type { RequestHandler } from "express";
2
import { getAccountForRequest, getUnicodeName, stripUnicodeSuffix } from "../../services/loginService.ts";
3
import type { IOid } from "../../types/commonTypes.ts";
4
import { Account } from "../../models/loginModel.ts";
5
import { Inventory } from "../../models/inventoryModels/inventoryModel.ts";
6
import type { IEquipmentClient } from "../../types/equipmentTypes.ts";
7
import { Loadout } from "../../models/inventoryModels/loadoutModel.ts";
8
import { LoadoutIndex } from "../../types/inventoryTypes/inventoryTypes.ts";
9
import { toObjectId } from "../../helpers/inventoryHelpers.ts";
10
11
export const asyncMissionAssistController: RequestHandler = async (req, res) => {
12
const account = await getAccountForRequest(req);
13
const payload = JSON.parse(String(req.body)) as IAsyncMissionAssistRequest;
14
const excludedOwnerIds = [account._id];
15
const accounts = await Account.find(
16
{ DisplayName: { $in: payload.ExcludedNames.map(stripUnicodeSuffix) } },
17
{ _id: 1 }
18
);
19
for (const acc of accounts) {
20
excludedOwnerIds.push(acc._id);
21
}
22
const candidateIds = payload.Candidates.map(c => toObjectId(c.$oid));
23
let inventories = await Inventory.find(
24
{
25
accountOwnerId: { $in: candidateIds, $nin: excludedOwnerIds },
26
QuestKeys: {
27
$elemMatch: { ItemType: "/Lotus/Types/Keys/OrokinMoonQuest/OrokinMoonQuestKeyChain", Completed: true }
28
},
29
OperatorSuits: { $elemMatch: { ItemType: "/Lotus/Powersuits/Operator/ChildOperatorSuitRemaster" } }
30
},
31
{ _id: 0, accountOwnerId: 1, FocusAbility: 1, OperatorSuits: 1, CurrentLoadOutIds: 1 }
32
).limit(payload.Count);
33
if (inventories.length < payload.Count) {
34
const remaining = payload.Count - inventories.length;
35
const otherInventories = await Inventory.aggregate([
36
{
37
$match: {
38
accountOwnerId: { $nin: [...excludedOwnerIds, ...inventories.map(i => i.accountOwnerId)] },
39
QuestKeys: {
40
$elemMatch: {
41
ItemType: "/Lotus/Types/Keys/OrokinMoonQuest/OrokinMoonQuestKeyChain",
42
Completed: true
43
}
44
},
45
OperatorSuits: { $elemMatch: { ItemType: "/Lotus/Powersuits/Operator/ChildOperatorSuitRemaster" } }
46
}
47
},
48
{ $sample: { size: remaining } },
49
{ $project: { _id: 0, accountOwnerId: 1, FocusAbility: 1, OperatorSuits: 1, CurrentLoadOutIds: 1 } }
50
]);
51
inventories = inventories.concat(otherInventories);
52
}
53
const ownerIds = inventories.map(i => i.accountOwnerId);
54
55
const accountsRaw = await Account.find({ _id: { $in: ownerIds } });
56
const accountsMap = new Map(accountsRaw.map(a => [a._id.toString(), a]));
57
const loadoutsRaw = await Loadout.find(
58
{ loadoutOwnerId: { $in: ownerIds } },
59
{ _id: 0, loadoutOwnerId: 1, OPERATOR: 1 }
60
);
61
const loadoutMap = new Map(loadoutsRaw.map(l => [l.loadoutOwnerId.toString(), l]));
62
const resp: IAsyncMissionAssistResponce = {
63
Results: []
64
};
65
66
for (const inventory of inventories) {
67
const operatorDb = inventory.OperatorSuits.find(
68
o => o.ItemType == "/Lotus/Powersuits/Operator/ChildOperatorSuitRemaster"
69
);
70
const loadout = loadoutMap.get(inventory.accountOwnerId.toString());
71
const configNum =
72
loadout?.OPERATOR.find(l => l._id == inventory.CurrentLoadOutIds[LoadoutIndex.OPERATOR])?.s?.cus || 0;
73
const operator: Omit<IEquipmentClient, "ItemId"> = {
74
ItemType: operatorDb?.ItemType || "/Lotus/Powersuits/Operator/ChildOperatorSuitRemaster",
75
Configs: operatorDb?.Configs.slice(configNum, configNum + 1) || [],
76
UpgradeVer: 101
77
};
78
const a = accountsMap.get(inventory.accountOwnerId.toString())!;
79
const focusAbility = inventory.FocusAbility || "/Lotus/Upgrades/Focus/Power/PowerFocusAbility";
80
const result: IAsyncMissionAssistResult = {
81
DeploymentType: focusAbilityToDeploymentType[focusAbility],
82
FocusAbility: focusAbility,
83
Location: payload.MissionTag, // not faithful, but game doesn't complain?
84
Name: getUnicodeName(a, undefined) || "cat\uE000",
85
Operator: operator
86
};
87
resp.Results.push(result);
88
}
89
90
while (resp.Results.length < payload.Count) {
91
const focusAbilities = Object.keys(focusAbilityToDeploymentType);
92
const focusAbility = focusAbilities[Math.floor(Math.random() * focusAbilities.length)];
93
resp.Results.push({
94
DeploymentType: focusAbilityToDeploymentType[focusAbility],
95
FocusAbility: focusAbility,
96
Location: payload.MissionTag,
97
Name: "cat" + resp.Results.length + "\uE000",
98
Operator: {
99
ItemType: "/Lotus/Powersuits/Operator/ChildOperatorSuitRemaster",
100
Configs: [],
101
UpgradeVer: 101
102
}
103
});
104
}
105
106
res.json(resp);
107
};
108
109
interface IAsyncMissionAssistRequest {
110
MissionTag: string;
111
Count: number;
112
Candidates: IOid[];
113
ExcludedNames: string[];
114
}
115
116
interface IAsyncMissionAssistResult {
117
DeploymentType: TDeploymentType;
118
FocusAbility: string;
119
Location: string;
120
Name: string;
121
Operator: Omit<IEquipmentClient, "ItemId">;
122
}
123
124
interface IAsyncMissionAssistResponce {
125
Results: IAsyncMissionAssistResult[];
126
}
127
128
type TDeploymentType = "MADURAI" | "VAZARIN" | "ZENURIK" | "NARAMON" | "UNAIRU";
129
130
const focusAbilityToDeploymentType: Record<string, TDeploymentType> = {
131
"/Lotus/Upgrades/Focus/Attack/AttackFocusAbility": "MADURAI",
132
"/Lotus/Upgrades/Focus/Defense/DefenseFocusAbility": "VAZARIN",
133
"/Lotus/Upgrades/Focus/Power/PowerFocusAbility": "ZENURIK",
134
"/Lotus/Upgrades/Focus/Tactic/TacticFocusAbility": "NARAMON",
135
"/Lotus/Upgrades/Focus/Ward/WardFocusAbility": "UNAIRU"
136
};
@@ -17,6 +17,7 @@ import { arcaneCommonController } from "../controllers/api/arcaneCommonControlle
17
17
import { archonFusionController } from "../controllers/api/archonFusionController.ts";
18
18
import { artifactsController } from "../controllers/api/artifactsController.ts";
19
19
import { artifactTransmutationController } from "../controllers/api/artifactTransmutationController.ts";
20
import { asyncMissionAssistController } from "../controllers/api/asyncMissionAssistController.ts";
20
21
import { cancelGuildAdvertisementController } from "../controllers/api/cancelGuildAdvertisementController.ts";
21
22
import { changeDojoRootController } from "../controllers/api/changeDojoRootController.ts";
22
23
import { changeDojoSpawnRoomController } from "../controllers/api/changeDojoSpawnRoomController.ts";
@@ -283,6 +284,7 @@ apiRouter.post("/arcaneCommon.php", arcaneCommonController);
283
284
apiRouter.post("/archonFusion.php", archonFusionController);
284
285
apiRouter.post("/artifacts.php", artifactsController);
285
286
apiRouter.post("/artifactTransmutation.php", artifactTransmutationController);
287
apiRouter.post("/asyncMissionAssist.php", asyncMissionAssistController)
286
288
apiRouter.post("/changeDojoRoot.php", changeDojoRootController);
287
289
apiRouter.post("/changeDojoSpawnRoom.php", changeDojoSpawnRoomController);
288
290
apiRouter.post("/claimCompletedRecipe.php", claimCompletedRecipeController);
@@ -164,7 +164,7 @@ export const getUnicodeName = (account: TAccountDocument, buildLabel: string | u
164
164
};
165
165
166
166
export const stripUnicodeSuffix = (name: string): string => {
167
if (name.charCodeAt(name.length) >= 0xe000) {
167
if (name.charCodeAt(name.length - 1) >= 0xe000) {
168
168
name = name.substring(0, name.length - 1);
169
169
}
170
170
return name;
@@ -297,7 +297,16 @@ export type TSolarMapRegion =
297
297
298
298
export enum LoadoutIndex {
299
299
NORMAL = 0,
300
DATAKNIFE = 7
300
SENTINEL = 1,
301
ARCHWING = 2,
302
NORMAL_PVP = 3,
303
LUNARO = 4,
304
OPERATOR = 5,
305
KDRIVE = 6,
306
DATAKNIFE = 7,
307
MECH = 8,
308
OPERATOR_ADULT = 9,
309
DRIFTER = 10
301
310
}
302
311
303
312
export interface IDailyAffiliations {