Implements player-NPC dialogue using the dialogue tree data model. Conversation state tracking manages active conversations and transitions NPCs to "converse" behavior state during dialogue. Commands support terminal node cleanup and display formatting with numbered choices.
111 lines
2.9 KiB
Python
111 lines
2.9 KiB
Python
"""Conversation state tracking for player-NPC dialogues."""
|
|
|
|
from dataclasses import dataclass
|
|
|
|
from mudlib.dialogue import DialogueNode, DialogueTree
|
|
from mudlib.entity import Mob
|
|
from mudlib.npc_behavior import transition_state
|
|
from mudlib.player import Player
|
|
|
|
|
|
@dataclass
|
|
class ConversationState:
|
|
"""Tracks an active conversation between a player and NPC."""
|
|
|
|
tree: DialogueTree
|
|
current_node: str
|
|
npc: Mob
|
|
previous_state: str = "idle"
|
|
|
|
|
|
# Global registry of active conversations (keyed by player name)
|
|
active_conversations: dict[str, ConversationState] = {}
|
|
|
|
|
|
def start_conversation(player: Player, mob: Mob, tree: DialogueTree) -> DialogueNode:
|
|
"""Start a conversation between player and NPC.
|
|
|
|
Args:
|
|
player: The player starting the conversation
|
|
mob: The NPC mob to talk to
|
|
tree: The dialogue tree to use
|
|
|
|
Returns:
|
|
The root DialogueNode
|
|
"""
|
|
# Store mob's previous state for restoration
|
|
previous_state = mob.behavior_state
|
|
|
|
# Transition mob to converse state
|
|
transition_state(mob, "converse")
|
|
|
|
# Create conversation state
|
|
state = ConversationState(
|
|
tree=tree,
|
|
current_node=tree.root_node,
|
|
npc=mob,
|
|
previous_state=previous_state,
|
|
)
|
|
active_conversations[player.name] = state
|
|
|
|
return tree.nodes[tree.root_node]
|
|
|
|
|
|
def advance_conversation(player: Player, choice_index: int) -> DialogueNode | None:
|
|
"""Advance conversation based on player's choice.
|
|
|
|
Args:
|
|
player: The player making the choice
|
|
choice_index: 1-indexed choice number
|
|
|
|
Returns:
|
|
Next DialogueNode (conversation state updated but not ended yet),
|
|
or None if invalid choice
|
|
"""
|
|
conv = active_conversations.get(player.name)
|
|
if conv is None:
|
|
return None
|
|
|
|
current = conv.tree.nodes[conv.current_node]
|
|
|
|
# Validate choice index
|
|
if choice_index < 1 or choice_index > len(current.choices):
|
|
return None
|
|
|
|
# Get the next node
|
|
choice = current.choices[choice_index - 1]
|
|
next_node = conv.tree.nodes[choice.next_node]
|
|
|
|
# Update conversation state to new node
|
|
conv.current_node = next_node.id
|
|
|
|
return next_node
|
|
|
|
|
|
def end_conversation(player: Player) -> None:
|
|
"""End the active conversation and clean up state.
|
|
|
|
Args:
|
|
player: The player whose conversation to end
|
|
"""
|
|
conv = active_conversations.get(player.name)
|
|
if conv is None:
|
|
return
|
|
|
|
# Transition mob back to previous state
|
|
transition_state(conv.npc, conv.previous_state)
|
|
|
|
# Remove conversation state
|
|
del active_conversations[player.name]
|
|
|
|
|
|
def get_conversation(player: Player) -> ConversationState | None:
|
|
"""Get the active conversation for a player.
|
|
|
|
Args:
|
|
player: The player to look up
|
|
|
|
Returns:
|
|
ConversationState if active, None otherwise
|
|
"""
|
|
return active_conversations.get(player.name)
|