2026-02-21 18:57:02 -05:00
|
|
|
# Item Type Template
|
|
|
|
|
|
2026-02-24 03:00:30 -05:00
|
|
|
This page is the practical template for the current plugin-driven item architecture.
|
2026-02-21 18:57:02 -05:00
|
|
|
|
|
|
|
|
## Plain-English Flow
|
|
|
|
|
|
2026-02-24 03:00:30 -05:00
|
|
|
When adding a new item type:
|
|
|
|
|
|
|
|
|
|
1. Server item module
|
|
|
|
|
- Add `server/app/items/types/<item_type>/module.py`.
|
|
|
|
|
- Define metadata/constants:
|
|
|
|
|
- `LABEL`, `TOOLTIP`
|
|
|
|
|
- `EDITABLE_PROPERTIES`
|
|
|
|
|
- `CAPABILITIES`
|
|
|
|
|
- `USE_SOUND`, `EMIT_SOUND`
|
|
|
|
|
- `USE_COOLDOWN_MS`, `EMIT_RANGE`, `DIRECTIONAL`
|
|
|
|
|
- `DEFAULT_TITLE`, `DEFAULT_PARAMS`
|
|
|
|
|
- `PROPERTY_METADATA`
|
2026-02-21 22:04:17 -05:00
|
|
|
- Implement behavior:
|
|
|
|
|
- `validate_update(item, next_params)`
|
|
|
|
|
- `use_item(item, nickname, clock_formatter)`
|
|
|
|
|
|
2026-02-24 03:00:30 -05:00
|
|
|
2. Server plugin file
|
|
|
|
|
- Add `server/app/items/types/<item_type>/plugin.py` exporting:
|
|
|
|
|
- `type`
|
|
|
|
|
- `order`
|
|
|
|
|
- `module`
|
2026-02-21 22:04:17 -05:00
|
|
|
|
2026-02-24 03:00:30 -05:00
|
|
|
3. Shared item-type unions
|
2026-02-21 22:04:17 -05:00
|
|
|
- Add the type in:
|
|
|
|
|
- `server/app/models.py`
|
|
|
|
|
- `client/src/network/protocol.ts`
|
|
|
|
|
- `client/src/state/gameState.ts`
|
|
|
|
|
|
2026-02-24 03:00:30 -05:00
|
|
|
4. Client runtime behavior (optional)
|
|
|
|
|
- Default: no item-specific client module needed.
|
|
|
|
|
- Add `client/src/items/types/<item_type>/behavior.ts` only if this item needs custom client runtime UX/audio logic (for example piano mode).
|
2026-02-21 18:57:02 -05:00
|
|
|
|
|
|
|
|
That is enough for a first working item type.
|
|
|
|
|
|
2026-02-21 22:04:17 -05:00
|
|
|
## Minimal Server Module Example: `counter`
|
2026-02-21 18:57:02 -05:00
|
|
|
|
2026-02-24 03:00:30 -05:00
|
|
|
`server/app/items/types/counter/module.py`:
|
2026-02-21 18:57:02 -05:00
|
|
|
|
|
|
|
|
```py
|
2026-02-21 22:04:17 -05:00
|
|
|
from __future__ import annotations
|
|
|
|
|
|
|
|
|
|
from typing import Callable
|
|
|
|
|
|
2026-02-24 03:00:30 -05:00
|
|
|
from ...item_types import ItemUseResult
|
|
|
|
|
from ...models import WorldItem
|
2026-02-21 22:04:17 -05:00
|
|
|
|
|
|
|
|
LABEL = "counter"
|
|
|
|
|
TOOLTIP = "Counts up each time you use it."
|
|
|
|
|
EDITABLE_PROPERTIES: tuple[str, ...] = ("title", "value")
|
|
|
|
|
CAPABILITIES: tuple[str, ...] = ("editable", "carryable", "deletable", "usable")
|
|
|
|
|
USE_SOUND: str | None = None
|
|
|
|
|
EMIT_SOUND: str | None = None
|
|
|
|
|
USE_COOLDOWN_MS = 1000
|
|
|
|
|
EMIT_RANGE = 15
|
|
|
|
|
DIRECTIONAL = False
|
|
|
|
|
DEFAULT_TITLE = "counter"
|
|
|
|
|
DEFAULT_PARAMS: dict = {"value": 0}
|
|
|
|
|
|
|
|
|
|
PROPERTY_METADATA: dict[str, dict[str, object]] = {
|
|
|
|
|
"title": {"valueType": "text", "tooltip": "Display name spoken and shown for this item."},
|
|
|
|
|
"value": {"valueType": "number", "tooltip": "Current counter value.", "range": {"min": 0, "max": 9999, "step": 1}},
|
2026-02-21 18:57:02 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2026-02-21 22:04:17 -05:00
|
|
|
def validate_update(_item: WorldItem, next_params: dict) -> dict:
|
2026-02-21 18:57:02 -05:00
|
|
|
try:
|
|
|
|
|
value = int(next_params.get("value", 0))
|
|
|
|
|
except (TypeError, ValueError) as exc:
|
|
|
|
|
raise ValueError("value must be a number.") from exc
|
|
|
|
|
if value < 0:
|
|
|
|
|
raise ValueError("value must be 0 or greater.")
|
|
|
|
|
next_params["value"] = value
|
|
|
|
|
return next_params
|
|
|
|
|
|
|
|
|
|
|
2026-02-21 22:04:17 -05:00
|
|
|
def use_item(item: WorldItem, nickname: str, _clock_formatter: Callable[[dict], str]) -> ItemUseResult:
|
|
|
|
|
next_value = int(item.params.get("value", 0)) + 1
|
2026-02-21 18:57:02 -05:00
|
|
|
return ItemUseResult(
|
|
|
|
|
self_message=f"{item.title}: {next_value}",
|
|
|
|
|
others_message=f"{nickname} uses {item.title}: {next_value}",
|
|
|
|
|
updated_params={**item.params, "value": next_value},
|
|
|
|
|
)
|
|
|
|
|
```
|
|
|
|
|
|
2026-02-24 03:00:30 -05:00
|
|
|
Then add plugin registration in `server/app/items/types/counter/plugin.py`:
|
2026-02-21 18:57:02 -05:00
|
|
|
|
2026-02-21 22:04:17 -05:00
|
|
|
```py
|
2026-02-24 03:00:30 -05:00
|
|
|
from . import module
|
2026-02-21 18:57:02 -05:00
|
|
|
|
2026-02-24 03:00:30 -05:00
|
|
|
ITEM_TYPE_PLUGIN = {
|
|
|
|
|
"type": "counter",
|
|
|
|
|
"order": 25,
|
|
|
|
|
"module": module,
|
2026-02-21 22:04:17 -05:00
|
|
|
}
|
2026-02-21 18:57:02 -05:00
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## Checklist Before Commit
|
|
|
|
|
|
|
|
|
|
1. Add/adjust server tests for `use` and `update` validation.
|
|
|
|
|
2. Run `cd server && uv run --extra dev pytest`.
|
|
|
|
|
3. Run `cd client && npm run lint && npm run build`.
|
|
|
|
|
4. Update `docs/item-types.md` and `docs/item-schema.md` if behavior/defaults changed.
|