recipe iteration
This commit is contained in:
@@ -1,3 +1,29 @@
|
||||
# recipes.toml
|
||||
#
|
||||
# First real-content iteration of the production tree. Quantities and
|
||||
# durations are a first guess; the balancing pass will tune them and assign
|
||||
# real unlock_at_station_level values (everything is unlocked for now so the
|
||||
# full tree is testable).
|
||||
#
|
||||
# Input chain per game phase — each phase adds exactly one new base input:
|
||||
#
|
||||
# early iron_ore + copper_ore -> ingots -> copper_wire, steel_plate,
|
||||
# circuit_board
|
||||
# mid + titanium_ore -> titanium_frame; assembler-made
|
||||
# mechanical_parts, targeting_unit,
|
||||
# drive_unit
|
||||
# late + advanced_alloy -> reinforced_plating, capital_core.
|
||||
# advanced_alloy CANNOT be mined; it only
|
||||
# comes from reprocessing salvaged scrap,
|
||||
# so capital production requires combat.
|
||||
#
|
||||
# Run tools/verify_recipes.py after editing to check that every consumed
|
||||
# item has a producer and every item has a visuals.toml entry.
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Mining (tier 0)
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
[[recipe]]
|
||||
id = "mine_iron_ore"
|
||||
building = "miner"
|
||||
@@ -12,6 +38,18 @@ inputs = []
|
||||
outputs = [{item = "copper_ore", amount = 1}]
|
||||
duration_seconds = 1.5
|
||||
|
||||
# Titanium is the midgame ore: mined three times slower than iron.
|
||||
[[recipe]]
|
||||
id = "mine_titanium_ore"
|
||||
building = "miner"
|
||||
inputs = []
|
||||
outputs = [{item = "titanium_ore", amount = 1}]
|
||||
duration_seconds = 3.0
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Smelting (tier 1)
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
[[recipe]]
|
||||
id = "iron_ingot"
|
||||
building = "smelter"
|
||||
@@ -27,54 +65,17 @@ outputs = [{item = "copper_ingot", amount = 1}]
|
||||
duration_seconds = 2.5
|
||||
|
||||
[[recipe]]
|
||||
id = "circuit_board"
|
||||
building = "assembler"
|
||||
inputs = [{item = "iron_ingot", amount = 1}, {item = "copper_ingot", amount = 2}]
|
||||
outputs = [{item = "circuit_board", amount = 1}]
|
||||
duration_seconds = 2.0
|
||||
|
||||
[[recipe]]
|
||||
id = "drone_hull"
|
||||
building = "assembler"
|
||||
inputs = [{item = "iron_ingot", amount = 5}, {item = "circuit_board", amount = 1}]
|
||||
outputs = [{item = "drone_hull", amount = 1}]
|
||||
id = "titanium_ingot"
|
||||
building = "smelter"
|
||||
inputs = [{item = "titanium_ore", amount = 3}]
|
||||
outputs = [{item = "titanium_ingot", amount = 1}]
|
||||
duration_seconds = 4.0
|
||||
|
||||
[[recipe]]
|
||||
id = "laser_cannon_xs_module"
|
||||
building = "assembler"
|
||||
inputs = [{item = "iron_ingot", amount = 2}, {item = "circuit_board", amount = 1}]
|
||||
outputs = [{item = "laser_cannon_xs_module", amount = 1}]
|
||||
duration_seconds = 3.0
|
||||
|
||||
[[recipe]]
|
||||
id = "laser_cannon_s_module"
|
||||
building = "assembler"
|
||||
inputs = [{item = "iron_ingot", amount = 4}, {item = "circuit_board", amount = 2}]
|
||||
outputs = [{item = "laser_cannon_s_module", amount = 1}]
|
||||
duration_seconds = 6.0
|
||||
|
||||
[[recipe]]
|
||||
id = "salvager_module"
|
||||
building = "assembler"
|
||||
inputs = [{item = "iron_ingot", amount = 1}, {item = "circuit_board", amount = 1}]
|
||||
outputs = [{item = "salvager_module", amount = 1}]
|
||||
duration_seconds = 6.0
|
||||
|
||||
[[recipe]]
|
||||
id = "repair_tool_module"
|
||||
building = "assembler"
|
||||
inputs = [{item = "iron_ingot", amount = 1}, {item = "circuit_board", amount = 2}]
|
||||
outputs = [{item = "repair_tool_module", amount = 1}]
|
||||
duration_seconds = 6.0
|
||||
|
||||
[[recipe]]
|
||||
id = "building_blocks"
|
||||
unlock_at_station_level = -1
|
||||
building = "assembler"
|
||||
inputs = [{item = "iron_ingot", amount = 4}]
|
||||
outputs = [{item = "building_block", amount = 10}]
|
||||
duration_seconds = 4.0
|
||||
# -----------------------------------------------------------------------------
|
||||
# Reprocessing
|
||||
#
|
||||
# The only source of advanced_alloy: salvaged scrap from destroyed ships.
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
[[recipe]]
|
||||
id = "reprocessing_cycle"
|
||||
@@ -85,15 +86,354 @@ duration_seconds = 3.0
|
||||
[[recipe.outputs]]
|
||||
item = "iron_ingot"
|
||||
amount = 2
|
||||
probability = 0.6
|
||||
probability = 0.45
|
||||
|
||||
[[recipe.outputs]]
|
||||
item = "circuit_board"
|
||||
item = "copper_ingot"
|
||||
amount = 1
|
||||
probability = 0.3
|
||||
probability = 0.25
|
||||
|
||||
[[recipe.outputs]]
|
||||
item = "titanium_ingot"
|
||||
amount = 1
|
||||
probability = 0.15
|
||||
|
||||
[[recipe.outputs]]
|
||||
item = "advanced_alloy"
|
||||
amount = 1
|
||||
probability = 0.1
|
||||
probability = 0.15
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Basic components (tier 2, early game)
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
[[recipe]]
|
||||
id = "copper_wire"
|
||||
unlock_at_station_level = -1
|
||||
building = "assembler"
|
||||
inputs = [{item = "copper_ingot", amount = 1}]
|
||||
outputs = [{item = "copper_wire", amount = 2}]
|
||||
duration_seconds = 1.5
|
||||
|
||||
[[recipe]]
|
||||
id = "steel_plate"
|
||||
unlock_at_station_level = -1
|
||||
building = "assembler"
|
||||
inputs = [{item = "iron_ingot", amount = 2}]
|
||||
outputs = [{item = "steel_plate", amount = 1}]
|
||||
duration_seconds = 2.0
|
||||
|
||||
[[recipe]]
|
||||
id = "circuit_board"
|
||||
unlock_at_station_level = -1
|
||||
building = "assembler"
|
||||
inputs = [{item = "iron_ingot", amount = 1}, {item = "copper_wire", amount = 2}]
|
||||
outputs = [{item = "circuit_board", amount = 1}]
|
||||
duration_seconds = 2.0
|
||||
|
||||
[[recipe]]
|
||||
id = "building_blocks"
|
||||
unlock_at_station_level = -1
|
||||
building = "assembler"
|
||||
inputs = [{item = "iron_ingot", amount = 4}]
|
||||
outputs = [{item = "building_block", amount = 10}]
|
||||
duration_seconds = 4.0
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Advanced components (tier 3, midgame)
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
[[recipe]]
|
||||
id = "mechanical_parts"
|
||||
unlock_at_station_level = -1
|
||||
building = "assembler"
|
||||
inputs = [{item = "steel_plate", amount = 1}, {item = "iron_ingot", amount = 1}]
|
||||
outputs = [{item = "mechanical_parts", amount = 2}]
|
||||
duration_seconds = 2.5
|
||||
|
||||
[[recipe]]
|
||||
id = "targeting_unit"
|
||||
unlock_at_station_level = -1
|
||||
building = "assembler"
|
||||
inputs = [{item = "circuit_board", amount = 2}, {item = "copper_wire", amount = 1}]
|
||||
outputs = [{item = "targeting_unit", amount = 1}]
|
||||
duration_seconds = 3.0
|
||||
|
||||
[[recipe]]
|
||||
id = "drive_unit"
|
||||
unlock_at_station_level = -1
|
||||
building = "assembler"
|
||||
inputs = [
|
||||
{item = "steel_plate", amount = 1},
|
||||
{item = "mechanical_parts", amount = 1},
|
||||
{item = "circuit_board", amount = 1},
|
||||
]
|
||||
outputs = [{item = "drive_unit", amount = 1}]
|
||||
duration_seconds = 4.0
|
||||
|
||||
[[recipe]]
|
||||
id = "titanium_frame"
|
||||
unlock_at_station_level = -1
|
||||
building = "assembler"
|
||||
inputs = [{item = "titanium_ingot", amount = 2}, {item = "steel_plate", amount = 1}]
|
||||
outputs = [{item = "titanium_frame", amount = 1}]
|
||||
duration_seconds = 4.0
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Capital components (tier 4, lategame — gated on advanced_alloy)
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
[[recipe]]
|
||||
id = "reinforced_plating"
|
||||
unlock_at_station_level = -1
|
||||
building = "assembler"
|
||||
inputs = [{item = "steel_plate", amount = 2}, {item = "advanced_alloy", amount = 1}]
|
||||
outputs = [{item = "reinforced_plating", amount = 1}]
|
||||
duration_seconds = 5.0
|
||||
|
||||
[[recipe]]
|
||||
id = "capital_core"
|
||||
unlock_at_station_level = -1
|
||||
building = "assembler"
|
||||
inputs = [
|
||||
{item = "targeting_unit", amount = 1},
|
||||
{item = "drive_unit", amount = 1},
|
||||
{item = "advanced_alloy", amount = 2},
|
||||
]
|
||||
outputs = [{item = "capital_core", amount = 1}]
|
||||
duration_seconds = 8.0
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Module items — early game
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
[[recipe]]
|
||||
id = "laser_cannon_s_module"
|
||||
unlock_at_station_level = -1
|
||||
building = "assembler"
|
||||
inputs = [{item = "iron_ingot", amount = 2}, {item = "circuit_board", amount = 1}]
|
||||
outputs = [{item = "laser_cannon_s_module", amount = 1}]
|
||||
duration_seconds = 3.0
|
||||
|
||||
[[recipe]]
|
||||
id = "salvager_module"
|
||||
unlock_at_station_level = -1
|
||||
building = "assembler"
|
||||
inputs = [{item = "steel_plate", amount = 1}, {item = "circuit_board", amount = 1}]
|
||||
outputs = [{item = "salvager_module", amount = 1}]
|
||||
duration_seconds = 4.0
|
||||
|
||||
[[recipe]]
|
||||
id = "repair_tool_module"
|
||||
unlock_at_station_level = -1
|
||||
building = "assembler"
|
||||
inputs = [{item = "circuit_board", amount = 2}, {item = "copper_wire", amount = 1}]
|
||||
outputs = [{item = "repair_tool_module", amount = 1}]
|
||||
duration_seconds = 4.0
|
||||
|
||||
[[recipe]]
|
||||
id = "armor_plates_module"
|
||||
unlock_at_station_level = -1
|
||||
building = "assembler"
|
||||
inputs = [{item = "steel_plate", amount = 2}]
|
||||
outputs = [{item = "armor_plates_module", amount = 1}]
|
||||
duration_seconds = 3.0
|
||||
|
||||
[[recipe]]
|
||||
id = "sensor_booster_module"
|
||||
unlock_at_station_level = -1
|
||||
building = "assembler"
|
||||
inputs = [{item = "circuit_board", amount = 1}, {item = "copper_wire", amount = 2}]
|
||||
outputs = [{item = "sensor_booster_module", amount = 1}]
|
||||
duration_seconds = 3.0
|
||||
|
||||
[[recipe]]
|
||||
id = "maneuvering_thrusters_module"
|
||||
unlock_at_station_level = -1
|
||||
building = "assembler"
|
||||
inputs = [{item = "mechanical_parts", amount = 1}, {item = "copper_wire", amount = 1}]
|
||||
outputs = [{item = "maneuvering_thrusters_module", amount = 1}]
|
||||
duration_seconds = 3.0
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Module items — midgame
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
[[recipe]]
|
||||
id = "afterburner_module"
|
||||
unlock_at_station_level = -1
|
||||
building = "assembler"
|
||||
inputs = [{item = "drive_unit", amount = 1}, {item = "steel_plate", amount = 1}]
|
||||
outputs = [{item = "afterburner_module", amount = 1}]
|
||||
duration_seconds = 4.0
|
||||
|
||||
[[recipe]]
|
||||
id = "weapon_upgrade_module"
|
||||
unlock_at_station_level = -1
|
||||
building = "assembler"
|
||||
inputs = [{item = "targeting_unit", amount = 1}, {item = "steel_plate", amount = 1}]
|
||||
outputs = [{item = "weapon_upgrade_module", amount = 1}]
|
||||
duration_seconds = 4.0
|
||||
|
||||
[[recipe]]
|
||||
id = "weapon_primer_module"
|
||||
unlock_at_station_level = -1
|
||||
building = "assembler"
|
||||
inputs = [{item = "targeting_unit", amount = 1}, {item = "copper_wire", amount = 2}]
|
||||
outputs = [{item = "weapon_primer_module", amount = 1}]
|
||||
duration_seconds = 4.0
|
||||
|
||||
[[recipe]]
|
||||
id = "weapon_stabilizer_module"
|
||||
unlock_at_station_level = -1
|
||||
building = "assembler"
|
||||
inputs = [{item = "targeting_unit", amount = 1}, {item = "mechanical_parts", amount = 1}]
|
||||
outputs = [{item = "weapon_stabilizer_module", amount = 1}]
|
||||
duration_seconds = 4.0
|
||||
|
||||
[[recipe]]
|
||||
id = "laser_cannon_m_module"
|
||||
unlock_at_station_level = -1
|
||||
building = "assembler"
|
||||
inputs = [{item = "targeting_unit", amount = 1}, {item = "titanium_frame", amount = 1}]
|
||||
outputs = [{item = "laser_cannon_m_module", amount = 1}]
|
||||
duration_seconds = 6.0
|
||||
|
||||
[[recipe]]
|
||||
id = "drone_bay_module"
|
||||
unlock_at_station_level = -1
|
||||
building = "assembler"
|
||||
inputs = [
|
||||
{item = "titanium_frame", amount = 1},
|
||||
{item = "mechanical_parts", amount = 1},
|
||||
{item = "circuit_board", amount = 1},
|
||||
]
|
||||
outputs = [{item = "drone_bay_module", amount = 1}]
|
||||
duration_seconds = 6.0
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Module items — lategame
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
[[recipe]]
|
||||
id = "laser_cannon_l_module"
|
||||
unlock_at_station_level = -1
|
||||
building = "assembler"
|
||||
inputs = [
|
||||
{item = "targeting_unit", amount = 2},
|
||||
{item = "reinforced_plating", amount = 2},
|
||||
{item = "titanium_frame", amount = 1},
|
||||
]
|
||||
outputs = [{item = "laser_cannon_l_module", amount = 1}]
|
||||
duration_seconds = 12.0
|
||||
|
||||
[[recipe]]
|
||||
id = "drone_hangar_module"
|
||||
unlock_at_station_level = -1
|
||||
building = "assembler"
|
||||
inputs = [
|
||||
{item = "capital_core", amount = 1},
|
||||
{item = "titanium_frame", amount = 2},
|
||||
{item = "reinforced_plating", amount = 2},
|
||||
]
|
||||
outputs = [{item = "drone_hangar_module", amount = 1}]
|
||||
duration_seconds = 20.0
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Ship hulls
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
[[recipe]]
|
||||
id = "drone_hull"
|
||||
unlock_at_station_level = -1
|
||||
building = "assembler"
|
||||
inputs = [{item = "iron_ingot", amount = 5}, {item = "circuit_board", amount = 1}]
|
||||
outputs = [{item = "drone_hull", amount = 1}]
|
||||
duration_seconds = 4.0
|
||||
|
||||
[[recipe]]
|
||||
id = "frigate_hull"
|
||||
unlock_at_station_level = -1
|
||||
building = "assembler"
|
||||
inputs = [
|
||||
{item = "steel_plate", amount = 3},
|
||||
{item = "mechanical_parts", amount = 1},
|
||||
{item = "circuit_board", amount = 1},
|
||||
]
|
||||
outputs = [{item = "frigate_hull", amount = 1}]
|
||||
duration_seconds = 8.0
|
||||
|
||||
[[recipe]]
|
||||
id = "destroyer_hull"
|
||||
unlock_at_station_level = -1
|
||||
building = "assembler"
|
||||
inputs = [
|
||||
{item = "steel_plate", amount = 5},
|
||||
{item = "mechanical_parts", amount = 2},
|
||||
{item = "circuit_board", amount = 1},
|
||||
]
|
||||
outputs = [{item = "destroyer_hull", amount = 1}]
|
||||
duration_seconds = 10.0
|
||||
|
||||
[[recipe]]
|
||||
id = "cruiser_hull"
|
||||
unlock_at_station_level = -1
|
||||
building = "assembler"
|
||||
inputs = [
|
||||
{item = "titanium_frame", amount = 2},
|
||||
{item = "steel_plate", amount = 4},
|
||||
{item = "drive_unit", amount = 1},
|
||||
]
|
||||
outputs = [{item = "cruiser_hull", amount = 1}]
|
||||
duration_seconds = 15.0
|
||||
|
||||
[[recipe]]
|
||||
id = "battlecruiser_hull"
|
||||
unlock_at_station_level = -1
|
||||
building = "assembler"
|
||||
inputs = [
|
||||
{item = "titanium_frame", amount = 3},
|
||||
{item = "steel_plate", amount = 6},
|
||||
{item = "drive_unit", amount = 1},
|
||||
{item = "targeting_unit", amount = 1},
|
||||
]
|
||||
outputs = [{item = "battlecruiser_hull", amount = 1}]
|
||||
duration_seconds = 20.0
|
||||
|
||||
[[recipe]]
|
||||
id = "battleship_hull"
|
||||
unlock_at_station_level = -1
|
||||
building = "assembler"
|
||||
inputs = [
|
||||
{item = "titanium_frame", amount = 4},
|
||||
{item = "reinforced_plating", amount = 2},
|
||||
{item = "drive_unit", amount = 2},
|
||||
]
|
||||
outputs = [{item = "battleship_hull", amount = 1}]
|
||||
duration_seconds = 30.0
|
||||
|
||||
[[recipe]]
|
||||
id = "dreadnought_hull"
|
||||
unlock_at_station_level = -1
|
||||
building = "assembler"
|
||||
inputs = [
|
||||
{item = "capital_core", amount = 1},
|
||||
{item = "titanium_frame", amount = 6},
|
||||
{item = "reinforced_plating", amount = 4},
|
||||
{item = "drive_unit", amount = 2},
|
||||
]
|
||||
outputs = [{item = "dreadnought_hull", amount = 1}]
|
||||
duration_seconds = 60.0
|
||||
|
||||
[[recipe]]
|
||||
id = "carrier_hull"
|
||||
unlock_at_station_level = -1
|
||||
building = "assembler"
|
||||
inputs = [
|
||||
{item = "capital_core", amount = 1},
|
||||
{item = "titanium_frame", amount = 5},
|
||||
{item = "reinforced_plating", amount = 3},
|
||||
{item = "drive_unit", amount = 2},
|
||||
]
|
||||
outputs = [{item = "carrier_hull", amount = 1}]
|
||||
duration_seconds = 60.0
|
||||
|
||||
@@ -106,6 +106,8 @@ glyph = "E"
|
||||
# drawn around it. One section per ItemType.
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
# --- ores ---
|
||||
|
||||
[items.iron_ore]
|
||||
fill = "#8a5a4a"
|
||||
outline = "#201010"
|
||||
@@ -114,6 +116,12 @@ outline = "#201010"
|
||||
fill = "#c47a3a"
|
||||
outline = "#3a1a0a"
|
||||
|
||||
[items.titanium_ore]
|
||||
fill = "#9aa3ad"
|
||||
outline = "#2a2e33"
|
||||
|
||||
# --- ingots ---
|
||||
|
||||
[items.iron_ingot]
|
||||
fill = "#b0b0b8"
|
||||
outline = "#202028"
|
||||
@@ -122,34 +130,80 @@ outline = "#202028"
|
||||
fill = "#d48a4a"
|
||||
outline = "#402010"
|
||||
|
||||
[items.circuit_board]
|
||||
fill = "#2ea35a"
|
||||
outline = "#0a2a14"
|
||||
[items.titanium_ingot]
|
||||
fill = "#c8d2dc"
|
||||
outline = "#3a4048"
|
||||
|
||||
[items.advanced_alloy]
|
||||
fill = "#a06acc"
|
||||
outline = "#201030"
|
||||
|
||||
[items.building_block]
|
||||
fill = "#c8b070"
|
||||
outline = "#302810"
|
||||
# --- salvage loop ---
|
||||
|
||||
[items.scrap]
|
||||
fill = "#7a7268"
|
||||
outline = "#201a14"
|
||||
|
||||
[items.drone_hull]
|
||||
fill = "#1b1b1b"
|
||||
outline = "#1402b3"
|
||||
[items.advanced_alloy]
|
||||
fill = "#a06acc"
|
||||
outline = "#201030"
|
||||
|
||||
[items.laser_cannon_xs_module]
|
||||
fill = "#691313"
|
||||
outline = "#f3ff4f"
|
||||
# --- basic components ---
|
||||
|
||||
[items.copper_wire]
|
||||
fill = "#e09a50"
|
||||
outline = "#3a2008"
|
||||
|
||||
[items.steel_plate]
|
||||
fill = "#8a92a0"
|
||||
outline = "#22262c"
|
||||
|
||||
[items.circuit_board]
|
||||
fill = "#2ea35a"
|
||||
outline = "#0a2a14"
|
||||
|
||||
[items.building_block]
|
||||
fill = "#c8b070"
|
||||
outline = "#302810"
|
||||
|
||||
# --- advanced components ---
|
||||
|
||||
[items.mechanical_parts]
|
||||
fill = "#6f7a66"
|
||||
outline = "#1c2018"
|
||||
|
||||
[items.targeting_unit]
|
||||
fill = "#3a9e8c"
|
||||
outline = "#0c2824"
|
||||
|
||||
[items.drive_unit]
|
||||
fill = "#4a6ad0"
|
||||
outline = "#101a38"
|
||||
|
||||
[items.titanium_frame]
|
||||
fill = "#b8c4d4"
|
||||
outline = "#343c48"
|
||||
|
||||
# --- capital components ---
|
||||
|
||||
[items.reinforced_plating]
|
||||
fill = "#8a6ad0"
|
||||
outline = "#1c1038"
|
||||
|
||||
[items.capital_core]
|
||||
fill = "#b040d0"
|
||||
outline = "#280c30"
|
||||
|
||||
# --- module items ---
|
||||
|
||||
[items.laser_cannon_s_module]
|
||||
fill = "#691313"
|
||||
outline = "#f3ff4f"
|
||||
|
||||
[items.laser_cannon_m_module]
|
||||
fill = "#892020"
|
||||
outline = "#f3ff4f"
|
||||
|
||||
[items.laser_cannon_l_module]
|
||||
fill = "#a92d2d"
|
||||
outline = "#f3ff4f"
|
||||
|
||||
[items.salvager_module]
|
||||
fill = "#b2cfdd"
|
||||
outline = "#236137"
|
||||
@@ -158,6 +212,76 @@ outline = "#236137"
|
||||
fill = "#2e9ba3"
|
||||
outline = "#689275"
|
||||
|
||||
[items.armor_plates_module]
|
||||
fill = "#808080"
|
||||
outline = "#202020"
|
||||
|
||||
[items.sensor_booster_module]
|
||||
fill = "#40a0ff"
|
||||
outline = "#102840"
|
||||
|
||||
[items.maneuvering_thrusters_module]
|
||||
fill = "#5090e0"
|
||||
outline = "#142438"
|
||||
|
||||
[items.afterburner_module]
|
||||
fill = "#6080c0"
|
||||
outline = "#182030"
|
||||
|
||||
[items.weapon_upgrade_module]
|
||||
fill = "#ff4040"
|
||||
outline = "#401010"
|
||||
|
||||
[items.weapon_primer_module]
|
||||
fill = "#e03838"
|
||||
outline = "#380e0e"
|
||||
|
||||
[items.weapon_stabilizer_module]
|
||||
fill = "#c03030"
|
||||
outline = "#300c0c"
|
||||
|
||||
[items.drone_bay_module]
|
||||
fill = "#cc66ff"
|
||||
outline = "#331040"
|
||||
|
||||
[items.drone_hangar_module]
|
||||
fill = "#9933cc"
|
||||
outline = "#260c33"
|
||||
|
||||
# --- ship hulls (outline matches the ship's fleet color in [ships.*]) ---
|
||||
|
||||
[items.drone_hull]
|
||||
fill = "#1b1b1b"
|
||||
outline = "#3366ff"
|
||||
|
||||
[items.frigate_hull]
|
||||
fill = "#1b1b1b"
|
||||
outline = "#44aaff"
|
||||
|
||||
[items.destroyer_hull]
|
||||
fill = "#1b1b1b"
|
||||
outline = "#33ccaa"
|
||||
|
||||
[items.cruiser_hull]
|
||||
fill = "#1b1b1b"
|
||||
outline = "#66cc33"
|
||||
|
||||
[items.battlecruiser_hull]
|
||||
fill = "#1b1b1b"
|
||||
outline = "#cccc33"
|
||||
|
||||
[items.battleship_hull]
|
||||
fill = "#1b1b1b"
|
||||
outline = "#ff9933"
|
||||
|
||||
[items.dreadnought_hull]
|
||||
fill = "#1b1b1b"
|
||||
outline = "#ff5533"
|
||||
|
||||
[items.carrier_hull]
|
||||
fill = "#1b1b1b"
|
||||
outline = "#cc66ff"
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Ships
|
||||
#
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
# Content Design — Ships & Modules
|
||||
|
||||
First real-content iteration (June 2026). This pass defines ship hull grids
|
||||
and module surface masks only. Stats, materials, recipes, and threat costs in
|
||||
the config files are placeholders; the recipe pass and the balancing pass
|
||||
come later.
|
||||
First real-content iterations (June 2026). Pass 1 defined ship hull grids and
|
||||
module surface masks; pass 2 defined the production tree (recipes). Stats and
|
||||
threat costs in the config files are still placeholders for the balancing
|
||||
pass.
|
||||
|
||||
## Design principle: footprint gating
|
||||
|
||||
@@ -129,6 +129,48 @@ Maximum simultaneous (disjoint) placements: m guns — cruiser 2,
|
||||
battlecruiser 3, battleship 4; l guns — battleship 1, dreadnought 3;
|
||||
drone hangar — carrier 1.
|
||||
|
||||
## Production tree
|
||||
|
||||
Design principle: each game phase adds exactly one new base input chain, so
|
||||
factory complexity ramps alongside ship size.
|
||||
|
||||
| Phase | New input | How acquired | Unlocks |
|
||||
|-------|-----------|--------------|---------|
|
||||
| early | iron_ore, copper_ore | mined | drone, frigate, destroyer; small guns and basic supports |
|
||||
| mid | titanium_ore | mined (3x slower than iron) | cruiser, battlecruiser; m guns, drone bay, weapon modifiers |
|
||||
| late | advanced_alloy | ONLY from reprocessing salvaged scrap | battleship, dreadnought, carrier; l guns, drone hangar |
|
||||
|
||||
The advanced_alloy gate is the core loop hook: capital ship production
|
||||
requires fighting (salvaging scrap from kills and reprocessing it), not just
|
||||
mining. The reprocessing plant turns 5 scrap into iron/copper/titanium ingots
|
||||
or advanced_alloy probabilistically.
|
||||
|
||||
Intermediate components, by tier:
|
||||
|
||||
- **Tier 2 (early):** copper_wire (copper), steel_plate (iron), circuit_board
|
||||
(iron + wire), building_block (iron).
|
||||
- **Tier 3 (mid):** mechanical_parts (steel + iron), targeting_unit (circuits
|
||||
+ wire), drive_unit (steel + mechanical_parts + circuit), titanium_frame
|
||||
(titanium + steel).
|
||||
- **Tier 4 (late):** reinforced_plating (steel + advanced_alloy),
|
||||
capital_core (targeting_unit + drive_unit + 2 advanced_alloy).
|
||||
|
||||
Hulls and modules consume intermediates of their tier: early items are built
|
||||
from tier-2 parts, midgame items require tier-3 parts (deeper chains, more
|
||||
assemblers), capital items require tier-4 parts (and therefore combat). Hull
|
||||
items are named `<ship>_hull`; module items `<module>_module`. Every item has
|
||||
an `[items.*]` entry in visuals.toml; hull item outlines match the ship's
|
||||
fleet color from `[ships.*]`.
|
||||
|
||||
Consistency is checked by `tools/verify_recipes.py` — re-run it after editing
|
||||
recipes, ship/module materials, or visuals:
|
||||
|
||||
python dota_factory/tools/verify_recipes.py
|
||||
|
||||
It verifies every consumed item has a producer, every item has a visuals
|
||||
entry, flags orphaned items, and prints which items are reprocessing-only
|
||||
(currently exactly advanced_alloy).
|
||||
|
||||
## Deliberate placeholders / open questions for later passes
|
||||
|
||||
- All new hulls have `threat.cost_formula = "0"` so enemy waves do not spawn
|
||||
@@ -136,14 +178,12 @@ drone hangar — carrier 1.
|
||||
eligible, regardless of unlock level). The balancing pass should set real
|
||||
threat costs together with `default_modules` loadouts so waves spawn them
|
||||
armed.
|
||||
- All new hulls are `unlock_at_station_level = -1` (available from the start)
|
||||
to make layout testing easy; the progression pass should stagger these.
|
||||
- Ship hull material items (`frigate_hull` … `carrier_hull`) and the new
|
||||
module items (`laser_cannon_m_module`, `laser_cannon_l_module`,
|
||||
`drone_bay_module`, `drone_hangar_module`, …) have no recipes yet — that is
|
||||
the recipe pass. The old `laser_cannon_xs_module` recipe is orphaned (the
|
||||
module was renamed to `laser_cannon_s`, consuming `laser_cannon_s_module`,
|
||||
which already has a recipe).
|
||||
- All new hulls and all assembler recipes are `unlock_at_station_level = -1`
|
||||
(available from the start) to make testing easy; the balancing pass should
|
||||
stagger these so mid/lategame recipes drop as schematics from enemy defence
|
||||
stations.
|
||||
- Recipe quantities and durations are a first guess, deliberately roughly
|
||||
tiered (capital hulls ~60 s, drones 4 s); the balancing pass tunes them.
|
||||
- `drone_bay` and `drone_hangar` are footprint-only placeholders: the drone
|
||||
launching capability does not exist in the simulation yet, so they define
|
||||
no capability section.
|
||||
|
||||
132
tools/verify_recipes.py
Normal file
132
tools/verify_recipes.py
Normal file
@@ -0,0 +1,132 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Verify the recipe tree is closed and consistent.
|
||||
|
||||
Reads recipes.toml, ships.toml, modules.toml, and visuals.toml, then checks:
|
||||
|
||||
1. Producers — every item consumed anywhere (recipe inputs, ship hull
|
||||
materials, module materials) is produced by some recipe. 'scrap' is
|
||||
exempt: it drops from destroyed ships.
|
||||
2. Visuals — every item that exists in the economy has an [items.*]
|
||||
entry in visuals.toml, and visuals.toml has no entries for items
|
||||
that no longer exist.
|
||||
3. Orphans — items that are produced but never consumed (warning only;
|
||||
'building_block' is exempt: the HQ consumes it).
|
||||
|
||||
It also prints which items are obtainable ONLY through reprocessing —
|
||||
the combat-gated materials — so changes to that gate are visible.
|
||||
|
||||
Usage (from the repository root or anywhere else):
|
||||
|
||||
python dota_factory/tools/verify_recipes.py
|
||||
python dota_factory/tools/verify_recipes.py --config-dir path/to/config
|
||||
|
||||
By default the config directory is resolved relative to this script
|
||||
(../bin/app/data/config). Requires the 'toml' package on Python < 3.11
|
||||
(pip install --user toml); on 3.11+ the standard tomllib is used.
|
||||
|
||||
Exits 1 if a producer or visuals check fails, 0 otherwise (warnings do
|
||||
not affect the exit code).
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import sys
|
||||
|
||||
|
||||
WORLD_SOURCED_ITEMS = {"scrap"} # dropped by destroyed ships
|
||||
IMPLICITLY_CONSUMED_ITEMS = {"building_block"} # consumed by the HQ
|
||||
|
||||
|
||||
def load_toml(path):
|
||||
try:
|
||||
import tomllib
|
||||
with open(path, "rb") as fh:
|
||||
return tomllib.load(fh)
|
||||
except ImportError:
|
||||
import toml
|
||||
return toml.load(path)
|
||||
|
||||
|
||||
def main():
|
||||
default_dir = os.path.normpath(os.path.join(
|
||||
os.path.dirname(os.path.abspath(__file__)),
|
||||
"..", "bin", "app", "data", "config"))
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Check recipe tree consistency across the config files.")
|
||||
parser.add_argument("--config-dir", default=default_dir,
|
||||
help="directory containing the config toml files"
|
||||
" (default: %(default)s)")
|
||||
args = parser.parse_args()
|
||||
|
||||
recipes = load_toml(os.path.join(args.config_dir, "recipes.toml"))["recipe"]
|
||||
ships = load_toml(os.path.join(args.config_dir, "ships.toml"))["ship"]
|
||||
modules = load_toml(os.path.join(args.config_dir, "modules.toml"))["module"]
|
||||
visuals = load_toml(os.path.join(args.config_dir, "visuals.toml"))
|
||||
|
||||
produced = {} # item id -> [producer descriptions]
|
||||
consumed = {} # item id -> [consumer descriptions]
|
||||
|
||||
for recipe in recipes:
|
||||
for output in recipe.get("outputs", []):
|
||||
produced.setdefault(output["item"], []).append(
|
||||
"recipe '{}'".format(recipe["id"]))
|
||||
for inp in recipe.get("inputs", []):
|
||||
consumed.setdefault(inp["item"], []).append(
|
||||
"recipe '{}'".format(recipe["id"]))
|
||||
|
||||
for ship in ships:
|
||||
for material in ship["schematic"]["materials"]:
|
||||
consumed.setdefault(material["item"], []).append(
|
||||
"ship '{}'".format(ship["id"]))
|
||||
|
||||
for module in modules:
|
||||
for material in module["materials"]:
|
||||
consumed.setdefault(material["item"], []).append(
|
||||
"module '{}'".format(module["id"]))
|
||||
|
||||
all_items = set(produced) | set(consumed) | WORLD_SOURCED_ITEMS
|
||||
visual_items = set(visuals.get("items", {}))
|
||||
|
||||
errors = []
|
||||
warnings = []
|
||||
|
||||
for item in sorted(consumed):
|
||||
if item not in produced and item not in WORLD_SOURCED_ITEMS:
|
||||
errors.append("no producer for '{}' (consumed by {})".format(
|
||||
item, ", ".join(sorted(set(consumed[item])))))
|
||||
|
||||
for item in sorted(all_items - visual_items):
|
||||
errors.append("no [items.{}] entry in visuals.toml".format(item))
|
||||
for item in sorted(visual_items - all_items):
|
||||
warnings.append("visuals.toml entry [items.{}] matches no known item"
|
||||
.format(item))
|
||||
|
||||
for item in sorted(produced):
|
||||
if item not in consumed and item not in IMPLICITLY_CONSUMED_ITEMS:
|
||||
warnings.append("'{}' is produced but never consumed (by {})"
|
||||
.format(item, ", ".join(sorted(set(produced[item])))))
|
||||
|
||||
reprocessing_only = sorted(
|
||||
item for item, producers in produced.items()
|
||||
if all("reprocessing" in p for p in producers))
|
||||
|
||||
print("{} items, {} recipes, {} ships, {} modules".format(
|
||||
len(all_items), len(recipes), len(ships), len(modules)))
|
||||
print("obtainable only via reprocessing: {}".format(
|
||||
", ".join(reprocessing_only) if reprocessing_only else "(none)"))
|
||||
print()
|
||||
|
||||
for warning in warnings:
|
||||
print("WARNING: {}".format(warning))
|
||||
for error in errors:
|
||||
print("ERROR: {}".format(error))
|
||||
if not errors and not warnings:
|
||||
print("all checks passed")
|
||||
elif not errors:
|
||||
print("no errors")
|
||||
|
||||
return 1 if errors else 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
Reference in New Issue
Block a user