File size: 17,341 Bytes
7a2b109
 
 
 
 
 
 
 
 
 
 
 
 
17641e3
7a2b109
 
 
 
 
 
ed2403c
 
 
 
 
 
 
 
7a2b109
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
17641e3
7a2b109
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
17641e3
 
 
 
7a2b109
 
 
 
448e53e
 
 
312ca27
 
 
 
 
 
 
 
 
 
 
 
7a2b109
17641e3
7a2b109
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
15e011a
 
 
 
b50413b
7a2b109
 
448e53e
7a2b109
 
 
 
448e53e
 
 
 
 
 
 
 
 
7a2b109
17641e3
7a2b109
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
17641e3
7e2a606
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
/* ============================================================
   STELLAR FRONTIER: RTS β€” shared universe data
   Carried over verbatim from the original Stellar Frontier (turn-based)
   game's data.js: meta resources, factions, the commodity catalog,
   production recipes, and the 20 charted worlds. Pure data, no game
   logic, so it dropped in unchanged as the shared-flavor seed for this
   real-time game. See README.md for what else did (and didn't) carry
   over from the original repo.
   ============================================================ */

"use strict";

/* ---------- Meta resources (not cargo, shown in the top bar) ---------- */
export const META = {
  credits:   { name: "Credits",   ico: "πŸ’°" },
  fuel:      { name: "Fuel",      ico: "β›½" },
  tech:      { name: "Tech Pts",  ico: "πŸ”¬" },
  influence: { name: "Influence", ico: "πŸ›οΈ" },
};

/* ---------- Factions ----------
   Lore/UI flavor only (name/icon/color/blurb for the starmap's faction-space
   emblems) β€” NOT the playable-faction gameplay data. That's engine/factions.js's
   FACTIONS (id/name/short/blurb/traits), a separate, unrelated export. Named
   LORE_FACTIONS specifically so the two can never be confused for one another
   at an import site.
*/
export const LORE_FACTIONS = {
  core:      { name: "Core Authority",     ico: "βš–οΈ", color: "#3b82f6",
               desc: "Lawful government of the inner worlds. Hates smugglers." },
  miners:    { name: "Mining Guild",       ico: "⛏️", color: "#b45309",
               desc: "Controls ore, metals and heavy industry." },
  agri:      { name: "Agri-Combine",       ico: "🌾", color: "#16a34a",
               desc: "Feeds the sector; values relief and fair trade." },
  syndicate: { name: "Tech Syndicate",     ico: "πŸ”¬", color: "#8b5cf6",
               desc: "Masters of electronics, research and... discretion." },
  frontier:  { name: "Frontier Coalition", ico: "πŸ›°οΈ", color: "#06b6d4",
               desc: "Free traders of the rim. Friendly to smugglers." },
};

