World-Builder Guide
Item System Guide
Create items, attacks, loot tables, and entities — the field-by-field JSON reference.
A generic reference for authoring items, attacks, loot tables, and entities in any Fragmented Planes area bundle. The rules and field definitions apply to every bundle you build. Concrete names (mobs, items, elements) come from the sample bundle the Scar purely as examples — replace them with your own.
1. Power Tiers & Drop Rate Caps
Every item and entity has a power_tier. Valid tiers are 1–10. A bundle is free to use any subset — the sample bundle the Scar uses only 1–3.
The federation enforces a maximum drop probability per tier for GLOBAL (transferable) items. The cap is checked twice — by the Editor at authoring time and by the Server at roll time — so a tampered bundle cannot exceed the budget. Soulbound items are unconstrained (they never leave the player, so they don't consume the shared economy budget) and are not checked against this table.
| Tier | Global drop-rate cap | Typical role |
|---|---|---|
| 1 | 0.10 | Early gear, common valuables |
| 2 | 0.05 | Mid-game equipment, ability stones |
| 3 | 0.02 | Strong gear, rare ability stones |
| 4 | 0.01 | Elite gear |
| 5 | 0.005 | Near end-game |
| 6–10 | 0.001 | End-game / federated rarities |
// GLOBAL item — probability must be ≤ its tier cap
{ "item_def_id": "dragon_scale", "probability": 0.02 } // tier 3 → ≤ 0.02 ✓
{ "item_def_id": "steel_sword", "probability": 0.05 } // tier 2 → ≤ 0.05 ✓
// SOULBOUND item (e.g. a bound consumable/quest item) — uncapped
{ "item_def_id": "health_potion", "probability": 0.25 } // fine: soulbound
The cap applies per drop-table entry, independently. Listing the same global item on several monsters is allowed — each entry just has to stay under the cap on its own (drops roll independently; see §10).
2. Item Categories
"category": "gear" | "consumable" | "ability" | "quest"
| Category | Purpose | Can have attack? | Can have stat_bonuses? |
|---|---|---|---|
gear | Equipment (weapons, armor, rings) | No | Yes |
consumable | Potions, drinks, herbs | No | Yes (flat bonuses on use?) |
ability | Skill stones that teach attacks | Yes | Yes |
quest | Quest items (keys, letters, coins) | No | No |
3. Item Classes (Binding)
"class": "soulbound" | "global"
| Class | Behavior |
|---|---|
soulbound | Binds to player on pickup. Cannot be traded. |
global | Tradeable, stashable. Used for premium/rare gear. |
Best practice:
- Quest items →
soulbound - Starter gear →
soulbound - Mid/high tier gear →
global(tradable economy) - Ability stones →
soulbound(prevent farming alts)
4. Recognized Stats (stat_bonuses)
These are the stats the engine recognizes. All values are additive (the bonus is added directly).
"stat_bonuses": {
"strength": // Increases physical damage
"intelligence": // Increases magic damage
"defense": // Flat damage reduction
"speed": // Movement speed (tiles/sec?)
"hp_max": // Bonus to max health
"mp_max": // Bonus to max mana
"resistance": // Flat elemental damage reduction
}
Typical values by tier:
| Stat | Tier 1 | Tier 2 | Tier 3 |
|---|---|---|---|
strength | 2–3 | 4–5 | 8–12 |
intelligence | — | 3–4 | 8–10 |
defense | 1–3 | 3–7 | 4–10 |
speed | 1–5 | 8 | 10 |
hp_max | — | 20–50 | 50+ |
mp_max | 10 | 10–40 | 30+ |
resistance | — | 2–4 | 5–10 |
5. Elemental Resistances
Items can grant per-element resistance:
"resistances": {
"fire": 0.5, // Take 50% of fire damage
"dark": 0.3 // Take 30% of dark damage
}
Resistance values are multipliers (same format as entity resistances):
| Value | Meaning |
|---|---|
0.0 | Immune (takes no damage) |
0.3 | Strong resistance (70% reduction) |
0.5 | Moderate resistance (50% reduction) |
1.0 | Neutral (no modifier) |
2.0 | Weak (takes 2× damage) |
Elements recognized by the engine (with example attacks from the sample bundle):
| Element | Example attack |
|---|---|
fire | fireball, fire_slash |
ice | ice_spike |
poison | poison_cloud |
holy | holy_beam |
dark | dark_swirl, shadow_stab |
wind | wind_cutter |
lightning | thunder_strike |
earth | earth_shaker |
water | water_cascade |
6. Damage Types
Every attack has a type field:
"type": "physical" | "magic"
| Type | Scales With | Stat Bonus |
|---|---|---|
physical | Character/entity strength | strength |
magic | Character/entity intelligence | intelligence |
7. Attack Blocks (Ability Items)
Ability items contain an embedded attack block. This is the full attack definition — it does NOT reference attacks/attacks.json.
{
"category": "ability",
"class": "soulbound",
"def_id": "fire_slash_stone",
"name": "Fire Slash Stone",
"power_tier": 2,
"sprite_id": "",
"stat_bonuses": {
"strength": 1
},
"attack": {
"attack_id": "fire_slash",
"name": "Fire Slash",
"type": "physical",
"element": "fire",
"scaling_factor": 1.2,
"hit_sprite_id": "sprite_766_0",
"qte": { ... }
}
}
attack vs attacks/attacks.json
attacks/attacks.json= global attack registry used by entitiesitem.attack= embedded attack definition for player-use from inventory
Both can coexist with the same attack_id. The one in attacks/attacks.json is the fallback/server-side definition; the one on the item overrides it for the player.
Attack Fields
| Field | Required | Description |
|---|---|---|
attack_id | Yes | Unique ID matching the attack |
name | Yes | Display name |
type | Yes | physical or magic |
element | No | Element for resistance checks |
scaling_factor | Yes | Damage multiplier (base_dmg × scaling) |
hit_sprite_id | Yes | Sprite for the visual effect |
qte | Yes | Quick-time event configuration |
QTE Configuration
⚠️ Read Monster Design guide §6 for what these numbers actually do.
parry_window_ms/dodge_window_msare ± tolerances around the window centre, not durations or "moments." The model is a continuous leak: full negation happens only inside a tiny perfect dead-zone (parry/4, capped at ±25 ms), then damage ramps to a full hit at the edge (dodge, capped at ±220 ms). A parry is a mirage unless it's near-perfect. The caps clamp only toward harder — authoring wider windows does not make an attack easier. The QTE also telegraphs for up to ~1.5 s with a visible sweep, so it is an anticipation test, not a reaction test. Set parry/dodge at the top level (the client preview ignores per-input overrides).
"qte": {
"attack_type": "targeted" | "aoe" | "global",
"inputs": [
{
"button": "a" | "b" | "x" | "y",
"window_start_ms": 100, // When button press becomes valid
"window_end_ms": 250, // When button press window closes
"dodge_window_ms": 700, // Optional: moment when button = dodge
"parry_window_ms": 450 // Optional: moment when button = parry
}
],
"dodge_window_ms": 800, // Global dodge timing (if not per-input)
"parry_window_ms": 300 // Global parry timing (if not per-input)
}
| attack_type | Behavior |
|---|---|
targeted | Hits one target. Can be dodged/parried. |
aoe | Hits area around target. Wider dodge window. |
global | Hits all enemies in the instance. Unavoidable. |
Attack Weight (Entity-Only)
In entity phase attacks, "weight": 1 or "weight": 2 determines how often an attack is selected by the AI relative to others. Higher weight = more frequent.
8. Entity Phases & Multi-Phase Bosses
An entity can have one or more phases. Each phase has its own stats, attacks, and a trigger condition.
"phases": [
{
"attacks": [ ... ], // Phase 1 attacks
"stats": { ... }, // Phase 1 stats
"trigger": {} // No trigger = phase 1
},
{
"attacks": [ ... ], // Phase 2 attacks
"stats": { ... }, // Phase 2 stats (can be harder)
"trigger": {
"hp_pct_below": 30 // Transitions when HP < 30%
}
}
]
Trigger Conditions
| Trigger | Effect |
|---|---|
{} | No trigger — this is the initial phase |
{"hp_pct_below": 30} | Transitions to this phase when HP drops below 30% |
{"hp_pct_below": 50} | Current standard for a second phase |
Triggers only exist on phase N+1. The first phase always has "trigger": {}.
Entity Stats
"stats": {
"power_tier": 1 | 2 | 3, // Controls drop tier caps
"hp": 50, // Current health (initial)
"hp_max": 50, // Max health
"damage_min": 5, // Base damage minimum
"damage_max": 10, // Base damage maximum
"defense": 3, // Damage reduction
"resistance": 0, // General elemental resist
"resistances": { // Per-element multipliers
"fire": 0.5, // (same multiplier convention as items)
"water": 2.0
},
"speed": 1, // Movement speed
"aggro_radius": 5, // Detection range
"respawn_time": 30 // Seconds until respawn
}
Default stat progression by tier:
| Stat | Tier 1 | Tier 2 | Tier 3 |
|---|---|---|---|
| HP | 20–50 | 35–80 | 70–120 |
| damage_min | 3–5 | 5–10 | 8–15 |
| damage_max | 6–10 | 12–18 | 16–25 |
| defense | 0–3 | 1–7 | 2–10 |
| resistance | 0 | 2–4 | 8–10 |
| speed | 1–2 | 1–3 | 1–2 |
| aggro_radius | 5–8 | 6–10 | 8–10 |
| respawn_time | 25–30 | 30–45 | 50–60 |
9. Sprite System
Sprite Descriptors (JSON)
Each sprite PNG needs a companion JSON descriptor:
{
"sprite_id": "sprite_836_0",
"image_file": "sprite_836_0.png",
"columns": 8,
"rows": 1,
"frame_width": 64,
"frame_height": 64,
"fps": 8
}
Where Sprites Are Referenced
| Field | File | Purpose |
|---|---|---|
hit_sprite_id | attacks/attacks.json, items.json (ability), entities.json | Visual effect when attack lands |
sprite_id | items.json | Inventory icon |
sprite_id | entities.json | Entity world sprite |
combat_sprite_id | entities.json | Entity combat/portrait sprite |
avatar_sprite_id | entities.json | NPC dialog portrait |
Rules
- Every
hit_sprite_idmust have a corresponding JSON + PNG pair insprites/ - The
sprite_idin the descriptor must match the filename stem - All attack sprites so far use: 8 columns, 1 row, 64×64 frames, 8 fps
10. Loot Tables
{
"entity_def_id": "fire_spirit",
"drops": [
{ "item_def_id": "fire_slash_stone", "probability": 0.1 },
{ "item_def_id": "flame_sword", "probability": 0.02 },
{ "item_def_id": "energy_drink", "probability": 0.15 }
]
}
Rules
- Every
entity_def_idreferenced in loot tables must exist inentities/entities.json - Every
item_def_idreferenced must exist inentities/items.json - A global item's drop probability must not exceed its power-tier cap (§1);
soulbound drops are uncapped
- Probabilities are independent per slot (each drop rolls separately)
- A guaranteed-drop entry (e.g. a forced quest item) uses
probability: 1— this
is only valid for soulbound items, since 1.0 exceeds every global cap
Drop Distribution Guide
Ranges below assume the drop is soulbound (uncapped). For global items, clamp to the tier cap in §1 (e.g. a tier-1 global item maxes at 0.10, not 0.25).
| Mob Tier | Should Drop | Probability Range |
|---|---|---|
| Tier 1 (trash) | T1 gear, consumables, quest items | 0.05–0.25 |
| Tier 2 (elite) | T2 gear, ability stones, valuables | 0.05–0.15 |
| Tier 3 (boss) | T3 gear, rare ability stones, specials | ≤ 0.02 global |
Give each mob a chance at low-value filler too (e.g. currency nuggets).
11. Portals (Item-Gated)
{
"portal_id": "portal_1783181396_1_2966730235",
"map_id": "map_main",
"position": { "x": 4, "y": 9 },
"entry_position": { "x": 11, "y": 2 },
"target_map_id": "cave_level_1",
"tile_id": 0,
"required_item_def_id": "house_key", // Optional: item required to use
"prompt": "Use {item} to open the door."
}
| Field | Description |
|---|---|
map_id | Source map |
position | Tile position of the portal on source map |
entry_position | Tile position the player lands on in target map |
target_map_id | Destination map |
required_item_def_id | Optional — item needed to activate portal |
prompt | Optional — tooltip shown, {item} is replaced with item name |
tile_id | Visual tile override (0 = default exit) |
12. Spawn Points
{
"spawn_id": "spawn_1783274848_1_900576479",
"entity_def_id": "goblin",
"map_id": "cave_level_1",
"position": { "x": 4, "y": 12 },
"max_count": 4,
"respawn_time": 30,
"aggro_radius": 10
}
| Field | Description |
|---|---|
entity_def_id | Must exist in entities.json |
map_id | Must exist as a map file in maps/ |
position | Spawn tile position |
max_count | How many can exist at once |
respawn_time | Seconds between spawn waves (overrides entity stat) |
aggro_radius | Detection range (overrides entity stat) |
13. Brain (Entity AI)
Defined in each entity, the brain array is a priority-ordered list of behaviors:
"brain": [
{ "do": { "type": "random_step" }, "when": { "type": "chance", "value": 65 } },
{ "do": { "type": "chase_player" }, "when": { "type": "player_within", "value": 10 } },
{ "do": { "type": "follow" }, "when": { "type": "chance", "value": 30 } },
{ "do": { "type": "hold" }, "when": { "type": "always" } }
]
Action Types (do.type)
| Action | Description |
|---|---|
hold | Idle / wait |
chase_player | Move toward the player |
random_step | Wander randomly |
follow | Follow another entity |
patrol_border | Walk the edge of the map |
patrol_line | Walk a line; has direction ("e"/"w"/"n"/"s") and length |
Condition Types (when.type)
| Condition | Behavior |
|---|---|
always | Always executes this action |
chance | Percentage chance per tick; has value (0–100) |
player_within | Triggers when player is within value tiles |
The first matching condition (checked top to bottom) wins.
14. Scripts (NPC Dialog)
NPCs with no combat (no phases, no brain) use Lua scripts:
{
"def_id": "grix",
"name": "Grix",
"avatar_sprite_id": "dwarf_face",
"sprite_id": "dwarf_base_1x4",
"phases": [],
"script_id": "grix"
}
The script_id maps to scripts/{script_id}.lua.
15. Quick Reference — File Inventory
| File | What's In It |
|---|---|
manifest.json | Area metadata, entry map, welcome message |
attacks/attacks.json | Global attack registry |
entities/entities.json | Entity, NPC, and monster definitions |
entities/items.json | All items (gear, consumables, abilities, quest) |
loot_tables.json | Drop tables linking entities to items |
spawns.json | Spawn points linking entities to maps+positions |
portals.json | Map connections, optionally item-gated |
sprites/*.json / sprites/*.png | Sprite descriptors and images |
maps/*.json | Map data |
tiles/registry.json | Tile definitions |
tilesets/*.json + *.png | Tileset descriptors |
audio/*.ogg | Music / sound effects |
scripts/*.lua | NPC dialog/behavior scripts |
16. Validation Checklist (Before Submitting a Bundle)
- All
def_idvalues unique within their file - All
attack_idvalues inattacks/attacks.jsonunique - Every
hit_sprite_idhas a sprite file insprites/(JSON + PNG) - Every
entity_def_idin spawns.json exists in entities.json - Every
entity_def_idin loot_tables.json exists in entities.json - Every
item_def_idin loot_tables.json exists in items.json - All JSON files parse without errors (
python3 -m json.tool) - No tier-3 item has drop probability > 0.02
- All
map_idreferences have corresponding map files - Portal entry positions have valid target map coordinates
- Sprites copied to the target server's
sprites/directory - Bundle is valid JSON (no trailing commas)