返回提交历史
Modified
src/services/serversideVendorsService.ts
+51
-32
Deleted
static/fixed_responses/getVendorInfo/HubsIronwakeDondaVendorManifest.json
+0
-125
XFEstudio/SpaceNinjaServer
chore: auto-generate palladino's vendor manifest (#2160)
A bit ugly, but having the self test forces correctness. Reviewed-on: https://onlyg.it/OpenWF/SpaceNinjaServer/pulls/2160 Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com> Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
30531124
代码差异
2 个文件
+51
-157
@@ -6,7 +6,7 @@ import { mixSeeds, SRng } from "@/src/services/rngService";
6
6
import { IMongoDate } from "@/src/types/commonTypes";
7
7
import { IItemManifest, IVendorInfo, IVendorManifest } from "@/src/types/vendorTypes";
8
8
import { logger } from "@/src/utils/logger";
9
import { ExportVendors, IRange, IVendor } from "warframe-public-export-plus";
9
import { ExportVendors, IRange, IVendor, IVendorOffer } from "warframe-public-export-plus";
10
10
11
11
import ArchimedeanVendorManifest from "@/static/fixed_responses/getVendorInfo/ArchimedeanVendorManifest.json";
12
12
import DeimosEntratiFragmentVendorProductsManifest from "@/static/fixed_responses/getVendorInfo/DeimosEntratiFragmentVendorProductsManifest.json";
@@ -21,7 +21,6 @@ import DeimosProspectorVendorManifest from "@/static/fixed_responses/getVendorIn
21
21
import DuviriAcrithisVendorManifest from "@/static/fixed_responses/getVendorInfo/DuviriAcrithisVendorManifest.json";
22
22
import EntratiLabsEntratiLabsCommisionsManifest from "@/static/fixed_responses/getVendorInfo/EntratiLabsEntratiLabsCommisionsManifest.json";
23
23
import EntratiLabsEntratiLabVendorManifest from "@/static/fixed_responses/getVendorInfo/EntratiLabsEntratiLabVendorManifest.json";
24
import HubsIronwakeDondaVendorManifest from "@/static/fixed_responses/getVendorInfo/HubsIronwakeDondaVendorManifest.json";
25
24
import HubsRailjackCrewMemberVendorManifest from "@/static/fixed_responses/getVendorInfo/HubsRailjackCrewMemberVendorManifest.json";
26
25
import MaskSalesmanManifest from "@/static/fixed_responses/getVendorInfo/MaskSalesmanManifest.json";
27
26
import Nova1999ConquestShopManifest from "@/static/fixed_responses/getVendorInfo/Nova1999ConquestShopManifest.json";
@@ -47,7 +46,6 @@ const rawVendorManifests: IVendorManifest[] = [
47
46
DuviriAcrithisVendorManifest,
48
47
EntratiLabsEntratiLabsCommisionsManifest,
49
48
EntratiLabsEntratiLabVendorManifest,
50
HubsIronwakeDondaVendorManifest, // uses preprocessing
51
49
HubsRailjackCrewMemberVendorManifest,
52
50
MaskSalesmanManifest,
53
51
Nova1999ConquestShopManifest,
@@ -83,10 +81,6 @@ const generatableVendors: IGeneratableVendorInfo[] = [
83
81
cycleOffset: 1744934400_000,
84
82
cycleDuration: 4 * unixTimesInMs.day
85
83
}
86
// {
87
// _id: { $oid: "5dbb4c41e966f7886c3ce939" },
88
// TypeName: "/Lotus/Types/Game/VendorManifests/Hubs/IronwakeDondaVendorManifest"
89
// }
90
84
];
91
85
92
86
const getVendorOid = (typeName: string): string => {
@@ -261,13 +255,8 @@ const generateVendorManifest = (vendorInfo: IGeneratableVendorInfo): IVendorMani
261
255
const cycleIndex = Math.trunc((Date.now() - cycleOffset) / cycleDuration);
262
256
const rng = new SRng(mixSeeds(vendorSeed, cycleIndex));
263
257
const manifest = ExportVendors[vendorInfo.TypeName];
264
const offersToAdd = [];
265
if (
266
manifest.numItems &&
267
(manifest.numItems.minValue != manifest.numItems.maxValue ||
268
manifest.items.length != manifest.numItems.minValue) &&
269
!manifest.isOneBinPerCycle
270
) {
258
const offersToAdd: IVendorOffer[] = [];
259
if (!manifest.isOneBinPerCycle) {
271
260
const remainingItemCapacity: Record<string, number> = {};
272
261
for (const item of manifest.items) {
273
262
remainingItemCapacity[item.storeItem] = 1 + item.duplicates;
@@ -275,31 +264,48 @@ const generateVendorManifest = (vendorInfo: IGeneratableVendorInfo): IVendorMani
275
264
for (const offer of info.ItemManifest) {
276
265
remainingItemCapacity[offer.StoreItem] -= 1;
277
266
}
278
const numItemsTarget = rng.randomInt(manifest.numItems.minValue, manifest.numItems.maxValue);
279
while (info.ItemManifest.length + offersToAdd.length < numItemsTarget) {
280
// TODO: Consider per-bin item limits
281
// TODO: Consider item probability weightings
282
const item = rng.randomElement(manifest.items)!;
283
if (remainingItemCapacity[item.storeItem] != 0) {
284
remainingItemCapacity[item.storeItem] -= 1;
285
offersToAdd.push(item);
267
if (manifest.numItems && manifest.items.length != manifest.numItems.minValue) {
268
const numItemsTarget = rng.randomInt(manifest.numItems.minValue, manifest.numItems.maxValue);
269
while (info.ItemManifest.length + offersToAdd.length < numItemsTarget) {
270
// TODO: Consider per-bin item limits
271
// TODO: Consider item probability weightings
272
const item = rng.randomElement(manifest.items)!;
273
if (remainingItemCapacity[item.storeItem] != 0) {
274
remainingItemCapacity[item.storeItem] -= 1;
275
offersToAdd.push(item);
276
}
277
}
278
} else {
279
for (const item of manifest.items) {
280
if (!item.alwaysOffered && remainingItemCapacity[item.storeItem] != 0) {
281
remainingItemCapacity[item.storeItem] -= 1;
282
offersToAdd.push(item);
283
}
284
}
285
for (const e of Object.entries(remainingItemCapacity)) {
286
const item = manifest.items.find(x => x.storeItem == e[0])!;
287
if (!item.alwaysOffered) {
288
while (e[1] != 0) {
289
e[1] -= 1;
290
offersToAdd.push(item);
291
}
292
}
286
293
}
294
for (const item of manifest.items) {
295
if (item.alwaysOffered && remainingItemCapacity[item.storeItem] != 0) {
296
remainingItemCapacity[item.storeItem] -= 1;
297
offersToAdd.push(item);
298
}
299
}
300
offersToAdd.reverse();
287
301
}
288
302
} else {
289
let binThisCycle;
290
if (manifest.isOneBinPerCycle) {
291
binThisCycle = cycleIndex % 2; // Note: May want to auto-compute the bin size, but this is only used for coda weapons right now.
292
}
303
const binThisCycle = cycleIndex % 2; // Note: May want to auto-compute the bin size, but this is only used for coda weapons right now.
293
304
for (const rawItem of manifest.items) {
294
if (!manifest.isOneBinPerCycle || rawItem.bin == binThisCycle) {
305
if (rawItem.bin == binThisCycle) {
295
306
offersToAdd.push(rawItem);
296
307
}
297
308
}
298
299
// For most vendors, the offers seem to roughly be in reverse order from the manifest. Coda weapons are an odd exception.
300
if (!manifest.isOneBinPerCycle) {
301
offersToAdd.reverse();
302
}
303
309
}
304
310
const cycleStart = cycleOffset + cycleIndex * cycleDuration;
305
311
for (const rawItem of offersToAdd) {
@@ -388,4 +394,17 @@ if (isDev) {
388
394
) {
389
395
logger.warn(`self test failed for /Lotus/Types/Game/VendorManifests/Hubs/GuildAdvertisementVendorManifest`);
390
396
}
397
398
const pall = getVendorManifestByTypeName("/Lotus/Types/Game/VendorManifests/Hubs/IronwakeDondaVendorManifest")!
399
.VendorInfo.ItemManifest;
400
if (
401
pall.length != 5 ||
402
pall[0].StoreItem != "/Lotus/StoreItems/Types/Items/ShipDecos/HarrowQuestKeyOrnament" ||
403
pall[1].StoreItem != "/Lotus/StoreItems/Types/BoosterPacks/RivenModPack" ||
404
pall[2].StoreItem != "/Lotus/StoreItems/Types/StoreItems/CreditBundles/150000Credits" ||
405
pall[3].StoreItem != "/Lotus/StoreItems/Types/Items/MiscItems/Kuva" ||
406
pall[4].StoreItem != "/Lotus/StoreItems/Types/BoosterPacks/RivenModPack"
407
) {
408
logger.warn(`self test failed for /Lotus/Types/Game/VendorManifests/Hubs/IronwakeDondaVendorManifest`);
409
}
391
410
}
@@ -1,125 +0,0 @@
1
{
2
"VendorInfo": {
3
"_id": {
4
"$oid": "5dbb4c41e966f7886c3ce939"
5
},
6
"TypeName": "/Lotus/Types/Game/VendorManifests/Hubs/IronwakeDondaVendorManifest",
7
"ItemManifest": [
8
{
9
"StoreItem": "/Lotus/StoreItems/Types/Items/ShipDecos/HarrowQuestKeyOrnament",
10
"ItemPrices": [
11
{
12
"ItemCount": 25,
13
"ItemType": "/Lotus/Types/Items/MiscItems/PrimeBucks",
14
"ProductCategory": "MiscItems"
15
}
16
],
17
"Bin": "BIN_0",
18
"QuantityMultiplier": 1,
19
"Expiry": {
20
"$date": {
21
"$numberLong": "604800000"
22
}
23
},
24
"AllowMultipurchase": true,
25
"Id": {
26
"$oid": "66fd60b20ba592c4c95e945f"
27
}
28
},
29
{
30
"StoreItem": "/Lotus/StoreItems/Types/BoosterPacks/RivenModPack",
31
"ItemPrices": [
32
{
33
"ItemCount": 10,
34
"ItemType": "/Lotus/Types/Items/MiscItems/RivenFragment",
35
"ProductCategory": "MiscItems"
36
}
37
],
38
"Bin": "BIN_0",
39
"QuantityMultiplier": 1,
40
"Expiry": {
41
"$date": {
42
"$numberLong": "604800000"
43
}
44
},
45
"PurchaseQuantityLimit": 1,
46
"AllowMultipurchase": false,
47
"Id": {
48
"$oid": "66fd60b20ba592c4c95e9468"
49
}
50
},
51
{
52
"StoreItem": "/Lotus/StoreItems/Types/StoreItems/CreditBundles/150000Credits",
53
"ItemPrices": [
54
{
55
"ItemCount": 5,
56
"ItemType": "/Lotus/Types/Items/MiscItems/RivenFragment",
57
"ProductCategory": "MiscItems"
58
}
59
],
60
"Bin": "BIN_0",
61
"QuantityMultiplier": 1,
62
"Expiry": {
63
"$date": {
64
"$numberLong": "604800000"
65
}
66
},
67
"PurchaseQuantityLimit": 1,
68
"AllowMultipurchase": false,
69
"Id": {
70
"$oid": "66fd60b20ba592c4c95e9469"
71
}
72
},
73
{
74
"StoreItem": "/Lotus/StoreItems/Types/Items/MiscItems/Kuva",
75
"ItemPrices": [
76
{
77
"ItemCount": 10,
78
"ItemType": "/Lotus/Types/Items/MiscItems/RivenFragment",
79
"ProductCategory": "MiscItems"
80
}
81
],
82
"Bin": "BIN_0",
83
"QuantityMultiplier": 35000,
84
"Expiry": {
85
"$date": {
86
"$numberLong": "604800000"
87
}
88
},
89
"PurchaseQuantityLimit": 1,
90
"AllowMultipurchase": false,
91
"Id": {
92
"$oid": "66fd60b20ba592c4c95e946a"
93
}
94
},
95
{
96
"StoreItem": "/Lotus/StoreItems/Types/BoosterPacks/RivenModPack",
97
"ItemPrices": [
98
{
99
"ItemCount": 10,
100
"ItemType": "/Lotus/Types/Items/MiscItems/RivenFragment",
101
"ProductCategory": "MiscItems"
102
}
103
],
104
"Bin": "BIN_0",
105
"QuantityMultiplier": 1,
106
"Expiry": {
107
"$date": {
108
"$numberLong": "604800000"
109
}
110
},
111
"PurchaseQuantityLimit": 1,
112
"AllowMultipurchase": false,
113
"Id": {
114
"$oid": "66fd60b20ba592c4c95e946b"
115
}
116
}
117
],
118
"PropertyTextHash": "62B64A8065B7C0FA345895D4BC234621",
119
"Expiry": {
120
"$date": {
121
"$numberLong": "604800000"
122
}
123
}
124
}
125
}