返回提交历史
Modified
config.json.example
+1
-0
Modified
package-lock.json
+4
-4
Modified
package.json
+1
-1
Modified
src/controllers/api/dronesController.ts
+136
-3
Modified
src/controllers/api/sellController.ts
+6
-0
Modified
src/models/inventoryModels/inventoryModel.ts
+18
-1
Modified
src/routes/api.ts
+1
-0
Modified
src/services/configService.ts
+1
-0
Modified
src/services/rngService.ts
+19
-1
Modified
src/types/inventoryTypes/inventoryTypes.ts
+7
-0
Modified
static/webui/index.html
+4
-0
Modified
static/webui/translations/en.js
+1
-0
Modified
static/webui/translations/ru.js
+1
-0
XFEstudio/XFESpaceNinjaServer
feat: resource extractor drones (#1068)
Closes #793 Reviewed-on: https://onlyg.it/OpenWF/SpaceNinjaServer/pulls/1068
b3003b9f
代码差异
13 个文件
+200
-10
@@ -31,6 +31,7 @@
31
31
"unlockExilusEverywhere": true,
32
32
"unlockArcanesEverywhere": true,
33
33
"noDailyStandingLimits": true,
34
"instantResourceExtractorDrones": false,
34
35
"noDojoResearchCosts": true,
35
36
"noDojoResearchTime": true,
36
37
"spoofMasteryRank": -1
@@ -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.38",
15
"warframe-public-export-plus": "^0.5.39",
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.38",
4087
"resolved": "https://registry.npmjs.org/warframe-public-export-plus/-/warframe-public-export-plus-0.5.38.tgz",
4088
"integrity": "sha512-yvc86eOmYPSnnU8LzLBhg/lR1AS1RHID24TqFHVcZuOzMYc934NL8Cv7rtllyefWAMyl7iA5x9tyXSuJWbi6CA=="
4086
"version": "0.5.39",
4087
"resolved": "https://registry.npmjs.org/warframe-public-export-plus/-/warframe-public-export-plus-0.5.39.tgz",
4088
"integrity": "sha512-sEGZedtW4I/M2ceoDs6MQ5eHD7sJgv1KRNLt8BWByXLuDa7qTR3Y9px5TGxqt/rBHKGUyPO1LUxu4bDGZi6yXw=="
4089
4089
},
4090
4090
"node_modules/warframe-riven-info": {
4091
4091
"version": "0.1.2",
@@ -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.38",
20
"warframe-public-export-plus": "^0.5.39",
21
21
"warframe-riven-info": "^0.1.2",
22
22
"winston": "^3.17.0",
23
23
"winston-daily-rotate-file": "^5.0.0"
@@ -1,7 +1,140 @@
1
import { toMongoDate, toOid } from "@/src/helpers/inventoryHelpers";
2
import { config } from "@/src/services/configService";
3
import { addMiscItems, getInventory } from "@/src/services/inventoryService";
4
import { getAccountIdForRequest } from "@/src/services/loginService";
5
import { getRandomInt, getRandomWeightedReward3 } from "@/src/services/rngService";
6
import { IMongoDate, IOid } from "@/src/types/commonTypes";
7
import { IDroneClient } from "@/src/types/inventoryTypes/inventoryTypes";
8
import { IInventoryChanges } from "@/src/types/purchaseTypes";
1
9
import { RequestHandler } from "express";
10
import { ExportDrones, ExportResources, ExportSystems } from "warframe-public-export-plus";
2
11
3
const dronesController: RequestHandler = (_req, res) => {
4
res.json({});
12
export const dronesController: RequestHandler = async (req, res) => {
13
const accountId = await getAccountIdForRequest(req);
14
const inventory = await getInventory(accountId);
15
if ("GetActive" in req.query) {
16
const activeDrones: IActiveDrone[] = [];
17
for (const drone of inventory.Drones) {
18
if (drone.DeployTime) {
19
activeDrones.push({
20
DeployTime: toMongoDate(drone.DeployTime),
21
System: drone.System!,
22
ItemId: toOid(drone._id),
23
ItemType: drone.ItemType,
24
CurrentHP: drone.CurrentHP,
25
DamageTime: toMongoDate(drone.DamageTime!),
26
PendingDamage: drone.PendingDamage!,
27
Resources: [
28
{
29
ItemType: drone.ResourceType!,
30
BinTotal: drone.ResourceCount!,
31
StartTime: toMongoDate(drone.DeployTime)
32
}
33
]
34
});
35
}
36
}
37
res.json({
38
ActiveDrones: activeDrones
39
});
40
} else if ("droneId" in req.query && "systemIndex" in req.query) {
41
const drone = inventory.Drones.id(req.query.droneId as string)!;
42
const droneMeta = ExportDrones[drone.ItemType];
43
drone.DeployTime = config.instantResourceExtractorDrones ? new Date(0) : new Date();
44
if (drone.RepairStart) {
45
const repairMinutes = (Date.now() - drone.RepairStart.getTime()) / 60_000;
46
const hpPerMinute = droneMeta.repairRate / 60;
47
drone.CurrentHP = Math.min(drone.CurrentHP + Math.round(repairMinutes * hpPerMinute), droneMeta.durability);
48
drone.RepairStart = undefined;
49
}
50
drone.System = parseInt(req.query.systemIndex as string);
51
const system = ExportSystems[drone.System - 1];
52
drone.DamageTime = config.instantResourceExtractorDrones
53
? new Date()
54
: new Date(Date.now() + getRandomInt(3 * 3600 * 1000, 4 * 3600 * 1000));
55
drone.PendingDamage =
56
Math.random() < system.damageChance
57
? getRandomInt(system.droneDamage.minValue, system.droneDamage.maxValue)
58
: 0;
59
const resource = getRandomWeightedReward3(system.resources, droneMeta.probabilities)!;
60
//logger.debug(`drone rolled`, resource);
61
drone.ResourceType = "/Lotus/" + resource.StoreItem.substring(18);
62
const resourceMeta = ExportResources[drone.ResourceType];
63
if (resourceMeta.pickupQuantity) {
64
const pickupsToCollect = droneMeta.binCapacity * droneMeta.capacityMultipliers[resource.Rarity];
65
drone.ResourceCount = 0;
66
for (let i = 0; i != pickupsToCollect; ++i) {
67
drone.ResourceCount += getRandomInt(
68
resourceMeta.pickupQuantity.minValue,
69
resourceMeta.pickupQuantity.maxValue
70
);
71
}
72
} else {
73
drone.ResourceCount = 1;
74
}
75
await inventory.save();
76
res.json({});
77
} else if ("collectDroneId" in req.query) {
78
const drone = inventory.Drones.id(req.query.collectDroneId as string)!;
79
80
if (new Date() >= drone.DamageTime!) {
81
drone.CurrentHP -= drone.PendingDamage!;
82
drone.RepairStart = new Date();
83
}
84
85
const inventoryChanges: IInventoryChanges = {};
86
if (drone.CurrentHP <= 0) {
87
inventory.RegularCredits += 100;
88
inventoryChanges.RegularCredits = 100;
89
inventory.Drones.pull({ _id: req.query.collectDroneId as string });
90
inventoryChanges.RemovedIdItems = [
91
{
92
ItemId: { $oid: req.query.collectDroneId }
93
}
94
];
95
} else {
96
const completionTime = drone.DeployTime!.getTime() + ExportDrones[drone.ItemType].fillRate * 3600_000;
97
if (Date.now() >= completionTime) {
98
const miscItemChanges = [
99
{
100
ItemType: drone.ResourceType!,
101
ItemCount: drone.ResourceCount!
102
}
103
];
104
addMiscItems(inventory, miscItemChanges);
105
inventoryChanges.MiscItems = miscItemChanges;
106
}
107
108
drone.DeployTime = undefined;
109
drone.System = undefined;
110
drone.DamageTime = undefined;
111
drone.PendingDamage = undefined;
112
drone.ResourceType = undefined;
113
drone.ResourceCount = undefined;
114
115
inventoryChanges.Drones = [drone.toJSON<IDroneClient>()];
116
}
117
118
await inventory.save();
119
res.json({
120
InventoryChanges: inventoryChanges
121
});
122
} else {
123
throw new Error(`drones.php query not handled`);
124
}
5
125
};
6
126
7
export { dronesController };
127
interface IActiveDrone {
128
DeployTime: IMongoDate;
129
System: number;
130
ItemId: IOid;
131
ItemType: string;
132
CurrentHP: number;
133
DamageTime: IMongoDate;
134
PendingDamage: number;
135
Resources: {
136
ItemType: string;
137
BinTotal: number;
138
StartTime: IMongoDate;
139
}[];
140
}
@@ -86,6 +86,11 @@ export const sellController: RequestHandler = async (req, res) => {
86
86
inventory.Hoverboards.pull({ _id: sellItem.String });
87
87
});
88
88
}
89
if (payload.Items.Drones) {
90
payload.Items.Drones.forEach(sellItem => {
91
inventory.Drones.pull({ _id: sellItem.String });
92
});
93
}
89
94
if (payload.Items.Consumables) {
90
95
const consumablesChanges = [];
91
96
for (const sellItem of payload.Items.Consumables) {
@@ -152,6 +157,7 @@ interface ISellRequest {
152
157
SentinelWeapons?: ISellItem[];
153
158
OperatorAmps?: ISellItem[];
154
159
Hoverboards?: ISellItem[];
160
Drones?: ISellItem[];
155
161
};
156
162
SellPrice: number;
157
163
SellCurrency:
@@ -336,7 +336,14 @@ const droneSchema = new Schema<IDroneDatabase>(
336
336
{
337
337
ItemType: String,
338
338
CurrentHP: Number,
339
RepairStart: { type: Date, default: undefined }
339
RepairStart: { type: Date, default: undefined },
340
341
DeployTime: { type: Date, default: undefined },
342
System: Number,
343
DamageTime: { type: Date, default: undefined },
344
PendingDamage: Number,
345
ResourceType: String,
346
ResourceCount: Number
340
347
},
341
348
{ id: false }
342
349
);
@@ -347,6 +354,16 @@ droneSchema.set("toJSON", {
347
354
const db = obj as IDroneDatabase;
348
355
349
356
client.ItemId = toOid(db._id);
357
if (db.RepairStart) {
358
client.RepairStart = toMongoDate(db.RepairStart);
359
}
360
361
delete db.DeployTime;
362
delete db.System;
363
delete db.DamageTime;
364
delete db.PendingDamage;
365
delete db.ResourceType;
366
delete db.ResourceCount;
350
367
351
368
delete obj._id;
352
369
delete obj.__v;
@@ -145,6 +145,7 @@ apiRouter.post("/claimCompletedRecipe.php", claimCompletedRecipeController);
145
145
apiRouter.post("/clearDialogueHistory.php", clearDialogueHistoryController);
146
146
apiRouter.post("/completeRandomModChallenge.php", completeRandomModChallengeController);
147
147
apiRouter.post("/createGuild.php", createGuildController);
148
apiRouter.post("/drones.php", dronesController);
148
149
apiRouter.post("/endlessXp.php", endlessXpController);
149
150
apiRouter.post("/evolveWeapon.php", evolveWeaponController);
150
151
apiRouter.post("/findSessions.php", findSessionsController);
@@ -57,6 +57,7 @@ interface IConfig {
57
57
unlockExilusEverywhere?: boolean;
58
58
unlockArcanesEverywhere?: boolean;
59
59
noDailyStandingLimits?: boolean;
60
instantResourceExtractorDrones?: boolean;
60
61
noDojoResearchCosts?: boolean;
61
62
noDojoResearchTime?: boolean;
62
63
spoofMasteryRank?: number;
@@ -18,7 +18,7 @@ export const getRandomInt = (min: number, max: number): number => {
18
18
return Math.floor(Math.random() * (max - min + 1)) + min;
19
19
};
20
20
21
export const getRandomReward = (pool: IRngResult[]): IRngResult | undefined => {
21
export const getRandomReward = <T extends { probability: number }>(pool: T[]): T | undefined => {
22
22
if (pool.length == 0) return;
23
23
24
24
const totalChance = pool.reduce((accum, item) => accum + item.probability, 0);
@@ -71,3 +71,21 @@ export const getRandomWeightedReward2 = (
71
71
}
72
72
return getRandomReward(resultPool);
73
73
};
74
75
export const getRandomWeightedReward3 = <T extends { Rarity: TRarity }>(
76
pool: T[],
77
weights: Record<TRarity, number>
78
): (T & { probability: number }) | undefined => {
79
const resultPool: (T & { probability: number })[] = [];
80
const rarityCounts: Record<TRarity, number> = { COMMON: 0, UNCOMMON: 0, RARE: 0, LEGENDARY: 0 };
81
for (const entry of pool) {
82
++rarityCounts[entry.Rarity];
83
}
84
for (const entry of pool) {
85
resultPool.push({
86
...entry,
87
probability: weights[entry.Rarity] / rarityCounts[entry.Rarity]
88
});
89
}
90
return getRandomReward(resultPool);
91
};
@@ -520,6 +520,13 @@ export interface IDroneDatabase {
520
520
CurrentHP: number;
521
521
_id: Types.ObjectId;
522
522
RepairStart?: Date;
523
524
DeployTime?: Date;
525
System?: number;
526
DamageTime?: Date;
527
PendingDamage?: number;
528
ResourceType?: string;
529
ResourceCount?: number;
523
530
}
524
531
525
532
export interface ITypeXPItem {
@@ -517,6 +517,10 @@
517
517
<input class="form-check-input" type="checkbox" id="noDailyStandingLimits" />
518
518
<label class="form-check-label" for="noDailyStandingLimits" data-loc="cheats_noDailyStandingLimits"></label>
519
519
</div>
520
<div class="form-check">
521
<input class="form-check-input" type="checkbox" id="instantResourceExtractorDrones" />
522
<label class="form-check-label" for="instantResourceExtractorDrones" data-loc="cheats_instantResourceExtractorDrones"></label>
523
</div>
520
524
<div class="form-check">
521
525
<input class="form-check-input" type="checkbox" id="noDojoResearchCosts" />
522
526
<label class="form-check-label" for="noDojoResearchCosts" data-loc="cheats_noDojoResearchCosts"></label>
@@ -111,6 +111,7 @@ dict = {
111
111
cheats_unlockExilusEverywhere: `Exilus Adapters Everywhere`,
112
112
cheats_unlockArcanesEverywhere: `Arcane Adapters Everywhere`,
113
113
cheats_noDailyStandingLimits: `No Daily Standing Limits`,
114
cheats_instantResourceExtractorDrones: `Instant Resource Extractor Drones`,
114
115
cheats_noDojoResearchCosts: `No Dojo Research Costs`,
115
116
cheats_noDojoResearchTime: `No Dojo Research Time`,
116
117
cheats_spoofMasteryRank: `Spoofed Mastery Rank (-1 to disable)`,