Cookbook: adding things to the game
Each recipe lists the files to touch, in the order to touch them, and the checks that must be green before you open a pull request. All of them assume the rules of Project rules that never change and a running local world (Local development setup). Tour of the code: Tour of the server code and Tour of the client code.
Commands used below, from the repository you are changing:
server:
cargo fmt && cargo clippy --locked --all-targets -- -D warningsand the PostgreSQL tests of Local development setup;client:
godot4 --headless --path . --editor --importafter adding an asset, andmake test-....
Add an item
Migration
ishtaria-server/migrations/NNNN_name.sql(next free number, never edit an old one):INSERT INTO item_types (id, name, calories, capacity_bonus, stack_limit, multi_slot, slot_cost, category) VALUES ('ruby', 'Ruby', 0, 0, 20, false, 1, 'valuable');
categoryis one of the values ofitem_types_category_check.miscis reserved for uncategorised items and a test fails if you use it; to add a category drop and recreate the constraint as0038_building_farming_fishing.sqldoes. Addwaterfor food that quenches thirst andmax_durabilityfor things that wear out. Set durability per item (not only by category): a better sword or shield must outlast a common one. Merchants inetc/shops.jsoncharge more for those tiers; weapon hits live inetc/creatures.jsonand armour defence inetc/combat.json.Name: add a row
Ruby,Ruby,Rubíntoishtaria-client/locales/client.csvand run the import (it rebuilds the.translationfiles).Icon:
ishtaria-client/assets/icons/items/ruby.png, 128x128 with an alpha channel. Draw it, render a bundled model withtools/bake-item-icons.gd, or generate it (record this in the README credits). A Food Kit picture can be reused withFOOD_FILESinscripts/item_icons.gd.Where it comes from: a drop (
etc/creatures.json,etc/resources.json), a recipe, a shop or a story reward. An item nobody can get is only a catalogue entry.Checks:
configured_items_exist(every item named inetc/exists) and the catalogue test insrc/tests/harvest.rs.
Add a recipe
Append an object to ishtaria-server/etc/recipes.json:
{"id": "make_ruby_ring", "station": "anvil",
"inputs": [{"item": "ruby", "quantity": 1}, {"item": "iron_ingot", "quantity": 2}],
"outputs": [{"item": "ring", "quantity": 1}], "xp": 20}
tool (an item the character owns, axe or pickaxe) and station (a placed object
within four metres: campfire, workbench, anvil) are optional. Stations come from
etc/placeables.json. The unit test configuration_is_consistent and configured_items_exist
cover the file; copy the pattern of weapons_and_armour_are_made_from_ore_and_hides in
src/tests/combat.rs for a crafting test. The client lists new recipes by itself.
Add a spell
Scroll item (migration, as above) named
scroll_<id>with categorydocument, and its icon.ishtaria-server/etc/spells.json:id,name,scroll,mana,cooldown_ms,min_leveland aneffectof kindheal,refresh,wardorbolt. A new kind of effect is Rust insrc/magic.rs(anEffectvariant and a match arm) and needs a note in ADR 0010 on balance.A recipe for the scroll at a workbench (not a shop: magic is earned, see ADR 0010).
Translations of the spell and scroll names in
client.csv. Spells are listed and cast by number keys automatically.Tests:
bundled_spells_are_consistentruns by itself; add a case tosrc/tests/magic.rs.
Add a crop, a thing to place, or fishing
ishtaria-server/etc/placeables.json has things (an item that can be put into the world, with
model, scale, spacing_m and an optional station), crops (seed_<name> with the
produce, amounts and grow_minutes) and fishing. Add the items by migration, the model to the
client under assets/ with its licence (models are named <kit>.<file> and the kits are in
PROP_KITS in scripts/surface_environment.gd), translations and icons. Crops grow by the
clock, so no code is needed. Test with src/tests/farming.rs as the pattern.
Add a merchant or a shop
Goods and prices:
ishtaria-server/etc/shops.json. Selling must pay less than buying costs (a unit test checks it), anddaily_sell_limit_goldbounds the gold a merchant creates. Better gear of the same kind needs a higher price and its ownmax_durability(see Add an item).A merchant is an NPC with the tag
shop:<id>: in a datadisk addtags: [shop, 'shop:general']to the NPC, or give a generated NPC the tag insrc/story/settlements.rs. Nothing else is needed; the client opens the shop when the player pressesEnext to the NPC.Wayfarers and ground loot are generated by the server (
src/loot.rs,etc/traveler.yaml); do not invent a parallel drop system in a datadisk. Rules: Trading, ground loot and wayfarers.
Add or change an animal
etc/creatures.json (hits to butcher, respawn, drops; the weapons table is the strength of
each weapon) and, if it bites back, etc/combat.json (fierce: damage and chance). The
model must exist in the world’s fauna (etc/world_fauna.json).
Add an endpoint
A function in the module of the area (or a new module with
fn routes() -> Router<AppState>merged inmain.rs) that follows How a request is handled in Tour of the server code.Add a migration for new tables with constraints that hold the invariants (
CHECK, unique indexes,ON DELETE); do not rely on the code alone.Test it with the helpers in
src/tests.rs: a refused request changes nothing, a repeated one is harmless, two players do not step on each other, the dead and strangers get the right status.Document the route in the server README and, if it is a persisted contract, in a decision record.
Add a window or a key to the client
A
*_client.gdwithstart(url, token),stop()(bump_generation), validators and fixed error keys; copyscripts/trading.gdorscripts/placed_client.gd.A panel (
CanvasLayer) that emits signals and only shows what it is given; copyscripts/trade_panel.gd.Create both in
scripts/main.gd, connect signals, start them where the others are started and stop them when the profile becomes empty (leaving a server).Keys: add an entry to
character_controls.ACTIONS(default + Settings label), handle it viamatches_actionin_input, and include the panel in_game_ui_open.A
tests/<name>.gdscript and amake test-<name>target; text keys inclient.csv.Look at the result with your own eyes.
Add an NPC or a quest
Story content belongs in a datadisk (see ishtaria-protocol/DATADISK-SPEC.md): places, NPCs,
dialogue trees, quests and strings in English and Czech. Datadisks are pinned by hash and carry no
code. Generated towns and their traders are in src/story/settlements.rs.
A character can tell what it knows: add a card to knowledge.yaml (text keys in every language, a picture below
media/knowledge/ with its credit, the Wikipedia titles) and name it with about on the NPC; the generated
characters use etc/knowledge.yaml and assets/knowledge/ of the server (decision 0019).