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.
- package/LICENSE +21 -0
- package/README.md +507 -0
- package/dist/cli/format.d.ts +13 -0
- package/dist/cli/format.js +319 -0
- package/dist/cli/index.d.ts +7 -0
- package/dist/cli/index.js +1121 -0
- package/dist/cli/setup.d.ts +8 -0
- package/dist/cli/setup.js +143 -0
- package/dist/core/async-engine.d.ts +411 -0
- package/dist/core/async-engine.js +2274 -0
- package/dist/core/async-worldgen.d.ts +19 -0
- package/dist/core/async-worldgen.js +221 -0
- package/dist/core/engine.d.ts +154 -0
- package/dist/core/engine.js +1104 -0
- package/dist/core/pathfinding.d.ts +38 -0
- package/dist/core/pathfinding.js +146 -0
- package/dist/core/types.d.ts +489 -0
- package/dist/core/types.js +359 -0
- package/dist/core/worldgen.d.ts +22 -0
- package/dist/core/worldgen.js +292 -0
- package/dist/db/database.d.ts +83 -0
- package/dist/db/database.js +829 -0
- package/dist/db/turso-database.d.ts +177 -0
- package/dist/db/turso-database.js +1586 -0
- package/dist/mcp/server.d.ts +7 -0
- package/dist/mcp/server.js +1877 -0
- package/dist/server/api.d.ts +8 -0
- package/dist/server/api.js +1234 -0
- package/dist/server/async-tick-server.d.ts +5 -0
- package/dist/server/async-tick-server.js +63 -0
- package/dist/server/errors.d.ts +78 -0
- package/dist/server/errors.js +156 -0
- package/dist/server/rate-limit.d.ts +22 -0
- package/dist/server/rate-limit.js +134 -0
- package/dist/server/tick-server.d.ts +9 -0
- package/dist/server/tick-server.js +114 -0
- package/dist/server/validation.d.ts +194 -0
- package/dist/server/validation.js +114 -0
- package/package.json +65 -0
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BURNRATE Pathfinding
|
|
3
|
+
* Find optimal routes between zones
|
|
4
|
+
*/
|
|
5
|
+
import { GameDatabase } from '../db/database.js';
|
|
6
|
+
import { Route } from './types.js';
|
|
7
|
+
/**
|
|
8
|
+
* Find shortest path between two zones using Dijkstra's algorithm
|
|
9
|
+
* Optimizes for distance by default, can also factor in risk
|
|
10
|
+
*/
|
|
11
|
+
export declare function findPath(db: GameDatabase, fromZoneId: string, toZoneId: string, options?: {
|
|
12
|
+
optimizeFor?: 'distance' | 'risk' | 'balanced';
|
|
13
|
+
}): {
|
|
14
|
+
path: string[];
|
|
15
|
+
totalDistance: number;
|
|
16
|
+
totalRisk: number;
|
|
17
|
+
routes: Route[];
|
|
18
|
+
} | null;
|
|
19
|
+
/**
|
|
20
|
+
* Find all paths up to a certain length (for showing alternatives)
|
|
21
|
+
*/
|
|
22
|
+
export declare function findAlternativePaths(db: GameDatabase, fromZoneId: string, toZoneId: string, maxPaths?: number): Array<{
|
|
23
|
+
path: string[];
|
|
24
|
+
totalDistance: number;
|
|
25
|
+
totalRisk: number;
|
|
26
|
+
}>;
|
|
27
|
+
/**
|
|
28
|
+
* Calculate total distance and ETA for a path with a specific shipment type
|
|
29
|
+
*/
|
|
30
|
+
export declare function calculatePathStats(routes: Route[], speedModifier: number): {
|
|
31
|
+
totalTicks: number;
|
|
32
|
+
totalDistance: number;
|
|
33
|
+
legs: Array<{
|
|
34
|
+
from: string;
|
|
35
|
+
to: string;
|
|
36
|
+
ticks: number;
|
|
37
|
+
}>;
|
|
38
|
+
};
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BURNRATE Pathfinding
|
|
3
|
+
* Find optimal routes between zones
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Find shortest path between two zones using Dijkstra's algorithm
|
|
7
|
+
* Optimizes for distance by default, can also factor in risk
|
|
8
|
+
*/
|
|
9
|
+
export function findPath(db, fromZoneId, toZoneId, options = {}) {
|
|
10
|
+
const { optimizeFor = 'distance' } = options;
|
|
11
|
+
// Get all zones and routes
|
|
12
|
+
const allZones = db.getAllZones();
|
|
13
|
+
const zoneIds = new Set(allZones.map(z => z.id));
|
|
14
|
+
if (!zoneIds.has(fromZoneId) || !zoneIds.has(toZoneId)) {
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
// Build adjacency map
|
|
18
|
+
const adjacency = new Map();
|
|
19
|
+
for (const zone of allZones) {
|
|
20
|
+
adjacency.set(zone.id, db.getRoutesFromZone(zone.id));
|
|
21
|
+
}
|
|
22
|
+
// Dijkstra's algorithm
|
|
23
|
+
const visited = new Set();
|
|
24
|
+
const costs = new Map();
|
|
25
|
+
const paths = new Map();
|
|
26
|
+
const risks = new Map();
|
|
27
|
+
const distances = new Map(); // Track actual distances separately
|
|
28
|
+
const routePaths = new Map();
|
|
29
|
+
costs.set(fromZoneId, 0);
|
|
30
|
+
paths.set(fromZoneId, [fromZoneId]);
|
|
31
|
+
risks.set(fromZoneId, 0);
|
|
32
|
+
distances.set(fromZoneId, 0);
|
|
33
|
+
routePaths.set(fromZoneId, []);
|
|
34
|
+
while (visited.size < zoneIds.size) {
|
|
35
|
+
// Find unvisited node with lowest cost
|
|
36
|
+
let current = null;
|
|
37
|
+
let lowestCost = Infinity;
|
|
38
|
+
for (const [zoneId, cost] of costs) {
|
|
39
|
+
if (!visited.has(zoneId) && cost < lowestCost) {
|
|
40
|
+
lowestCost = cost;
|
|
41
|
+
current = zoneId;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
if (current === null)
|
|
45
|
+
break;
|
|
46
|
+
if (current === toZoneId)
|
|
47
|
+
break;
|
|
48
|
+
visited.add(current);
|
|
49
|
+
// Check all neighbors
|
|
50
|
+
const routes = adjacency.get(current) || [];
|
|
51
|
+
for (const route of routes) {
|
|
52
|
+
const neighbor = route.toZoneId;
|
|
53
|
+
if (visited.has(neighbor))
|
|
54
|
+
continue;
|
|
55
|
+
// Calculate cost based on optimization strategy
|
|
56
|
+
let edgeCost;
|
|
57
|
+
switch (optimizeFor) {
|
|
58
|
+
case 'risk':
|
|
59
|
+
edgeCost = route.baseRisk * route.chokepointRating * 100;
|
|
60
|
+
break;
|
|
61
|
+
case 'balanced':
|
|
62
|
+
edgeCost = route.distance + route.baseRisk * route.chokepointRating * 50;
|
|
63
|
+
break;
|
|
64
|
+
case 'distance':
|
|
65
|
+
default:
|
|
66
|
+
edgeCost = route.distance;
|
|
67
|
+
}
|
|
68
|
+
const newCost = (costs.get(current) || 0) + edgeCost;
|
|
69
|
+
const currentNeighborCost = costs.get(neighbor) ?? Infinity;
|
|
70
|
+
if (newCost < currentNeighborCost) {
|
|
71
|
+
costs.set(neighbor, newCost);
|
|
72
|
+
paths.set(neighbor, [...(paths.get(current) || []), neighbor]);
|
|
73
|
+
risks.set(neighbor, (risks.get(current) || 0) + route.baseRisk * route.chokepointRating);
|
|
74
|
+
distances.set(neighbor, (distances.get(current) || 0) + route.distance);
|
|
75
|
+
routePaths.set(neighbor, [...(routePaths.get(current) || []), route]);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
const finalPath = paths.get(toZoneId);
|
|
80
|
+
if (!finalPath)
|
|
81
|
+
return null;
|
|
82
|
+
return {
|
|
83
|
+
path: finalPath,
|
|
84
|
+
totalDistance: distances.get(toZoneId) || 0, // Return actual distance, not cost
|
|
85
|
+
totalRisk: risks.get(toZoneId) || 0,
|
|
86
|
+
routes: routePaths.get(toZoneId) || []
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Find all paths up to a certain length (for showing alternatives)
|
|
91
|
+
*/
|
|
92
|
+
export function findAlternativePaths(db, fromZoneId, toZoneId, maxPaths = 3) {
|
|
93
|
+
const results = [];
|
|
94
|
+
// Get shortest path
|
|
95
|
+
const shortest = findPath(db, fromZoneId, toZoneId, { optimizeFor: 'distance' });
|
|
96
|
+
if (shortest) {
|
|
97
|
+
results.push({
|
|
98
|
+
path: shortest.path,
|
|
99
|
+
totalDistance: shortest.totalDistance,
|
|
100
|
+
totalRisk: shortest.totalRisk
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
// Get safest path
|
|
104
|
+
const safest = findPath(db, fromZoneId, toZoneId, { optimizeFor: 'risk' });
|
|
105
|
+
if (safest && !pathsEqual(safest.path, shortest?.path || [])) {
|
|
106
|
+
results.push({
|
|
107
|
+
path: safest.path,
|
|
108
|
+
totalDistance: safest.totalDistance,
|
|
109
|
+
totalRisk: safest.totalRisk
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
// Get balanced path
|
|
113
|
+
const balanced = findPath(db, fromZoneId, toZoneId, { optimizeFor: 'balanced' });
|
|
114
|
+
if (balanced && !pathsEqual(balanced.path, shortest?.path || []) && !pathsEqual(balanced.path, safest?.path || [])) {
|
|
115
|
+
results.push({
|
|
116
|
+
path: balanced.path,
|
|
117
|
+
totalDistance: balanced.totalDistance,
|
|
118
|
+
totalRisk: balanced.totalRisk
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
return results.slice(0, maxPaths);
|
|
122
|
+
}
|
|
123
|
+
function pathsEqual(a, b) {
|
|
124
|
+
if (a.length !== b.length)
|
|
125
|
+
return false;
|
|
126
|
+
return a.every((v, i) => v === b[i]);
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Calculate total distance and ETA for a path with a specific shipment type
|
|
130
|
+
*/
|
|
131
|
+
export function calculatePathStats(routes, speedModifier) {
|
|
132
|
+
let totalTicks = 0;
|
|
133
|
+
let totalDistance = 0;
|
|
134
|
+
const legs = [];
|
|
135
|
+
for (const route of routes) {
|
|
136
|
+
const ticks = Math.ceil(route.distance * speedModifier);
|
|
137
|
+
totalTicks += ticks;
|
|
138
|
+
totalDistance += route.distance;
|
|
139
|
+
legs.push({
|
|
140
|
+
from: route.fromZoneId,
|
|
141
|
+
to: route.toZoneId,
|
|
142
|
+
ticks
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
return { totalTicks, totalDistance, legs };
|
|
146
|
+
}
|
|
@@ -0,0 +1,489 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BURNRATE Core Types
|
|
3
|
+
* The front doesn't feed itself.
|
|
4
|
+
*/
|
|
5
|
+
/** T0 Raw Materials */
|
|
6
|
+
export type RawResource = 'ore' | 'fuel' | 'grain' | 'fiber';
|
|
7
|
+
/** T1 Processed Goods */
|
|
8
|
+
export type ProcessedResource = 'metal' | 'chemicals' | 'rations' | 'textiles';
|
|
9
|
+
/** T2 Strategic Goods */
|
|
10
|
+
export type StrategicResource = 'ammo' | 'medkits' | 'parts' | 'comms';
|
|
11
|
+
/** All tradeable resources */
|
|
12
|
+
export type Resource = RawResource | ProcessedResource | StrategicResource;
|
|
13
|
+
/** Resource inventory (including SU) */
|
|
14
|
+
export interface Inventory {
|
|
15
|
+
ore: number;
|
|
16
|
+
fuel: number;
|
|
17
|
+
grain: number;
|
|
18
|
+
fiber: number;
|
|
19
|
+
metal: number;
|
|
20
|
+
chemicals: number;
|
|
21
|
+
rations: number;
|
|
22
|
+
textiles: number;
|
|
23
|
+
ammo: number;
|
|
24
|
+
medkits: number;
|
|
25
|
+
parts: number;
|
|
26
|
+
comms: number;
|
|
27
|
+
credits: number;
|
|
28
|
+
}
|
|
29
|
+
/** Empty inventory factory */
|
|
30
|
+
export declare function emptyInventory(): Inventory;
|
|
31
|
+
/** SU recipe: what's needed to create 1 Supply Unit */
|
|
32
|
+
export declare const SU_RECIPE: {
|
|
33
|
+
readonly rations: 2;
|
|
34
|
+
readonly fuel: 1;
|
|
35
|
+
readonly parts: 1;
|
|
36
|
+
readonly ammo: 1;
|
|
37
|
+
};
|
|
38
|
+
/** Production recipes: inputs required to produce 1 unit of output */
|
|
39
|
+
export declare const RECIPES: Record<string, {
|
|
40
|
+
inputs: Partial<Record<Resource, number>>;
|
|
41
|
+
tier: number;
|
|
42
|
+
isUnit?: boolean;
|
|
43
|
+
}>;
|
|
44
|
+
/** What raw resource a Field produces based on its name */
|
|
45
|
+
export declare function getFieldResource(fieldName: string): RawResource | null;
|
|
46
|
+
export type ZoneType = 'hub' | 'factory' | 'field' | 'junction' | 'front' | 'stronghold';
|
|
47
|
+
export interface Zone {
|
|
48
|
+
id: string;
|
|
49
|
+
name: string;
|
|
50
|
+
type: ZoneType;
|
|
51
|
+
/** Faction that controls this zone (null = neutral) */
|
|
52
|
+
ownerId: string | null;
|
|
53
|
+
/** Current supply level (0-100+, percentage of burn rate covered) */
|
|
54
|
+
supplyLevel: number;
|
|
55
|
+
/** SU burned per tick to maintain control */
|
|
56
|
+
burnRate: number;
|
|
57
|
+
/** Consecutive ticks at 100%+ supply */
|
|
58
|
+
complianceStreak: number;
|
|
59
|
+
/** Current SU stockpile */
|
|
60
|
+
suStockpile: number;
|
|
61
|
+
/** Zone inventory (for production, markets) */
|
|
62
|
+
inventory: Inventory;
|
|
63
|
+
/** Production capacity (units/tick) for factory zones */
|
|
64
|
+
productionCapacity: number;
|
|
65
|
+
/** Garrison strength (faction investment) */
|
|
66
|
+
garrisonLevel: number;
|
|
67
|
+
/** Market depth multiplier */
|
|
68
|
+
marketDepth: number;
|
|
69
|
+
/** Medkit stockpile for combat bonuses */
|
|
70
|
+
medkitStockpile: number;
|
|
71
|
+
/** Comms stockpile for intel defense */
|
|
72
|
+
commsStockpile: number;
|
|
73
|
+
}
|
|
74
|
+
export type SupplyState = 'fortified' | 'supplied' | 'strained' | 'critical' | 'collapsed';
|
|
75
|
+
export declare function getSupplyState(supplyLevel: number, complianceStreak?: number): SupplyState;
|
|
76
|
+
/** Front efficiency: bonuses from supply level and compliance streak */
|
|
77
|
+
export interface ZoneEfficiency {
|
|
78
|
+
/** Supply state label */
|
|
79
|
+
state: SupplyState;
|
|
80
|
+
/** Multiplier for raid resistance (higher = harder to raid) */
|
|
81
|
+
raidResistance: number;
|
|
82
|
+
/** Multiplier for capture difficulty (higher = harder to capture) */
|
|
83
|
+
captureDefense: number;
|
|
84
|
+
/** Production bonus (0.0 = none, 0.2 = +20%) */
|
|
85
|
+
productionBonus: number;
|
|
86
|
+
/** Medkit combat bonus (reduces unit attrition) */
|
|
87
|
+
medkitBonus: number;
|
|
88
|
+
/** Comms intel bonus (reduces enemy scan quality) */
|
|
89
|
+
commsDefense: number;
|
|
90
|
+
}
|
|
91
|
+
/** Calculate zone efficiency from supply state, streak, and stockpiles */
|
|
92
|
+
export declare function getZoneEfficiency(supplyLevel: number, complianceStreak: number, medkitStockpile?: number, commsStockpile?: number): ZoneEfficiency;
|
|
93
|
+
/** Burn rates by zone type */
|
|
94
|
+
export declare const BURN_RATES: Record<ZoneType, number>;
|
|
95
|
+
export interface Route {
|
|
96
|
+
id: string;
|
|
97
|
+
fromZoneId: string;
|
|
98
|
+
toZoneId: string;
|
|
99
|
+
/** Ticks to traverse */
|
|
100
|
+
distance: number;
|
|
101
|
+
/** Max cargo per tick */
|
|
102
|
+
capacity: number;
|
|
103
|
+
/** Base interception probability (0-1) */
|
|
104
|
+
baseRisk: number;
|
|
105
|
+
/** Chokepoint multiplier for ambushes (1-3) */
|
|
106
|
+
chokepointRating: number;
|
|
107
|
+
}
|
|
108
|
+
export type ShipmentType = 'courier' | 'freight' | 'convoy';
|
|
109
|
+
export interface Shipment {
|
|
110
|
+
id: string;
|
|
111
|
+
playerId: string;
|
|
112
|
+
type: ShipmentType;
|
|
113
|
+
/** Route path (zone IDs in order) */
|
|
114
|
+
path: string[];
|
|
115
|
+
/** Current position (index in path) */
|
|
116
|
+
currentPosition: number;
|
|
117
|
+
/** Ticks remaining until next zone */
|
|
118
|
+
ticksToNextZone: number;
|
|
119
|
+
/** Cargo being shipped */
|
|
120
|
+
cargo: Partial<Inventory>;
|
|
121
|
+
/** Escort unit IDs attached */
|
|
122
|
+
escortIds: string[];
|
|
123
|
+
/** Tick when shipment was created */
|
|
124
|
+
createdAt: number;
|
|
125
|
+
/** Status */
|
|
126
|
+
status: 'in_transit' | 'arrived' | 'intercepted';
|
|
127
|
+
}
|
|
128
|
+
export declare const SHIPMENT_SPECS: Record<ShipmentType, {
|
|
129
|
+
capacity: number;
|
|
130
|
+
speedModifier: number;
|
|
131
|
+
visibilityModifier: number;
|
|
132
|
+
}>;
|
|
133
|
+
/** License unlock requirements */
|
|
134
|
+
export declare const LICENSE_REQUIREMENTS: Record<ShipmentType, {
|
|
135
|
+
reputationRequired: number;
|
|
136
|
+
creditsCost: number;
|
|
137
|
+
description: string;
|
|
138
|
+
}>;
|
|
139
|
+
/** Reputation rewards for various actions */
|
|
140
|
+
export declare const REPUTATION_REWARDS: {
|
|
141
|
+
readonly shipmentDelivered: 5;
|
|
142
|
+
readonly shipmentIntercepted: -10;
|
|
143
|
+
readonly escortSuccess: 3;
|
|
144
|
+
readonly contractCompleted: 10;
|
|
145
|
+
readonly contractFailed: -20;
|
|
146
|
+
readonly contractBonus: 5;
|
|
147
|
+
readonly supplyDelivered: 2;
|
|
148
|
+
readonly zoneCaptured: 25;
|
|
149
|
+
readonly raiderDestroyed: 5;
|
|
150
|
+
readonly escortDestroyed: -5;
|
|
151
|
+
readonly tradeCompleted: 1;
|
|
152
|
+
readonly dailyDecay: -1;
|
|
153
|
+
readonly maxReputation: 1000;
|
|
154
|
+
};
|
|
155
|
+
/** Reputation thresholds for titles */
|
|
156
|
+
export declare const REPUTATION_TITLES: {
|
|
157
|
+
threshold: number;
|
|
158
|
+
title: string;
|
|
159
|
+
}[];
|
|
160
|
+
/** Get reputation title based on rep value */
|
|
161
|
+
export declare function getReputationTitle(reputation: number): string;
|
|
162
|
+
export type UnitType = 'escort' | 'raider';
|
|
163
|
+
export interface Unit {
|
|
164
|
+
id: string;
|
|
165
|
+
playerId: string;
|
|
166
|
+
type: UnitType;
|
|
167
|
+
/** Current location (zone ID) */
|
|
168
|
+
locationId: string;
|
|
169
|
+
/** Combat strength */
|
|
170
|
+
strength: number;
|
|
171
|
+
/** Movement speed (affects raider interception range) */
|
|
172
|
+
speed: number;
|
|
173
|
+
/** Maintenance cost (credits/tick) */
|
|
174
|
+
maintenance: number;
|
|
175
|
+
/** If assigned to a shipment (escort) or route (raider) */
|
|
176
|
+
assignmentId: string | null;
|
|
177
|
+
/** If listed for sale, the asking price (null = not for sale) */
|
|
178
|
+
forSalePrice: number | null;
|
|
179
|
+
}
|
|
180
|
+
export type SubscriptionTier = 'freelance' | 'operator' | 'command';
|
|
181
|
+
export interface Player {
|
|
182
|
+
id: string;
|
|
183
|
+
name: string;
|
|
184
|
+
tier: SubscriptionTier;
|
|
185
|
+
/** Personal inventory and credits */
|
|
186
|
+
inventory: Inventory;
|
|
187
|
+
/** Current location (zone ID) */
|
|
188
|
+
locationId: string;
|
|
189
|
+
/** Faction membership */
|
|
190
|
+
factionId: string | null;
|
|
191
|
+
/** Global reputation (0-1000) */
|
|
192
|
+
reputation: number;
|
|
193
|
+
/** Actions taken today */
|
|
194
|
+
actionsToday: number;
|
|
195
|
+
/** Last action timestamp (for rate limiting) */
|
|
196
|
+
lastActionTick: number;
|
|
197
|
+
/** Licenses unlocked */
|
|
198
|
+
licenses: {
|
|
199
|
+
courier: boolean;
|
|
200
|
+
freight: boolean;
|
|
201
|
+
convoy: boolean;
|
|
202
|
+
};
|
|
203
|
+
/** Tutorial progress (0-5, each step completed) */
|
|
204
|
+
tutorialStep: number;
|
|
205
|
+
}
|
|
206
|
+
export declare const TIER_LIMITS: Record<SubscriptionTier, {
|
|
207
|
+
dailyActions: number;
|
|
208
|
+
eventHistory: number;
|
|
209
|
+
concurrentContracts: number;
|
|
210
|
+
marketOrders: number;
|
|
211
|
+
}>;
|
|
212
|
+
export type FactionRank = 'founder' | 'officer' | 'member';
|
|
213
|
+
/** Permissions for each rank */
|
|
214
|
+
export declare const FACTION_PERMISSIONS: Record<FactionRank, {
|
|
215
|
+
canInvite: boolean;
|
|
216
|
+
canKick: boolean;
|
|
217
|
+
canPromote: boolean;
|
|
218
|
+
canDemote: boolean;
|
|
219
|
+
canWithdraw: boolean;
|
|
220
|
+
canSetDoctrine: boolean;
|
|
221
|
+
canDeclareWar: boolean;
|
|
222
|
+
canUpgrade: boolean;
|
|
223
|
+
}>;
|
|
224
|
+
export interface FactionMember {
|
|
225
|
+
playerId: string;
|
|
226
|
+
rank: FactionRank;
|
|
227
|
+
joinedAt: number;
|
|
228
|
+
}
|
|
229
|
+
export interface Faction {
|
|
230
|
+
id: string;
|
|
231
|
+
name: string;
|
|
232
|
+
tag: string;
|
|
233
|
+
/** Founder player ID */
|
|
234
|
+
founderId: string;
|
|
235
|
+
/** Treasury */
|
|
236
|
+
treasury: Inventory;
|
|
237
|
+
/** Daily withdrawal limit for officers (credits) */
|
|
238
|
+
officerWithdrawLimit: number;
|
|
239
|
+
/** Member list with ranks */
|
|
240
|
+
members: FactionMember[];
|
|
241
|
+
/** Zone IDs controlled by this faction */
|
|
242
|
+
controlledZones: string[];
|
|
243
|
+
/** Doctrine hash (for compliance tracking) */
|
|
244
|
+
doctrineHash: string | null;
|
|
245
|
+
/** Infrastructure upgrades */
|
|
246
|
+
upgrades: {
|
|
247
|
+
relayNetwork: number;
|
|
248
|
+
routeFortification: number;
|
|
249
|
+
productionBonus: number;
|
|
250
|
+
garrisonStrength: number;
|
|
251
|
+
marketDepth: number;
|
|
252
|
+
};
|
|
253
|
+
/** Alliance/war state with other factions */
|
|
254
|
+
relations: Record<string, 'allied' | 'neutral' | 'war'>;
|
|
255
|
+
}
|
|
256
|
+
/** Intel freshness states based on age */
|
|
257
|
+
export type IntelFreshness = 'fresh' | 'stale' | 'expired';
|
|
258
|
+
/** Intel freshness thresholds (in ticks) */
|
|
259
|
+
export declare const INTEL_DECAY_THRESHOLDS: {
|
|
260
|
+
readonly fresh: 10;
|
|
261
|
+
readonly stale: 50;
|
|
262
|
+
};
|
|
263
|
+
/** Calculate intel freshness based on age */
|
|
264
|
+
export declare function getIntelFreshness(gatheredAt: number, currentTick: number): IntelFreshness;
|
|
265
|
+
/** Calculate effective signal quality with decay */
|
|
266
|
+
export declare function getDecayedSignalQuality(originalQuality: number, gatheredAt: number, currentTick: number): number;
|
|
267
|
+
/** Apply decay to intel data based on freshness */
|
|
268
|
+
export declare function applyIntelDecay(data: Record<string, unknown>, freshness: IntelFreshness): Record<string, unknown>;
|
|
269
|
+
export interface IntelReport {
|
|
270
|
+
id: string;
|
|
271
|
+
playerId: string;
|
|
272
|
+
/** Faction this intel is shared with (null = private) */
|
|
273
|
+
factionId: string | null;
|
|
274
|
+
/** What was scanned */
|
|
275
|
+
targetType: 'zone' | 'route';
|
|
276
|
+
targetId: string;
|
|
277
|
+
/** Tick when gathered */
|
|
278
|
+
gatheredAt: number;
|
|
279
|
+
/** Data captured (varies by target type) */
|
|
280
|
+
data: Record<string, unknown>;
|
|
281
|
+
/** Signal quality when gathered (0-100) */
|
|
282
|
+
signalQuality: number;
|
|
283
|
+
}
|
|
284
|
+
/** Intel report with freshness information */
|
|
285
|
+
export interface IntelReportWithFreshness extends IntelReport {
|
|
286
|
+
freshness: IntelFreshness;
|
|
287
|
+
effectiveSignalQuality: number;
|
|
288
|
+
ageInTicks: number;
|
|
289
|
+
}
|
|
290
|
+
export type OrderSide = 'buy' | 'sell';
|
|
291
|
+
export interface MarketOrder {
|
|
292
|
+
id: string;
|
|
293
|
+
playerId: string;
|
|
294
|
+
zoneId: string;
|
|
295
|
+
resource: Resource;
|
|
296
|
+
side: OrderSide;
|
|
297
|
+
/** Price per unit */
|
|
298
|
+
price: number;
|
|
299
|
+
/** Quantity remaining */
|
|
300
|
+
quantity: number;
|
|
301
|
+
/** Original quantity */
|
|
302
|
+
originalQuantity: number;
|
|
303
|
+
/** Tick when placed */
|
|
304
|
+
createdAt: number;
|
|
305
|
+
}
|
|
306
|
+
export interface Trade {
|
|
307
|
+
id: string;
|
|
308
|
+
zoneId: string;
|
|
309
|
+
resource: Resource;
|
|
310
|
+
buyerId: string;
|
|
311
|
+
sellerId: string;
|
|
312
|
+
price: number;
|
|
313
|
+
quantity: number;
|
|
314
|
+
executedAt: number;
|
|
315
|
+
}
|
|
316
|
+
export type ContractType = 'haul' | 'supply' | 'scout' | 'tutorial';
|
|
317
|
+
/** Tutorial contract definitions */
|
|
318
|
+
export declare const TUTORIAL_CONTRACTS: {
|
|
319
|
+
step: number;
|
|
320
|
+
title: string;
|
|
321
|
+
description: string;
|
|
322
|
+
type: ContractType;
|
|
323
|
+
reward: {
|
|
324
|
+
credits: number;
|
|
325
|
+
reputation: number;
|
|
326
|
+
};
|
|
327
|
+
}[];
|
|
328
|
+
export interface Contract {
|
|
329
|
+
id: string;
|
|
330
|
+
type: ContractType;
|
|
331
|
+
/** Who posted (player ID or faction ID) */
|
|
332
|
+
posterId: string;
|
|
333
|
+
posterType: 'player' | 'faction';
|
|
334
|
+
/** Who accepted (null if open) */
|
|
335
|
+
acceptedBy: string | null;
|
|
336
|
+
/** Contract details (varies by type) */
|
|
337
|
+
details: {
|
|
338
|
+
fromZoneId?: string;
|
|
339
|
+
toZoneId?: string;
|
|
340
|
+
resource?: Resource;
|
|
341
|
+
quantity?: number;
|
|
342
|
+
targetId?: string;
|
|
343
|
+
targetType?: 'zone' | 'route';
|
|
344
|
+
};
|
|
345
|
+
/** Deadline (tick) */
|
|
346
|
+
deadline: number;
|
|
347
|
+
/** Reward */
|
|
348
|
+
reward: {
|
|
349
|
+
credits: number;
|
|
350
|
+
reputation: number;
|
|
351
|
+
};
|
|
352
|
+
/** Bonus for early completion */
|
|
353
|
+
bonus?: {
|
|
354
|
+
deadline: number;
|
|
355
|
+
credits: number;
|
|
356
|
+
};
|
|
357
|
+
status: 'open' | 'active' | 'completed' | 'failed' | 'expired';
|
|
358
|
+
createdAt: number;
|
|
359
|
+
}
|
|
360
|
+
export type GameEventType = 'tick' | 'shipment_created' | 'shipment_moved' | 'shipment_arrived' | 'shipment_intercepted' | 'trade_executed' | 'order_placed' | 'order_cancelled' | 'zone_supplied' | 'zone_state_changed' | 'zone_captured' | 'combat_resolved' | 'intel_gathered' | 'contract_posted' | 'contract_accepted' | 'contract_completed' | 'contract_failed' | 'faction_created' | 'faction_joined' | 'faction_left' | 'player_action' | 'stockpile_deposited' | 'tutorial_completed' | 'doctrine_updated' | 'webhook_triggered';
|
|
361
|
+
export interface GameEvent {
|
|
362
|
+
id: string;
|
|
363
|
+
type: GameEventType;
|
|
364
|
+
tick: number;
|
|
365
|
+
timestamp: Date;
|
|
366
|
+
/** Actor (player/faction) who caused the event */
|
|
367
|
+
actorId: string | null;
|
|
368
|
+
actorType: 'player' | 'faction' | 'system';
|
|
369
|
+
/** Event-specific data */
|
|
370
|
+
data: Record<string, unknown>;
|
|
371
|
+
}
|
|
372
|
+
/** Season configuration */
|
|
373
|
+
export declare const SEASON_CONFIG: {
|
|
374
|
+
readonly ticksPerWeek: 1008;
|
|
375
|
+
readonly weeksPerSeason: 4;
|
|
376
|
+
readonly ticksPerSeason: 4032;
|
|
377
|
+
readonly scoring: {
|
|
378
|
+
readonly zonesControlled: 100;
|
|
379
|
+
readonly supplyDelivered: 1;
|
|
380
|
+
readonly shipmentsCompleted: 10;
|
|
381
|
+
readonly contractsCompleted: 25;
|
|
382
|
+
readonly reputationGained: 2;
|
|
383
|
+
readonly combatVictories: 50;
|
|
384
|
+
};
|
|
385
|
+
};
|
|
386
|
+
/** Season score entry */
|
|
387
|
+
export interface SeasonScore {
|
|
388
|
+
id: string;
|
|
389
|
+
seasonNumber: number;
|
|
390
|
+
entityId: string;
|
|
391
|
+
entityType: 'player' | 'faction';
|
|
392
|
+
entityName: string;
|
|
393
|
+
zonesControlled: number;
|
|
394
|
+
supplyDelivered: number;
|
|
395
|
+
shipmentsCompleted: number;
|
|
396
|
+
contractsCompleted: number;
|
|
397
|
+
reputationGained: number;
|
|
398
|
+
combatVictories: number;
|
|
399
|
+
totalScore: number;
|
|
400
|
+
rank?: number;
|
|
401
|
+
}
|
|
402
|
+
/** Calculate total score from components */
|
|
403
|
+
export declare function calculateSeasonScore(score: Omit<SeasonScore, 'id' | 'totalScore' | 'rank'>): number;
|
|
404
|
+
export interface WorldState {
|
|
405
|
+
currentTick: number;
|
|
406
|
+
seasonNumber: number;
|
|
407
|
+
seasonWeek: number;
|
|
408
|
+
zones: Map<string, Zone>;
|
|
409
|
+
routes: Map<string, Route>;
|
|
410
|
+
players: Map<string, Player>;
|
|
411
|
+
factions: Map<string, Faction>;
|
|
412
|
+
shipments: Map<string, Shipment>;
|
|
413
|
+
units: Map<string, Unit>;
|
|
414
|
+
orders: Map<string, MarketOrder>;
|
|
415
|
+
contracts: Map<string, Contract>;
|
|
416
|
+
intel: Map<string, IntelReport>;
|
|
417
|
+
}
|
|
418
|
+
export interface Doctrine {
|
|
419
|
+
id: string;
|
|
420
|
+
factionId: string;
|
|
421
|
+
title: string;
|
|
422
|
+
content: string;
|
|
423
|
+
version: number;
|
|
424
|
+
createdAt: number;
|
|
425
|
+
updatedAt: number;
|
|
426
|
+
createdBy: string;
|
|
427
|
+
}
|
|
428
|
+
export type WebhookEventType = 'shipment_arrived' | 'shipment_intercepted' | 'zone_critical' | 'zone_collapsed' | 'zone_captured' | 'contract_completed' | 'contract_expired' | 'market_order_filled' | 'under_attack';
|
|
429
|
+
export interface Webhook {
|
|
430
|
+
id: string;
|
|
431
|
+
playerId: string;
|
|
432
|
+
url: string;
|
|
433
|
+
events: WebhookEventType[];
|
|
434
|
+
active: boolean;
|
|
435
|
+
createdAt: number;
|
|
436
|
+
lastTriggeredAt: number | null;
|
|
437
|
+
failCount: number;
|
|
438
|
+
}
|
|
439
|
+
export type AdvancedOrderType = 'conditional' | 'time_weighted';
|
|
440
|
+
export interface ConditionalOrder {
|
|
441
|
+
id: string;
|
|
442
|
+
playerId: string;
|
|
443
|
+
zoneId: string;
|
|
444
|
+
resource: Resource;
|
|
445
|
+
side: 'buy' | 'sell';
|
|
446
|
+
triggerPrice: number;
|
|
447
|
+
quantity: number;
|
|
448
|
+
condition: 'price_below' | 'price_above';
|
|
449
|
+
status: 'active' | 'triggered' | 'cancelled';
|
|
450
|
+
createdAt: number;
|
|
451
|
+
}
|
|
452
|
+
export interface TimeWeightedOrder {
|
|
453
|
+
id: string;
|
|
454
|
+
playerId: string;
|
|
455
|
+
zoneId: string;
|
|
456
|
+
resource: Resource;
|
|
457
|
+
side: 'buy' | 'sell';
|
|
458
|
+
price: number;
|
|
459
|
+
totalQuantity: number;
|
|
460
|
+
remainingQuantity: number;
|
|
461
|
+
quantityPerTick: number;
|
|
462
|
+
status: 'active' | 'completed' | 'cancelled';
|
|
463
|
+
createdAt: number;
|
|
464
|
+
}
|
|
465
|
+
export interface GarrisonUpgrade {
|
|
466
|
+
level: number;
|
|
467
|
+
defense: number;
|
|
468
|
+
raidResist: number;
|
|
469
|
+
maintenance: number;
|
|
470
|
+
}
|
|
471
|
+
export declare const GARRISON_LEVELS: GarrisonUpgrade[];
|
|
472
|
+
export type DiplomacyStatus = 'allied' | 'neutral' | 'war' | 'nap';
|
|
473
|
+
export interface DiplomacyRelation {
|
|
474
|
+
factionId: string;
|
|
475
|
+
targetFactionId: string;
|
|
476
|
+
status: DiplomacyStatus;
|
|
477
|
+
proposedBy: string;
|
|
478
|
+
acceptedBy: string | null;
|
|
479
|
+
createdAt: number;
|
|
480
|
+
}
|
|
481
|
+
export interface AuditLogEntry {
|
|
482
|
+
id: string;
|
|
483
|
+
factionId: string;
|
|
484
|
+
playerId: string;
|
|
485
|
+
action: string;
|
|
486
|
+
details: Record<string, unknown>;
|
|
487
|
+
tick: number;
|
|
488
|
+
timestamp: Date;
|
|
489
|
+
}
|