Last active
October 15, 2025 01:02
-
-
Save mbutler/5e4f9725b348fe3ad6ce0b0319cde323 to your computer and use it in GitHub Desktop.
D&D 4e Power Schema
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| { | |
| "$schema": "https://json-schema.org/draft/2020-12/schema", | |
| "$id": "https://example.com/4e-power.schema.json", | |
| "title": "D&D 4e Power Schema with Augments", | |
| "type": "object", | |
| "required": ["name", "level", "type", "action_type", "range", "targeting"], | |
| "properties": { | |
| "name": { "type": "string" }, | |
| "level": { "type": "integer", "minimum": 1 }, | |
| "class": { "type": "string" }, | |
| "subtype": { "type": "string" }, | |
| "type": { | |
| "type": "string", | |
| "enum": ["At-Will", "Encounter", "Daily", "Utility", "Item", "Monster"] | |
| }, | |
| "scope": { "type": "string", "enum": ["power", "item"] }, | |
| "usage": { | |
| "type": "object", | |
| "properties": { | |
| "frequency": { | |
| "type": "string", | |
| "enum": ["At-Will", "Encounter", "Daily", "Recharge"] | |
| }, | |
| "charges": { "type": "integer" }, | |
| "per": { "type": "string", "enum": ["character", "item"] }, | |
| "consumed_on": { "type": "string", "enum": ["activation", "hit"] } | |
| } | |
| }, | |
| "action_type": { | |
| "type": "string", | |
| "enum": ["Standard", "Move", "Minor", "Free", "Immediate Interrupt", "Immediate Reaction", "Opportunity", "No Action"] | |
| }, | |
| "keywords": { | |
| "type": "array", | |
| "items": { "$ref": "#/$defs/keyword" } | |
| }, | |
| "source": { | |
| "type": "object", | |
| "properties": { | |
| "book": { "type": "string" }, | |
| "page": { "type": "integer" } | |
| } | |
| }, | |
| "flavor_text": { "type": "string" }, | |
| "range": { | |
| "type": "object", | |
| "required": ["type"], | |
| "properties": { | |
| "type": { | |
| "type": "string", | |
| "enum": ["Melee", "Ranged", "Close", "Area", "Personal", "Aura"] | |
| }, | |
| "distance": { "type": "integer" }, | |
| "shape": { "type": "string", "enum": ["burst", "blast"] }, | |
| "radius": { "type": "integer" }, | |
| "reach": { "type": "integer" } | |
| } | |
| }, | |
| "targeting": { | |
| "type": "object", | |
| "required": ["target_type", "target_number"], | |
| "properties": { | |
| "target_type": { "type": "string" }, | |
| "target_number": { "type": "string" }, | |
| "target_ref": { "type": "string" }, | |
| "selection": { "type": "string" } | |
| } | |
| }, | |
| "attack": { | |
| "type": "object", | |
| "properties": { | |
| "ability_used": { "type": "string" }, | |
| "vs_defense": { "type": "string", "enum": ["AC", "Fortitude", "Reflex", "Will"] }, | |
| "weapon_keyword": { "type": "boolean" }, | |
| "implement_keyword": { "type": "boolean" }, | |
| "on_hit": { "$ref": "#/$defs/hitEffect" }, | |
| "on_miss": { "$ref": "#/$defs/hitEffect" }, | |
| "critical": { "$ref": "#/$defs/hitEffect" } | |
| } | |
| }, | |
| "effects": { | |
| "type": "array", | |
| "items": { "$ref": "#/$defs/effectBlock" } | |
| }, | |
| "augment": { | |
| "type": "array", | |
| "items": { | |
| "type": "object", | |
| "required": ["cost"], | |
| "properties": { | |
| "cost": { "type": "integer" }, | |
| "notes": { "type": "string" }, | |
| "attack": { "$ref": "#/properties/attack" }, | |
| "effects": { | |
| "type": "array", | |
| "items": { "$ref": "#/$defs/effectBlock" } | |
| } | |
| } | |
| } | |
| }, | |
| "sustain": { "$ref": "#/$defs/sustainBlock" }, | |
| "special": { | |
| "type": "array", | |
| "items": { "$ref": "#/$defs/specialBlock" } | |
| }, | |
| "notes": { "type": "string" }, | |
| "custom_logic": { "type": "string" } | |
| }, | |
| "$defs": { | |
| "condition": { | |
| "type": "string", | |
| "enum": [ | |
| "Blinded", "Dazed", "Deafened", "Dominated", "Helpless", | |
| "Immobilized", "Marked", "Petrified", "Prone", "Restrained", | |
| "Slowed", "Stunned", "Surprised", "Unconscious", "Weakened", | |
| "Ongoing Damage", "Grants Combat Advantage" | |
| ] | |
| }, | |
| "damageType": { | |
| "type": "string", | |
| "enum": [ | |
| "Acid", "Cold", "Fire", "Force", "Lightning", "Necrotic", | |
| "Poison", "Psychic", "Radiant", "Thunder", "Untyped" | |
| ] | |
| }, | |
| "keyword": { | |
| "type": "string", | |
| "enum": [ | |
| "Arcane", "Divine", "Martial", "Primal", "Psionic", "Shadow", | |
| "Healing", "Fear", "Charm", "Polymorph", "Teleportation", | |
| "Invigorating", "Reliable", "Augmentable", "Zone", "Stance", | |
| "Aura", "Conjuration", "Summoning", "Disease", "Gaze", "Poison", | |
| "Sleep", "Spirit", "Mount", "Elemental", "Runic", "Enchantment", | |
| "Evocation" | |
| ] | |
| }, | |
| "damage": { | |
| "type": "object", | |
| "properties": { | |
| "amount": { "type": "string" }, | |
| "type": { "$ref": "#/$defs/damageType" } | |
| } | |
| }, | |
| "hitEffect": { | |
| "type": "object", | |
| "properties": { | |
| "damage": { "$ref": "#/$defs/damage" }, | |
| "effects": { | |
| "type": "array", | |
| "items": { "$ref": "#/$defs/effectBlock" } | |
| } | |
| } | |
| }, | |
| "effectBlock": { | |
| "type": "object", | |
| "required": ["timing", "effect_type"], | |
| "properties": { | |
| "id": { "type": "string" }, | |
| "timing": { | |
| "type": "string", | |
| "enum": ["on_hit", "on_miss", "after_attack_resolution", "start_of_turn", "end_of_turn", "immediate", "triggered", "sustain", "reappearance"] | |
| }, | |
| "trigger": { "$ref": "#/$defs/triggerCondition" }, | |
| "effect_type": { | |
| "type": "string", | |
| "enum": ["condition", "damage", "healing", "movement", "teleportation", "forced_movement", "zone", "summon", "conjuration", "buff", "debuff", "stance", "aura", "custom"] | |
| }, | |
| "condition": { "$ref": "#/$defs/conditionEffect" }, | |
| "damage": { "$ref": "#/$defs/damage" }, | |
| "healing": { | |
| "type": "object", | |
| "properties": { | |
| "amount": { "type": "string" }, | |
| "temp": { "type": "boolean" } | |
| } | |
| }, | |
| "movement": { | |
| "type": "object", | |
| "properties": { | |
| "type": { "type": "string", "enum": ["slide", "push", "pull"] }, | |
| "distance": { "type": "integer" } | |
| } | |
| }, | |
| "teleportation": { | |
| "type": "object", | |
| "properties": { | |
| "distance": { "type": "integer" } | |
| } | |
| }, | |
| "zone": { "$ref": "#/$defs/zoneEffect" }, | |
| "applies_while": { "$ref": "#/$defs/conditionExpr" }, | |
| "grants": { | |
| "type": "array", | |
| "items": { "$ref": "#/$defs/modifierGrant" } | |
| }, | |
| "ends_if": { | |
| "type": "array", | |
| "items": { "$ref": "#/$defs/endCondition" } | |
| }, | |
| "duration": { "$ref": "#/$defs/duration" }, | |
| "custom_text": { "type": "string" } | |
| } | |
| }, | |
| "triggerCondition": { | |
| "type": "object", | |
| "properties": { | |
| "event": { "type": "string" }, | |
| "timing": { "type": "string" }, | |
| "source": { "type": "string" }, | |
| "context": { "type": "object" } | |
| } | |
| }, | |
| "conditionEffect": { | |
| "type": "object", | |
| "properties": { | |
| "type": { "$ref": "#/$defs/condition" }, | |
| "target": { "type": "string" } | |
| } | |
| }, | |
| "modifierGrant": { | |
| "type": "object", | |
| "properties": { | |
| "type": { "type": "string", "enum": ["bonus", "penalty", "override"] }, | |
| "target": { "type": "string" }, | |
| "attribute": { "type": "string" }, | |
| "amount": { "type": ["integer", "string"] }, | |
| "bonus_type": { "type": "string", "enum": ["power", "feat", "item", "untyped"] }, | |
| "applies_while": { "$ref": "#/$defs/conditionExpr" } | |
| } | |
| }, | |
| "conditionExpr": { | |
| "type": "object", | |
| "properties": { | |
| "type": { "type": "string" }, | |
| "a": { "type": "string" }, | |
| "b": { "type": "string" }, | |
| "within_squares": { "type": "integer" }, | |
| "of": { "type": "string" }, | |
| "who": { "type": "string" }, | |
| "condition": { "$ref": "#/$defs/condition" }, | |
| "expr": { "$ref": "#/$defs/conditionExpr" }, | |
| "all": { | |
| "type": "array", | |
| "items": { "$ref": "#/$defs/conditionExpr" } | |
| }, | |
| "any": { | |
| "type": "array", | |
| "items": { "$ref": "#/$defs/conditionExpr" } | |
| } | |
| } | |
| }, | |
| "endCondition": { | |
| "type": "object", | |
| "properties": { | |
| "type": { "type": "string" }, | |
| "expr": { "$ref": "#/$defs/endCondition" }, | |
| "condition": { "$ref": "#/$defs/conditionExpr" } | |
| } | |
| }, | |
| "duration": { | |
| "oneOf": [ | |
| { "type": "object", "properties": { "type": { "const": "save_ends" } } }, | |
| { | |
| "type": "object", | |
| "properties": { | |
| "type": { "enum": ["end_of_next_turn", "start_of_next_turn"] }, | |
| "agent": { "enum": ["caster", "target"] } | |
| }, | |
| "required": ["type", "agent"] | |
| }, | |
| { | |
| "type": "object", | |
| "properties": { "type": { "const": "end_of_encounter" } } | |
| }, | |
| { | |
| "type": "object", | |
| "properties": { | |
| "type": { "const": "while" }, | |
| "condition": { "$ref": "#/$defs/conditionExpr" } | |
| }, | |
| "required": ["type", "condition"] | |
| }, | |
| { | |
| "type": "object", | |
| "properties": { | |
| "type": { "const": "sustain" }, | |
| "action_type": { "type": "string", "enum": ["Minor", "Standard", "Move"] } | |
| }, | |
| "required": ["type", "action_type"] | |
| }, | |
| { | |
| "type": "object", | |
| "properties": { "type": { "const": "permanent" } } | |
| } | |
| ] | |
| }, | |
| "zoneEffect": { | |
| "type": "object", | |
| "properties": { | |
| "area": { | |
| "type": "object", | |
| "properties": { | |
| "shape": { "type": "string", "enum": ["burst", "blast"] }, | |
| "radius": { "type": "integer" } | |
| } | |
| }, | |
| "duration": { "$ref": "#/$defs/duration" }, | |
| "affects": { "type": "string" }, | |
| "effects": { | |
| "type": "array", | |
| "items": { "$ref": "#/$defs/effectBlock" } | |
| }, | |
| "excludes": { | |
| "type": "array", | |
| "items": { "type": "string" } | |
| } | |
| } | |
| }, | |
| "sustainBlock": { | |
| "type": "object", | |
| "properties": { | |
| "action_type": { "type": "string", "enum": ["Minor", "Standard", "Move"] }, | |
| "duration_extension": { "type": "string", "enum": ["1_turn", "indefinite"] }, | |
| "modifies": { "type": "string" } | |
| } | |
| }, | |
| "specialBlock": { | |
| "type": "object", | |
| "properties": { | |
| "condition": { "type": "string" }, | |
| "modifies": { "type": "string" }, | |
| "modification": { "type": "object" }, | |
| "note": { "type": "string" } | |
| } | |
| } | |
| } | |
| } |
Author
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
System role
You convert D&D 4e power text into strict JSON that conforms to the “D&D 4e Power Schema” (v1). The output is directly consumed by a game engine; it must be machine-actionable.
Output rules (critical)
Return JSON only (no comments, no markdown).
Use only fields in this spec.
Never use natural-language placeholders like “see text”.
Prefer structure over prose. If something cannot be modeled, use "custom_text" as a last resort.
Durations like “until the end of your next turn” must include "agent": "caster" (or "target").
If a rider references “the attack you just made”, use trigger.event and targeting.target_ref.
Required core shape
Power {
name: string;
level: number;
class?: string;
subtype?: string; // e.g., "Utility"
type: "At-Will" | "Encounter" | "Daily" | "Utility" | "Item" | "Monster";
scope?: "power" | "item";
usage?: { frequency: "At-Will" | "Encounter" | "Daily" | "Recharge"; charges?: number; per?: "character" | "item"; consumed_on?: "activation" | "hit" };
action_type: "Standard" | "Move" | "Minor" | "Free" | "Immediate Interrupt" | "Immediate Reaction" | "Opportunity" | "No Action";
keywords: Keyword[];
source?: { book: string; page?: number };
flavor_text?: string;
range: { type: "Melee" | "Ranged" | "Close" | "Area" | "Personal" | "Aura"; distance?: number; shape?: "burst" | "blast"; radius?: number; reach?: number };
targeting: { target_type: string; target_number: string; target_ref?: string; selection?: string };
attack?: Attack;
effects?: EffectBlock[];
sustain?: SustainBlock;
special?: SpecialBlock[];
notes?: string;
custom_logic?: string;
}
Attack { ability_used: string; vs_defense: "AC"|"Fortitude"|"Reflex"|"Will"; weapon_keyword?: boolean; implement_keyword?: boolean; on_hit: HitEffect; on_miss?: HitEffect; critical?: HitEffect }
HitEffect { damage?: Damage; effects?: EffectBlock[] }
Damage { amount: string; type?: DamageType }
Controlled vocab (enums)
Keyword
Arcane, Divine, Martial, Primal, Psionic, Shadow, Healing, Fear, Charm, Polymorph, Teleportation, Invigorating, Reliable, Augmentable, Zone, Stance, Aura, Conjuration, Summoning, Disease, Gaze, Poison, Sleep, Spirit, Mount, Elemental, Runic, Enchantment, Evocation
DamageType
Acid, Cold, Fire, Force, Lightning, Necrotic, Poison, Psychic, Radiant, Thunder, Untyped
Condition
Blinded, Dazed, Deafened, Dominated, Helpless, Immobilized, Marked, Petrified, Prone, Restrained, Slowed, Stunned, Surprised, Unconscious, Weakened, Ongoing Damage, Grants Combat Advantage
Effects
EffectBlock {
id?: string;
timing: "on_hit" | "on_miss" | "after_attack_resolution" | "start_of_turn" | "end_of_turn" | "immediate" | "triggered" | "sustain" | "reappearance";
effect_type: "condition" | "damage" | "healing" | "movement" | "teleportation" | "forced_movement" | "zone" | "summon" | "conjuration" | "buff" | "debuff" | "stance" | "aura" | "custom";
trigger?: TriggerCondition;
condition?: { type: Condition; target?: string };
damage?: Damage;
healing?: { amount: string; temp?: boolean };
movement?: { type: "slide" | "push" | "pull"; distance: number };
teleportation?: { distance: number };
zone?: ZoneEffect;
applies_while?: ConditionExpr; // live gating for stances/auras/conditional buffs
grants?: ModifierGrant[]; // structured bonuses/penalties/overrides
ends_if?: EndCondition[]; // stance/aura termination
duration?: Duration; // for timed effects
custom_text?: string; // last resort
}
TriggerCondition { event?: string; timing?: string; source?: string; context?: object }
ZoneEffect { area: { shape: "burst"|"blast"; radius: number }; duration: Duration; affects?: string; effects?: EffectBlock[]; excludes?: string[] }
SustainBlock { action_type: "Minor"|"Standard"|"Move"; duration_extension: "1_turn"|"indefinite"; modifies?: string }
SpecialBlock { condition?: string; modifies?: string; modification?: object; note?: string }
Duration (must be explicit)
Duration =
| { type: "save_ends" }
| { type: "end_of_next_turn"; agent: "caster" | "target" }
| { type: "start_of_next_turn"; agent: "caster" | "target" }
| { type: "end_of_encounter" }
| { type: "while"; condition: ConditionExpr }
| { type: "sustain"; action_type: "Minor" | "Standard" | "Move" }
| { type: "permanent" }
ConditionExpr (predicate DSL)
Use to model “while X” gating and complex conditionals. Compose with and/or/not.
ConditionExpr =
| { type: "adjacent"; a: "caster"|"target"|"ally"|"enemy"; b: "ally"|"enemy"|"target"; within_squares: 1 }
| { type: "within_squares"; of: "caster"|"target"|"zone:"; max: number }
| { type: "has_condition"; who: "caster"|"target"|"creature"; condition: Condition; negated?: boolean }
| { type: "bloodied"; who: "caster"|"target" }
| { type: "wielding"; who: "caster"; item_tag?: string; weapon_category?: string }
| { type: "in_stance"; who: "caster"; stance_id?: string }
| { type: "in_zone"; who: "target"|"creature"; zone_id: string }
| { type: "and"; all: ConditionExpr[] }
| { type: "or"; any: ConditionExpr[] }
| { type: "not"; expr: ConditionExpr };
ModifierGrant (structured bonuses/penalties)
ModifierGrant {
type: "bonus" | "penalty" | "override";
target: "caster" | "target" | "adjacent_ally" | "allies_in_condition" | "creature_in_condition";
attribute: "attack_roll" | "damage_roll" | "saving_throws" | "speed"
| "defense.AC" | "defense.Fortitude" | "defense.Reflex" | "defense.Will"
| "skill:";
amount: number | { dice?: string; flat?: number };
bonus_type: "power" | "feat" | "item" | "untyped";
applies_while?: ConditionExpr;
}
Patterns & mapping rules
Riders (“when you hit with this weapon”) → trigger.event: "weapon_attack_hit", targeting.target_ref: "trigger.primary_target", scope: "item" if an item power.
“While in the zone / adjacent / bloodied” → applies_while: ConditionExpr (not prose).
Stances/Auras → effect_type: "stance" | "aura", long duration (end_of_encounter or while), with grants and optional ends_if.
“Until end/start of your next turn” → duration with agent:"caster"; for “its next turn”, use agent:"target".
Few-shot examples
Input (summary): “Back to Back — Daily, Minor, Personal. While adjacent to an ally, you and that ally gain +1 to attack rolls (stance).”
Output:
{
"name": "Back to Back",
"level": 6,
"class": "Warlord",
"type": "Daily",
"subtype": "Utility",
"action_type": "Minor",
"keywords": ["Martial", "Stance"],
"range": { "type": "Personal" },
"targeting": { "target_type": "self", "target_number": "self" },
"effects": [
{
"id": "stance_back_to_back",
"timing": "immediate",
"effect_type": "stance",
"duration": { "type": "end_of_encounter" },
"applies_while": { "type": "adjacent", "a": "caster", "b": "ally", "within_squares": 1 },
"grants": [
{ "type": "bonus", "target": "caster", "attribute": "attack_roll", "amount": 1, "bonus_type": "power" },
{ "type": "bonus", "target": "adjacent_ally", "attribute": "attack_roll", "amount": 1, "bonus_type": "power" }
],
"ends_if": [{ "type": "assumes_other_stance" }, { "type": "caster_unconscious" }]
}
]
}
Input (summary): “Daily (Free): Use when you hit with this weapon; target is dazed until end of your next turn.”
Output:
{
"name": "Dazing Weapon Rider",
"type": "Daily",
"scope": "item",
"usage": { "frequency": "Daily", "charges": 1, "per": "item", "consumed_on": "activation" },
"action_type": "Free",
"keywords": ["Weapon"],
"range": { "type": "Personal" },
"targeting": { "target_type": "enemy", "target_number": "one", "target_ref": "trigger.primary_target" },
"effects": [
{
"timing": "triggered",
"effect_type": "condition",
"trigger": {
"event": "weapon_attack_hit",
"timing": "immediate",
"source": "caster",
"context": { "weapon_id": "this_item" }
},
"condition": { "type": "Dazed", "target": "target" },
"duration": { "type": "end_of_next_turn", "agent": "caster" }
}
]
}
Validation checklist (the model must self-check)
Include name, level, type, action_type, range, targeting.
If there’s an attack roll, include attack with ability_used and vs_defense.
Timed clauses use duration; stance/aura gating uses applies_while.
No natural-language mechanics outside flavor_text or, if unavoidable, custom_text.
Instruction to the model: Given a single power (raw text, HTML, or key/value JSON), produce exactly one JSON object conforming to this spec.