ogamejs 3.0.0 → 4.0.1

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 (93) hide show
  1. package/README.md +492 -228
  2. package/package.json +51 -4
  3. package/src/buildings/buildTime.js +25 -0
  4. package/src/buildings/crystal.js +18 -36
  5. package/src/buildings/deut.js +19 -34
  6. package/src/buildings/fusion-reactor.js +19 -34
  7. package/src/buildings/index.js +11 -0
  8. package/src/buildings/info.js +42 -0
  9. package/src/buildings/infocompte.js +116 -57
  10. package/src/buildings/metal.js +13 -28
  11. package/src/buildings/production.js +150 -0
  12. package/src/buildings/solar-plant.js +13 -21
  13. package/src/buildings/storage.js +59 -0
  14. package/src/cost.js +70 -0
  15. package/src/fleets/combat.js +291 -0
  16. package/src/fleets/distance.js +39 -0
  17. package/src/fleets/flight.js +105 -0
  18. package/src/fleets/getDebris.js +11 -10
  19. package/src/fleets/index.js +12 -0
  20. package/src/fleets/speed.js +108 -0
  21. package/src/i18n.js +43 -0
  22. package/src/index.js +12 -1
  23. package/src/models/buildings.js +285 -25
  24. package/src/models/destroyable.js +216 -110
  25. package/src/models/research.js +169 -0
  26. package/src/research/index.js +9 -0
  27. package/src/research/researchTime.js +25 -0
  28. package/src/trades/parseRate.js +7 -2
  29. package/src/types.js +146 -0
  30. package/types/buildings/buildTime.d.ts +16 -0
  31. package/types/buildings/buildTime.d.ts.map +1 -0
  32. package/types/buildings/crystal.d.ts +12 -0
  33. package/types/buildings/crystal.d.ts.map +1 -0
  34. package/types/buildings/deut.d.ts +12 -0
  35. package/types/buildings/deut.d.ts.map +1 -0
  36. package/types/buildings/fusion-reactor.d.ts +15 -0
  37. package/types/buildings/fusion-reactor.d.ts.map +1 -0
  38. package/types/buildings/index.d.ts +27 -0
  39. package/types/buildings/index.d.ts.map +1 -0
  40. package/types/buildings/info.d.ts +17 -0
  41. package/types/buildings/info.d.ts.map +1 -0
  42. package/types/buildings/infocompte.d.ts +41 -0
  43. package/types/buildings/infocompte.d.ts.map +1 -0
  44. package/types/buildings/metal.d.ts +11 -0
  45. package/types/buildings/metal.d.ts.map +1 -0
  46. package/types/buildings/production.d.ts +78 -0
  47. package/types/buildings/production.d.ts.map +1 -0
  48. package/types/buildings/solar-plant.d.ts +10 -0
  49. package/types/buildings/solar-plant.d.ts.map +1 -0
  50. package/types/buildings/storage.d.ts +29 -0
  51. package/types/buildings/storage.d.ts.map +1 -0
  52. package/types/cost.d.ts +25 -0
  53. package/types/cost.d.ts.map +1 -0
  54. package/types/fleets/combat.d.ts +57 -0
  55. package/types/fleets/combat.d.ts.map +1 -0
  56. package/types/fleets/distance.d.ts +13 -0
  57. package/types/fleets/distance.d.ts.map +1 -0
  58. package/types/fleets/flight.d.ts +57 -0
  59. package/types/fleets/flight.d.ts.map +1 -0
  60. package/types/fleets/getDebris.d.ts +12 -0
  61. package/types/fleets/getDebris.d.ts.map +1 -0
  62. package/types/fleets/index.d.ts +18 -0
  63. package/types/fleets/index.d.ts.map +1 -0
  64. package/types/fleets/speed.d.ts +54 -0
  65. package/types/fleets/speed.d.ts.map +1 -0
  66. package/types/i18n.d.ts +23 -0
  67. package/types/i18n.d.ts.map +1 -0
  68. package/types/index.d.ts +1316 -0
  69. package/types/index.d.ts.map +1 -0
  70. package/types/models/buildings.d.ts +373 -0
  71. package/types/models/buildings.d.ts.map +1 -0
  72. package/types/models/destroyable.d.ts +695 -0
  73. package/types/models/destroyable.d.ts.map +1 -0
  74. package/types/models/research.d.ts +262 -0
  75. package/types/models/research.d.ts.map +1 -0
  76. package/types/research/index.d.ts +8 -0
  77. package/types/research/index.d.ts.map +1 -0
  78. package/types/research/researchTime.d.ts +16 -0
  79. package/types/research/researchTime.d.ts.map +1 -0
  80. package/types/trades/crystal.d.ts +6 -0
  81. package/types/trades/crystal.d.ts.map +1 -0
  82. package/types/trades/deut.d.ts +6 -0
  83. package/types/trades/deut.d.ts.map +1 -0
  84. package/types/trades/index.d.ts +12 -0
  85. package/types/trades/index.d.ts.map +1 -0
  86. package/types/trades/metal.d.ts +6 -0
  87. package/types/trades/metal.d.ts.map +1 -0
  88. package/types/trades/parseRate.d.ts +2 -0
  89. package/types/trades/parseRate.d.ts.map +1 -0
  90. package/types/trades/utils.d.ts +2 -0
  91. package/types/trades/utils.d.ts.map +1 -0
  92. package/types/types.d.ts +449 -0
  93. package/types/types.d.ts.map +1 -0
package/README.md CHANGED
@@ -1,235 +1,499 @@
1
- # A library for Ogame
2
-
3
- ## Introduction
4
-
5
- I always wanted an Ogame calculator that suited me, all those I could see online are, either too old in graphic terms, or it didn't work, it was calculating badly the number of resources to send for each exchange...
6
-
7
- So I decided to write a library in Javascript to create accessible methods to perform all calculations in a simple way for anyone who wants to create a graphical interface.
8
-
9
- ## Api documentation
10
-
11
- All available methods will be listed below
12
-
13
- ### Building
14
-
15
- * `getCrystalMine` Return information about the crystal mine given a specific level
16
-
17
- ``` javascript
18
- /**
19
- *
20
- * Return information about the crystal mine given a specific level
21
- * @param {object} mine The crystal mine base information
22
- * @param {number} targetLevel the crystal mine target level
23
- * @param {number} pos pos 1/2/3 have a 15/10/5%
24
- * @param {number} universeSpeed production factor is increased for some universe
25
- * @returns {Object} informations about the crystal mine at this specific level
26
- */
27
- function getCrystalMine(mine, targetLevel, pos, universeSpeed = 1) {
28
- @return {
29
- crystal: Number,
30
- deut: Number,
31
- energy: Number,
32
- metal: Number,
33
- production: Number
34
- }
35
- }
36
- ```
37
-
38
- * `getDeutSynth` Return information about the deuterium synth given a specific level
39
-
40
- ``` javascript
41
- /**
42
- *
43
- * Return information about the deuterium synth given a specific level
44
- * @param {object} mine The deut synth base information
45
- * @param {number} targetLevel the deuterieum synth target level
46
- * @param {number} avg planet average temperature - The lower the higher the prod is
47
- * @param {number} universeSpeed production factor is increased for some universe
48
- * @returns {Object} informations about the deut synth at this specific level
49
- */
50
- function getDeutSynth(mine, targetLevel, avg, universeSpeed = 1) {
51
- @return {
52
- crystal: Number,
53
- deut: Number,
54
- energy: Number,
55
- metal: Number,
56
- production: Number
57
- }
58
- }
59
- ```
60
-
61
- * `getMetalMine` Return information about the metal mine given a specific level
62
-
63
- ``` javascript
64
- /**
65
- *
66
- * Return information about the metal mine given a specific level
67
- * @param {object} mine The metal mine base information
68
- * @param {number} targetLevel
69
- * @param {number} universeSpeed production factor is increased for some universe
70
- * @returns {Object} informations about the metal mine at this specific level
71
- */
72
- function getMetalMine(mine, targetLevel, universeSpeed = 1) {
73
- @return {
74
- crystal: Number,
75
- deut: Number,
76
- energy: Number,
77
- metal: Number,
78
- production: Number
79
- }
80
- }
81
- ```
82
-
83
- * `getSolarPlant` Return information about the solar plant given a specific level
84
-
85
- ``` javascript
86
- /**
87
- *
88
- * Return information about the solar plant given a specific level
89
- * @param {object} solarPlant The solarPlant base information
90
- * @param {number} targetLevel
91
- * @returns {Object} informations about the solar plant at this specific level
92
- */
93
- function getSolarPlant(solarPlant, targetLevel) {
94
- @return {
95
- crystal: Number,
96
- deut: Number,
97
- energy: Number,
98
- metal: Number,
99
- production: Number
100
- }
101
- }
102
- ```
103
-
104
- * `getFusionReactor` Return information about the fusion reactor given a specific level
105
-
106
- ``` javascript
107
- /**
108
- *
109
- * Return information about the fusion reactor given a specific level
110
- * @param {object} reactor The fusion react base information
111
- * @param {number} targetLevel
112
- * @param {number} energyTech The technology energy level
113
- * @param {number} universeSpeed production factor is increased for some universe
114
- * @returns {Object} informations about the fusion reactor at this specific level
115
- */
116
-
117
- function getFusionReactor(reactor, targetLevel, energyTech, universeSpeed = 1) {
118
- @return {
119
- crystal: Number,
120
- deut: Number,
121
- energy: Number,
122
- metal: Number,
123
- production: Number
124
- }
125
- }
126
- ```
127
-
128
- * `parseInfoCompteData` Return the number of debris generated
129
-
130
- ``` javascript
131
- /**
132
- *
133
- * Return information about the crystal mine given a specific level
134
- * @param {object} data The infocompte bb-code
135
- * @returns {Object} The parsed JSON object of infocompte
136
- */
137
- function parseInfoCompteData(data) {
138
- @return {
139
- planet: String,
140
- metal: String,
141
- crystal: String,
142
- deut: String,
143
- temperature: Number,
144
- }
145
- }
146
- ```
147
-
148
- ### Fleet
149
-
150
- * `getDebris` Return the number of debris generated
151
-
152
- ``` javascript
153
- /**
154
- *
155
- * Returns the number of debris generated
156
- * @param {number} shipId The ship identifier
157
- * @param {number} number The number of ship
158
- * @param {number} factor The universe debris factor
159
- * @return {Object} The debris generated
160
- */
161
- getDebris(ship, number, facror) {
162
- @return {
163
- metal: Number,
164
- crystal: Number,
165
- }
166
- }
167
- ```
168
-
169
- ### Trader
170
-
171
- * `sellDeut` Exchange of deuterium against `Metal` and `Crystal`
172
-
173
- ``` javascript
174
- // All params are optionnals
175
- // values placed here will be used if not specified
176
- sellDeut(deut = 0, percentM = 60, percentC = 40, rate = '2:1.5:1') {
177
- @return {
178
- metal: Number,
179
- crystal: Number
180
- }
181
- }
182
- ```
183
-
184
- * `sellMetal` Exchange of deuterium against `Metal` and `Crystal`
185
-
186
- ``` javascript
187
- // All params are optionnals
188
- // values placed here will be used if not specified
189
- sellMetal(metal = 0, percentD = 40, percentC = 60, rate = '2:1.5:1') {
190
- @return {
191
- deut: Number,
192
- metal: Number
193
- }
194
- }
195
- ```
196
-
197
- * `sellCrystal` Exchange of deuterium against `Metal` and `Crystal`
198
-
199
- ``` javascript
200
- // All params are optionnals
201
- // values placed here will be used if not specified
202
- sellCrystal(crystal = 0, percentD = 40, percentM = 60, rate = '2:1.5:1') {
203
- @return {
204
- deut: Number,
205
- metal: Number
206
- }
207
- }
208
- ```
209
-
210
- * `parseRate()` Parse the exchange rate
211
-
212
- ``` javascript
213
- // All params are optionnals
214
- // values placed here will be used if not specified
215
- parseRate(rate = '2:1.5:1') {
216
- @return {
217
- rateDeut: Number,
218
- rateMetal: Number,
219
- rateCrystal: Number
220
- };
221
- }
1
+ # ogamejs
2
+
3
+ A small, dependency-free JavaScript library that reproduces [OGame](https://gameforge.com/en-GB/play/ogame)'s formulas: building and research costs, production and bonuses, build and research times, storage capacity, flight times and fuel, combat, and the marketplace exchange rates.
4
+
5
+ It ships only the math — no UI, no state, no network — so you can build your own calculator, bot, or dashboard on top of it.
6
+
7
+ ## Requirements
8
+
9
+ - **Node.js >= 24**
10
+ - The package is **ESM-only** (use `import`, not `require`)
11
+ - TypeScript declarations are bundled; no `@types` package needed
12
+
13
+ ## Installation
14
+
15
+ ```bash
16
+ npm install ogamejs
17
+ ```
18
+
19
+ > Upgrading from 3.x? See [MIGRATION.md](./MIGRATION.md) — 4.0 renames several model fields and changes the calculator signatures.
20
+
21
+ ## Quick start
22
+
23
+ ```javascript
24
+ import Ogame from 'ogamejs';
25
+
26
+ const { Buildings, Destroyable, Research } = Ogame.models;
27
+
28
+ // A level 10 metal mine on a x5 universe
29
+ Ogame.Building.getMetalMine(Buildings[1], 10, 5);
30
+ // → { metal: 2306, crystal: 576, deuterium: 0, energyCost: 0,
31
+ // energyConsumption: 259, deuteriumConsumption: 0, production: 3890 }
32
+
33
+ // What a whole planet actually makes per hour
34
+ Ogame.Building.getPlanetProduction(
35
+ { metalMine: 30, crystalMine: 26, deutSynth: 24, position: 8, temperature: -23, universeSpeed: 6 },
36
+ { plasmaTech: 15, geologist: true },
37
+ );
38
+ // → { metal: 117962, crystal: 44673, deuterium: 23668, energyConsumption: 13059, bonus: {...} }
39
+
40
+ // A raid: how long, and how much deuterium
41
+ Ogame.Fleets.getTrip(
42
+ [{ ship: Destroyable[12], count: 250 }, { ship: Destroyable[9], count: 40 }],
43
+ { galaxy: 1, system: 1, position: 8 },
44
+ { galaxy: 3, system: 42, position: 6 },
45
+ { drives: { combustion: 16, hyperspace: 12 } },
46
+ );
47
+ // → { distance: 40000, fleetSpeed: 19500, duration: 1595, fuel: 258287,
48
+ // cargo: 6650000, cargoAfterFuel: 6391713 }
49
+
50
+ // And how it would go
51
+ Ogame.Fleets.simulateCombat(
52
+ { fleet: [{ ship: Destroyable[1], count: 500 }], techs: { weapons: 14, shielding: 12, armour: 15 } },
53
+ { fleet: [{ ship: Destroyable[201], count: 200 }], techs: { weapons: 10 } },
54
+ { seed: 42 },
55
+ );
56
+ // → { winner: 'attacker', rounds: 3, seed: 42, attacker: {...}, defender: {...}, debris: {...} }
57
+
58
+ // Sell 10 000 deuterium at the default 2:1.5:1 rate
59
+ Ogame.Trader.sellDeut(10000);
60
+ // → { metal: 12000, crystal: 6000 }
61
+ ```
62
+
63
+ `Ogame` is the default export and exposes these namespaces:
64
+
65
+ | Namespace | Purpose |
66
+ | ---------------- | -------------------------------------------------------------- |
67
+ | `Ogame.Building` | Building costs, production, build times, storage |
68
+ | `Ogame.Research` | Technology costs and research times |
69
+ | `Ogame.Fleets` | Distance, speed, flight time, fuel, debris, combat |
70
+ | `Ogame.Trader` | Marketplace exchange helpers |
71
+ | `Ogame.i18n` | Localised names (`getName`, `findByName`) |
72
+ | `Ogame.models` | Reference data (`Buildings`, `Destroyable`, `Research`) |
73
+
74
+ Each namespace is also importable on its own:
75
+
76
+ ```javascript
77
+ import Building from 'ogamejs/buildings';
78
+ import Fleets from 'ogamejs/fleets';
79
+ import Research from 'ogamejs/research';
80
+ import Trader from 'ogamejs/trades';
81
+ import BUILDINGS from 'ogamejs/models/buildings';
82
+ import { getName } from 'ogamejs/i18n';
83
+ ```
84
+
85
+ ## Conventions
86
+
87
+ A few rules hold across the whole library, so you rarely have to check the docs twice.
88
+
89
+ **Calculators take a model entry**, never its `base`. `getMetalMine(Buildings[1], 10)`, not `Buildings[1].base` — the entry carries the cost `factor` the calculator needs.
90
+
91
+ **Levels start at 1.** A cost is the cost *of* that level, not the sum up to it. Anything below 1 throws.
92
+
93
+ **Energy and deuterium flows are named after what they are.** No field means two things depending on the building:
94
+
95
+ | Field | Meaning |
96
+ | ---------------------- | ---------------------------------------------- |
97
+ | `metal`/`crystal`/`deuterium` | Resources paid to build it |
98
+ | `energyCost` | Energy paid to build it (Terraformer, Space Dock, Graviton) |
99
+ | `energyConsumption` | Energy it consumes once built (the mines) |
100
+ | `deuteriumConsumption` | Deuterium it burns once built (Fusion Reactor) |
101
+ | `production` | What it produces, resources or energy |
102
+
103
+ **Every building calculator returns the same seven fields**, `0` where one does not apply. So you can read `production` off a result without knowing which building produced it.
104
+
105
+ **Times are in seconds**, production is per hour.
106
+
107
+ ## API reference
108
+
109
+ ### `Ogame.Building`
110
+
111
+ #### `getMetalMine(building, targetLevel, universeSpeed = 1)`
112
+
113
+ ```javascript
114
+ Ogame.Building.getMetalMine(Ogame.models.Buildings[1], 10, 5);
115
+ // → { metal: 2306, crystal: 576, deuterium: 0, energyCost: 0,
116
+ // energyConsumption: 259, deuteriumConsumption: 0, production: 3890 }
117
+ ```
118
+
119
+ #### `getCrystalMine(building, targetLevel, pos, universeSpeed = 1)`
120
+
121
+ `pos` is the planet position; 1, 2 and 3 produce 30%, 22.5% and 15% more crystal.
122
+
123
+ ```javascript
124
+ Ogame.Building.getCrystalMine(Ogame.models.Buildings[2], 10, 1, 5);
125
+ // → { ..., energyConsumption: 259, production: 3371 }
126
+ ```
127
+
128
+ #### `getDeutSynth(building, targetLevel, avg, universeSpeed = 1)`
129
+
130
+ `avg` is the planet's average temperature — the colder the planet, the more deuterium.
131
+
132
+ ```javascript
133
+ Ogame.Building.getDeutSynth(Ogame.models.Buildings[3], 10, 40, 5);
134
+ // → { ..., energyConsumption: 518, production: 1554 }
135
+ ```
136
+
137
+ #### `getSolarPlant(building, targetLevel)`
138
+
139
+ ```javascript
140
+ Ogame.Building.getSolarPlant(Ogame.models.Buildings[4], 10);
141
+ // → { ..., production: 518 } // production is energy here
142
+ ```
143
+
144
+ #### `getFusionReactor(building, targetLevel, energyTech, universeSpeed = 1)`
145
+
146
+ `production` is the energy delivered, `deuteriumConsumption` the deuterium burned per hour.
147
+
148
+ ```javascript
149
+ Ogame.Building.getFusionReactor(Ogame.models.Buildings[5], 10, 12, 5);
150
+ // → { metal: 178523, crystal: 71409, deuterium: 35704, energyCost: 0,
151
+ // energyConsumption: 0, deuteriumConsumption: 1296, production: 1442 }
222
152
  ```
223
153
 
224
- ### Models
154
+ #### `getBuildingCost(entry, targetLevel)`
155
+
156
+ Cost of **any** building at a given level, from its `factor`. Also accepts `Research` entries — `Ogame.Research.getResearchCost` is this very function.
157
+
158
+ ```javascript
159
+ Ogame.Building.getBuildingCost(Ogame.models.Buildings[21], 12); // Shipyard
160
+ // → { metal: 819200, crystal: 409600, deuterium: 204800, energyCost: 0 }
161
+ ```
162
+
163
+ #### `getBuildTime(building, targetLevel, roboticsLevel = 0, naniteLevel = 0, universeSpeed = 1)`
164
+
165
+ `(metal + crystal) / (2500 × (1 + robotics) × 2 ** nanites)` hours, divided by the universe economy speed.
166
+
167
+ ```javascript
168
+ Ogame.Building.getBuildTime(Ogame.models.Buildings[15], 4, 10, 3, 6); // Nanite factory
169
+ // → 32727
170
+ ```
171
+
172
+ #### `getStorage(storage, targetLevel)`
173
+
174
+ Cost **and** capacity of a storage building (`Buildings[22]`, `[23]` or `[24]`).
175
+
176
+ ```javascript
177
+ Ogame.Building.getStorage(Ogame.models.Buildings[22], 12);
178
+ // → { metal: 2048000, crystal: 0, deuterium: 0, energyCost: 0, capacity: 18005000 }
179
+ ```
180
+
181
+ #### `getStorageCapacity(level)` / `getStorageLevelFor(amount)`
182
+
183
+ `5000 × ⌊2.5 × e^(20 × level / 33)⌋`, and its inverse.
184
+
185
+ ```javascript
186
+ Ogame.Building.getStorageCapacity(0); // → 10000, the free capacity
187
+ Ogame.Building.getStorageLevelFor(1_000_000); // → 8
188
+ ```
189
+
190
+ #### `getProductionBonus(options = {})`
191
+
192
+ The multiplier each resource gets. Bonuses **add up**, they do not compound.
193
+
194
+ ```javascript
195
+ Ogame.Building.getProductionBonus({
196
+ plasmaTech: 15, // +1% metal, +0.66% crystal, +0.33% deuterium per level
197
+ geologist: true, // +10% on all three
198
+ collectorClass: true, // +25% on all three
199
+ items: { metal: 0.3 },// booster items, as fractions
200
+ });
201
+ // → { metal: 1.8, crystal: 1.449, deuterium: 1.3995 }
202
+ ```
203
+
204
+ #### `getPlanetProduction(planet, bonuses = {})`
205
+
206
+ The hourly production of a whole planet: raw mine output, then the bonuses, then the flat planet income (30 metal and 15 crystal per hour × universe speed) which no bonus touches.
207
+
208
+ ```javascript
209
+ Ogame.Building.getPlanetProduction(
210
+ { metalMine: 30, crystalMine: 26, deutSynth: 24, position: 8, temperature: -23, universeSpeed: 6 },
211
+ { plasmaTech: 15, geologist: true },
212
+ );
213
+ // → { metal: 117962, crystal: 44673, deuterium: 23668,
214
+ // energyConsumption: 13059, bonus: { metal: 1.25, ... } }
215
+ ```
216
+
217
+ A mine at level 0 simply produces nothing. Pass `energyEfficiency` (0 to 1) to model a planet running in an energy deficit — `energyConsumption` tells you what the mines ask for, so you can work it out against your plants.
218
+
219
+ #### `parseInfoCompteData(data, options = {})`
220
+
221
+ Parses the BBCode of an OGame "infocompte" report. The language comes from the `s165-fr` header.
222
+
223
+ ```javascript
224
+ const report = Ogame.Building.parseInfoCompteData(bbcodeString);
225
+ // → {
226
+ // planets: [{ planet: 'Planète 01', metal: 36, crystal: 31, deut: 31, temperature: -94 }, ...],
227
+ // production: { hourly: {...}, daily: {...}, weekly: {...} },
228
+ // points: { metal, crystal, deut, total },
229
+ // plasma: Number, universe: Number, lang: String,
230
+ // }
231
+ ```
232
+
233
+ French is the reference language; English is best effort. For anything else, pass your own labels — see `LOCALES` in [`src/buildings/infocompte.js`](./src/buildings/infocompte.js):
234
+
235
+ ```javascript
236
+ Ogame.Building.parseInfoCompteData(bbcode, { locale: 'fr' });
237
+ Ogame.Building.parseInfoCompteData(bbcode, { labels: { planet: 'Planet', /* ... */ } });
238
+ ```
239
+
240
+ ### `Ogame.Research`
241
+
242
+ #### `getResearchCost(research, targetLevel)`
243
+
244
+ ```javascript
245
+ Ogame.Research.getResearchCost(Ogame.models.Research[124], 5); // Astrophysics
246
+ // → { metal: 37600, crystal: 75100, deuterium: 37600, energyCost: 0 }
247
+ ```
248
+
249
+ Astrophysics has the game's only non-integer factor (1.75) and its costs are rounded up to the nearest hundred; Graviton is paid entirely in `energyCost`.
250
+
251
+ #### `getResearchTime(research, targetLevel, labLevel = 0, researchSpeed = 1)`
252
+
253
+ `(metal + crystal) / (1000 × (1 + labs))` hours, divided by the universe research speed. `labLevel` is the Research Lab level, or the sum of every connected lab once the Intergalactic Research Network is up.
254
+
255
+ ```javascript
256
+ Ogame.Research.getResearchTime(Ogame.models.Research[122], 10, 12, 2); // Plasma
257
+ // → 425354
258
+ ```
259
+
260
+ ### `Ogame.Fleets`
261
+
262
+ #### `getDistance(origin, target)`
263
+
264
+ Coordinates are `{ galaxy, system, position }`. The scale is not linear.
265
+
266
+ ```javascript
267
+ Ogame.Fleets.getDistance({ galaxy: 1, system: 1, position: 1 }, { galaxy: 4, system: 1, position: 1 });
268
+ // → 60000
269
+ ```
270
+
271
+ #### `getShipSpeed(ship, drives = {})` / `getFleetSpeed(fleet, drives = {})` / `getActiveDrive(ship, drives = {})`
272
+
273
+ `drives` holds the `{ combustion, impulse, hyperspace }` levels. Each level adds 10%, 20% or 30% of the ship's base speed, and only the drive the ship actually flies on counts.
274
+
275
+ A few ships switch to a better drive once the matching technology is high enough. `getActiveDrive` tells you which one is in use — and since a switch also changes the fuel burn, it reports that too.
276
+
277
+ ```javascript
278
+ Ogame.Fleets.getShipSpeed(Ogame.models.Destroyable[11], { combustion: 6 });
279
+ // → 8000
280
+ Ogame.Fleets.getActiveDrive(Ogame.models.Destroyable[11], { combustion: 6, impulse: 5 });
281
+ // → { drive: 'impulse', speed: 20000, fuelConsumption: 20 }
282
+
283
+ // A fleet moves at the speed of its slowest ship
284
+ Ogame.Fleets.getFleetSpeed([{ ship: Ogame.models.Destroyable[1], count: 100 }], { combustion: 10 });
285
+ // → 25000
286
+ ```
287
+
288
+ #### `getFlightTime(distance, fleetSpeed, speedPercent = 100, universeFleetSpeed = 1)`
289
+
290
+ `(10 + 35000 / speedPercent × √(distance × 10 / fleetSpeed)) / universeFleetSpeed`, in seconds.
291
+
292
+ #### `getFuelConsumption(fleet, distance, speedPercent = 100, drives = {})`
293
+
294
+ `1 + round(Σ(consumption × count) × distance / 35000 × (speedPercent / 100 + 1)²)`, in deuterium.
295
+
296
+ #### `getTrip(fleet, origin, target, options = {})`
297
+
298
+ The three above in one call, plus the cargo maths.
299
+
300
+ ```javascript
301
+ Ogame.Fleets.getTrip(
302
+ [{ ship: Ogame.models.Destroyable[12], count: 250 }, { ship: Ogame.models.Destroyable[9], count: 40 }],
303
+ { galaxy: 1, system: 1, position: 8 },
304
+ { galaxy: 3, system: 42, position: 6 },
305
+ { drives: { combustion: 16, hyperspace: 12 }, speedPercent: 100, roundTrip: false },
306
+ );
307
+ // → { distance: 40000, fleetSpeed: 19500, duration: 1595, fuel: 258287,
308
+ // cargo: 6650000, cargoAfterFuel: 6391713 }
309
+ ```
310
+
311
+ `cargoAfterFuel` is what is left once the deuterium is loaded — the number that decides whether a raid is worth flying.
312
+
313
+ #### `getDebris(ship, number, factor, deuteriumFactor = 0)`
314
+
315
+ The debris field left by destroyed units. `factor` is the universe debris factor, `deuteriumFactor` its deuterium debris factor (`0` on most universes).
316
+
317
+ ```javascript
318
+ Ogame.Fleets.getDebris(Ogame.models.Destroyable[1], 100, 0.3);
319
+ // → { metal: 90000, crystal: 30000, deuterium: 0 }
320
+ ```
321
+
322
+ #### `simulateCombat(attacker, defender, options = {})`
323
+
324
+ A full battle: up to six rounds, every unit fires once per round at a random enemy, rapid fire grants extra shots, shots below 1% of the target's shield bounce off, shields come back every round, and a unit under 70% hull may explode at the end of the round.
325
+
326
+ ```javascript
327
+ const battle = Ogame.Fleets.simulateCombat(
328
+ {
329
+ fleet: [{ ship: Ogame.models.Destroyable[1], count: 500 }],
330
+ techs: { weapons: 14, shielding: 12, armour: 15 },
331
+ },
332
+ {
333
+ fleet: [{ ship: Ogame.models.Destroyable[201], count: 200 }],
334
+ techs: { weapons: 10, shielding: 10, armour: 10 },
335
+ },
336
+ { seed: 42, debrisFactor: 0.3 },
337
+ );
338
+ // → {
339
+ // winner: 'attacker' | 'defender' | 'draw',
340
+ // rounds: Number,
341
+ // seed: Number,
342
+ // attacker: { survivors: [{ ship, count }], losses: [{ ship, count }] },
343
+ // defender: { survivors: [...], losses: [...] },
344
+ // debris: { metal, crystal, deuterium },
345
+ // }
346
+ ```
347
+
348
+ A battle is **random**, so one run is one possible outcome. Pass a `seed` to replay the exact same fight; average several seeds to get a feel for the likely result:
349
+
350
+ ```javascript
351
+ const runs = Array.from({ length: 100 }, (_, seed) => simulateCombat(a, d, { seed }));
352
+ const winRate = runs.filter((run) => run.winner === 'attacker').length / runs.length;
353
+ ```
354
+
355
+ Destroyed defenses are left out of the debris field unless you pass `defenseDebris: true`. Defense repair after a battle is not modelled.
356
+
357
+ ### `Ogame.Trader`
358
+
359
+ Converts one resource into the two others. Rates are a `metal:crystal:deut` string (default `'2:1.5:1'`), and the percentages split the traded amount between the two target resources. **All parameters are optional.**
360
+
361
+ ```javascript
362
+ Ogame.Trader.sellDeut(10000); // → { metal: 12000, crystal: 6000 }
363
+ Ogame.Trader.sellMetal(10000); // → { deut, crystal }
364
+ Ogame.Trader.sellCrystal(10000); // → { deut, metal }
365
+ ```
366
+
367
+ #### `parseRate(rate = '2:1.5:1', type = 'deut')`
368
+
369
+ Normalizes a rate relative to a reference resource (`'metal'`, `'crystal'` or `'deut'`). Throws if the rate is malformed.
370
+
371
+ ```javascript
372
+ Ogame.Trader.parseRate('3:2:1', 'deut');
373
+ // → { rateMetal: 3, rateCrystal: 2, rateDeut: 1 }
374
+ ```
375
+
376
+ The reference term always comes back as `1`, so a rate and any multiple of it are the same rate, whichever resource you sell:
377
+
378
+ ```javascript
379
+ Ogame.Trader.parseRate('4:3:2', 'deut');
380
+ // → { rateMetal: 2, rateCrystal: 1.5, rateDeut: 1 } — same as '2:1.5:1'
381
+ ```
382
+
383
+ ### `Ogame.i18n`
384
+
385
+ Every model entry carries `names: { en, fr }`.
386
+
387
+ ```javascript
388
+ Ogame.i18n.getName(Ogame.models.Buildings[22]); // → 'Metal Storage'
389
+ Ogame.i18n.getName(Ogame.models.Buildings[22], 'fr'); // → 'Hangar de métal'
390
+
391
+ // Case and accent insensitive, matches any supported language
392
+ Ogame.i18n.findByName(Ogame.models.Destroyable, 'etoile de la mort').ogameId; // → 214
393
+ ```
394
+
395
+ ### `Ogame.models`
396
+
397
+ Frozen reference datasets you feed into the calculators.
398
+
399
+ - **`Buildings`** — the 19 buildings (mines, plants, storages, facilities, moon buildings).
400
+ See [`src/models/buildings.js`](./src/models/buildings.js).
401
+ - **`Destroyable`** — the 27 ships, defenses and missiles, with structure, shield, attack, speed, cargo, fuel, drive and rapid-fire table.
402
+ See [`src/models/destroyable.js`](./src/models/destroyable.js).
403
+ - **`Research`** — the 16 technologies.
404
+ See [`src/models/research.js`](./src/models/research.js).
405
+ - **`ATTRIBUTES`** — the `TYPES`, `CATEGORIES` and `DRIVES` enums used by `Destroyable`.
406
+
407
+ ```javascript
408
+ Ogame.models.Buildings[22];
409
+ // → {
410
+ // ogameId: 22,
411
+ // names: { en: 'Metal Storage', fr: 'Hangar de métal' },
412
+ // category: 'resources',
413
+ // factor: 2,
414
+ // storage: 'metal',
415
+ // base: { metal, crystal, deuterium, energyCost,
416
+ // energyConsumption, deuteriumConsumption, production },
417
+ // }
418
+
419
+ Ogame.models.Destroyable[11];
420
+ // → {
421
+ // ogameId: 202,
422
+ // names: { en: 'Small Cargo', fr: 'Petit transporteur' },
423
+ // structure: 4000, shield: 10, attack: 5,
424
+ // speed: 5000, cargo: 5000, fuelConsumption: 10,
425
+ // drive: 'combustion',
426
+ // driveUpgrades: [{ drive: 'impulse', minLevel: 5, speed: 10000, fuelConsumption: 20 }],
427
+ // type: 'civil', category: 'ships',
428
+ // rapidFire: [{ target: 15, fire: 5 }, ...],
429
+ // cost: { metal: 2000, crystal: 2000, deuterium: 0 },
430
+ // }
431
+ ```
432
+
433
+ #### Ids
434
+
435
+ Ids 1–5 of `Buildings` and 1–302 of `Destroyable` are this library's own historical numbering. Every entry also carries **`ogameId`**, the id the game itself uses — that's the one to use when talking to OGame.
436
+
437
+ ```javascript
438
+ Ogame.models.Buildings[5].ogameId; // → 12 (Fusion Reactor)
439
+ Ogame.models.Destroyable[1].ogameId; // → 204 (Light Fighter)
440
+ Ogame.models.Destroyable[201].ogameId; // → 401 (Rocket Launcher)
441
+ ```
442
+
443
+ `rapidFire` targets are **library** ids, so they index straight into `Destroyable`.
444
+
445
+ ## TypeScript
446
+
447
+ Declarations are generated from the JSDoc and shipped in `types/`. Nothing to install:
448
+
449
+ ```typescript
450
+ import Ogame from 'ogamejs';
451
+ import BUILDINGS from 'ogamejs/models/buildings';
452
+
453
+ const cost = Ogame.Building.getBuildingCost(BUILDINGS[21], 12);
454
+ const total: number = cost.metal + cost.crystal + cost.deuterium;
455
+ ```
456
+
457
+ The shared types are importable too, for typing your own helpers:
458
+
459
+ ```typescript
460
+ import type { BuildingEntry, FleetEntry, Coordinates } from 'ogamejs/types';
461
+ ```
462
+
463
+ ## Known gaps
464
+
465
+ Stated plainly, so you know what you are not getting:
466
+
467
+ - **Lifeforms** (the 2021 expansion) are not modelled — neither their buildings nor their technologies. This is a data problem, not a code one: the tables run to roughly 120 entries and we would rather have them sourced than guessed.
468
+ - **Drive upgrade values** for the three ships that switch drive (small cargo, recycler, bomber) come from community tables rather than a first-party source. The speeds are solid; the post-switch fuel figures deserve an in-game check.
469
+ - **Defense repair** after a battle is not simulated, and neither are moon-creation odds.
470
+ - Officers other than the geologist, and alliance classes, are not modelled in the production bonuses.
471
+
472
+ ## Development
473
+
474
+ ```bash
475
+ npm install # install dependencies
476
+ npm test # run the test suite (Vitest)
477
+ npm run test:watch
478
+ npm run lint # ESLint (flat config)
479
+ npm run types # type check and write types/
480
+ ```
481
+
482
+ The library is written in native ESM and published straight from `src/` — the only build step is the declaration files, which `prepack` generates for you.
483
+
484
+ ## Releases
485
+
486
+ Releases are fully automated with [semantic-release](https://github.com/semantic-release/semantic-release), driven by [Conventional Commits](https://www.conventionalcommits.org/):
487
+
488
+ - pushes to `master` publish a stable release,
489
+ - pushes to `develop` publish a pre-release.
225
490
 
226
- Listed below are categories of Buildings / Ships / Defenses
491
+ The version, `CHANGELOG.md`, git tag, GitHub release and npm publish (via OIDC trusted publishing) are all handled by CI. Do not bump the version manually.
227
492
 
228
- #### Buildings
493
+ ## Contributing
229
494
 
230
- [Buildings](./src/models/buildings.js) Here you will find all listed Buildings in the game
231
-
232
- #### Destroyable
495
+ See [CONTRIBUTING.md](./CONTRIBUTING.md).
233
496
 
234
- [Destroyable](./src/models/destroyable.js) Here you will find all listed Ships / Defense in the game
497
+ ## License
235
498
 
499
+ [MIT](./LICENSE.md)