weapon
Registers under Weapons · source: modules/weapon/v1
Builds and registers a weapon record under the "weapon" category. `icon_id` is the filename stem of this item's art in assets/items/ (`icon_id="rusty_sword"` -> assets/items/rusty_sword.png) — pure display data like `description`, never read by the engine; empty (or no such file) renders the category's placeholder. See bot/item_icons.py. `name` is the player-facing display label (e.g. "Rusty Sword" for `id="rusty_sword"`) — every place a player sees this weapon (Shop, inventory, character sheet) shows `name`, never the raw `id`; `id` stays the thing typed into a command's arguments. `damage_types` is at most two ids (base + optional hybrid — see combat-engine-readme.md → Typed Resistance's weapon cap; adding the second one post-acquisition goes through the Artificer, not this module). `hitdie` is a Content-authored {"diceCount": n, "dieSize": n} pair. `tier` is resolved into a concrete `baseDamage` multiplier here, at build time — the engine never sees `tier`/`Tier.LEGENDARY`, only the flattened number. `can_be_in_shop`/`shop_price` feed the Shop's `CanBeInShop: true` pool (see combat-engine-readme.md → Gear → Locations & Economy) — rotation cadence/pool size are explicitly TBD, so this module just tags eligibility and a price, nothing more. `base_damage` now defaults to a flat placeholder (6.0) rather than being required — a real per-weapon-category baseline is just as undecided as everything else `Tier`/`TIER_MODIFIER` stand in for (see content-open-questions.md → Gear), and this specific default also has to exist for `COMMUNITY_SCHEMA` below to actually be callable: `base_damage` isn't a community-settable field (see toolchain-architecture.md → Community Content Authoring — it's a raw balance number, exactly what that section says a community author should never touch directly), so every required-with-no-default parameter of this factory MUST either be in COMMUNITY_SCHEMA or have a default — otherwise a `.resource` file could never successfully call this module at all. See gamemaster/builder/resource_parser.py's own build-time check for this exact invariant. `damage_multiplier`, when given, replaces the flat `TIER_MODIFIER[tier]` lookup entirely — this is what gives two same-tier weapons different real damage instead of an identical fixed number. It's deliberately a *separate* field from `base_damage`: `base_damage` stays closed (still the raw balance input), while `damage_multiplier` is the one new field a `.resource` submission is actually allowed to carry, and in practice it's the registry_site Pipeline that's expected to set it — rolled once from content/tuning.py's weaponTierDamageMultiplierRange for the weapon's own `tier`, at Plan time, with the rolled literal baked straight into the saved file (see registry_site/pipeline.py). Nothing stops a human from hand-typing this field too (COMMUNITY_SCHEMA can't tell who wrote a line), so `VALID_DAMAGE_MULTIPLIER_RANGE` clamps it to a generous sanity rail regardless of source — real per-tier balance stays the Pipeline's table's job, not this module's. `shop_price` is the exact same shape as `damage_multiplier` above: schema-allowed so a Pipeline-written value survives a rebuild, clamped to `VALID_SHOP_PRICE_RANGE` as a backstop, and in practice rolled by the Pipeline (content/tuning.py's shopPriceRangeByTier, keyed by `tier`, only once `can_be_in_shop` is set) rather than hand-typed. Left at its 0.0 default (can_be_in_shop off, or a Pipeline submission the Pipeline didn't roll a price for), the live shopPriceByTier fallback in bot/item_pricing.py still applies — that fallback is for trusted-Python content that never goes through the Pipeline at all, unaffected by any of this. `tags`: free-text labels (e.g. "pixel art") for the registry-site Catalog's filter (DND-30, 2026-10-20) — pure display/browse metadata, same "never read by the engine" status as `description`. No fixed vocabulary; the Catalog just surfaces whatever's actually in use.
Example Usage
resource "apprentice_wand" {
source: modules/weapon/v1
id: "apprentice_wand"
name: "Apprentice Wand"
weapon_category: "magic"
tier: "common"
damage_types: ["fire"]
can_be_in_shop: true
description: "A knobby length of ash, warm to the touch. Every Wizard starts with one."
icon_id: "apprentice_wand"
tags: ["pixel art"]
}
Arguments Reference
The following arguments are supported:
-
id- (Required) The unique id this record is registered and referenced under — what a `.resource` file's own filename-independent identity is, and what any other content's foreign-key-style field (a dungeon node's `next`, a skill node's `parent_id`, and so on) points at. community -
name- (Required) `name` is the player-facing display label (e.g. "Rusty Sword" for `id="rusty_sword"`) — every place a player sees this weapon (Shop, inventory, character sheet) shows `name`, never the raw `id`; `id` stays the thing typed into a command's arguments. community -
weapon_category- (Required) Currently registered:magic,meleeandranged. community -
base_damage- (Optional) `base_damage` now defaults to a flat placeholder (6.0) rather than being required — a real per-weapon-category baseline is just as undecided as everything else `Tier`/`TIER_MODIFIER` stand in for (see content-open-questions.md → Gear), and this specific default also has to exist for `COMMUNITY_SCHEMA` below to actually be callable: `base_damage` isn't a community-settable field (see toolchain-architecture.md → Community Content Authoring — it's a raw balance number, exactly what that section says a community author should never touch directly), so every required-with-no-default parameter of this factory MUST either be in COMMUNITY_SCHEMA or have a default — otherwise a `.resource` file could never successfully call this module at all. Defaults to6.0. developer -
tier- (Optional) `tier` is resolved into a concrete `baseDamage` multiplier here, at build time — the engine never sees `tier`/`Tier.LEGENDARY`, only the flattened number. Valid options arecommon,rareandlegendary. Defaults to'common'. community -
damage_multiplier- (Optional) `damage_multiplier`, when given, replaces the flat `TIER_MODIFIER[tier]` lookup entirely — this is what gives two same-tier weapons different real damage instead of an identical fixed number. Defaults toNone. community -
slot- (Optional) Defaults to'mainhand'. developer -
damage_types- (Optional) `damage_types` is at most two ids (base + optional hybrid — see combat-engine-readme.md → Typed Resistance's weapon cap; adding the second one post-acquisition goes through the Artificer, not this module). Defaults toNone. community -
hitdie- (Optional) `hitdie` is a Content-authored {"diceCount": n, "dieSize": n} pair. Defaults toNone. developer -
essence_cost- (Optional) Defaults to'light'. developer -
can_be_in_shop- (Optional) `can_be_in_shop`/`shop_price` feed the Shop's `CanBeInShop: true` pool (see combat-engine-readme.md → Gear → Locations & Economy) — rotation cadence/pool size are explicitly TBD, so this module just tags eligibility and a price, nothing more. Defaults toFalse. community -
shop_price- (Optional) `shop_price` is the exact same shape as `damage_multiplier` above: schema-allowed so a Pipeline-written value survives a rebuild, clamped to `VALID_SHOP_PRICE_RANGE` as a backstop, and in practice rolled by the Pipeline (content/tuning.py's shopPriceRangeByTier, keyed by `tier`, only once `can_be_in_shop` is set) rather than hand-typed. Defaults to0.0. community -
description- (Optional) Flavor text shown to players — pure display data with no effect on game mechanics. community -
icon_id- (Optional) `icon_id` is the filename stem of this item's art in assets/items/ (`icon_id="rusty_sword"` -> assets/items/rusty_sword.png) — pure display data like `description`, never read by the engine; empty (or no such file) renders the category's placeholder. community -
tags- (Optional) `tags`: free-text labels (e.g. "pixel art") for the registry-site Catalog's filter (DND-30, 2026-10-20) — pure display/browse metadata, same "never read by the engine" status as `description`. Defaults toNone. community