/* ---------- Commodity catalog (tiered) ----------
   tier:    Raw | Refined | Component | Finished | Luxury | Strategic
   base:    reference price
   extract: mine | forage | capture | exploit  (raw resources only)
   illegalAt: list of planet ids where carrying/selling is contraband
   hazard:  true if dangerous to carry without a Shielded Hold
*/
export const COM = {
  // ----- RAW -----
  ore:         { name: "Ore",            ico: "πŸͺ¨", tier: "Raw", base: 9,  extract: "mine" },
  crystals:    { name: "Crystals",       ico: "πŸ’Ž", tier: "Raw", base: 30, extract: "mine" },
  radioactives:{ name: "Radioactives",   ico: "☒️", tier: "Raw", base: 36, extract: "mine",
                 illegalAt: ["terra", "verdani"], hazard: true },
  ice:         { name: "Ice",            ico: "🧊", tier: "Raw", base: 8,  extract: "mine" },
  biomass:     { name: "Biomass",        ico: "🌿", tier: "Raw", base: 7,  extract: "forage" },
  spice:       { name: "Spice",          ico: "🌢️", tier: "Raw", base: 34, extract: "forage" },
  gas:         { name: "Helium-3",       ico: "🎈", tier: "Raw", base: 18, extract: "capture" },
  relics:      { name: "Relics",         ico: "🏺", tier: "Raw", base: 52, extract: "exploit",
                 illegalAt: ["terra", "verdani", "kybernet", "forge"], hazard: false },
  // ----- REFINED -----
  metals:      { name: "Metals",         ico: "⛓️", tier: "Refined", base: 22 },
  energy:      { name: "Energy Cells",   ico: "⚑", tier: "Refined", base: 9 },
  fuel:        { name: "Fuel",           ico: "β›½", tier: "Refined", base: 14, isFuel: true },
  chemicals:   { name: "Chemicals",      ico: "βš—οΈ", tier: "Refined", base: 16 },
  medicine:    { name: "Medicine",       ico: "πŸ’Š", tier: "Refined", base: 78 },
  // ----- COMPONENTS -----
  alloys:      { name: "Alloys",         ico: "πŸ”©", tier: "Component", base: 80 },
  electronics: { name: "Electronics",    ico: "πŸ–₯️", tier: "Component", base: 95 },
  // ----- FINISHED -----
  goods:       { name: "Consumer Goods", ico: "πŸ“¦", tier: "Finished", base: 130 },
  machinery:   { name: "Machinery",      ico: "βš™οΈ", tier: "Finished", base: 250 },
  weapons:     { name: "Weapons",        ico: "πŸ”«", tier: "Finished", base: 270,
                 illegalAt: ["terra", "verdani"], hazard: true },
  // ----- LUXURY / STRATEGIC -----
  luxury:      { name: "Luxury Goods",   ico: "πŸ’ ", tier: "Luxury", base: 220 },
  drones:      { name: "Combat Drones",  ico: "πŸ›Έ", tier: "Component", base: 140 },
  ai:          { name: "AI Cores",       ico: "🧠", tier: "Strategic", base: 380,
                 illegalAt: ["terra", "verdani"] },
  antimatter:  { name: "Antimatter",     ico: "πŸŒ€", tier: "Strategic", base: 420,
                 illegalAt: ["terra", "verdani", "kybernet", "forge", "glacius"], hazard: true },
  plasmatorp:  { name: "Plasma Torpedoes", ico: "πŸ’₯", tier: "Strategic", base: 460,
                 illegalAt: ["terra", "verdani", "kybernet", "forge", "glacius"], hazard: true },
};
export const COM_IDS = Object.keys(COM);
export const CARGO_IDS = COM_IDS.filter(id => !COM[id].isFuel); // everything except fuel uses the ship's cargo hold
export const STORE_IDS = CARGO_IDS.concat(["fuel"]);            // base/colony storage & the trade network also hold/move fuel
export const TIERS = ["Raw", "Refined", "Component", "Finished", "Luxury", "Strategic"];

