burnrate 0.1.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.
Files changed (39) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +507 -0
  3. package/dist/cli/format.d.ts +13 -0
  4. package/dist/cli/format.js +319 -0
  5. package/dist/cli/index.d.ts +7 -0
  6. package/dist/cli/index.js +1121 -0
  7. package/dist/cli/setup.d.ts +8 -0
  8. package/dist/cli/setup.js +143 -0
  9. package/dist/core/async-engine.d.ts +411 -0
  10. package/dist/core/async-engine.js +2274 -0
  11. package/dist/core/async-worldgen.d.ts +19 -0
  12. package/dist/core/async-worldgen.js +221 -0
  13. package/dist/core/engine.d.ts +154 -0
  14. package/dist/core/engine.js +1104 -0
  15. package/dist/core/pathfinding.d.ts +38 -0
  16. package/dist/core/pathfinding.js +146 -0
  17. package/dist/core/types.d.ts +489 -0
  18. package/dist/core/types.js +359 -0
  19. package/dist/core/worldgen.d.ts +22 -0
  20. package/dist/core/worldgen.js +292 -0
  21. package/dist/db/database.d.ts +83 -0
  22. package/dist/db/database.js +829 -0
  23. package/dist/db/turso-database.d.ts +177 -0
  24. package/dist/db/turso-database.js +1586 -0
  25. package/dist/mcp/server.d.ts +7 -0
  26. package/dist/mcp/server.js +1877 -0
  27. package/dist/server/api.d.ts +8 -0
  28. package/dist/server/api.js +1234 -0
  29. package/dist/server/async-tick-server.d.ts +5 -0
  30. package/dist/server/async-tick-server.js +63 -0
  31. package/dist/server/errors.d.ts +78 -0
  32. package/dist/server/errors.js +156 -0
  33. package/dist/server/rate-limit.d.ts +22 -0
  34. package/dist/server/rate-limit.js +134 -0
  35. package/dist/server/tick-server.d.ts +9 -0
  36. package/dist/server/tick-server.js +114 -0
  37. package/dist/server/validation.d.ts +194 -0
  38. package/dist/server/validation.js +114 -0
  39. package/package.json +65 -0
