Problem
DM/character commands are inconsistent about who they target:
- Target by Minecraft username:
/combat add <player>, /dmgive <player>
- Target by character name:
/rest <character>, /restoreresource <character>, /consumeresource <character>
This is confusing because one player has both a username and a character name (chat suffix), and a single player can own multiple saved sheets (CharacterSheetManager keys on player-UUID + character-UUID).
Decision
Standardize on character name as the canonical target identity across all commands. Rationale: it's the D&D-facing identity, it's per-character (not per-player), and it works while the owning player is offline.
Design
-
Shared target resolver — a single resolveTarget(name) used by all commands. Lookup order:
- Combatants in an active
CombatSession (player-character or NPC entity) — reuses the existing Combatant / findCombatantByName() abstraction
- Loaded
CharacterSheets by name (findCharacterByName)
- Online username as a last-ditch fallback
This mostly consolidates lookups that already exist rather than adding new logic.
-
Enforce unique character names at creation/finalization (case-insensitive) — reject or auto-suffix duplicates. This eliminates the duplicate-name ambiguity at the source and is good table UX (two PCs named "Thorin" confuse humans too). This is the only genuinely new piece of logic.
-
Multi-word names via quotes — e.g. /rest "Thorin Oakenshield" short. Reuse the quote/stripQuotes parsing already present in CombatCommand; lift it into a shared helper.
NPC / entity targeting (future-facing)
The resolver above naturally supports targeting NPCs by name (e.g. /dmgive "Goblin Scout" potion), because Combatant already unifies PCs and entities. However, NPC "inventories" are currently just display strings (NpcData.getInventory()), not real containers — so functional item-giving to entities is a separate future feature. The resolver should be designed to allow it, but /dmgive stays player-only in practice until entity inventories are implemented.
Affected files
- New: shared
TargetResolver (or similar) + shared quoted-name parsing helper
RestCommand, RestoreResourceCommand, ConsumeResourceCommand (+ their tab-completers)
- Character creation finalize step (uniqueness enforcement)
CombatCommand / DmGiveCommand (adopt the shared resolver; usage strings)
- Update
plugin.yml usage strings and CLAUDE.md
Doc bug found while scoping
CLAUDE.md documents /dmgive <item_type> <item_id> [amount], but the command actually takes /dmgive <player> <item_id> [amount] (auto-detects item type). Fix while here.
Acceptance criteria
Suggested priority
Not blocking the first playtest — the inconsistency is annoying, not broken. Slot after the combat MVP (#99 attack rolls, #100 damage/healing) but before broad content/command expansion, while pre-alpha makes the refactor cheap. Related to #95 (codebase restructure).
Problem
DM/character commands are inconsistent about who they target:
/combat add <player>,/dmgive <player>/rest <character>,/restoreresource <character>,/consumeresource <character>This is confusing because one player has both a username and a character name (chat suffix), and a single player can own multiple saved sheets (
CharacterSheetManagerkeys on player-UUID + character-UUID).Decision
Standardize on character name as the canonical target identity across all commands. Rationale: it's the D&D-facing identity, it's per-character (not per-player), and it works while the owning player is offline.
Design
Shared target resolver — a single
resolveTarget(name)used by all commands. Lookup order:CombatSession(player-character or NPC entity) — reuses the existingCombatant/findCombatantByName()abstractionCharacterSheets by name (findCharacterByName)This mostly consolidates lookups that already exist rather than adding new logic.
Enforce unique character names at creation/finalization (case-insensitive) — reject or auto-suffix duplicates. This eliminates the duplicate-name ambiguity at the source and is good table UX (two PCs named "Thorin" confuse humans too). This is the only genuinely new piece of logic.
Multi-word names via quotes — e.g.
/rest "Thorin Oakenshield" short. Reuse the quote/stripQuotesparsing already present inCombatCommand; lift it into a shared helper.NPC / entity targeting (future-facing)
The resolver above naturally supports targeting NPCs by name (e.g.
/dmgive "Goblin Scout" potion), becauseCombatantalready unifies PCs and entities. However, NPC "inventories" are currently just display strings (NpcData.getInventory()), not real containers — so functional item-giving to entities is a separate future feature. The resolver should be designed to allow it, but/dmgivestays player-only in practice until entity inventories are implemented.Affected files
TargetResolver(or similar) + shared quoted-name parsing helperRestCommand,RestoreResourceCommand,ConsumeResourceCommand(+ their tab-completers)CombatCommand/DmGiveCommand(adopt the shared resolver; usage strings)plugin.ymlusage strings andCLAUDE.mdDoc bug found while scoping
CLAUDE.mddocuments/dmgive <item_type> <item_id> [amount], but the command actually takes/dmgive <player> <item_id> [amount](auto-detects item type). Fix while here.Acceptance criteria
CLAUDE.md/dmgiveusage correctedSuggested priority
Not blocking the first playtest — the inconsistency is annoying, not broken. Slot after the combat MVP (#99 attack rolls, #100 damage/healing) but before broad content/command expansion, while pre-alpha makes the refactor cheap. Related to #95 (codebase restructure).