/* ---------- Production recipes ----------
   out: commodity produced, qty: per-batch yield, in: input map, req: tech id
   kind: "refine" | "make" (for grouping); boosted differently by modules

   LIVE vs LEGACY: the Odyssey RTS wires factories to a subset of these by id
   (engine/entities.js BUILDINGS[].recipe β†’ smelt/alloy/chipfab/machine/antifab/
   plasmafab/aifab/chem/consumer, plus reactor/gasgen for power). 'chem'/'consumer'
   were promoted off this legacy list into the trade-industry branch (docs/
   improvement-proposals.md "Promote the legacy consumer-goods recipes into a
   trade-industry branch") β€” engine/techtree.js TECHS.chemistry/consumerfab gate
   engine/entities.js BUILDINGS.chemplant/fabricator, the exact "tech + wiring"
   promotion path this comment reserves. The rest are carried-over turn-based data
   and are still UNREACHABLE in the RTS: their `req` tokens (gasfuel, biotech,
   weapontech, dronetech) are NOT nodes in engine/techtree.js TECHS, no building
   runs them, and engine/industry.js never even reads `recipe.req`. Their outputs
   (weapons, drones, medicine, luxury) have no consumer or market listing. Treat
   anything not on the wired list above as legacy β€” do NOT assume `req` gates a
   new factory (it won't) without adding the tech + wiring.
*/
export const RECIPES = [
  { id: "smelt",   out: "metals",      qty: 2, in: { ore: 2, energy: 2 },                         kind: "refine" },
  { id: "biogen",  out: "energy",      qty: 2, in: { biomass: 2 },                                kind: "refine" },
  { id: "gasgen",  out: "energy",      qty: 3, in: { gas: 1 },                                    kind: "refine" },
  { id: "reactor", out: "energy",      qty: 5, in: { radioactives: 1 }, req: "reactors",          kind: "refine", reactor: true },
  { id: "crackice",out: "fuel",        qty: 2, in: { ice: 3 },                                    kind: "refine" },
  { id: "crackgas",out: "fuel",        qty: 2, in: { gas: 1 }, req: "gasfuel",                    kind: "refine" },
  { id: "chem",    out: "chemicals",   qty: 2, in: { biomass: 2, energy: 1 },                     kind: "refine" },
  { id: "medlab",  out: "medicine",    qty: 1, in: { spice: 1, chemicals: 1, energy: 1 }, req: "biotech", kind: "make" },
  { id: "alloy",   out: "alloys",      qty: 1, in: { metals: 2, energy: 2 }, req: "metallurgy",   kind: "make" },
  { id: "chipfab", out: "electronics", qty: 1, in: { crystals: 1, metals: 1, energy: 2 }, req: "electronics", kind: "make" },
  { id: "consumer",out: "goods",       qty: 1, in: { alloys: 1, chemicals: 1, energy: 1 },        kind: "make" },
  { id: "machine", out: "machinery",   qty: 1, in: { alloys: 1, electronics: 1, energy: 1 },      kind: "make" },
  { id: "weapfab", out: "weapons",     qty: 1, in: { alloys: 1, electronics: 1, radioactives: 1 }, req: "weapontech", kind: "make" },
  { id: "luxefab", out: "luxury",      qty: 1, in: { spice: 2, electronics: 1, energy: 1 },       kind: "make" },
  { id: "dronefab",out: "drones",      qty: 1, in: { alloys: 1, electronics: 1, energy: 1 }, req: "dronetech",  kind: "make" },
  { id: "aifab",   out: "ai",          qty: 1, in: { electronics: 2, crystals: 1, energy: 3 }, req: "aicores",   kind: "make" },
  // RTS recost: antimatter is the apex of the UNIVERSAL chain (machinery is reachable
  // on every world) rather than the turn-based relics recipe, so the Odyssey endgame
  // win is never seed-locked to a relic world. See engine/wonder.js.
  { id: "antifab", out: "antimatter",  qty: 1, in: { machinery: 1, radioactives: 2, energy: 3 }, req: "antimatter", kind: "make" },
  { id: "plasmafab", out: "plasmatorp", qty: 1, in: { antimatter: 1, alloys: 2, radioactives: 2, energy: 2 }, req: "antimatter", kind: "make" },
];

