Skip to content

Standardize command target identity (character name) + unique names #108

Description

@FirebrickRed

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

  1. Shared target resolver — a single resolveTarget(name) used by all commands. Lookup order:

    1. Combatants in an active CombatSession (player-character or NPC entity) — reuses the existing Combatant / findCombatantByName() abstraction
    2. Loaded CharacterSheets by name (findCharacterByName)
    3. Online username as a last-ditch fallback
      This mostly consolidates lookups that already exist rather than adding new logic.
  2. 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.

  3. 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

  • All targeting commands accept a character name; usage strings + tab-completion are consistent
  • Multi-word names work via quotes
  • Character names are unique (case-insensitive); duplicate creation is handled gracefully
  • Shared resolver handles PC and (where applicable) NPC names
  • CLAUDE.md /dmgive usage corrected

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).

Metadata

Metadata

Assignees

No one assigned

    Labels

    characterpertaining to the players characterdm-toolsDM-specific commands and featuresenhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions