screepsmod-market-simulator 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.js ADDED
@@ -0,0 +1,504 @@
1
+ const path = require('path');
2
+ const fs = require('fs');
3
+
4
+ // ============================================================
5
+ // Default Configuration
6
+ // ============================================================
7
+ const DEFAULTS = {
8
+ // Interval between order recreation cycles (5 minutes)
9
+ intervalMs: 5 * 60 * 1000,
10
+
11
+ // NPC user for phantom orders
12
+ npcUserId: '2',
13
+ npcUsername: 'NPC_Market',
14
+
15
+ // NPC credit backing: credits (display) and money (engine-internal integer, credits × 1000)
16
+ // Restocked to this value on every refresh cycle
17
+ npcCredits: 10000000,
18
+ npcMoney: 10000000 * 1000,
19
+
20
+ // Phantom room names — interior corners of 22×22 map for max coverage
21
+ phantomRooms: [
22
+ 'W9N9',
23
+ 'E9N9',
24
+ 'W9S9',
25
+ 'E9S9',
26
+ 'W5S5',
27
+ 'E5S5'
28
+ ],
29
+
30
+ // Price spread factors for buy and sell orders
31
+ buySpreadFactors: [0.85, 0.92, 1.0],
32
+ sellSpreadFactors: [1.0, 1.08, 1.15],
33
+
34
+ // Hard cap on order amounts (units per order per resource per spread level)
35
+ maxOrderAmount: 50000,
36
+
37
+ // Terminal store: amount per resource, restocked on every refresh
38
+ terminalStoreAmount: 200000,
39
+
40
+ // Terminal capacity: must hold storeAmount × resourceCount + energy buffer
41
+ terminalStoreCapacity: 30000000,
42
+
43
+ // How to store prices: multiply by this and store as integer
44
+ priceMultiplier: 1000,
45
+
46
+ // Batch size for order insertion
47
+ insertionBatchSize: 500,
48
+
49
+ debug: true
50
+ };
51
+
52
+ // ============================================================
53
+ // All 89 Screeps Resource Types with Base Prices
54
+ // ============================================================
55
+ const RESOURCES = [
56
+ { type: 'energy', basePrice: 0.01 },
57
+ // Base minerals
58
+ { type: 'U', basePrice: 0.2 },
59
+ { type: 'L', basePrice: 0.2 },
60
+ { type: 'K', basePrice: 0.2 },
61
+ { type: 'Z', basePrice: 0.2 },
62
+ { type: 'O', basePrice: 0.2 },
63
+ { type: 'H', basePrice: 0.2 },
64
+ { type: 'X', basePrice: 0.2 },
65
+ { type: 'G', basePrice: 0.5 },
66
+ // Base compounds
67
+ { type: 'ZK', basePrice: 0.3 },
68
+ { type: 'UL', basePrice: 0.3 },
69
+ { type: 'OH', basePrice: 0.5 },
70
+ // Processed base resources
71
+ { type: 'silicon', basePrice: 0.3 },
72
+ { type: 'metal', basePrice: 0.3 },
73
+ { type: 'mist', basePrice: 0.4 },
74
+ { type: 'oxidant', basePrice: 0.3 },
75
+ { type: 'reductant', basePrice: 0.3 },
76
+ { type: 'purifier', basePrice: 0.3 },
77
+ { type: 'biomass', basePrice: 0.3 },
78
+ { type: 'zymite', basePrice: 1.0 },
79
+ { type: 'catalytic_agent', basePrice: 1.0 },
80
+ { type: 'hydroxide', basePrice: 0.5 },
81
+ // Tier 1 boosts
82
+ { type: 'UH', basePrice: 0.5 },
83
+ { type: 'UO', basePrice: 0.5 },
84
+ { type: 'KH', basePrice: 0.5 },
85
+ { type: 'KO', basePrice: 0.5 },
86
+ { type: 'LH', basePrice: 0.5 },
87
+ { type: 'LO', basePrice: 0.5 },
88
+ { type: 'ZH', basePrice: 0.5 },
89
+ { type: 'ZO', basePrice: 0.5 },
90
+ // Tier 1 Ghodium boosts
91
+ { type: 'GH', basePrice: 1.0 },
92
+ { type: 'GO', basePrice: 1.0 },
93
+ // Tier 2 boosts
94
+ { type: 'UH2O', basePrice: 1.0 },
95
+ { type: 'UHO2', basePrice: 1.0 },
96
+ { type: 'KH2O', basePrice: 1.0 },
97
+ { type: 'KHO2', basePrice: 1.0 },
98
+ { type: 'LH2O', basePrice: 1.0 },
99
+ { type: 'LHO2', basePrice: 1.0 },
100
+ { type: 'ZH2O', basePrice: 1.0 },
101
+ { type: 'ZHO2', basePrice: 1.0 },
102
+ // Tier 2 Ghodium boosts
103
+ { type: 'GH2O', basePrice: 2.0 },
104
+ { type: 'GHO2', basePrice: 2.0 },
105
+ // Tier 3 catalyzed boosts
106
+ { type: 'XUH2O', basePrice: 2.0 },
107
+ { type: 'XUHO2', basePrice: 2.0 },
108
+ { type: 'XKH2O', basePrice: 2.0 },
109
+ { type: 'XKHO2', basePrice: 2.0 },
110
+ { type: 'XLH2O', basePrice: 2.0 },
111
+ { type: 'XLHO2', basePrice: 2.0 },
112
+ { type: 'XZH2O', basePrice: 2.0 },
113
+ { type: 'XZHO2', basePrice: 2.0 },
114
+ // Tier 3 catalyzed Ghodium boosts
115
+ { type: 'XGH2O', basePrice: 4.0 },
116
+ { type: 'XGHO2', basePrice: 4.0 },
117
+ // Commodities tier 1
118
+ { type: 'wire', basePrice: 0.5 },
119
+ { type: 'alloy', basePrice: 0.5 },
120
+ { type: 'cell', basePrice: 0.5 },
121
+ { type: 'concentrate', basePrice: 0.5 },
122
+ { type: 'board', basePrice: 0.7 },
123
+ // Commodities tier 2
124
+ { type: 'switch', basePrice: 0.7 },
125
+ { type: 'phlegm', basePrice: 0.7 },
126
+ { type: 'tube', basePrice: 0.7 },
127
+ { type: 'extract', basePrice: 0.7 },
128
+ // Commodities tier 3
129
+ { type: 'transistor', basePrice: 1.0 },
130
+ { type: 'tissue', basePrice: 1.0 },
131
+ { type: 'fixtures', basePrice: 1.0 },
132
+ { type: 'spirit', basePrice: 1.0 },
133
+ // Commodities tier 4
134
+ { type: 'microchip', basePrice: 1.5 },
135
+ { type: 'muscle', basePrice: 1.5 },
136
+ { type: 'frame', basePrice: 1.5 },
137
+ { type: 'emanation', basePrice: 1.5 },
138
+ // Commodities tier 5
139
+ { type: 'circuit', basePrice: 2.0 },
140
+ { type: 'organoid', basePrice: 2.0 },
141
+ { type: 'hydraulics', basePrice: 2.0 },
142
+ { type: 'condensate', basePrice: 2.0 },
143
+ // High-tier commodities
144
+ { type: 'device', basePrice: 2.5 },
145
+ { type: 'machine', basePrice: 3.0 },
146
+ { type: 'organism', basePrice: 3.0 },
147
+ // Base products
148
+ { type: 'battery', basePrice: 0.2 },
149
+ { type: 'composite', basePrice: 0.2 },
150
+ { type: 'crystal', basePrice: 0.2 },
151
+ { type: 'liquid', basePrice: 0.2 },
152
+ // Bars
153
+ { type: 'utrium_bar', basePrice: 0.5 },
154
+ { type: 'lemergium_bar', basePrice: 0.5 },
155
+ { type: 'zynthium_bar', basePrice: 0.5 },
156
+ { type: 'keanium_bar', basePrice: 0.5 },
157
+ { type: 'ghodium_melt', basePrice: 1.0 },
158
+ // Power resources
159
+ { type: 'power', basePrice: 0.6 },
160
+ { type: 'ops', basePrice: 0.5 },
161
+ { type: 'essence', basePrice: 1.0 },
162
+ { type: 'mitochondrion', basePrice: 5.0 }
163
+ ];
164
+
165
+ const RESOURCE_COUNT = RESOURCES.length;
166
+
167
+ // ============================================================
168
+ // Helper: Store price as integer (price * multiplier)
169
+ // ============================================================
170
+ function storePrice(price, cfg) {
171
+ return Math.round(price * cfg.priceMultiplier);
172
+ }
173
+
174
+ // ============================================================
175
+ // Build all orders for a single phantom room
176
+ // ============================================================
177
+ function buildOrdersForRoom(roomName, cfg) {
178
+ const orders = [];
179
+ const now = Date.now();
180
+ const amount = cfg.maxOrderAmount;
181
+
182
+ RESOURCES.forEach((res, resIdx) => {
183
+ const basePrice = res.basePrice;
184
+
185
+ cfg.buySpreadFactors.forEach((factor, buyIdx) => {
186
+ const rawPrice = basePrice * factor;
187
+ const price = storePrice(rawPrice, cfg);
188
+ const orderId = `buy_${res.type}_${roomName}_${resIdx}_${buyIdx}`;
189
+ orders.push({
190
+ _id: orderId,
191
+ user: cfg.npcUserId,
192
+ roomName: roomName,
193
+ resourceType: res.type,
194
+ type: 'buy',
195
+ price: price,
196
+ amount: amount,
197
+ remainingAmount: amount,
198
+ totalAmount: amount,
199
+ active: true,
200
+ created: now,
201
+ createdTimestamp: now
202
+ });
203
+ });
204
+
205
+ cfg.sellSpreadFactors.forEach((factor, sellIdx) => {
206
+ const rawPrice = basePrice * factor;
207
+ const price = storePrice(rawPrice, cfg);
208
+ const orderId = `sell_${res.type}_${roomName}_${resIdx}_${sellIdx}`;
209
+ orders.push({
210
+ _id: orderId,
211
+ user: cfg.npcUserId,
212
+ roomName: roomName,
213
+ resourceType: res.type,
214
+ type: 'sell',
215
+ price: price,
216
+ amount: amount,
217
+ remainingAmount: amount,
218
+ totalAmount: amount,
219
+ active: true,
220
+ created: now,
221
+ createdTimestamp: now
222
+ });
223
+ });
224
+ });
225
+
226
+ return orders;
227
+ }
228
+
229
+ // ============================================================
230
+ // Build all orders across all phantom rooms
231
+ // ============================================================
232
+ function buildAllOrders(cfg) {
233
+ let allOrders = [];
234
+ cfg.phantomRooms.forEach(roomName => {
235
+ const roomOrders = buildOrdersForRoom(roomName, cfg);
236
+ allOrders = allOrders.concat(roomOrders);
237
+ });
238
+ return allOrders;
239
+ }
240
+
241
+ // ============================================================
242
+ // Build terminal store object with all resources
243
+ // ============================================================
244
+ function buildStoreObject(cfg) {
245
+ const store = {};
246
+ RESOURCES.forEach(res => {
247
+ store[res.type] = cfg.terminalStoreAmount;
248
+ });
249
+ return store;
250
+ }
251
+
252
+ // ============================================================
253
+ // Restock NPC user: credits + money to baseline
254
+ // ============================================================
255
+ async function restockNpcUser(db, cfg) {
256
+ try {
257
+ await db['users'].update(
258
+ { _id: cfg.npcUserId },
259
+ {
260
+ $setOnInsert: {
261
+ _id: cfg.npcUserId,
262
+ username: cfg.npcUsername,
263
+ cpu: 100,
264
+ active: true
265
+ },
266
+ $set: {
267
+ money: cfg.npcMoney,
268
+ credits: cfg.npcCredits,
269
+ lastLoginTime: Date.now()
270
+ }
271
+ },
272
+ { upsert: true }
273
+ );
274
+ if (cfg.debug) console.log(`[MarketSim] NPC user restocked: ${cfg.npcCredits.toLocaleString()} credits`);
275
+ } catch (e) {
276
+ if (cfg.debug) console.log(`[MarketSim] Note: upsert NPC user: ${e.message}`);
277
+ }
278
+ }
279
+
280
+ // ============================================================
281
+ // Ensure phantom room documents and terminal objects exist
282
+ // ============================================================
283
+ async function ensurePhantomRooms(db, cfg) {
284
+ const npcUserId = cfg.npcUserId;
285
+
286
+ for (const roomName of cfg.phantomRooms) {
287
+ try {
288
+ await db['rooms'].update(
289
+ { _id: roomName },
290
+ {
291
+ $setOnInsert: {
292
+ _id: roomName,
293
+ status: 'normal',
294
+ active: true
295
+ }
296
+ },
297
+ { upsert: true }
298
+ );
299
+ } catch (e) {
300
+ if (cfg.debug) console.log(`[MarketSim] Note: upsert room ${roomName}: ${e.message}`);
301
+ }
302
+
303
+ const store = buildStoreObject(cfg);
304
+ const terminalId = `terminal_${roomName}`;
305
+
306
+ try {
307
+ await db['rooms.objects'].update(
308
+ { _id: terminalId },
309
+ {
310
+ $set: {
311
+ _id: terminalId,
312
+ type: 'terminal',
313
+ room: roomName,
314
+ user: npcUserId,
315
+ x: 25,
316
+ y: 25,
317
+ store: store,
318
+ storeCapacity: cfg.terminalStoreCapacity,
319
+ energy: 0,
320
+ cooldown: 0,
321
+ nextSendTick: 0,
322
+ send: {}
323
+ }
324
+ },
325
+ { upsert: true }
326
+ );
327
+ } catch (e) {
328
+ if (cfg.debug) console.log(`[MarketSim] Note: upsert terminal for ${roomName}: ${e.message}`);
329
+ }
330
+ }
331
+
332
+ if (cfg.debug) console.log(`[MarketSim] Phantom rooms initialized: ${cfg.phantomRooms.join(', ')}`);
333
+ }
334
+
335
+ // ============================================================
336
+ // Restock terminal stores to baseline for all phantom rooms
337
+ // ============================================================
338
+ async function restockTerminals(db, cfg) {
339
+ const store = buildStoreObject(cfg);
340
+
341
+ for (const roomName of cfg.phantomRooms) {
342
+ const terminalId = `terminal_${roomName}`;
343
+ try {
344
+ await db['rooms.objects'].update(
345
+ { _id: terminalId },
346
+ { $set: { store: store, storeCapacity: cfg.terminalStoreCapacity } }
347
+ );
348
+ } catch (e) {
349
+ if (cfg.debug) console.log(`[MarketSim] Note: restock terminal ${roomName}: ${e.message}`);
350
+ }
351
+ }
352
+
353
+ if (cfg.debug) console.log(`[MarketSim] Terminal stores restocked for ${cfg.phantomRooms.length} rooms`);
354
+ }
355
+
356
+ // ============================================================
357
+ // Remove NPC orders, then recreate them (targeted refresh)
358
+ // ============================================================
359
+ async function recreateOrders(db, cfg) {
360
+ try {
361
+ // 1. Restock NPC credits and money
362
+ await restockNpcUser(db, cfg);
363
+
364
+ // 2. Restock terminal stores
365
+ await restockTerminals(db, cfg);
366
+
367
+ // 3. Remove only NPC-created orders (not player orders)
368
+ try {
369
+ await db['market.orders'].removeWhere({ user: cfg.npcUserId });
370
+ } catch (e) {
371
+ // LokiJS uses removeWhere, MongoDB uses deleteMany
372
+ if (cfg.debug) console.log(`[MarketSim] Note: removeWhere not supported, trying deleteMany`);
373
+ try {
374
+ await db['market.orders'].deleteMany({ user: cfg.npcUserId });
375
+ } catch (e2) {
376
+ if (cfg.debug) console.log(`[MarketSim] Note: deleteMany also not supported, trying remove`);
377
+ try {
378
+ await db['market.orders'].remove({ user: cfg.npcUserId });
379
+ } catch (e3) {
380
+ if (cfg.debug) console.log(`[MarketSim] Warning: Could not remove old NPC orders: ${e3.message}`);
381
+ }
382
+ }
383
+ }
384
+
385
+ // 4. Build new orders
386
+ const allOrders = buildAllOrders(cfg);
387
+
388
+ // 5. Insert in batches (staggered to avoid blocking the storage thread)
389
+ const batchSize = cfg.insertionBatchSize || 500;
390
+ let inserted = 0;
391
+
392
+ for (let i = 0; i < allOrders.length; i += batchSize) {
393
+ const batch = allOrders.slice(i, i + batchSize);
394
+
395
+ try {
396
+ await db['market.orders'].insertMany(batch);
397
+ inserted += batch.length;
398
+ } catch (e) {
399
+ // Fall back to individual upserts
400
+ for (const order of batch) {
401
+ try {
402
+ await db['market.orders'].update(
403
+ { _id: order._id },
404
+ order,
405
+ { upsert: true }
406
+ );
407
+ inserted++;
408
+ } catch (e2) {
409
+ if (cfg.debug) console.log(`[MarketSim] Failed to insert order ${order._id}: ${e2.message}`);
410
+ }
411
+ }
412
+ }
413
+ }
414
+
415
+ const roomCount = cfg.phantomRooms.length;
416
+ const resCount = RESOURCE_COUNT;
417
+ const ordersPerResource = cfg.buySpreadFactors.length + cfg.sellSpreadFactors.length;
418
+ if (cfg.debug) {
419
+ console.log(`[MarketSim] Refreshed ${inserted} orders across ${roomCount} rooms ` +
420
+ `(${resCount} resources × ${ordersPerResource} orders each per room, ` +
421
+ `${cfg.maxOrderAmount.toLocaleString()} units each)`);
422
+ }
423
+ } catch (err) {
424
+ console.log(`[MarketSim] Error recreating orders: ${err.message}`);
425
+ }
426
+ }
427
+
428
+ // ============================================================
429
+ // Main mod entry point
430
+ // ============================================================
431
+ module.exports = function (config, pluginContext) {
432
+ if (!config || !config.backend) return;
433
+
434
+ // Load optional user config from marketSimulatorConfig.js
435
+ let userConfig = {};
436
+ const configPath = path.join(__dirname, 'marketSimulatorConfig.js');
437
+ try {
438
+ if (fs.existsSync(configPath)) {
439
+ userConfig = require(configPath);
440
+ if (typeof userConfig === 'function') {
441
+ userConfig = userConfig(config, pluginContext);
442
+ }
443
+ console.log('[MarketSim] Loaded custom config from marketSimulatorConfig.js');
444
+ }
445
+ } catch (e) {
446
+ if (DEFAULTS.debug) {
447
+ console.log(`[MarketSim] No marketSimulatorConfig.js found, using defaults`);
448
+ }
449
+ }
450
+
451
+ // Merge configs (deep merge for arrays)
452
+ const cfg = Object.assign({}, DEFAULTS, userConfig);
453
+ if (userConfig.phantomRooms) cfg.phantomRooms = userConfig.phantomRooms;
454
+ if (userConfig.buySpreadFactors) cfg.buySpreadFactors = userConfig.buySpreadFactors;
455
+ if (userConfig.sellSpreadFactors) cfg.sellSpreadFactors = userConfig.sellSpreadFactors;
456
+
457
+ const db = config.common && config.common.storage && config.common.storage.db;
458
+
459
+ if (!db) {
460
+ console.log('[MarketSim] ERROR: No database available via config.common.storage.db');
461
+ return;
462
+ }
463
+
464
+ console.log('[MarketSim] Mod loaded — initializing phantom market system...');
465
+
466
+ const roomsCount = cfg.phantomRooms.length;
467
+ const resCount = RESOURCE_COUNT;
468
+ const ordersPerRes = cfg.buySpreadFactors.length + cfg.sellSpreadFactors.length;
469
+ const totalOrders = roomsCount * resCount * ordersPerRes;
470
+ console.log(`[MarketSim] Expected: ${totalOrders} orders ` +
471
+ `(${roomsCount} rooms × ${resCount} resources × ${ordersPerRes} price levels)`);
472
+
473
+ // Initialize phantom rooms then start refresh cycle
474
+ restockNpcUser(db, cfg)
475
+ .then(() => ensurePhantomRooms(db, cfg))
476
+ .then(() => {
477
+ console.log('[MarketSim] Phantom rooms ready');
478
+ recreateOrders(db, cfg);
479
+
480
+ // Prefer cronjobs if available, fall back to setInterval
481
+ if (config.cronjobs) {
482
+ config.cronjobs.marketSimRefresh = [
483
+ cfg.intervalMs / 1000,
484
+ () => recreateOrders(db, cfg)
485
+ ];
486
+ console.log(`[MarketSim] Registered cronjob: marketSimRefresh every ${cfg.intervalMs / 1000}s`);
487
+ } else {
488
+ const intervalId = setInterval(() => {
489
+ recreateOrders(db, cfg);
490
+ }, cfg.intervalMs);
491
+ console.log(`[MarketSim] Using setInterval every ${cfg.intervalMs / 1000}s`);
492
+
493
+ if (pluginContext && pluginContext.onUnload) {
494
+ pluginContext.onUnload(() => {
495
+ clearInterval(intervalId);
496
+ console.log('[MarketSim] Unloaded');
497
+ });
498
+ }
499
+ }
500
+ })
501
+ .catch(err => {
502
+ console.log(`[MarketSim] Initialization failed: ${err.message}`);
503
+ });
504
+ };
@@ -0,0 +1,89 @@
1
+ /**
2
+ * Configuration overrides for screepsmod-market-simulator.
3
+ *
4
+ * This file is optional. If absent or exporting an empty object, the defaults
5
+ * in index.js are used. Uncomment and modify any settings below to customize.
6
+ *
7
+ * --- Market Simulation Overview ---
8
+ *
9
+ * The mod seeds the market order book with buy and sell orders for ALL 89
10
+ * Screeps resource types across configurable phantom rooms. Each order has a
11
+ * hard-capped amount (maxOrderAmount) to prevent runaway CPU/memory usage.
12
+ * On every refresh cycle (default 5 min), NPC credits, terminal stores, and
13
+ * all orders are restocked to their configured baselines.
14
+ *
15
+ * The Screeps engine handles ALL transactional aspects of dealing (resource
16
+ * transfer, credit exchange, energy costs, cooldowns). This mod only seeds
17
+ * the database.
18
+ *
19
+ * --- Key Settings ---
20
+ *
21
+ * maxOrderAmount Hard cap on every order's amount, remainingAmount,
22
+ * and totalAmount. This prevents the engine from
23
+ * computing absurd amounts from NPC money.
24
+ * Higher = more market depth, but larger DB writes.
25
+ *
26
+ * npcCredits / npcMoney NPC backing for buy orders. Restocked to this value
27
+ * every refresh. npcMoney is the internal integer field
28
+ * (credits × 1000). Keep these high enough to cover all
29
+ * active buy orders at once.
30
+ *
31
+ * terminalStoreAmount Amount of EACH resource in NPC terminal stores,
32
+ * restocked every refresh. Backs sell orders.
33
+ * Must fit within terminalStoreCapacity when summed.
34
+ *
35
+ * terminalStoreCapacity Total NPC terminal capacity. Must exceed:
36
+ * terminalStoreAmount × 89 (resource count) + energy.
37
+ *
38
+ * buySpreadFactors Price multipliers for buy orders relative to base
39
+ * price. E.g., 0.85 = 85% of base = below market.
40
+ *
41
+ * sellSpreadFactors Price multipliers for sell orders relative to base
42
+ * price. E.g., 1.15 = 115% of base = above market.
43
+ */
44
+
45
+ module.exports = {
46
+
47
+ // --- Phantom Rooms ---
48
+ // Room names that don't exist in the world. Each gets a terminal
49
+ // and posts buy/sell orders for all resources.
50
+ // phantomRooms: ['W777N777', 'E777N777', 'W777S777', 'E777S777'],
51
+
52
+ // --- NPC Identity ---
53
+ // npcUserId: '2',
54
+ // npcUsername: 'NPC_Market',
55
+
56
+ // --- NPC Credit Backing ---
57
+ // Restocked every refresh. Money is the internal integer field
58
+ // used by the engine (credits × 1000).
59
+ // npcCredits: 5000000,
60
+ // npcMoney: 5000000000,
61
+
62
+ // --- Order Size ---
63
+ // Hard cap on EACH order's amount/remainingAmount/totalAmount.
64
+ // Limits CPU/memory impact of engine order processing.
65
+ // maxOrderAmount: 25000,
66
+
67
+ // --- Price Spreads ---
68
+ // Multiplied against basePrice to compute order price.
69
+ // Must have at least 1 entry each.
70
+ // buySpreadFactors: [0.80, 0.90, 0.95, 1.0],
71
+ // sellSpreadFactors: [1.0, 1.05, 1.10, 1.20],
72
+
73
+ // --- Terminal Stores ---
74
+ // terminalStoreAmount must be >= maxOrderAmount so sell orders
75
+ // are properly backed. terminalStoreCapacity must hold all
76
+ // resources × storeAmount plus energy buffer.
77
+ // terminalStoreAmount: 100000,
78
+ // terminalStoreCapacity: 15000000,
79
+
80
+ // --- Refresh Interval ---
81
+ // Milliseconds between restock + order refresh cycles.
82
+ // intervalMs: 300000, // 5 minutes
83
+
84
+ // --- Prices ---
85
+ // priceMultiplier: 1000, // Store prices as integers
86
+
87
+ // --- Debugging ---
88
+ // debug: false // Set false to suppress log output
89
+ };
package/package.json ADDED
@@ -0,0 +1,9 @@
1
+ {
2
+ "name": "screepsmod-market-simulator",
3
+ "version": "1.0.0",
4
+ "description": "Populates the Screeps market order book with buy/sell orders for phantom rooms to test bot trading systems",
5
+ "main": "index.js",
6
+ "author": "Custom",
7
+ "license": "MIT",
8
+ "screeps_mod": true
9
+ }