/* ---------- Planets ----------
   deposits: { commodityId: yieldMult }  β€” what can be extracted HERE
   salvage / bounty: special capture actions available here
   enforce: 0..1 contraband enforcement strength
   faction: controlling faction id

   LIVE vs LEGACY: only the ids in engine/aiArchetypes.js ODYSSEY_WORLDS (the nine
   PLANET_ARCHETYPE worlds + the two ODYSSEY_EXTRA_ARCHETYPE worlds) are ever
   reachable β€” the skirmish map picker and the Odyssey starmap iterate that roster.
   Every other planet below (and the flags that only they carry: `colonizable`,
   `hidden`, `enforce`, `illegalAt`, `bounty`, and the "Deep-Space Survey" comment)
   is carried-over turn-based data with NO live system behind it in the RTS. Kept for
   reference / possible future promotion (a "hidden world" could become a premium
   late-game jump destination), but do not read these flags as active mechanics.
*/
export const PLANETS = [
  { id: "terra", name: "Terra Nova", tag: "Capital β€’ Garden World", color: "#3b82f6", x: 0,
    faction: "core", industry: 6, tech: 7, enforce: 0.92,
    desc: "Cradle of the colonies β€” lush, populous, politically dominant and mineral-poor. Smuggling here is suicide.",
    deposits: { biomass: 1.4, spice: 0.6 } },
  { id: "glacius", name: "Glacius", tag: "Ice World", color: "#7dd3fc", x: 2,
    faction: "core", industry: 2, tech: 3, enforce: 0.5,
    desc: "A frozen ball of water-ice. Crack it for fuel and life-support across the sector.",
    deposits: { ice: 2.0, gas: 0.4 } },
  { id: "ferros", name: "Ferros Prime", tag: "Mining World", color: "#b45309", x: 3,
    faction: "miners", industry: 4, tech: 3, enforce: 0.42,
    desc: "A scarred iron giant. Drowning in ore and radioactives, starved for food.",
    deposits: { ore: 2.0, crystals: 0.7, radioactives: 1.0 } },
  { id: "verdani", name: "Verdani", tag: "Agri-World", color: "#16a34a", x: 5,
    faction: "agri", industry: 3, tech: 4, enforce: 0.7,
    desc: "Endless terraced farms feed half the sector. Spice grows wild in the highlands.",
    deposits: { biomass: 2.0, spice: 1.0 } },
  { id: "helix", name: "Helix Belt", tag: "Asteroid Belt", color: "#9ca3af", x: 6,
    faction: "miners", industry: 3, tech: 4, enforce: 0.4, salvage: true,
    desc: "A glittering ring of rubble β€” ore, crystals and the wrecks of those who came before.",
    deposits: { ore: 1.6, crystals: 1.4, radioactives: 1.0 } },
  { id: "kybernet", name: "Kybernet", tag: "Tech Hub", color: "#8b5cf6", x: 8,
    faction: "syndicate", industry: 8, tech: 10, enforce: 0.6,
    desc: "A neon arcology of laboratories and fabricators. Highest tech, high prices, looser laws.",
    deposits: { crystals: 1.2 } },
  { id: "nimbus", name: "Nimbus", tag: "Gas Giant", color: "#f59e0b", x: 9,
    faction: "frontier", industry: 3, tech: 5, enforce: 0.3,
    desc: "A banded storm-world. Skim its clouds for Helium-3 β€” if your ship can take the pressure.",
    deposits: { gas: 2.0 } },
  { id: "forge", name: "Forge Station", tag: "Industrial World", color: "#ef4444", x: 11,
    faction: "miners", industry: 10, tech: 6, enforce: 0.6,
    desc: "A planet-sized factory. Turns raw materials into finished goods at unmatched scale.",
    deposits: { ore: 1.0 } },
  { id: "oort", name: "Oort Reach", tag: "Frontier Outpost", color: "#06b6d4", x: 15,
    faction: "frontier", industry: 2, tech: 2, enforce: 0.15, salvage: true, bounty: true,
    desc: "The lawless edge of charted space. Rich, dangerous, and the best place to move hot cargo.",
    deposits: { ore: 1.2, radioactives: 1.2, relics: 0.6 } },
  { id: "erebus", name: "Erebus", tag: "Ancient Ruins", color: "#a78bfa", x: 18,
    faction: "frontier", industry: 1, tech: 3, enforce: 0.05, salvage: true, bounty: true,
    desc: "A dead world wrapped in the ruins of a vanished civilisation. Relics for the brave, law for no one.",
    deposits: { relics: 1.3, radioactives: 0.6 } },
  { id: "aquaria", name: "Aquaria", tag: "Ocean World", color: "#0ea5e9", x: 4,
    faction: "agri", industry: 2, tech: 5, enforce: 0.65,
    desc: "A world of endless seas. Vast kelp farms and clean water keep the inner colonies alive.",
    deposits: { biomass: 1.6, ice: 1.0 } },
  { id: "pyralis", name: "Pyralis", tag: "Desert World", color: "#fbbf24", x: 7,
    faction: "core", industry: 4, tech: 5, enforce: 0.72,
    desc: "Sun-blasted dunes that glitter with crystal fields and hide isotopes beneath the sand.",
    deposits: { crystals: 1.4, radioactives: 0.8 } },
  { id: "cobalt", name: "Cobalt Hub", tag: "Free Port", color: "#6366f1", x: 10,
    faction: "syndicate", industry: 6, tech: 7, enforce: 0.45,
    desc: "A free-port arcology where every commodity changes hands. Deep markets, slim margins, few questions.",
    deposits: { crystals: 0.9, gas: 0.5 } },
  { id: "korrath", name: "Korrath", tag: "Warlord World", color: "#dc2626", x: 14,
    faction: "frontier", industry: 3, tech: 2, enforce: 0.1, salvage: true, bounty: true,
    desc: "A contested frontier world. Bounties posted on every screen, wrecks in every orbit, law a rumour.",
    deposits: { ore: 1.3, radioactives: 1.1, relics: 0.7 } },
  { id: "vesper", name: "Vesper", tag: "Twilight World", color: "#64748b", x: 17,
    faction: "miners", industry: 5, tech: 4, enforce: 0.45,
    desc: "A perpetual-dusk world straddling the asteroid lanes β€” ore and gas hauled out in equal measure.",
    deposits: { ore: 1.5, crystals: 1.0, gas: 0.6 } },

  // ---- Colonizable frontier worlds (undeveloped; you grow their economy) ----
  { id: "aurora", name: "Aurora", tag: "Untamed World", color: "#34d399", x: 13,
    faction: "frontier", industry: 1, tech: 1, enforce: 0.1, colonizable: true,
    desc: "A green, unclaimed world ripe for settlement. Fertile soil and shallow ore seams await a founder.",
    deposits: { biomass: 1.3, ore: 0.9 } },
  { id: "cinder", name: "Cinder", tag: "Volcanic World", color: "#f97316", x: 16,
    faction: "frontier", industry: 1, tech: 1, enforce: 0.08, colonizable: true,
    desc: "A restless volcanic world β€” harsh, but its crust is gorged with ore, crystals and isotopes.",
    deposits: { ore: 1.5, crystals: 1.1, radioactives: 1.0 } },

  // ---- Hidden worlds (revealed by Deep-Space Survey) ----
  { id: "pandora", name: "Pandora", tag: "Jungle World", color: "#22c55e", x: 21,
    faction: "frontier", industry: 1, tech: 2, enforce: 0.05, colonizable: true, hidden: true,
    desc: "A riotous jungle world rumoured beyond the Reach. Drips with biomass and rare spice.",
    deposits: { biomass: 1.8, spice: 1.4 } },
  { id: "tartarus", name: "Tartarus", tag: "Shattered World", color: "#b91c1c", x: 24,
    faction: "frontier", industry: 1, tech: 1, enforce: 0.03, colonizable: true, hidden: true, salvage: true,
    desc: "A broken planet of metal and bone at the edge of the charts β€” ore, isotopes and ancient relics.",
    deposits: { ore: 1.6, radioactives: 1.3, relics: 1.0 } },
  { id: "elysium", name: "Elysium", tag: "Paradise World", color: "#38bdf8", x: 28,
    faction: "frontier", industry: 1, tech: 2, enforce: 0.02, colonizable: true, hidden: true,
    desc: "A legendary garden world said to lie past the dark. The finest colony site in the galaxy.",
    deposits: { biomass: 2.0, spice: 1.2, crystals: 1.0 } },
];
// pristine snapshot of the charted 20's stats, captured once at parse time β€” every
// applyCoreVariance() call (in game.js) recomputes FROM this baseline, never from
// whatever the previous seed/playthrough left behind, so re-applying it (a mid-session
// New Game reuses these same 20 objects, unlike the frontier ring) can never compound drift.
export const CORE_BASELINE = PLANETS.map(p => ({ id: p.id, industry: p.industry, tech: p.tech, enforce: p.enforce, deposits: { ...p.deposits } }));

// A world's display name from its id, falling back to the id itself for anything
// off the roster. One definition, reused by the HUD, the starmap and the galaxy
// notifications (all of which previously hand-wrote this lookup).
export const planetName = id => PLANETS.find(p => p.id === id)?.name || id;