Event JSON
{
"id": "5e9a8cee19d464f5f0322518ac9ccaf2399c69da6572346b4fb12d36acb17a27",
"pubkey": "a8d1560d6a647d501699167246f237b36fb123f89168fda11dc743533fec7a08",
"created_at": 1776875770,
"kind": 30817,
"tags": [
[
"d",
"nostrmon-decentralized-monster-taming-world-protocol"
],
[
"title",
"Nostrmon — Decentralized Monster-Taming World Protocol"
],
[
"k",
"30597"
],
[
"k",
"30259"
],
[
"k",
"38267"
],
[
"k",
"36011"
],
[
"k",
"35253"
],
[
"k",
"39951"
],
[
"client",
"nostrhub.io"
]
],
"content": "# NIP-XX: Nostrmon — Decentralized Monster-Taming World Protocol\n\n`draft` `optional`\n\n## Abstract\n\nThis NIP defines a decentralized monster-taming game protocol on Nostr. \"Nostrmon\" (Nostr Monsters) is a framework inspired by games like Pokémon, where **regions**, **maps**, **Nostrmon creatures**, **NPCs**, and **game saves** are all Nostr addressable events. Anyone can create and publish content — regions, maps, creatures, or NPCs — and clients can assemble these events into a playable, traversable world.\n\n---\n\n## Event Kinds\n\n| Kind | Name | Description |\n|-------|---------------|--------------------------------------------------|\n| 30597 | Region | A named region containing references to maps |\n| 30259 | Map | A 2D grid-based map with layered tile data |\n| 38267 | Nostrmon | A Nostr Monster creature definition |\n| 36011 | NPC | A Non-Player Character (simple or AI-driven) |\n| 35253 | Game Save | A player's game progress snapshot |\n| 39951 | Item | A usable, holdable, or key item definition |\n\n---\n\n## Kind 30597 — Region\n\nA **Region** is a named collection of maps that form a coherent geographical area. Each user may publish as many regions as they wish.\n\n### Tags\n\n| Tag | Req. | Description |\n|------------|------|-----------------------------------------------------------------|\n| `d` | ✓ | Unique identifier for the region (slug) |\n| `name` | ✓ | Human-readable region name |\n| `map` | ✓ | One or more references to map events. Value: `naddr` of map |\n| `entry` | | The `d` tag of the map that serves as the travel entry/exit point for this region |\n| `summary` | | Short description of the region |\n| `image` | | URL of a banner or splash image for the region |\n| `music` | | `naddr` of a default music event (kind 36787) for the region |\n| `t` | | Hashtags for discovery (e.g., `nostrmon`) |\n| `alt` | ✓ | NIP-31 human-readable description: `\"Nostrmon region: \u003cname\u003e\"` |\n\n### Example\n\n```json\n{\n \"kind\": 30597,\n \"content\": \"A vast volcanic region with ancient ruins and mysterious tunnels.\",\n \"tags\": [\n [\"d\", \"ashfire-region\"],\n [\"name\", \"Ashfire Region\"],\n [\"map\", \"naddr1...map1\"],\n [\"map\", \"naddr1...map2\"],\n [\"entry\", \"ashfire-gateway\"],\n [\"summary\", \"A volcanic region full of fire-type Nostrmon.\"],\n [\"image\", \"https://example.com/ashfire-banner.jpg\"],\n [\"music\", \"naddr1...battletheme\"],\n [\"t\", \"nostrmon\"],\n [\"alt\", \"Nostrmon region: Ashfire Region\"]\n ]\n}\n```\n\n---\n\n## Kind 30259 — Map\n\nA **Map** is a 2D, grid-based environment with up to 5 layers. Maps are the atomic playable unit of the world.\n\n### Tags\n\n| Tag | Req. | Description |\n|--------------|------|-----------------------------------------------------------------------------|\n| `d` | ✓ | Unique identifier (slug) for the map |\n| `name` | ✓ | Human-readable map name |\n| `type` | ✓ | Map type: `route`, `area`, `town`, `city`, `dungeon`, `house` |\n| `width` | ✓ | Map width in tiles (integer, as string) |\n| `height` | ✓ | Map height in tiles (integer, as string) |\n| `region` | | `naddr` of the region this map belongs to. Absent = \"Unknown Region\" |\n| `music` | | `naddr` of a music event (kind 36787) for this map |\n| `multiplayer`| | `\"true\"` or `\"false\"`. Default: `\"false\"` |\n| `prison` | | `\"true\"` or `\"false\"`. Default: `\"false\"`. Disables fast travel on map |\n| `daynight` | | `\"true\"` or `\"false\"`. Whether day/night cycle is visible. Default: `\"true\"` |\n| `entry-map` | | `\"true\"` or `\"false\"`. Marks this map as the region's travel gateway. Entry maps can be linked to entry maps of other regions for cross-region warps. |\n| `fasttravel-x` | | X tile coordinate where the player spawns when fast-traveling to this map (integer string). Default: `\"1\"` |\n| `fasttravel-y` | | Y tile coordinate where the player spawns when fast-traveling to this map (integer string). Default: `\"1\"` |\n| `image` | | URL of a preview image for the map |\n| `t` | | Tags for discovery, always include `nostrmon` |\n| `alt` | ✓ | NIP-31 description: `\"Nostrmon map: \u003cname\u003e\"` |\n\n### Content\n\nThe `content` field is a JSON object with the following structure:\n\n```typescript\ninterface MapContent {\n // Layer 1 (REQUIRED): Terrain layer\n // 2D array [row][col] of terrain tile IDs (integers)\n terrain: number[][];\n\n // Layer 2 (REQUIRED): Interactive layer\n // Sparse array of interactive objects keyed by \"x,y\"\n interactive: Record\u003cstring, InteractiveObject\u003e;\n\n // Layer 3 (OPTIONAL): Height map for 3D rendering\n // 2D array [row][col] of height values (0.0–1.0)\n heightmap?: number[][];\n\n // Layer 4 (OPTIONAL): Wild encounter zones\n // Array of encounter zone definitions\n encounters?: EncounterZone[];\n\n // Layer 5 (OPTIONAL): Named zones\n namedZones?: NamedZone[];\n}\n```\n\n### Terrain Tile IDs\n\nTerrain tile IDs are integers. Clients are responsible for rendering tilesets. Suggested standard tile IDs:\n\n| ID | Type | Category | Passable | Notes |\n|-----|-------------------|------------|----------|------------------------------------|\n| 0 | Void/Empty | Special | No | No tile |\n| 1 | Grass | Outdoor | Yes | May trigger wild encounters |\n| 2 | Tall Grass | Outdoor | Yes | High encounter rate |\n| 3 | Water | Outdoor | Surf | Requires Surf ability |\n| 4 | Deep Water | Outdoor | Surf | Requires Dive ability |\n| 5 | Sand | Outdoor | Yes | |\n| 6 | Snow | Outdoor | Yes | May slow movement |\n| 7 | Ice | Outdoor | Yes | Sliding movement |\n| 8 | Mountain | Outdoor | No | Solid obstacle |\n| 9 | Rock | Outdoor | No | Solid obstacle |\n| 10 | Tree | Outdoor | No | Can be Cut |\n| 11 | Flower | Outdoor | Yes | |\n| 12 | Path/Road | Outdoor | Yes | |\n| 13 | Bridge | Outdoor | Yes | |\n| 14 | Cliff | Outdoor | No | Can Surf below |\n| 15 | Cave Floor | Indoor | Yes | |\n| 16 | Cave Wall | Indoor | No | |\n| 17 | Cave Water | Indoor | Surf | |\n| 18 | Floor (Indoor) | Indoor | Yes | Generic indoor floor |\n| 19 | Wall (Indoor) | Indoor | No | |\n| 20 | Carpet | Indoor | Yes | |\n| 21 | Counter | Indoor | No | Shop counter |\n| 22 | Bookshelf | Indoor | No | |\n| 23 | Lava | Special | No | Damages player |\n| 24 | Pit | Special | Fall | Player falls through |\n| 25 | Warp Pad | Special | Yes | Visual warp indicator |\n| 26 | Tall Grass (Cave) | Indoor | Yes | Encounters in caves |\n| 27 | Sand (Indoor) | Indoor | Yes | |\n| 28 | Swamp | Outdoor | Yes | Slow movement, encounters |\n| 29 | Roof/Overhang | Overhead | — | Renders over player (z-layered) |\n| 30 | Rail/Track | Indoor | Yes | |\n\n#### House Tiles (Outdoor)\n\nHouse tiles are used on outdoor maps to represent buildings the player can enter via warps.\n\n| ID | Type | Category | Passable | Notes |\n|-----|-----------------------|----------------|----------|--------------------------------------------|\n| 31 | House Wall | Outdoor/House | No | Generic exterior house wall |\n| 32 | House Roof | Outdoor/House | No | Roof tile; renders above player level |\n| 33 | House Door | Outdoor/House | Warp | Entrance/exit point; triggers warp action |\n| 34 | House Window | Outdoor/House | No | Decorative; may show light at night |\n| 35 | House Chimney | Outdoor/House | No | Decorative chimney top |\n| 36 | House Foundation | Outdoor/House | No | Base/step of a building |\n| 37 | Shop Awning | Outdoor/House | No | Overhead awning above shop doorways |\n| 38 | House Roof (Peaked) | Outdoor/House | No | Peaked/angled roof section |\n\n#### Fence \u0026 Wall Tiles (Special — Outdoor or Indoor)\n\nFence and wall tiles are traversal obstacles. They can appear in both outdoor and indoor settings.\n\n| ID | Type | Category | Passable | Notes |\n|-----|-------------------|-------------------|----------|------------------------------------------------|\n| 40 | Wooden Fence | Special/Fence | No | Common outdoor low fence; can be jumped over |\n| 41 | Stone Wall (Ext.) | Special/Fence | No | Exterior stone boundary wall |\n| 42 | Iron Fence | Special/Fence | No | Metal fence; urban/city areas |\n| 43 | Hedge | Special/Fence | No | Living plant fence; can be Cut |\n| 44 | Picket Fence | Special/Fence | No | Decorative short fence; used near houses |\n| 45 | Low Wall | Special/Fence | No | Short interior divider; indoor and outdoor |\n| 46 | Barricade | Special/Fence | No | Temporary blockade; can be conditional |\n| 47 | Corner Wall | Special/Wall | No | 90° corner wall piece for interiors |\n| 48 | Pillar | Special/Wall | No | Decorative or structural column; indoor/outdoor|\n| 49 | Fence Gate | Special/Fence | Cond. | Passable only when a condition is met |\n| 50 | Cave Fence | Special/Fence | No | Fence type suited for cave environments |\n| 51 | Fence Post | Special/Fence | No | Single post; connects fence segments |\n\nClients may extend this list with custom tile IDs above 100.\n\n### Interactive Object Types\n\n```typescript\ninterface InteractiveObject {\n type: \"npc\" | \"sign\" | \"warp\" | \"item\" | \"trigger\";\n\n // For type \"npc\"\n npc?: string; // naddr of the NPC event (kind 36011)\n facing?: \"up\" | \"down\" | \"left\" | \"right\";\n\n // For type \"sign\"\n text?: string; // Text content shown when examined\n\n // For type \"warp\"\n // Target map reference. Resolution order:\n // 1. Full naddr (naddr1…) — unambiguous, preferred for cross-user warps\n // 2. d-tag slug — resolved against the same author's maps first, then globally\n target?: string;\n // Tile coordinates on the target map where the player arrives.\n // If omitted, the target map's fasttravel-x/fasttravel-y tags are used as fallback.\n targetX?: number;\n targetY?: number;\n direction?: \"both\" | \"one-way\"; // Default: \"both\"\n condition?: string; // Script condition (see Script System)\n visible?: boolean | string; // true/false or script expression\n\n // For type \"item\"\n item?: string; // Item identifier\n condition?: string; // Script condition (only show/take if met)\n\n // For type \"trigger\"\n script?: string; // Script executed when player steps on tile\n triggerOn?: \"step\" | \"action\"; // Default: \"step\"\n}\n```\n\n### Encounter Zone\n\n```typescript\ninterface EncounterZone {\n // Tiles included in this zone, as array of \"x,y\" coordinates\n // OR a rect: [x, y, width, height]\n tiles?: string[];\n rect?: [number, number, number, number];\n\n // Encounter rate: 0.0–1.0\n rate: number;\n\n // Nostrmon that can appear here\n nostrmon: EncounterEntry[];\n}\n\ninterface EncounterEntry {\n // naddr of the Nostrmon event (kind 38267), or its d-tag\n ref: string;\n\n // Level range\n minLevel: number;\n maxLevel: number;\n\n // Relative rarity weight (default: 1)\n weight?: number;\n\n // Which form index to use (default: 0)\n formIndex?: number;\n}\n```\n\n### Named Zone\n\n```typescript\ninterface NamedZone {\n name: string;\n // Zone type for minimap: \"area\", \"town\", \"city\", \"dungeon\"\n type: \"area\" | \"town\" | \"city\" | \"dungeon\";\n // Rectangle: [x, y, width, height] in tiles\n rect: [number, number, number, number];\n // Optional hex color for minimap rendering\n color?: string;\n}\n```\n\n### Script System\n\nThe interactive layer supports a simple scripting language for conditions and triggers. Scripts are plain strings using a minimal expression syntax:\n\n```\n# Variables (set/check flags)\nSET flag_name\nUNSET flag_name\nHAS flag_name # Check if flag is set\nNOT HAS flag_name\n\n# Item checks\nHAS_ITEM item_id\nNOT HAS_ITEM item_id\nGIVE_ITEM item_id\n\n# Badge/Achievement checks\nHAS_BADGE badge_id\n\n# Dialogue\nSAY \"Line 1\" \"Line 2\" \"Line 3\"\nSAY_CONDITIONAL HAS flag_name \"You already did this.\" \"You haven't done this yet.\"\n\n# Choice boxes\nCHOICE \"Option A\" \"Option B\"\nON_CHOICE 0 SET chose_a\nON_CHOICE 1 SET chose_b\n\n# Warp\nWARP naddr_or_dtag x y\n\n# Battle\nBATTLE naddr_npc\nBATTLE_WILD naddr_nostrmon level\n\n# Control flow\nIF HAS flag_name THEN ... END\n```\n\n---\n\n## Kind 38267 — Nostrmon\n\nA **Nostrmon** is an addressable event defining a creature species, including its stats, types, moves, and sprites.\n\n### Tags\n\n| Tag | Req. | Description |\n|-----------|------|-----------------------------------------------------------------|\n| `d` | ✓ | Unique species identifier (slug, e.g. `flamewulf`) |\n| `name` | ✓ | Species name |\n| `type` | ✓ | One, two, or three types (repeat tag). e.g. `[\"type\", \"fire\"]` |\n| `sprite` | ✓ | URL of the front sprite image |\n| `backsprite` | | URL of the back sprite (for battle screen) |\n| `model3d` | | URL of a 3D model file (glTF/glb) for 3D clients |\n| `hp` | ✓ | Base HP stat (integer string) |\n| `atk` | ✓ | Base Attack stat |\n| `def` | ✓ | Base Defense stat |\n| `spatk` | ✓ | Base Special Attack stat |\n| `spdef` | ✓ | Base Special Defense stat |\n| `spd` | ✓ | Base Speed stat |\n| `move` | ✓ | Learnable move. Format: `[\"move\", \"move-id\", \"learn-method\", \"level-or-condition\"]` |\n| `t` | | `nostrmon` and any other tags |\n| `alt` | ✓ | NIP-31 description: `\"Nostrmon creature: \u003cname\u003e\"` |\n\n### Content\n\nThe `content` field is a JSON object:\n\n```typescript\ninterface NostrmonContent {\n // Flavor text / Pokédex-style description\n description?: string;\n\n // Forms (if more than one form exists)\n forms?: NostrmonForm[];\n}\n\ninterface NostrmonForm {\n name: string; // Form name (e.g., \"Mega\", \"Shiny\", \"Winter\")\n sprite: string; // Front sprite URL for this form\n backsprite?: string; // Back sprite URL for this form\n model3d?: string; // 3D model URL for this form\n types?: string[]; // Override types for this form\n // Override stats (omitted = use base stats)\n hp?: number;\n atk?: number;\n def?: number;\n spatk?: number;\n spdef?: number;\n spd?: number;\n}\n```\n\n### Move Learn Methods\n\nThe `move` tag's learn method field accepts:\n\n| Method | Description |\n|------------|---------------------------------------------------|\n| `level` | Learned at the given level number |\n| `tm` | Taught via Technical Machine item |\n| `egg` | Learned via breeding (egg move) |\n| `tutor` | Taught by a move tutor NPC |\n| `evolution`| Learned upon evolution |\n\n### Types\n\nStandard Nostrmon types (clients may define custom types):\n\n`normal`, `fire`, `water`, `electric`, `grass`, `ice`, `fighting`, `poison`, `ground`, `flying`, `psychic`, `bug`, `rock`, `ghost`, `dragon`, `dark`, `steel`, `fairy`, `cosmic`, `digital`, `sound`, `nature`\n\n### Example\n\n```json\n{\n \"kind\": 38267,\n \"content\": \"{\\\"description\\\":\\\"A canine Nostrmon wreathed in spectral flame. It is said to guard the gates of forgotten servers.\\\",\\\"forms\\\":[]}\",\n \"tags\": [\n [\"d\", \"flamewulf\"],\n [\"name\", \"Flamewulf\"],\n [\"type\", \"fire\"],\n [\"type\", \"ghost\"],\n [\"sprite\", \"https://example.com/flamewulf-front.png\"],\n [\"backsprite\", \"https://example.com/flamewulf-back.png\"],\n [\"hp\", \"75\"],\n [\"atk\", \"90\"],\n [\"def\", \"60\"],\n [\"spatk\", \"85\"],\n [\"spdef\", \"65\"],\n [\"spd\", \"100\"],\n [\"move\", \"ember\", \"level\", \"5\"],\n [\"move\", \"shadow-ball\", \"level\", \"30\"],\n [\"move\", \"flamethrower\", \"tm\", \"\"],\n [\"t\", \"nostrmon\"],\n [\"alt\", \"Nostrmon creature: Flamewulf\"]\n ]\n}\n```\n\n---\n\n## Kind 36011 — NPC\n\nAn **NPC** (Non-Player Character) is a character that can be placed on maps. NPCs come in two varieties: **Simple** (scripted dialogue) and **AI** (language model driven).\n\n### Tags\n\n| Tag | Req. | Description |\n|-------------|------|------------------------------------------------------------------------|\n| `d` | ✓ | Unique NPC identifier (slug) |\n| `name` | ✓ | NPC display name |\n| `npc-type` | ✓ | `\"simple\"` or `\"ai\"` |\n| `sprite` | ✓ | URL of the NPC's sprite image |\n| `nostrmon` | | `naddr` of a Nostrmon. Repeat for multiple Nostrmon (used in battles) |\n| `nostrmon-level` | | Level for each Nostrmon (paired by index with `nostrmon` tags) |\n| `trainer-class` | | Trainer class string (e.g., `\"Bug Catcher\"`, `\"Gym Leader\"`) |\n| `battle-music` | | `naddr` of a music event for the battle against this NPC |\n| `t` | | `nostrmon` and discovery tags |\n| `alt` | ✓ | NIP-31 description: `\"Nostrmon NPC: \u003cname\u003e\"` |\n\n### Content\n\nThe `content` field is a JSON object:\n\n```typescript\ninterface NPCContent {\n // Scripted dialogue (required for simple NPCs, used as fallback for AI NPCs)\n dialogue: string[];\n\n // Choice-based dialogue branches (optional)\n choices?: NPCChoice[];\n\n // For AI NPCs only\n ai?: {\n // Describe the NPC's knowledge, backstory, and behavior in natural language\n knowledge: string;\n // NPC personality and behavior description\n behavior: string;\n // Preferred languages (English always required)\n languages?: string[];\n // Additional freeform properties\n notes?: string;\n };\n}\n\ninterface NPCChoice {\n prompt: string; // The player's choice text\n response: string[]; // NPC response dialogue\n condition?: string; // Script condition to show this choice\n effect?: string; // Script effect when chosen\n}\n```\n\n### Example (Simple NPC)\n\n```json\n{\n \"kind\": 36011,\n \"content\": \"{\\\"dialogue\\\":[\\\"Greetings, traveler!\\\",\\\"The Ashfire Caves to the east are dangerous.\\\",\\\"Make sure your Nostrmon are well rested before you go!\\\"]}\",\n \"tags\": [\n [\"d\", \"old-hermit-ashfire\"],\n [\"name\", \"Old Hermit\"],\n [\"npc-type\", \"simple\"],\n [\"sprite\", \"https://example.com/hermit-sprite.png\"],\n [\"t\", \"nostrmon\"],\n [\"alt\", \"Nostrmon NPC: Old Hermit\"]\n ]\n}\n```\n\n### Example (AI NPC)\n\n```json\n{\n \"kind\": 36011,\n \"content\": \"{\\\"dialogue\\\":[\\\"Hello! I'm Professor Nakamoto. I study Nostrmon across the decentralized world.\\\"],\\\"ai\\\":{\\\"knowledge\\\":\\\"Expert on Nostrmon biology, types, and habitats. Knows all maps in Ashfire Region.\\\",\\\"behavior\\\":\\\"Friendly and enthusiastic. Loves giving hints. Never breaks character.\\\",\\\"languages\\\":[\\\"English\\\",\\\"Japanese\\\"],\\\"notes\\\":\\\"Occasionally quotes Satoshi Nakamoto philosophically.\\\"}}\",\n \"tags\": [\n [\"d\", \"professor-nakamoto\"],\n [\"name\", \"Prof. Nakamoto\"],\n [\"npc-type\", \"ai\"],\n [\"sprite\", \"https://example.com/professor-sprite.png\"],\n [\"t\", \"nostrmon\"],\n [\"alt\", \"Nostrmon NPC: Prof. Nakamoto\"]\n ]\n}\n```\n\n---\n\n## Kind 35253 — Game Save\n\nA **Game Save** records a player's progress. It is a replaceable event per `d` tag, allowing players to maintain named save slots. Players may also export/import saves locally.\n\n### Tags\n\n| Tag | Req. | Description |\n|-------------|------|-------------------------------------------------------------------|\n| `d` | ✓ | Save slot identifier (e.g., `\"save-1\"`) |\n| `save-name` | | Human-readable save name |\n| `region` | | `naddr` of the current region |\n| `map` | | `d` tag of the current map |\n| `playtime` | | Total playtime in seconds (integer string) |\n| `version` | ✓ | Protocol version of this save format (e.g., `\"1\"`) |\n| `alt` | ✓ | NIP-31 description: `\"Nostrmon game save\"` |\n\n### Content\n\nThe `content` field is **NIP-44 encrypted** to the player's own pubkey and contains the full game state as JSON:\n\n```typescript\ninterface GameSaveContent {\n // Player info\n playerName: string;\n badges: string[]; // Badge IDs earned\n\n // Current position\n currentMap: string; // d-tag of current map\n currentRegion?: string; // naddr of current region\n positionX: number;\n positionY: number;\n\n // Active team (up to 6 Nostrmon)\n team: OwnedNostrmon[];\n\n // PC Storage boxes\n storage: OwnedNostrmon[][];\n\n // Inventory\n inventory: Record\u003cstring, number\u003e; // item_id -\u003e quantity\n\n // World flags (script system state)\n flags: Record\u003cstring, boolean\u003e;\n\n // Completed NPC battles\n defeatedTrainers: string[]; // NPC d-tags\n\n // Timestamps\n savedAt: number;\n startedAt: number;\n}\n\ninterface OwnedNostrmon {\n speciesRef: string; // naddr or d-tag of species (kind 38267)\n nickname?: string;\n level: number;\n experience: number;\n formIndex: number; // Current form (default: 0)\n hp: number; // Current HP\n moves: string[]; // Up to 4 move IDs currently equipped\n learnedMoves: string[];// All moves ever learned\n // Individual Values (genetic variation, 0-31 per stat)\n ivs: { hp: number; atk: number; def: number; spatk: number; spdef: number; spd: number; };\n // Effort Values (training gains)\n evs: { hp: number; atk: number; def: number; spatk: number; spdef: number; spd: number; };\n // Personality traits (affect stat calculations)\n nature: string;\n caughtAt: number; // Unix timestamp\n caughtMap?: string; // d-tag of map where caught\n}\n```\n\n---\n\n## Kind 39951 — Item\n\nAn **Item** is an addressable event defining a usable, holdable, or key item that can be found in the world, bought in shops, or given by NPCs. Items from any author can be referenced.\n\n### Tags\n\n| Tag | Req. | Description |\n|-------------|------|--------------------------------------------------------------------------|\n| `d` | ✓ | Unique item identifier (slug, e.g. `potion`, `escape-rope`) |\n| `name` | ✓ | Human-readable item name |\n| `item-type` | ✓ | Category: `consumable`, `hold`, `key`, `tm`, `ball`, `badge`, `misc` |\n| `sprite` | | URL of the item's icon/sprite image |\n| `price` | | Default shop buy price in gold (integer string). `\"0\"` = not for sale |\n| `sell-price`| | Default sell price (integer string). Omit = half of `price` |\n| `single-use`| | `\"true\"` if consumed on use. Default: `\"true\"` for consumables |\n| `holdable` | | `\"true\"` if a Nostrmon can hold this item in battle |\n| `t` | | `nostrmon` and any additional tags |\n| `alt` | ✓ | NIP-31 description: `\"Nostrmon item: \u003cname\u003e\"` |\n\n### Content\n\nThe `content` field is a JSON object:\n\n```typescript\ninterface ItemContent {\n // Description shown in the bag/item screen\n description: string;\n\n // What happens when this item is used (plain-text script or freeform description)\n // Clients interpret this to apply effects. Common effect keywords:\n // HEAL_HP \u003camount\u003e — restore HP to one Nostrmon\n // HEAL_HP_ALL \u003camount\u003e — restore HP to all team Nostrmon\n // HEAL_STATUS — cure a status condition\n // REVIVE — revive a fainted Nostrmon to half HP\n // REVIVE_FULL — revive to full HP\n // CATCH_RATE \u003cmultiplier\u003e — ball catch-rate multiplier (for ball items)\n // TEACH_MOVE \u003cmove-id\u003e — teach a move (for TM items)\n // WARP_ESCAPE — escape from a dungeon to last town\n // BADGE \u003cbadge-id\u003e — grants a badge (for badge items)\n effect?: string;\n}\n```\n\n### Item Categories\n\n| Category | Description |\n|-------------|------------------------------------------------------------------------|\n| `consumable`| Used from the bag; disappears after use (e.g. Potion, Antidote) |\n| `hold` | Held by a Nostrmon during battle for passive effects |\n| `key` | Key item; never consumed; triggers world events or opens warps |\n| `tm` | Technical Machine; teaches a move to a compatible Nostrmon |\n| `ball` | Used in battle to catch wild Nostrmon; has a `CATCH_RATE` multiplier |\n| `badge` | Proof of a gym/trainer victory; unlocks abilities or areas |\n| `misc` | Miscellaneous item with no standard category |\n\n### Example\n\n```json\n{\n \"kind\": 39951,\n \"content\": \"{\\\"description\\\":\\\"Restores 20 HP to a single Nostrmon.\\\",\\\"effect\\\":\\\"HEAL_HP 20\\\"}\",\n \"tags\": [\n [\"d\", \"potion\"],\n [\"name\", \"Potion\"],\n [\"item-type\", \"consumable\"],\n [\"sprite\", \"https://example.com/items/potion.png\"],\n [\"price\", \"300\"],\n [\"sell-price\", \"150\"],\n [\"single-use\", \"true\"],\n [\"holdable\", \"false\"],\n [\"t\", \"nostrmon\"],\n [\"alt\", \"Nostrmon item: Potion\"]\n ]\n}\n```\n\n---\n\n## Querying Content\n\n### Discover all Nostrmon (any author)\n\n```json\n[{ \"kinds\": [38267], \"#t\": [\"nostrmon\"], \"limit\": 50 }]\n```\n\n### Discover all regions (any author)\n\n```json\n[{ \"kinds\": [30597], \"#t\": [\"nostrmon\"], \"limit\": 50 }]\n```\n\n### Get a specific NPC by author and d-tag\n\n```json\n[{ \"kinds\": [36011], \"authors\": [\"\u003cpubkey\u003e\"], \"#d\": [\"old-hermit-ashfire\"] }]\n```\n\n### Discover all items (any author)\n\n```json\n[{ \"kinds\": [39951], \"#t\": [\"nostrmon\"], \"limit\": 100 }]\n```\n\n### Get all maps in a region (by region naddr reference)\n\nMaps reference their region via the `region` tag. Clients should query maps and filter by region reference client-side, or use a relay that supports tag filtering.\n\n---\n\n## Battle Rules\n\n### Wild Battles\n\n- A wild encounter is triggered when the player steps on a tile inside an encounter zone, according to the zone's `rate` (0–1).\n- **Auto-catch rule**: Wild Nostrmon at **level 1–5** are caught automatically at **100% success rate** with no battle required. This ensures new players always get their first Nostrmon.\n- If the player has no Nostrmon in their team, wild encounters at any level trigger auto-catch instead of a battle.\n- For wild Nostrmon above level 5, a full battle takes place. The player may fight, use items, switch Nostrmon, or attempt to flee.\n- **Catching** requires a ball item. Catch rate = `0.3 + (1 − currentHpRatio) × 0.7` modified by the ball's `CATCH_RATE` multiplier.\n- The player can flee from any wild battle without penalty.\n\n### Trainer Battles\n\n- NPCs with one or more `nostrmon` tags initiate trainer battles when the player interacts with them.\n- The player **cannot** catch the Nostrmon of another trainer.\n- The player **cannot** flee a trainer battle.\n- Defeating a trainer stores their `d`-tag in `defeatedTrainers` in the save. Re-interacting with a defeated trainer shows fallback dialogue instead of battle.\n- Trainer battle music uses the NPC's `battle-music` tag if set, otherwise the region's default battle theme.\n\n### Stat \u0026 Damage Calculation\n\n- Stats use the standard formula: `floor((2×base + IV + floor(EV/4)) × level / 100) + 5` (or `+level+10` for HP)\n- IVs: random 0–31 per stat, generated at catch/receive time\n- EVs: start at 0, gained from defeating Nostrmon\n- Natures: 25 natures, each raises one stat by 10% and lowers another (or neutral)\n- Level-up: cubic XP formula `XP_needed = (level+1)³ − level³`\n\n### XP \u0026 Levelling\n\n- Clients MUST award XP to the active Nostrmon after defeating or catching a wild Nostrmon\n- XP reward: `floor((sum_of_base_stats × enemy_level) / 7)`\n- When a Nostrmon's XP exceeds the threshold, it levels up and its stats recalculate\n- Clients SHOULD check for move learning on level-up (from the species `move` tags with `learn-method: level`)\n\n---\n\n## Multiplayer Presence\n\nOn maps with `multiplayer: \"true\"`, clients SHOULD broadcast the player's position using **ephemeral kind 20001** events (\"Presence Events\").\n\n### Kind 20001 — Presence Event (Ephemeral)\n\n| Field | Value |\n|-------|-------|\n| `kind` | `20001` |\n| `content` | JSON: `{\"x\": \u003ctile_x\u003e, \"y\": \u003ctile_y\u003e, \"name\": \"\u003cdisplay_name\u003e\"}` |\n| `tags` | `[\"d\", \"\u003cmap_d_tag\u003e\"]`, `[\"expiration\", \"\u003cunix_timestamp+30\u003e\"]` |\n\n- Clients broadcast their position every ~5 seconds while on a multiplayer map\n- Clients subscribe to kind 20001 events filtered by the current map's `d` tag\n- Players not seen for 20+ seconds should be removed from the display\n- Other players are shown as coloured dots or avatars on the map\n- Position data is **not saved** — it is purely ephemeral\n\n---\n\n## Client Requirements\n\n### MUST\n\n- Clients MUST be able to render and play maps using terrain and interactive layers\n- Clients MUST support Nostrmon battles (wild and trainer)\n- Clients MUST auto-catch wild Nostrmon at level 1–5 at 100% rate without battle\n- Clients MUST prevent catching in trainer battles\n- Clients MUST prevent fleeing in trainer battles\n- Clients MUST support game saves (kind 35253) with NIP-44 encryption\n- Clients MUST persist saves locally (e.g. localStorage) in addition to Nostr\n- Clients MUST treat maps without a `region` tag as belonging to \"Unknown Region\"\n- Clients MUST use `fasttravel-x` / `fasttravel-y` as the spawn position when fast-traveling to a map\n- Clients MUST NOT allow fast-traveling to a map with `prison: \"true\"` unless it belongs to the current user\n- Clients MUST implement an in-game menu with at minimum: team management, bag/inventory, save game\n\n### SHOULD\n\n- Clients SHOULD render a minimap for regions with map type-appropriate icons\n- Clients SHOULD support keyboard navigation and touch controls on mobile\n- Clients SHOULD support gamepad/controller input\n- Clients SHOULD render day/night cycles for maps where `daynight` is `\"true\"`\n- Clients SHOULD handle AI NPCs with configurable AI provider/model\n- Clients SHOULD list entry maps (`entry-map: \"true\"`) from all authors for cross-region warp selection\n- Clients SHOULD broadcast and receive presence events on multiplayer maps (kind 20001)\n- Clients SHOULD show PC box storage for Nostrmon overflow management\n\n### MAY\n\n- Clients MAY render maps in 2.5D or 3D using the height map layer\n- Clients MAY provide editors for regions, maps, Nostrmon, NPCs, and items\n- Clients MAY support custom tilesets beyond the standard tile IDs\n- Clients MAY support NPC shops (using item `price` tags for buy/sell)\n- Clients MAY implement Nostrmon trading between players on multiplayer maps\n\n---\n\n## Interoperability Notes\n\n- All creature references use `naddr` (kind 38267) or the `d` tag — clients should support both\n- Map warps referencing other users' maps are valid and encouraged for cross-region travel\n- Nostrmon from any author may be referenced in encounter zones\n- Music references use kind 36787 (Nostr Music events)\n- The `t: nostrmon` tag is used for all discovery queries — clients SHOULD include it on all published events\n\n---\n\n*This NIP was authored as part of the Nostrmon World client. Feedback welcome.*",
"sig": "a05e672bebb9da149e8af39383a855233f08c95c0caf7fe4d99df0f016cc197914034f3b50273a4bd29dd66ff02d2df1b55f818f96fa1a38e02204f162f2f00d"
}