@@ -0,0 +1,19 @@
1
+ /**
2
+ * BURNRATE Async World Generator
3
+ * Creates the initial map for a new season (returns data for async database)
4
+ */
5
+ import { Zone, Route } from './types.js';
6
+ interface WorldGenConfig {
7
+ hubs: number;
8
+ factories: number;
9
+ fields: number;
10
+ junctions: number;
11
+ fronts: number;
12
+ strongholds: number;
13
+ }
14
+ export declare function generateWorldData(config?: WorldGenConfig): {
15
+ zones: Omit<Zone, 'id'>[];
16
+ routes: Omit<Route, 'id'>[];
17
+ };
18
+ export declare function mapRouteNamesToIds(routes: Omit<Route, 'id'>[], zoneNameToId: Map<string, string>): Omit<Route, 'id'>[];
19
+ export {};
@@ -0,0 +1,221 @@
1
+ /**
2
+ * BURNRATE Async World Generator
3
+ * Creates the initial map for a new season (returns data for async database)
4
+ */
5
+ import { BURN_RATES, emptyInventory } from './types.js';
6
+ const DEFAULT_CONFIG = {
7
+ hubs: 3,
8
+ factories: 8,
9
+ fields: 12,
10
+ junctions: 10,
11
+ fronts: 6,
12
+ strongholds: 3
13
+ };
14
+ export function generateWorldData(config = DEFAULT_CONFIG) {
15
+ const zones = [];
16
+ const zoneNames = [];
17
+ // Generate Hubs (safe starting areas)
18
+ const hubNames = ['Hub.Central', 'Hub.East', 'Hub.West'];
19
+ for (let i = 0; i < config.hubs; i++) {
20
+ const name = hubNames[i] || `Hub.${i + 1}`;
21
+ zoneNames.push(name);
22
+ zones.push({
23
+ name,
24
+ type: 'hub',
25
+ ownerId: null,
26
+ supplyLevel: 100,
27
+ burnRate: BURN_RATES.hub,
28
+ complianceStreak: 0,
29
+ suStockpile: 0,
30
+ inventory: { ...emptyInventory(), credits: 10000 },
31
+ productionCapacity: 0,
32
+ garrisonLevel: 10,
33
+ marketDepth: 2.0,
34
+ medkitStockpile: 0,
35
+ commsStockpile: 0
36
+ });
37
+ }
38
+ // Generate Factories
39
+ const factoryPrefixes = ['Factory', 'Mill', 'Works', 'Plant'];
40
+ const factorySuffixes = ['North', 'South', 'East', 'West', 'Central', 'Upper', 'Lower', 'Main'];
41
+ for (let i = 0; i < config.factories; i++) {
42
+ const prefix = factoryPrefixes[i % factoryPrefixes.length];
43
+ const suffix = factorySuffixes[i % factorySuffixes.length];
44
+ const name = `${prefix}.${suffix}`;
45
+ zoneNames.push(name);
46
+ zones.push({
47
+ name,
48
+ type: 'factory',
49
+ ownerId: null,
50
+ supplyLevel: 100,
51
+ burnRate: BURN_RATES.factory,
52
+ complianceStreak: 0,
53
+ suStockpile: 0,
54
+ inventory: emptyInventory(),
55
+ productionCapacity: 100,
56
+ garrisonLevel: 2,
57
+ marketDepth: 1.0,
58
+ medkitStockpile: 0,
59
+ commsStockpile: 0
60
+ });
61
+ }
62
+ // Generate Fields
63
+ const fieldTypes = [
64
+ { prefix: 'Mine', resource: 'ore' },
65
+ { prefix: 'Refinery', resource: 'fuel' },
66
+ { prefix: 'Farm', resource: 'grain' },
67
+ { prefix: 'Grove', resource: 'fiber' }
68
+ ];
69
+ for (let i = 0; i < config.fields; i++) {
70
+ const fieldType = fieldTypes[i % fieldTypes.length];
71
+ const name = `${fieldType.prefix}.${i + 1}`;
72
+ zoneNames.push(name);
73
+ const inventory = emptyInventory();
74
+ inventory[fieldType.resource] = 500;
75
+ zones.push({
76
+ name,
77
+ type: 'field',
78
+ ownerId: null,
79
+ supplyLevel: 100,
80
+ burnRate: BURN_RATES.field,
81
+ complianceStreak: 0,
82
+ suStockpile: 0,
83
+ inventory,
84
+ productionCapacity: 50,
85
+ garrisonLevel: 0,
86
+ marketDepth: 0.5,
87
+ medkitStockpile: 0,
88
+ commsStockpile: 0
89
+ });
90
+ }
91
+ // Generate Junctions
92
+ for (let i = 0; i < config.junctions; i++) {
93
+ const name = `Junction.${String.fromCharCode(65 + i)}`;
94
+ zoneNames.push(name);
95
+ zones.push({
96
+ name,
97
+ type: 'junction',
98
+ ownerId: null,
99
+ supplyLevel: 100,
100
+ burnRate: BURN_RATES.junction,
101
+ complianceStreak: 0,
102
+ suStockpile: 0,
103
+ inventory: emptyInventory(),
104
+ productionCapacity: 0,
105
+ garrisonLevel: 0,
106
+ marketDepth: 0.5,
107
+ medkitStockpile: 0,
108
+ commsStockpile: 0
109
+ });
110
+ }
111
+ // Generate Fronts
112
+ for (let i = 0; i < config.fronts; i++) {
113
+ const name = `Front.${['Alpha', 'Bravo', 'Charlie', 'Delta', 'Echo', 'Foxtrot'][i] || `F${i + 1}`}`;
114
+ zoneNames.push(name);
115
+ zones.push({
116
+ name,
117
+ type: 'front',
118
+ ownerId: null,
119
+ supplyLevel: 50,
120
+ burnRate: BURN_RATES.front,
121
+ complianceStreak: 0,
122
+ suStockpile: 0,
123
+ inventory: emptyInventory(),
124
+ productionCapacity: 0,
125
+ garrisonLevel: 5,
126
+ marketDepth: 0.2,
127
+ medkitStockpile: 0,
128
+ commsStockpile: 0
129
+ });
130
+ }
131
+ // Generate Strongholds
132
+ for (let i = 0; i < config.strongholds; i++) {
133
+ const name = `Stronghold.${['Prime', 'Omega', 'Nexus'][i] || `S${i + 1}`}`;
134
+ zoneNames.push(name);
135
+ zones.push({
136
+ name,
137
+ type: 'stronghold',
138
+ ownerId: null,
139
+ supplyLevel: 25,
140
+ burnRate: BURN_RATES.stronghold,
141
+ complianceStreak: 0,
142
+ suStockpile: 0,
143
+ inventory: emptyInventory(),
144
+ productionCapacity: 0,
145
+ garrisonLevel: 10,
146
+ marketDepth: 0.1,
147
+ medkitStockpile: 0,
148
+ commsStockpile: 0
149
+ });
150
+ }
151
+ // Generate routes (create a connected graph)
152
+ const routes = [];
153
+ const totalZones = zones.length;
154
+ // Connect hubs to factories
155
+ for (let i = 0; i < config.hubs; i++) {
156
+ const hubIdx = i;
157
+ for (let j = 0; j < 3; j++) {
158
+ const factoryIdx = config.hubs + (i * 3 + j) % config.factories;
159
+ routes.push(createRoute(zoneNames[hubIdx], zoneNames[factoryIdx], 2, 0.05));
160
+ }
161
+ }
162
+ // Connect factories to fields
163
+ for (let i = 0; i < config.factories; i++) {
164
+ const factoryIdx = config.hubs + i;
165
+ const field1Idx = config.hubs + config.factories + (i * 2) % config.fields;
166
+ const field2Idx = config.hubs + config.factories + (i * 2 + 1) % config.fields;
167
+ routes.push(createRoute(zoneNames[factoryIdx], zoneNames[field1Idx], 3, 0.1));
168
+ routes.push(createRoute(zoneNames[factoryIdx], zoneNames[field2Idx], 3, 0.1));
169
+ }
170
+ // Connect junctions
171
+ const junctionStart = config.hubs + config.factories + config.fields;
172
+ for (let i = 0; i < config.junctions; i++) {
173
+ const jIdx = junctionStart + i;
174
+ // Connect to some factories
175
+ const factoryIdx = config.hubs + (i * 2) % config.factories;
176
+ routes.push(createRoute(zoneNames[jIdx], zoneNames[factoryIdx], 2, 0.15));
177
+ // Connect to next junction
178
+ if (i < config.junctions - 1) {
179
+ routes.push(createRoute(zoneNames[jIdx], zoneNames[jIdx + 1], 2, 0.2));
180
+ }
181
+ }
182
+ // Connect fronts to junctions and other fronts
183
+ const frontStart = junctionStart + config.junctions;
184
+ for (let i = 0; i < config.fronts; i++) {
185
+ const fIdx = frontStart + i;
186
+ const jIdx = junctionStart + (i * 2) % config.junctions;
187
+ routes.push(createRoute(zoneNames[fIdx], zoneNames[jIdx], 3, 0.3));
188
+ // Connect to next front
189
+ if (i < config.fronts - 1) {
190
+ routes.push(createRoute(zoneNames[fIdx], zoneNames[fIdx + 1], 2, 0.35));
191
+ }
192
+ }
193
+ // Connect strongholds to fronts
194
+ const strongholdStart = frontStart + config.fronts;
195
+ for (let i = 0; i < config.strongholds; i++) {
196
+ const sIdx = strongholdStart + i;
197
+ const f1Idx = frontStart + (i * 2) % config.fronts;
198
+ const f2Idx = frontStart + (i * 2 + 1) % config.fronts;
199
+ routes.push(createRoute(zoneNames[sIdx], zoneNames[f1Idx], 3, 0.4));
200
+ routes.push(createRoute(zoneNames[sIdx], zoneNames[f2Idx], 3, 0.4));
201
+ }
202
+ return { zones, routes };
203
+ }
204
+ function createRoute(fromName, toName, distance, risk) {
205
+ return {
206
+ fromZoneId: fromName, // Will be replaced with actual IDs after zone creation
207
+ toZoneId: toName,
208
+ distance,
209
+ capacity: 1000,
210
+ baseRisk: risk,
211
+ chokepointRating: 1.0 + Math.random() * 0.5
212
+ };
213
+ }
214
+ // Helper to convert zone names to IDs after creation
215
+ export function mapRouteNamesToIds(routes, zoneNameToId) {
216
+ return routes.map(r => ({
217
+ ...r,
218
+ fromZoneId: zoneNameToId.get(r.fromZoneId) || r.fromZoneId,
219
+ toZoneId: zoneNameToId.get(r.toZoneId) || r.toZoneId
220
+ }));
221
+ }
@@ -0,0 +1,154 @@
1
+ /**
2
+ * BURNRATE Game Engine
3
+ * Handles tick processing, supply burn, shipment movement, and combat resolution
4
+ */
5
+ import { GameDatabase } from '../db/database.js';
6
+ import { Shipment, Unit, MarketOrder, GameEvent, Inventory, Resource } from './types.js';
7
+ export declare class GameEngine {
8
+ private db;
9
+ constructor(db: GameDatabase);
10
+ /**
11
+ * Process a single game tick
12
+ * This is the core simulation loop
13
+ */
14
+ processTick(): {
15
+ tick: number;
16
+ events: GameEvent[];
17
+ };
18
+ private processSupplyBurn;
19
+ private processShipments;
20
+ private completeShipment;
21
+ private checkInterception;
22
+ /**
23
+ * Get all raiders deployed on a specific route
24
+ */
25
+ private getRaidersOnRoute;
26
+ private interceptShipment;
27
+ private processUnitMaintenance;
28
+ private processContractExpiration;
29
+ private resetDailyActions;
30
+ private processFieldRegeneration;
31
+ /**
32
+ * Check if player can perform an action (rate limiting)
33
+ */
34
+ canPlayerAct(playerId: string): {
35
+ allowed: boolean;
36
+ reason?: string;
37
+ };
38
+ /**
39
+ * Record that player took an action
40
+ */
41
+ recordPlayerAction(playerId: string): void;
42
+ /**
43
+ * Create a new shipment with an explicit path
44
+ * Players must specify waypoints - no automatic pathfinding
45
+ */
46
+ createShipmentWithPath(playerId: string, type: 'courier' | 'freight' | 'convoy', path: string[], cargo: Partial<Inventory>): {
47
+ success: boolean;
48
+ shipment?: Shipment;
49
+ error?: string;
50
+ };
51
+ /**
52
+ * Create a new shipment (legacy - uses pathfinding internally)
53
+ * @deprecated Use createShipmentWithPath for explicit control
54
+ */
55
+ createShipment(playerId: string, type: 'courier' | 'freight' | 'convoy', fromZoneId: string, toZoneId: string, cargo: Partial<Inventory>): {
56
+ success: boolean;
57
+ shipment?: Shipment;
58
+ error?: string;
59
+ };
60
+ /**
61
+ * Place a market order
62
+ */
63
+ placeOrder(playerId: string, zoneId: string, resource: Resource, side: 'buy' | 'sell', price: number, quantity: number): {
64
+ success: boolean;
65
+ order?: MarketOrder;
66
+ error?: string;
67
+ };
68
+ /**
69
+ * Match buy and sell orders
70
+ */
71
+ private matchOrders;
72
+ private executeTrade;
73
+ /**
74
+ * Deposit SU to a zone
75
+ */
76
+ depositSU(playerId: string, zoneId: string, amount: number): {
77
+ success: boolean;
78
+ error?: string;
79
+ };
80
+ /**
81
+ * Scan a zone or route for intel
82
+ * If player is in a faction, intel is automatically shared with faction members
83
+ */
84
+ scan(playerId: string, targetType: 'zone' | 'route', targetId: string): {
85
+ success: boolean;
86
+ intel?: any;
87
+ error?: string;
88
+ };
89
+ /**
90
+ * Produce resources or units at a Factory
91
+ */
92
+ produce(playerId: string, output: string, quantity: number): {
93
+ success: boolean;
94
+ produced?: number;
95
+ units?: Unit[];
96
+ error?: string;
97
+ };
98
+ /**
99
+ * Extract raw resources from a Field
100
+ */
101
+ extract(playerId: string, quantity: number): {
102
+ success: boolean;
103
+ extracted?: {
104
+ resource: string;
105
+ amount: number;
106
+ };
107
+ error?: string;
108
+ };
109
+ /**
110
+ * Capture a neutral zone for a faction
111
+ */
112
+ captureZone(playerId: string, zoneId: string): {
113
+ success: boolean;
114
+ error?: string;
115
+ };
116
+ /**
117
+ * Assign an escort unit to a shipment
118
+ */
119
+ assignEscort(playerId: string, unitId: string, shipmentId: string): {
120
+ success: boolean;
121
+ error?: string;
122
+ };
123
+ /**
124
+ * List a unit for sale at current location (must be at Hub)
125
+ */
126
+ listUnitForSale(playerId: string, unitId: string, price: number): {
127
+ success: boolean;
128
+ error?: string;
129
+ };
130
+ /**
131
+ * Remove a unit from sale
132
+ */
133
+ unlistUnit(playerId: string, unitId: string): {
134
+ success: boolean;
135
+ error?: string;
136
+ };
137
+ /**
138
+ * Hire (buy) a unit that's listed for sale
139
+ */
140
+ hireUnit(playerId: string, unitId: string): {
141
+ success: boolean;
142
+ unit?: Unit;
143
+ error?: string;
144
+ };
145
+ /**
146
+ * Deploy a raider unit to interdict a route
147
+ */
148
+ deployRaider(playerId: string, unitId: string, routeId: string): {
149
+ success: boolean;
150
+ error?: string;
151
+ };
152
+ private findRoute;
153
+ private recordEvent;
154
+ }