use action context managers, sort modules
This commit is contained in:
parent
03c324ef60
commit
36f29dcffa
2
Makefile
2
Makefile
|
@ -56,4 +56,4 @@ lint-fix:
|
||||||
style: lint-fix
|
style: lint-fix
|
||||||
|
|
||||||
typecheck:
|
typecheck:
|
||||||
mypy feedme
|
mypy adventure
|
||||||
|
|
|
@ -3,8 +3,13 @@ from logging import getLogger
|
||||||
|
|
||||||
from packit.utils import could_be_json
|
from packit.utils import could_be_json
|
||||||
|
|
||||||
from adventure.context import broadcast, get_actor_agent_for_name, get_current_context
|
from adventure.context import (
|
||||||
from adventure.search import (
|
action_context,
|
||||||
|
broadcast,
|
||||||
|
get_actor_agent_for_name,
|
||||||
|
world_context,
|
||||||
|
)
|
||||||
|
from adventure.utils.search import (
|
||||||
find_actor_in_room,
|
find_actor_in_room,
|
||||||
find_item_in_actor,
|
find_item_in_actor,
|
||||||
find_item_in_room,
|
find_item_in_room,
|
||||||
|
@ -22,35 +27,36 @@ def action_look(target: str) -> str:
|
||||||
Args:
|
Args:
|
||||||
target: The name of the target to look at.
|
target: The name of the target to look at.
|
||||||
"""
|
"""
|
||||||
_, action_room, action_actor = get_current_context()
|
|
||||||
broadcast(f"{action_actor.name} looks at {target}")
|
|
||||||
|
|
||||||
if target.lower() == action_room.name.lower():
|
with action_context() as (action_room, action_actor):
|
||||||
broadcast(f"{action_actor.name} saw the {action_room.name} room")
|
broadcast(f"{action_actor.name} looks at {target}")
|
||||||
return describe_entity(action_room)
|
|
||||||
|
|
||||||
target_actor = find_actor_in_room(action_room, target)
|
if target.lower() == action_room.name.lower():
|
||||||
if target_actor:
|
broadcast(f"{action_actor.name} saw the {action_room.name} room")
|
||||||
broadcast(
|
return describe_entity(action_room)
|
||||||
f"{action_actor.name} saw the {target_actor.name} actor in the {action_room.name} room"
|
|
||||||
)
|
|
||||||
return describe_entity(target_actor)
|
|
||||||
|
|
||||||
target_item = find_item_in_room(action_room, target)
|
target_actor = find_actor_in_room(action_room, target)
|
||||||
if target_item:
|
if target_actor:
|
||||||
broadcast(
|
broadcast(
|
||||||
f"{action_actor.name} saw the {target_item.name} item in the {action_room.name} room"
|
f"{action_actor.name} saw the {target_actor.name} actor in the {action_room.name} room"
|
||||||
)
|
)
|
||||||
return describe_entity(target_item)
|
return describe_entity(target_actor)
|
||||||
|
|
||||||
target_item = find_item_in_actor(action_actor, target)
|
target_item = find_item_in_room(action_room, target)
|
||||||
if target_item:
|
if target_item:
|
||||||
broadcast(
|
broadcast(
|
||||||
f"{action_actor.name} saw the {target_item.name} item in their inventory"
|
f"{action_actor.name} saw the {target_item.name} item in the {action_room.name} room"
|
||||||
)
|
)
|
||||||
return describe_entity(target_item)
|
return describe_entity(target_item)
|
||||||
|
|
||||||
return "You do not see that item or character in the room."
|
target_item = find_item_in_actor(action_actor, target)
|
||||||
|
if target_item:
|
||||||
|
broadcast(
|
||||||
|
f"{action_actor.name} saw the {target_item.name} item in their inventory"
|
||||||
|
)
|
||||||
|
return describe_entity(target_item)
|
||||||
|
|
||||||
|
return "You do not see that item or character in the room."
|
||||||
|
|
||||||
|
|
||||||
def action_move(direction: str) -> str:
|
def action_move(direction: str) -> str:
|
||||||
|
@ -60,21 +66,21 @@ def action_move(direction: str) -> str:
|
||||||
Args:
|
Args:
|
||||||
direction: The direction to move in.
|
direction: The direction to move in.
|
||||||
"""
|
"""
|
||||||
action_world, action_room, action_actor = get_current_context()
|
|
||||||
|
|
||||||
destination_name = action_room.portals.get(direction.lower())
|
with world_context() as (action_world, action_room, action_actor):
|
||||||
if not destination_name:
|
destination_name = action_room.portals.get(direction.lower())
|
||||||
return f"You cannot move {direction} from here."
|
if not destination_name:
|
||||||
|
return f"You cannot move {direction} from here."
|
||||||
|
|
||||||
destination_room = find_room(action_world, destination_name)
|
destination_room = find_room(action_world, destination_name)
|
||||||
if not destination_room:
|
if not destination_room:
|
||||||
return f"The {destination_name} room does not exist."
|
return f"The {destination_name} room does not exist."
|
||||||
|
|
||||||
broadcast(f"{action_actor.name} moves {direction} to {destination_name}")
|
broadcast(f"{action_actor.name} moves {direction} to {destination_name}")
|
||||||
action_room.actors.remove(action_actor)
|
action_room.actors.remove(action_actor)
|
||||||
destination_room.actors.append(action_actor)
|
destination_room.actors.append(action_actor)
|
||||||
|
|
||||||
return f"You move {direction} and arrive at {destination_name}."
|
return f"You move {direction} and arrive at {destination_name}."
|
||||||
|
|
||||||
|
|
||||||
def action_take(item_name: str) -> str:
|
def action_take(item_name: str) -> str:
|
||||||
|
@ -84,16 +90,15 @@ def action_take(item_name: str) -> str:
|
||||||
Args:
|
Args:
|
||||||
item_name: The name of the item to take.
|
item_name: The name of the item to take.
|
||||||
"""
|
"""
|
||||||
_, action_room, action_actor = get_current_context()
|
with action_context() as (action_room, action_actor):
|
||||||
|
item = find_item_in_room(action_room, item_name)
|
||||||
|
if not item:
|
||||||
|
return "The {item_name} item is not in the room."
|
||||||
|
|
||||||
item = find_item_in_room(action_room, item_name)
|
|
||||||
if item:
|
|
||||||
broadcast(f"{action_actor.name} takes the {item_name} item")
|
broadcast(f"{action_actor.name} takes the {item_name} item")
|
||||||
action_room.items.remove(item)
|
action_room.items.remove(item)
|
||||||
action_actor.items.append(item)
|
action_actor.items.append(item)
|
||||||
return "You take the {item_name} item and put it in your inventory."
|
return "You take the {item_name} item and put it in your inventory."
|
||||||
else:
|
|
||||||
return "The {item_name} item is not in the room."
|
|
||||||
|
|
||||||
|
|
||||||
def action_ask(character: str, question: str) -> str:
|
def action_ask(character: str, question: str) -> str:
|
||||||
|
@ -105,33 +110,32 @@ def action_ask(character: str, question: str) -> str:
|
||||||
question: The question to ask them.
|
question: The question to ask them.
|
||||||
"""
|
"""
|
||||||
# capture references to the current actor and room, because they will be overwritten
|
# capture references to the current actor and room, because they will be overwritten
|
||||||
_world, _room, action_actor = get_current_context()
|
with action_context() as (_, action_actor):
|
||||||
|
# sanity checks
|
||||||
|
question_actor, question_agent = get_actor_agent_for_name(character)
|
||||||
|
if question_actor == action_actor:
|
||||||
|
return "You cannot ask yourself a question. Stop talking to yourself. Try another action."
|
||||||
|
|
||||||
# sanity checks
|
if not question_actor:
|
||||||
question_actor, question_agent = get_actor_agent_for_name(character)
|
return f"The {character} character is not in the room."
|
||||||
if question_actor == action_actor:
|
|
||||||
return "You cannot ask yourself a question. Stop talking to yourself. Try another action."
|
|
||||||
|
|
||||||
if not question_actor:
|
if not question_agent:
|
||||||
return f"The {character} character is not in the room."
|
return f"The {character} character does not exist."
|
||||||
|
|
||||||
if not question_agent:
|
broadcast(f"{action_actor.name} asks {character}: {question}")
|
||||||
return f"The {character} character does not exist."
|
answer = question_agent(
|
||||||
|
f"{action_actor.name} asks you: {question}. Reply with your response to them. "
|
||||||
|
f"Do not include the question or any JSON. Only include your answer for {action_actor.name}."
|
||||||
|
)
|
||||||
|
|
||||||
broadcast(f"{action_actor.name} asks {character}: {question}")
|
if could_be_json(answer) and action_tell.__name__ in answer:
|
||||||
answer = question_agent(
|
answer = loads(answer).get("parameters", {}).get("message", "")
|
||||||
f"{action_actor.name} asks you: {question}. Reply with your response to them. "
|
|
||||||
f"Do not include the question or any JSON. Only include your answer for {action_actor.name}."
|
|
||||||
)
|
|
||||||
|
|
||||||
if could_be_json(answer) and action_tell.__name__ in answer:
|
if len(answer.strip()) > 0:
|
||||||
answer = loads(answer).get("parameters", {}).get("message", "")
|
broadcast(f"{character} responds to {action_actor.name}: {answer}")
|
||||||
|
return f"{character} responds: {answer}"
|
||||||
|
|
||||||
if len(answer.strip()) > 0:
|
return f"{character} does not respond."
|
||||||
broadcast(f"{character} responds to {action_actor.name}: {answer}")
|
|
||||||
return f"{character} responds: {answer}"
|
|
||||||
|
|
||||||
return f"{character} does not respond."
|
|
||||||
|
|
||||||
|
|
||||||
def action_tell(character: str, message: str) -> str:
|
def action_tell(character: str, message: str) -> str:
|
||||||
|
@ -143,33 +147,33 @@ def action_tell(character: str, message: str) -> str:
|
||||||
message: The message to tell them.
|
message: The message to tell them.
|
||||||
"""
|
"""
|
||||||
# capture references to the current actor and room, because they will be overwritten
|
# capture references to the current actor and room, because they will be overwritten
|
||||||
_world, _room, action_actor = get_current_context()
|
|
||||||
|
|
||||||
# sanity checks
|
with action_context() as (_, action_actor):
|
||||||
question_actor, question_agent = get_actor_agent_for_name(character)
|
# sanity checks
|
||||||
if question_actor == action_actor:
|
question_actor, question_agent = get_actor_agent_for_name(character)
|
||||||
return "You cannot tell yourself a message. Stop talking to yourself. Try another action."
|
if question_actor == action_actor:
|
||||||
|
return "You cannot tell yourself a message. Stop talking to yourself. Try another action."
|
||||||
|
|
||||||
if not question_actor:
|
if not question_actor:
|
||||||
return f"The {character} character is not in the room."
|
return f"The {character} character is not in the room."
|
||||||
|
|
||||||
if not question_agent:
|
if not question_agent:
|
||||||
return f"The {character} character does not exist."
|
return f"The {character} character does not exist."
|
||||||
|
|
||||||
broadcast(f"{action_actor.name} tells {character}: {message}")
|
broadcast(f"{action_actor.name} tells {character}: {message}")
|
||||||
answer = question_agent(
|
answer = question_agent(
|
||||||
f"{action_actor.name} tells you: {message}. Reply with your response to them. "
|
f"{action_actor.name} tells you: {message}. Reply with your response to them. "
|
||||||
f"Do not include the message or any JSON. Only include your reply to {action_actor.name}."
|
f"Do not include the message or any JSON. Only include your reply to {action_actor.name}."
|
||||||
)
|
)
|
||||||
|
|
||||||
if could_be_json(answer) and action_tell.__name__ in answer:
|
if could_be_json(answer) and action_tell.__name__ in answer:
|
||||||
answer = loads(answer).get("parameters", {}).get("message", "")
|
answer = loads(answer).get("parameters", {}).get("message", "")
|
||||||
|
|
||||||
if len(answer.strip()) > 0:
|
if len(answer.strip()) > 0:
|
||||||
broadcast(f"{character} responds to {action_actor.name}: {answer}")
|
broadcast(f"{character} responds to {action_actor.name}: {answer}")
|
||||||
return f"{character} responds: {answer}"
|
return f"{character} responds: {answer}"
|
||||||
|
|
||||||
return f"{character} does not respond."
|
return f"{character} does not respond."
|
||||||
|
|
||||||
|
|
||||||
def action_give(character: str, item_name: str) -> str:
|
def action_give(character: str, item_name: str) -> str:
|
||||||
|
@ -180,21 +184,20 @@ def action_give(character: str, item_name: str) -> str:
|
||||||
character: The name of the character to give the item to.
|
character: The name of the character to give the item to.
|
||||||
item_name: The name of the item to give.
|
item_name: The name of the item to give.
|
||||||
"""
|
"""
|
||||||
_, action_room, action_actor = get_current_context()
|
with action_context() as (action_room, action_actor):
|
||||||
|
destination_actor = find_actor_in_room(action_room, character)
|
||||||
|
if not destination_actor:
|
||||||
|
return f"The {character} character is not in the room."
|
||||||
|
|
||||||
destination_actor = find_actor_in_room(action_room, character)
|
item = find_item_in_actor(action_actor, item_name)
|
||||||
if not destination_actor:
|
if not item:
|
||||||
return f"The {character} character is not in the room."
|
return f"You do not have the {item_name} item in your inventory."
|
||||||
|
|
||||||
item = find_item_in_actor(action_actor, item_name)
|
broadcast(f"{action_actor.name} gives {character} the {item_name} item.")
|
||||||
if not item:
|
action_actor.items.remove(item)
|
||||||
return f"You do not have the {item_name} item in your inventory."
|
destination_actor.items.append(item)
|
||||||
|
|
||||||
broadcast(f"{action_actor.name} gives {character} the {item_name} item.")
|
return f"You give the {item_name} item to {character}."
|
||||||
action_actor.items.remove(item)
|
|
||||||
destination_actor.items.append(item)
|
|
||||||
|
|
||||||
return f"You give the {item_name} item to {character}."
|
|
||||||
|
|
||||||
|
|
||||||
def action_drop(item_name: str) -> str:
|
def action_drop(item_name: str) -> str:
|
||||||
|
@ -205,14 +208,13 @@ def action_drop(item_name: str) -> str:
|
||||||
item_name: The name of the item to drop.
|
item_name: The name of the item to drop.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
_, action_room, action_actor = get_current_context()
|
with action_context() as (action_room, action_actor):
|
||||||
|
item = find_item_in_actor(action_actor, item_name)
|
||||||
|
if not item:
|
||||||
|
return f"You do not have the {item_name} item in your inventory."
|
||||||
|
|
||||||
item = find_item_in_actor(action_actor, item_name)
|
broadcast(f"{action_actor.name} drops the {item_name} item")
|
||||||
if not item:
|
action_actor.items.remove(item)
|
||||||
return f"You do not have the {item_name} item in your inventory."
|
action_room.items.append(item)
|
||||||
|
|
||||||
broadcast(f"{action_actor.name} drops the {item_name} item")
|
return f"You drop the {item_name} item."
|
||||||
action_actor.items.remove(item)
|
|
||||||
action_room.items.append(item)
|
|
||||||
|
|
||||||
return f"You drop the {item_name} item."
|
|
||||||
|
|
|
@ -33,14 +33,14 @@ from adventure.player import (
|
||||||
remove_player,
|
remove_player,
|
||||||
set_player,
|
set_player,
|
||||||
)
|
)
|
||||||
from adventure.render_comfy import render_event
|
from adventure.render.comfy import render_event
|
||||||
|
|
||||||
logger = getLogger(__name__)
|
logger = getLogger(__name__)
|
||||||
client = None
|
client = None
|
||||||
bot_config: DiscordBotConfig = DEFAULT_CONFIG.bot.discord
|
bot_config: DiscordBotConfig = DEFAULT_CONFIG.bot.discord
|
||||||
|
|
||||||
active_tasks = set()
|
active_tasks = set()
|
||||||
event_messages: Dict[str, str | GameEvent] = {}
|
event_messages: Dict[int, str | GameEvent] = {}
|
||||||
event_queue: Queue[GameEvent] = Queue()
|
event_queue: Queue[GameEvent] = Queue()
|
||||||
|
|
||||||
|
|
||||||
|
@ -81,13 +81,13 @@ class AdventureClient(Client):
|
||||||
channel = message.channel
|
channel = message.channel
|
||||||
user_name = author.name # include nick
|
user_name = author.name # include nick
|
||||||
|
|
||||||
world = get_current_world()
|
|
||||||
if world:
|
|
||||||
active_world = f"Active world: {world.name} (theme: {world.theme})"
|
|
||||||
else:
|
|
||||||
active_world = "No active world"
|
|
||||||
|
|
||||||
if message.content.startswith("!adventure"):
|
if message.content.startswith("!adventure"):
|
||||||
|
world = get_current_world()
|
||||||
|
if world:
|
||||||
|
active_world = f"Active world: {world.name} (theme: {world.theme})"
|
||||||
|
else:
|
||||||
|
active_world = "No active world"
|
||||||
|
|
||||||
await message.channel.send(f"Hello! Welcome to Adventure! {active_world}")
|
await message.channel.send(f"Hello! Welcome to Adventure! {active_world}")
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -215,7 +215,14 @@ def stop_bot():
|
||||||
global client
|
global client
|
||||||
|
|
||||||
if client:
|
if client:
|
||||||
client.close()
|
close_task = client.loop.create_task(client.close())
|
||||||
|
active_tasks.add(close_task)
|
||||||
|
|
||||||
|
def on_close_task_done(future):
|
||||||
|
logger.info("discord client closed")
|
||||||
|
active_tasks.discard(future)
|
||||||
|
|
||||||
|
close_task.add_done_callback(on_close_task_done)
|
||||||
client = None
|
client = None
|
||||||
|
|
||||||
|
|
||||||
|
@ -299,7 +306,7 @@ async def broadcast_event(message: str | GameEvent):
|
||||||
event_messages[event_message.id] = message
|
event_messages[event_message.id] = message
|
||||||
|
|
||||||
|
|
||||||
def embed_from_event(event: GameEvent) -> Embed:
|
def embed_from_event(event: GameEvent) -> Embed | None:
|
||||||
if isinstance(event, GenerateEvent):
|
if isinstance(event, GenerateEvent):
|
||||||
return embed_from_generate(event)
|
return embed_from_generate(event)
|
||||||
elif isinstance(event, ResultEvent):
|
elif isinstance(event, ResultEvent):
|
|
@ -37,14 +37,21 @@ game_systems: List[GameSystem] = []
|
||||||
# TODO: where should this one go?
|
# TODO: where should this one go?
|
||||||
actor_agents: Dict[str, Tuple[Actor, Agent]] = {}
|
actor_agents: Dict[str, Tuple[Actor, Agent]] = {}
|
||||||
|
|
||||||
|
STRING_EVENT_TYPE = "message"
|
||||||
|
|
||||||
|
|
||||||
|
def get_event_name(event: GameEvent | Type[GameEvent]):
|
||||||
|
return f"event:{event.type}"
|
||||||
|
|
||||||
|
|
||||||
def broadcast(message: str | GameEvent):
|
def broadcast(message: str | GameEvent):
|
||||||
if isinstance(message, GameEvent):
|
if isinstance(message, GameEvent):
|
||||||
logger.debug(f"broadcasting {message.type}")
|
event_name = get_event_name(message)
|
||||||
event_emitter.emit(message.type, message)
|
logger.debug(f"broadcasting {event_name}")
|
||||||
|
event_emitter.emit(event_name, message)
|
||||||
else:
|
else:
|
||||||
logger.warning("broadcasting a string message is deprecated")
|
logger.warning("broadcasting a string message is deprecated")
|
||||||
event_emitter.emit("message", message)
|
event_emitter.emit(STRING_EVENT_TYPE, message)
|
||||||
|
|
||||||
|
|
||||||
def is_union(type_: Type | UnionType):
|
def is_union(type_: Type | UnionType):
|
||||||
|
@ -62,10 +69,13 @@ def subscribe(
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
|
if event_type is str:
|
||||||
|
event_name = STRING_EVENT_TYPE
|
||||||
|
else:
|
||||||
|
event_name = get_event_name(event_type)
|
||||||
|
|
||||||
logger.debug(f"subscribing {callback.__name__} to {event_type}")
|
logger.debug(f"subscribing {callback.__name__} to {event_type}")
|
||||||
event_emitter.on(
|
event_emitter.on(event_name, callback)
|
||||||
event_type.type, callback
|
|
||||||
) # TODO: should this use str or __name__?
|
|
||||||
|
|
||||||
|
|
||||||
def has_dungeon_master():
|
def has_dungeon_master():
|
||||||
|
@ -74,11 +84,17 @@ def has_dungeon_master():
|
||||||
|
|
||||||
# region context manager
|
# region context manager
|
||||||
@contextmanager
|
@contextmanager
|
||||||
def with_action_context():
|
def action_context():
|
||||||
room, actor = get_action_context()
|
room, actor = get_action_context()
|
||||||
yield room, actor
|
yield room, actor
|
||||||
|
|
||||||
|
|
||||||
|
@contextmanager
|
||||||
|
def world_context():
|
||||||
|
world, room, actor = get_world_context()
|
||||||
|
yield world, room, actor
|
||||||
|
|
||||||
|
|
||||||
# endregion
|
# endregion
|
||||||
|
|
||||||
|
|
||||||
|
@ -94,7 +110,7 @@ def get_action_context() -> Tuple[Room, Actor]:
|
||||||
return (current_room, current_actor)
|
return (current_room, current_actor)
|
||||||
|
|
||||||
|
|
||||||
def get_current_context() -> Tuple[World, Room, Actor]:
|
def get_world_context() -> Tuple[World, Room, Actor]:
|
||||||
if not current_world:
|
if not current_world:
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
"The current world must be set before calling action functions"
|
"The current world must be set before calling action functions"
|
||||||
|
|
|
@ -246,19 +246,19 @@ def main():
|
||||||
threads = []
|
threads = []
|
||||||
|
|
||||||
if args.render:
|
if args.render:
|
||||||
from adventure.render_comfy import launch_render, render_generated
|
from adventure.render.comfy import launch_render, render_generated
|
||||||
|
|
||||||
threads.extend(launch_render(config.render))
|
threads.extend(launch_render(config.render))
|
||||||
if args.render_generated:
|
if args.render_generated:
|
||||||
subscribe(GenerateEvent, render_generated)
|
subscribe(GenerateEvent, render_generated)
|
||||||
|
|
||||||
if args.discord:
|
if args.discord:
|
||||||
from adventure.bot_discord import launch_bot
|
from adventure.bot.discord import launch_bot
|
||||||
|
|
||||||
threads.extend(launch_bot(config.bot.discord))
|
threads.extend(launch_bot(config.bot.discord))
|
||||||
|
|
||||||
if args.server:
|
if args.server:
|
||||||
from adventure.server_socket import launch_server, server_system
|
from adventure.server.websocket import launch_server
|
||||||
|
|
||||||
threads.extend(launch_server(config.server.websocket))
|
threads.extend(launch_server(config.server.websocket))
|
||||||
|
|
||||||
|
@ -300,6 +300,8 @@ def main():
|
||||||
|
|
||||||
# make sure the server system runs after any updates
|
# make sure the server system runs after any updates
|
||||||
if args.server:
|
if args.server:
|
||||||
|
from adventure.server.websocket import server_system
|
||||||
|
|
||||||
extra_systems.append(GameSystem(simulate=server_system))
|
extra_systems.append(GameSystem(simulate=server_system))
|
||||||
|
|
||||||
# load or generate the world
|
# load or generate the world
|
||||||
|
|
|
@ -4,16 +4,17 @@ from typing import Callable, List
|
||||||
from packit.agent import Agent, agent_easy_connect
|
from packit.agent import Agent, agent_easy_connect
|
||||||
|
|
||||||
from adventure.context import (
|
from adventure.context import (
|
||||||
|
action_context,
|
||||||
broadcast,
|
broadcast,
|
||||||
get_agent_for_actor,
|
get_agent_for_actor,
|
||||||
get_current_context,
|
|
||||||
get_dungeon_master,
|
get_dungeon_master,
|
||||||
has_dungeon_master,
|
has_dungeon_master,
|
||||||
set_dungeon_master,
|
set_dungeon_master,
|
||||||
|
world_context,
|
||||||
)
|
)
|
||||||
from adventure.generate import OPPOSITE_DIRECTIONS, generate_item, generate_room
|
from adventure.generate import OPPOSITE_DIRECTIONS, generate_item, generate_room
|
||||||
from adventure.search import find_actor_in_room
|
|
||||||
from adventure.utils.effect import apply_effect
|
from adventure.utils.effect import apply_effect
|
||||||
|
from adventure.utils.search import find_actor_in_room
|
||||||
from adventure.utils.world import describe_actor, describe_entity
|
from adventure.utils.world import describe_actor, describe_entity
|
||||||
|
|
||||||
logger = getLogger(__name__)
|
logger = getLogger(__name__)
|
||||||
|
@ -39,34 +40,31 @@ def action_explore(direction: str) -> str:
|
||||||
direction: The direction to explore: north, south, east, or west.
|
direction: The direction to explore: north, south, east, or west.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
current_world, current_room, current_actor = get_current_context()
|
with world_context() as (action_world, action_room, action_actor):
|
||||||
dungeon_master = get_dungeon_master()
|
dungeon_master = get_dungeon_master()
|
||||||
|
|
||||||
if not current_world:
|
if direction in action_room.portals:
|
||||||
raise ValueError("No world found")
|
dest_room = action_room.portals[direction]
|
||||||
|
return f"You cannot explore {direction} from here, that direction already leads to {dest_room}. Please use the move action to go there."
|
||||||
|
|
||||||
if direction in current_room.portals:
|
existing_rooms = [room.name for room in action_world.rooms]
|
||||||
dest_room = current_room.portals[direction]
|
try:
|
||||||
return f"You cannot explore {direction} from here, that direction already leads to {dest_room}. Please use the move action to go there."
|
new_room = generate_room(
|
||||||
|
dungeon_master, action_world.theme, existing_rooms=existing_rooms
|
||||||
|
)
|
||||||
|
action_world.rooms.append(new_room)
|
||||||
|
|
||||||
existing_rooms = [room.name for room in current_world.rooms]
|
# link the rooms together
|
||||||
try:
|
action_room.portals[direction] = new_room.name
|
||||||
new_room = generate_room(
|
new_room.portals[OPPOSITE_DIRECTIONS[direction]] = action_room.name
|
||||||
dungeon_master, current_world.theme, existing_rooms=existing_rooms
|
|
||||||
)
|
|
||||||
current_world.rooms.append(new_room)
|
|
||||||
|
|
||||||
# link the rooms together
|
broadcast(
|
||||||
current_room.portals[direction] = new_room.name
|
f"{action_actor.name} explores {direction} of {action_room.name} and finds a new room: {new_room.name}"
|
||||||
new_room.portals[OPPOSITE_DIRECTIONS[direction]] = current_room.name
|
)
|
||||||
|
return f"You explore {direction} and find a new room: {new_room.name}"
|
||||||
broadcast(
|
except Exception:
|
||||||
f"{current_actor.name} explores {direction} of {current_room.name} and finds a new room: {new_room.name}"
|
logger.exception("error generating room")
|
||||||
)
|
return f"You cannot explore {direction} from here, there is no room in that direction."
|
||||||
return f"You explore {direction} and find a new room: {new_room.name}"
|
|
||||||
except Exception:
|
|
||||||
logger.exception("error generating room")
|
|
||||||
return f"You cannot explore {direction} from here, there is no room in that direction."
|
|
||||||
|
|
||||||
|
|
||||||
def action_search(unused: bool) -> str:
|
def action_search(unused: bool) -> str:
|
||||||
|
@ -74,30 +72,32 @@ def action_search(unused: bool) -> str:
|
||||||
Search the room for hidden items.
|
Search the room for hidden items.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
action_world, action_room, action_actor = get_current_context()
|
with world_context() as (action_world, action_room, action_actor):
|
||||||
dungeon_master = get_dungeon_master()
|
dungeon_master = get_dungeon_master()
|
||||||
|
|
||||||
if len(action_room.items) > 2:
|
if len(action_room.items) > 2:
|
||||||
return "You find nothing hidden in the room. There is no room for more items."
|
return (
|
||||||
|
"You find nothing hidden in the room. There is no room for more items."
|
||||||
|
)
|
||||||
|
|
||||||
existing_items = [item.name for item in action_room.items]
|
existing_items = [item.name for item in action_room.items]
|
||||||
|
|
||||||
try:
|
try:
|
||||||
new_item = generate_item(
|
new_item = generate_item(
|
||||||
dungeon_master,
|
dungeon_master,
|
||||||
action_world.theme,
|
action_world.theme,
|
||||||
existing_items=existing_items,
|
existing_items=existing_items,
|
||||||
dest_room=action_room.name,
|
dest_room=action_room.name,
|
||||||
)
|
)
|
||||||
action_room.items.append(new_item)
|
action_room.items.append(new_item)
|
||||||
|
|
||||||
broadcast(
|
broadcast(
|
||||||
f"{action_actor.name} searches {action_room.name} and finds a new item: {new_item.name}"
|
f"{action_actor.name} searches {action_room.name} and finds a new item: {new_item.name}"
|
||||||
)
|
)
|
||||||
return f"You search the room and find a new item: {new_item.name}"
|
return f"You search the room and find a new item: {new_item.name}"
|
||||||
except Exception:
|
except Exception:
|
||||||
logger.exception("error generating item")
|
logger.exception("error generating item")
|
||||||
return "You find nothing hidden in the room."
|
return "You find nothing hidden in the room."
|
||||||
|
|
||||||
|
|
||||||
def action_use(item: str, target: str) -> str:
|
def action_use(item: str, target: str) -> str:
|
||||||
|
@ -108,69 +108,69 @@ def action_use(item: str, target: str) -> str:
|
||||||
item: The name of the item to use.
|
item: The name of the item to use.
|
||||||
target: The name of the character to use the item on, or "self" to use the item on yourself.
|
target: The name of the character to use the item on, or "self" to use the item on yourself.
|
||||||
"""
|
"""
|
||||||
_, action_room, action_actor = get_current_context()
|
with action_context() as (action_room, action_actor):
|
||||||
dungeon_master = get_dungeon_master()
|
dungeon_master = get_dungeon_master()
|
||||||
|
|
||||||
action_item = next(
|
action_item = next(
|
||||||
(
|
(
|
||||||
search_item
|
search_item
|
||||||
for search_item in (action_actor.items + action_room.items)
|
for search_item in (action_actor.items + action_room.items)
|
||||||
if search_item.name == item
|
if search_item.name == item
|
||||||
),
|
),
|
||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
if not action_item:
|
if not action_item:
|
||||||
return f"The {item} item is not available to use."
|
return f"The {item} item is not available to use."
|
||||||
|
|
||||||
if target == "self":
|
if target == "self":
|
||||||
target_actor = action_actor
|
target_actor = action_actor
|
||||||
target = action_actor.name
|
target = action_actor.name
|
||||||
else:
|
else:
|
||||||
target_actor = find_actor_in_room(action_room, target)
|
target_actor = find_actor_in_room(action_room, target)
|
||||||
if not target_actor:
|
if not target_actor:
|
||||||
return f"The {target} character is not in the room."
|
return f"The {target} character is not in the room."
|
||||||
|
|
||||||
effect_names = [effect.name for effect in action_item.effects]
|
effect_names = [effect.name for effect in action_item.effects]
|
||||||
chosen_name = dungeon_master(
|
chosen_name = dungeon_master(
|
||||||
f"{action_actor.name} uses {item} on {target}. "
|
f"{action_actor.name} uses {item} on {target}. "
|
||||||
f"{item} has the following effects: {effect_names}. "
|
f"{item} has the following effects: {effect_names}. "
|
||||||
"Which effect should be applied? Specify the name of the effect to apply."
|
"Which effect should be applied? Specify the name of the effect to apply."
|
||||||
"Do not include the question or any JSON. Only include the name of the effect to apply."
|
"Do not include the question or any JSON. Only include the name of the effect to apply."
|
||||||
)
|
)
|
||||||
chosen_name = chosen_name.strip()
|
chosen_name = chosen_name.strip()
|
||||||
|
|
||||||
chosen_effect = next(
|
chosen_effect = next(
|
||||||
(
|
(
|
||||||
search_effect
|
search_effect
|
||||||
for search_effect in action_item.effects
|
for search_effect in action_item.effects
|
||||||
if search_effect.name == chosen_name
|
if search_effect.name == chosen_name
|
||||||
),
|
),
|
||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
if not chosen_effect:
|
if not chosen_effect:
|
||||||
# TODO: should retry the question if the effect is not found
|
# TODO: should retry the question if the effect is not found
|
||||||
return f"The {chosen_name} effect is not available to apply."
|
return f"The {chosen_name} effect is not available to apply."
|
||||||
|
|
||||||
apply_effect(chosen_effect, target_actor.attributes)
|
apply_effect(chosen_effect, target_actor.attributes)
|
||||||
|
|
||||||
broadcast(
|
broadcast(
|
||||||
f"{action_actor.name} uses the {chosen_name} effect of {item} on {target}"
|
f"{action_actor.name} uses the {chosen_name} effect of {item} on {target}"
|
||||||
)
|
)
|
||||||
outcome = dungeon_master(
|
outcome = dungeon_master(
|
||||||
f"{action_actor.name} uses the {chosen_name} effect of {item} on {target}. "
|
f"{action_actor.name} uses the {chosen_name} effect of {item} on {target}. "
|
||||||
f"{describe_actor(action_actor)}. {describe_actor(target_actor)}. {describe_entity(action_item)}. "
|
f"{describe_actor(action_actor)}. {describe_actor(target_actor)}. {describe_entity(action_item)}. "
|
||||||
f"What happens? How does {target} react? Be creative with the results. The outcome can be good, bad, or neutral."
|
f"What happens? How does {target} react? Be creative with the results. The outcome can be good, bad, or neutral."
|
||||||
"Decide based on the characters involved and the item being used."
|
"Decide based on the characters involved and the item being used."
|
||||||
"Specify the outcome of the action. Do not include the question or any JSON. Only include the outcome of the action."
|
"Specify the outcome of the action. Do not include the question or any JSON. Only include the outcome of the action."
|
||||||
)
|
)
|
||||||
broadcast(f"The action resulted in: {outcome}")
|
broadcast(f"The action resulted in: {outcome}")
|
||||||
|
|
||||||
# make sure both agents remember the outcome
|
# make sure both agents remember the outcome
|
||||||
target_agent = get_agent_for_actor(target_actor)
|
target_agent = get_agent_for_actor(target_actor)
|
||||||
if target_agent:
|
if target_agent and target_agent.memory:
|
||||||
target_agent.memory.append(outcome)
|
target_agent.memory.append(outcome)
|
||||||
|
|
||||||
return outcome
|
return outcome
|
||||||
|
|
||||||
|
|
||||||
def init() -> List[Callable]:
|
def init() -> List[Callable]:
|
||||||
|
|
|
@ -8,7 +8,7 @@ from langchain_core.messages import AIMessage, BaseMessage, HumanMessage
|
||||||
from packit.agent import Agent
|
from packit.agent import Agent
|
||||||
from packit.utils import could_be_json
|
from packit.utils import could_be_json
|
||||||
|
|
||||||
from adventure.context import get_current_context
|
from adventure.context import action_context
|
||||||
from adventure.models.event import PromptEvent
|
from adventure.models.event import PromptEvent
|
||||||
|
|
||||||
logger = getLogger(__name__)
|
logger = getLogger(__name__)
|
||||||
|
@ -183,21 +183,21 @@ class RemotePlayer(BasePlayer):
|
||||||
formatted_prompt = prompt.format(**kwargs)
|
formatted_prompt = prompt.format(**kwargs)
|
||||||
self.memory.append(HumanMessage(content=formatted_prompt))
|
self.memory.append(HumanMessage(content=formatted_prompt))
|
||||||
|
|
||||||
_, current_room, current_actor = get_current_context()
|
with action_context() as (current_room, current_actor):
|
||||||
prompt_event = PromptEvent(
|
prompt_event = PromptEvent(
|
||||||
prompt=formatted_prompt, room=current_room, actor=current_actor
|
prompt=formatted_prompt, room=current_room, actor=current_actor
|
||||||
)
|
)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
logger.info(f"prompting remote player: {self.name}")
|
logger.info(f"prompting remote player: {self.name}")
|
||||||
if self.send_prompt(prompt_event):
|
if self.send_prompt(prompt_event):
|
||||||
reply = self.input_queue.get(timeout=60)
|
reply = self.input_queue.get(timeout=60)
|
||||||
logger.info(f"got reply from remote player: {reply}")
|
logger.info(f"got reply from remote player: {reply}")
|
||||||
return self.parse_input(reply)
|
return self.parse_input(reply)
|
||||||
except Exception:
|
except Exception:
|
||||||
logger.exception("error getting reply from remote player")
|
logger.exception("error getting reply from remote player")
|
||||||
|
|
||||||
if self.fallback_agent:
|
if self.fallback_agent:
|
||||||
return self.fallback_agent(prompt, **kwargs)
|
return self.fallback_agent(prompt, **kwargs)
|
||||||
|
|
||||||
return ""
|
return ""
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
from random import randint
|
from random import randint
|
||||||
|
|
||||||
from adventure.context import broadcast, get_current_context, get_dungeon_master
|
from adventure.context import broadcast, get_dungeon_master, world_context
|
||||||
from adventure.generate import generate_item
|
from adventure.generate import generate_item
|
||||||
from adventure.models.base import dataclass
|
from adventure.models.base import dataclass
|
||||||
from adventure.models.entity import Item
|
from adventure.models.entity import Item
|
||||||
|
@ -26,46 +26,49 @@ def action_craft(item_name: str) -> str:
|
||||||
Args:
|
Args:
|
||||||
item_name: The name of the item to craft.
|
item_name: The name of the item to craft.
|
||||||
"""
|
"""
|
||||||
action_world, _, action_actor = get_current_context()
|
with world_context() as (action_world, _, action_actor):
|
||||||
|
if item_name not in recipes:
|
||||||
|
return f"There is no recipe to craft a {item_name}."
|
||||||
|
|
||||||
if item_name not in recipes:
|
recipe = recipes[item_name]
|
||||||
return f"There is no recipe to craft a {item_name}."
|
|
||||||
|
|
||||||
recipe = recipes[item_name]
|
# Check if the actor has the required skill level
|
||||||
|
skill = randint(1, 20)
|
||||||
|
if skill < recipe.difficulty:
|
||||||
|
return f"You need a crafting skill level of {recipe.difficulty} to craft {item_name}."
|
||||||
|
|
||||||
# Check if the actor has the required skill level
|
# Collect inventory items names
|
||||||
skill = randint(1, 20)
|
inventory_items = {item.name for item in action_actor.items}
|
||||||
if skill < recipe.difficulty:
|
|
||||||
return f"You need a crafting skill level of {recipe.difficulty} to craft {item_name}."
|
|
||||||
|
|
||||||
# Collect inventory items names
|
# Check for sufficient ingredients
|
||||||
inventory_items = {item.name for item in action_actor.items}
|
missing_items = [
|
||||||
|
item for item in recipe.ingredients if item not in inventory_items
|
||||||
|
]
|
||||||
|
if missing_items:
|
||||||
|
return (
|
||||||
|
f"You are missing {' and '.join(missing_items)} to craft {item_name}."
|
||||||
|
)
|
||||||
|
|
||||||
# Check for sufficient ingredients
|
# Deduct the ingredients from inventory
|
||||||
missing_items = [item for item in recipe.ingredients if item not in inventory_items]
|
for ingredient in recipe.ingredients:
|
||||||
if missing_items:
|
item_to_remove = next(
|
||||||
return f"You are missing {' and '.join(missing_items)} to craft {item_name}."
|
item for item in action_actor.items if item.name == ingredient
|
||||||
|
)
|
||||||
|
action_actor.items.remove(item_to_remove)
|
||||||
|
|
||||||
# Deduct the ingredients from inventory
|
# Create and add the crafted item to inventory
|
||||||
for ingredient in recipe.ingredients:
|
result_item = next(
|
||||||
item_to_remove = next(
|
(item for item in action_actor.items if item.name == recipe.result), None
|
||||||
item for item in action_actor.items if item.name == ingredient
|
|
||||||
)
|
)
|
||||||
action_actor.items.remove(item_to_remove)
|
if result_item:
|
||||||
|
new_item = Item(**vars(result_item)) # Copying the item
|
||||||
|
else:
|
||||||
|
dungeon_master = get_dungeon_master()
|
||||||
|
new_item = generate_item(
|
||||||
|
dungeon_master, action_world.theme
|
||||||
|
) # TODO: pass recipe item
|
||||||
|
|
||||||
# Create and add the crafted item to inventory
|
action_actor.items.append(new_item)
|
||||||
result_item = next(
|
|
||||||
(item for item in action_actor.items if item.name == recipe.result), None
|
|
||||||
)
|
|
||||||
if result_item:
|
|
||||||
new_item = Item(**vars(result_item)) # Copying the item
|
|
||||||
else:
|
|
||||||
dungeon_master = get_dungeon_master()
|
|
||||||
new_item = generate_item(
|
|
||||||
dungeon_master, action_world.theme
|
|
||||||
) # TODO: pass recipe item
|
|
||||||
|
|
||||||
action_actor.items.append(new_item)
|
broadcast(f"{action_actor.name} crafts a {item_name}.")
|
||||||
|
return f"You successfully craft a {item_name}."
|
||||||
broadcast(f"{action_actor.name} crafts a {item_name}.")
|
|
||||||
return f"You successfully craft a {item_name}."
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
from adventure.context import broadcast, get_current_context
|
from adventure.context import action_context, broadcast
|
||||||
from adventure.search import find_item_in_actor
|
from adventure.utils.search import find_item_in_actor
|
||||||
|
|
||||||
|
|
||||||
def action_read(item_name: str) -> str:
|
def action_read(item_name: str) -> str:
|
||||||
|
@ -9,14 +9,13 @@ def action_read(item_name: str) -> str:
|
||||||
Args:
|
Args:
|
||||||
item_name: The name of the item to read.
|
item_name: The name of the item to read.
|
||||||
"""
|
"""
|
||||||
_, _, action_actor = get_current_context()
|
with action_context() as (_, action_actor):
|
||||||
|
item = find_item_in_actor(action_actor, item_name)
|
||||||
|
if not item:
|
||||||
|
return f"You do not have a {item_name} to read."
|
||||||
|
|
||||||
item = find_item_in_actor(action_actor, item_name)
|
if "text" in item.attributes:
|
||||||
if not item:
|
broadcast(f"{action_actor.name} reads {item_name}")
|
||||||
return f"You do not have a {item_name} to read."
|
return str(item.attributes["text"])
|
||||||
|
|
||||||
if "text" in item.attributes:
|
|
||||||
broadcast(f"{action_actor.name} reads {item_name}")
|
|
||||||
return str(item.attributes["text"])
|
|
||||||
else:
|
|
||||||
return f"The {item_name} has nothing to read."
|
return f"The {item_name} has nothing to read."
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
from random import randint
|
from random import randint
|
||||||
|
|
||||||
from adventure.context import broadcast, get_current_context, get_dungeon_master
|
from adventure.context import action_context, broadcast, get_dungeon_master
|
||||||
from adventure.search import find_actor_in_room
|
from adventure.utils.search import find_actor_in_room
|
||||||
|
|
||||||
|
|
||||||
def action_cast(spell: str, target: str) -> str:
|
def action_cast(spell: str, target: str) -> str:
|
||||||
|
@ -12,26 +12,25 @@ def action_cast(spell: str, target: str) -> str:
|
||||||
spell: The name of the spell to cast.
|
spell: The name of the spell to cast.
|
||||||
target: The target of the spell.
|
target: The target of the spell.
|
||||||
"""
|
"""
|
||||||
_, action_room, action_actor = get_current_context()
|
with action_context() as (action_room, action_actor):
|
||||||
|
target_actor = find_actor_in_room(action_room, target)
|
||||||
|
dungeon_master = get_dungeon_master()
|
||||||
|
|
||||||
target_actor = find_actor_in_room(action_room, target)
|
# Check for spell availability and mana costs
|
||||||
dungeon_master = get_dungeon_master()
|
if spell not in action_actor.attributes["spells"]:
|
||||||
|
return f"You do not know the spell '{spell}'."
|
||||||
|
if action_actor.attributes["mana"] < action_actor.attributes["spells"][spell]:
|
||||||
|
return "You do not have enough mana to cast this spell."
|
||||||
|
|
||||||
# Check for spell availability and mana costs
|
action_actor.attributes["mana"] -= action_actor.attributes["spells"][spell]
|
||||||
if spell not in action_actor.attributes["spells"]:
|
# Get flavor text from the dungeon master
|
||||||
return f"You do not know the spell '{spell}'."
|
flavor_text = dungeon_master(f"Describe the effects of {spell} on {target}.")
|
||||||
if action_actor.attributes["mana"] < action_actor.attributes["spells"][spell]:
|
broadcast(f"{action_actor.name} casts {spell} on {target}. {flavor_text}")
|
||||||
return "You do not have enough mana to cast this spell."
|
|
||||||
|
|
||||||
action_actor.attributes["mana"] -= action_actor.attributes["spells"][spell]
|
# Apply effects based on the spell
|
||||||
# Get flavor text from the dungeon master
|
if spell == "heal" and target_actor:
|
||||||
flavor_text = dungeon_master(f"Describe the effects of {spell} on {target}.")
|
heal_amount = randint(10, 30)
|
||||||
broadcast(f"{action_actor.name} casts {spell} on {target}. {flavor_text}")
|
target_actor.attributes["health"] += heal_amount
|
||||||
|
return f"{target} is healed for {heal_amount} points."
|
||||||
|
|
||||||
# Apply effects based on the spell
|
return f"{spell} was successfully cast on {target}."
|
||||||
if spell == "heal" and target_actor:
|
|
||||||
heal_amount = randint(10, 30)
|
|
||||||
target_actor.attributes["health"] += heal_amount
|
|
||||||
return f"{target} is healed for {heal_amount} points."
|
|
||||||
|
|
||||||
return f"{spell} was successfully cast on {target}."
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
from random import randint
|
from random import randint
|
||||||
|
|
||||||
from adventure.context import broadcast, get_current_context, get_dungeon_master
|
from adventure.context import action_context, broadcast, get_dungeon_master
|
||||||
from adventure.search import find_item_in_room
|
from adventure.utils.search import find_item_in_room
|
||||||
|
|
||||||
|
|
||||||
def action_climb(target: str) -> str:
|
def action_climb(target: str) -> str:
|
||||||
|
@ -11,27 +11,28 @@ def action_climb(target: str) -> str:
|
||||||
Args:
|
Args:
|
||||||
target: The object or feature to climb.
|
target: The object or feature to climb.
|
||||||
"""
|
"""
|
||||||
_, action_room, action_actor = get_current_context()
|
with action_context() as (action_room, action_actor):
|
||||||
|
dungeon_master = get_dungeon_master()
|
||||||
|
# Assume 'climbable' is an attribute that marks climbable targets
|
||||||
|
climbable_feature = find_item_in_room(action_room, target)
|
||||||
|
|
||||||
dungeon_master = get_dungeon_master()
|
if climbable_feature and climbable_feature.attributes.get("climbable", False):
|
||||||
# Assume 'climbable' is an attribute that marks climbable targets
|
climb_difficulty = int(climbable_feature.attributes.get("difficulty", 5))
|
||||||
climbable_feature = find_item_in_room(action_room, target)
|
climb_roll = randint(1, 20)
|
||||||
|
|
||||||
if climbable_feature and climbable_feature.attributes.get("climbable", False):
|
# Get flavor text for the climb attempt
|
||||||
climb_difficulty = int(climbable_feature.attributes.get("difficulty", 5))
|
flavor_text = dungeon_master(
|
||||||
climb_roll = randint(1, 20)
|
f"Describe {action_actor.name}'s attempt to climb {target}."
|
||||||
|
|
||||||
# Get flavor text for the climb attempt
|
|
||||||
flavor_text = dungeon_master(
|
|
||||||
f"Describe {action_actor.name}'s attempt to climb {target}."
|
|
||||||
)
|
|
||||||
if climb_roll > climb_difficulty:
|
|
||||||
broadcast(
|
|
||||||
f"{action_actor.name} successfully climbs the {target}. {flavor_text}"
|
|
||||||
)
|
)
|
||||||
return f"You successfully climb the {target}."
|
if climb_roll > climb_difficulty:
|
||||||
|
broadcast(
|
||||||
|
f"{action_actor.name} successfully climbs the {target}. {flavor_text}"
|
||||||
|
)
|
||||||
|
return f"You successfully climb the {target}."
|
||||||
|
else:
|
||||||
|
broadcast(
|
||||||
|
f"{action_actor.name} fails to climb the {target}. {flavor_text}"
|
||||||
|
)
|
||||||
|
return f"You fail to climb the {target}."
|
||||||
else:
|
else:
|
||||||
broadcast(f"{action_actor.name} fails to climb the {target}. {flavor_text}")
|
return f"The {target} is not climbable."
|
||||||
return f"You fail to climb the {target}."
|
|
||||||
else:
|
|
||||||
return f"The {target} is not climbable."
|
|
||||||
|
|
|
@ -36,9 +36,9 @@ from adventure.player import (
|
||||||
remove_player,
|
remove_player,
|
||||||
set_player,
|
set_player,
|
||||||
)
|
)
|
||||||
from adventure.render_comfy import render_entity, render_event
|
from adventure.render.comfy import render_entity, render_event
|
||||||
from adventure.search import find_actor, find_item, find_room
|
|
||||||
from adventure.state import snapshot_world, world_json
|
from adventure.state import snapshot_world, world_json
|
||||||
|
from adventure.utils.search import find_actor, find_item, find_room
|
||||||
|
|
||||||
logger = getLogger(__name__)
|
logger = getLogger(__name__)
|
||||||
|
|
||||||
|
@ -233,7 +233,7 @@ def render_input(data):
|
||||||
elif "item" in data:
|
elif "item" in data:
|
||||||
item_name = data["item"]
|
item_name = data["item"]
|
||||||
item = find_item(
|
item = find_item(
|
||||||
world, item_name, include_actor_inventory=True, include_room_inventory=True
|
world, item_name, include_actor_inventory=True, include_item_inventory=True
|
||||||
)
|
)
|
||||||
if item:
|
if item:
|
||||||
render_entity(item)
|
render_entity(item)
|
|
@ -1,10 +1,10 @@
|
||||||
from adventure.context import (
|
from adventure.context import (
|
||||||
|
action_context,
|
||||||
broadcast,
|
broadcast,
|
||||||
get_agent_for_actor,
|
get_agent_for_actor,
|
||||||
get_current_context,
|
|
||||||
get_dungeon_master,
|
get_dungeon_master,
|
||||||
)
|
)
|
||||||
from adventure.search import find_actor_in_room, find_item_in_room
|
from adventure.utils.search import find_actor_in_room, find_item_in_room
|
||||||
from adventure.utils.world import describe_entity
|
from adventure.utils.world import describe_entity
|
||||||
|
|
||||||
|
|
||||||
|
@ -16,46 +16,46 @@ def action_attack(target: str) -> str:
|
||||||
target: The name of the character or item to attack.
|
target: The name of the character or item to attack.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
_, action_room, action_actor = get_current_context()
|
with action_context() as (action_room, action_actor):
|
||||||
|
# make sure the target is in the room
|
||||||
|
target_actor = find_actor_in_room(action_room, target)
|
||||||
|
target_item = find_item_in_room(action_room, target)
|
||||||
|
|
||||||
# make sure the target is in the room
|
dungeon_master = get_dungeon_master()
|
||||||
target_actor = find_actor_in_room(action_room, target)
|
if target_actor:
|
||||||
target_item = find_item_in_room(action_room, target)
|
target_agent = get_agent_for_actor(target_actor)
|
||||||
|
if not target_agent:
|
||||||
|
raise ValueError(f"no agent found for actor {target_actor.name}")
|
||||||
|
|
||||||
dungeon_master = get_dungeon_master()
|
reaction = target_agent(
|
||||||
if target_actor:
|
f"{action_actor.name} is attacking you in the {action_room.name}. How do you react?"
|
||||||
target_agent = get_agent_for_actor(target_actor)
|
"Respond with 'fighting', 'fleeing', or 'surrendering'."
|
||||||
if not target_agent:
|
)
|
||||||
raise ValueError(f"no agent found for actor {target_actor.name}")
|
|
||||||
|
|
||||||
reaction = target_agent(
|
outcome = dungeon_master(
|
||||||
f"{action_actor.name} is attacking you in the {action_room.name}. How do you react?"
|
f"{action_actor.name} attacks {target} in the {action_room.name}. {describe_entity(action_room)}."
|
||||||
"Respond with 'fighting', 'fleeing', or 'surrendering'."
|
f"{describe_entity(action_actor)}. {describe_entity(target_actor)}."
|
||||||
)
|
f"{target} reacts by {reaction}. What is the outcome of the attack? Describe the result in detail."
|
||||||
|
)
|
||||||
|
|
||||||
outcome = dungeon_master(
|
description = (
|
||||||
f"{action_actor.name} attacks {target} in the {action_room.name}. {describe_entity(action_room)}."
|
f"{action_actor.name} attacks the {target} in the {action_room.name}."
|
||||||
f"{describe_entity(action_actor)}. {describe_entity(target_actor)}."
|
f"{target} reacts by {reaction}. {outcome}"
|
||||||
f"{target} reacts by {reaction}. What is the outcome of the attack? Describe the result in detail."
|
)
|
||||||
)
|
broadcast(description)
|
||||||
|
return description
|
||||||
|
|
||||||
description = (
|
if target_item:
|
||||||
f"{action_actor.name} attacks the {target} in the {action_room.name}."
|
outcome = dungeon_master(
|
||||||
f"{target} reacts by {reaction}. {outcome}"
|
f"{action_actor.name} attacks {target} in the {action_room.name}. {describe_entity(action_room)}."
|
||||||
)
|
f"{describe_entity(action_actor)}. {describe_entity(target_item)}."
|
||||||
broadcast(description)
|
f"What is the outcome of the attack? Describe the result in detail."
|
||||||
return description
|
)
|
||||||
elif target_item:
|
|
||||||
outcome = dungeon_master(
|
description = f"{action_actor.name} attacks the {target} in the {action_room.name}. {outcome}"
|
||||||
f"{action_actor.name} attacks {target} in the {action_room.name}. {describe_entity(action_room)}."
|
broadcast(description)
|
||||||
f"{describe_entity(action_actor)}. {describe_entity(target_item)}."
|
return description
|
||||||
f"What is the outcome of the attack? Describe the result in detail."
|
|
||||||
)
|
|
||||||
|
|
||||||
description = f"{action_actor.name} attacks the {target} in the {action_room.name}. {outcome}"
|
|
||||||
broadcast(description)
|
|
||||||
return description
|
|
||||||
else:
|
|
||||||
return f"{target} is not in the {action_room.name}."
|
return f"{target} is not in the {action_room.name}."
|
||||||
|
|
||||||
|
|
||||||
|
@ -68,22 +68,21 @@ def action_cast(target: str, spell: str) -> str:
|
||||||
spell: The name of the spell to cast.
|
spell: The name of the spell to cast.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
_, action_room, action_actor = get_current_context()
|
with action_context() as (action_room, action_actor):
|
||||||
|
# make sure the target is in the room
|
||||||
|
target_actor = find_actor_in_room(action_room, target)
|
||||||
|
target_item = find_item_in_room(action_room, target)
|
||||||
|
|
||||||
# make sure the target is in the room
|
if not target_actor and not target_item:
|
||||||
target_actor = find_actor_in_room(action_room, target)
|
return f"{target} is not in the {action_room.name}."
|
||||||
target_item = find_item_in_room(action_room, target)
|
|
||||||
|
|
||||||
if not target_actor and not target_item:
|
dungeon_master = get_dungeon_master()
|
||||||
return f"{target} is not in the {action_room.name}."
|
outcome = dungeon_master(
|
||||||
|
f"{action_actor.name} casts {spell} on {target} in the {action_room.name}. {describe_entity(action_room)}."
|
||||||
|
f"{describe_entity(action_actor)}. {describe_entity(target_actor) if target_actor else describe_entity(target_item)}."
|
||||||
|
f"What is the outcome of the spell? Describe the result in detail."
|
||||||
|
)
|
||||||
|
|
||||||
dungeon_master = get_dungeon_master()
|
description = f"{action_actor.name} casts {spell} on the {target} in the {action_room.name}. {outcome}"
|
||||||
outcome = dungeon_master(
|
broadcast(description)
|
||||||
f"{action_actor.name} casts {spell} on {target} in the {action_room.name}. {describe_entity(action_room)}."
|
return description
|
||||||
f"{describe_entity(action_actor)}. {describe_entity(target_actor) if target_actor else describe_entity(target_item)}."
|
|
||||||
f"What is the outcome of the spell? Describe the result in detail."
|
|
||||||
)
|
|
||||||
|
|
||||||
description = f"{action_actor.name} casts {spell} on the {target} in the {action_room.name}. {outcome}"
|
|
||||||
broadcast(description)
|
|
||||||
return description
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
from adventure.context import get_current_context
|
from adventure.context import action_context
|
||||||
from adventure.search import find_item_in_actor
|
from adventure.utils.search import find_item_in_actor
|
||||||
|
|
||||||
|
|
||||||
def action_cook(item: str) -> str:
|
def action_cook(item: str) -> str:
|
||||||
|
@ -9,25 +9,24 @@ def action_cook(item: str) -> str:
|
||||||
Args:
|
Args:
|
||||||
item: The name of the item to cook.
|
item: The name of the item to cook.
|
||||||
"""
|
"""
|
||||||
_, _, action_actor = get_current_context()
|
with action_context() as (_, action_actor):
|
||||||
|
target_item = find_item_in_actor(action_actor, item)
|
||||||
|
if target_item is None:
|
||||||
|
return "You don't have the item to cook."
|
||||||
|
|
||||||
target_item = find_item_in_actor(action_actor, item)
|
# Check if the item is edible
|
||||||
if target_item is None:
|
edible = target_item.attributes.get("edible", False)
|
||||||
return "You don't have the item to cook."
|
if not edible:
|
||||||
|
return "You can't cook that."
|
||||||
|
|
||||||
# Check if the item is edible
|
# Check if the item is raw
|
||||||
edible = target_item.attributes.get("edible", False)
|
cooked = target_item.attributes.get("cooked", False)
|
||||||
if not edible:
|
if cooked:
|
||||||
return "You can't cook that."
|
return "That item is already cooked."
|
||||||
|
|
||||||
# Check if the item is raw
|
# Cook the item
|
||||||
cooked = target_item.attributes.get("cooked", False)
|
target_item.attributes["cooked"] = True
|
||||||
if cooked:
|
return f"You cook the {item}."
|
||||||
return "That item is already cooked."
|
|
||||||
|
|
||||||
# Cook the item
|
|
||||||
target_item.attributes["cooked"] = True
|
|
||||||
return f"You cook the {item}."
|
|
||||||
|
|
||||||
|
|
||||||
def action_eat(item: str) -> str:
|
def action_eat(item: str) -> str:
|
||||||
|
@ -37,33 +36,32 @@ def action_eat(item: str) -> str:
|
||||||
Args:
|
Args:
|
||||||
item: The name of the item to eat.
|
item: The name of the item to eat.
|
||||||
"""
|
"""
|
||||||
_, _, action_actor = get_current_context()
|
with action_context() as (_, action_actor):
|
||||||
|
target_item = find_item_in_actor(action_actor, item)
|
||||||
|
if target_item is None:
|
||||||
|
return "You don't have the item to eat."
|
||||||
|
|
||||||
target_item = find_item_in_actor(action_actor, item)
|
# Check if the item is edible
|
||||||
if target_item is None:
|
edible = target_item.attributes.get("edible", False)
|
||||||
return "You don't have the item to eat."
|
if not edible:
|
||||||
|
return "You can't eat that."
|
||||||
|
|
||||||
# Check if the item is edible
|
# Check if the item is cooked
|
||||||
edible = target_item.attributes.get("edible", False)
|
cooked = target_item.attributes.get("cooked", False)
|
||||||
if not edible:
|
if not cooked:
|
||||||
return "You can't eat that."
|
return "You can't eat that raw."
|
||||||
|
|
||||||
# Check if the item is cooked
|
# Check if the item is rotten
|
||||||
cooked = target_item.attributes.get("cooked", False)
|
spoiled = target_item.attributes.get("spoiled", False)
|
||||||
if not cooked:
|
if spoiled:
|
||||||
return "You can't eat that raw."
|
return "You can't eat that item, it is rotten."
|
||||||
|
|
||||||
# Check if the item is rotten
|
# Check if the actor is hungry
|
||||||
spoiled = target_item.attributes.get("spoiled", False)
|
hunger = action_actor.attributes.get("hunger", None)
|
||||||
if spoiled:
|
if hunger != "hungry":
|
||||||
return "You can't eat that item, it is rotten."
|
return "You're not hungry."
|
||||||
|
|
||||||
# Check if the actor is hungry
|
# Eat the item
|
||||||
hunger = action_actor.attributes.get("hunger", None)
|
action_actor.items.remove(target_item)
|
||||||
if hunger != "hungry":
|
action_actor.attributes["hunger"] = "full"
|
||||||
return "You're not hungry."
|
return f"You eat the {item}."
|
||||||
|
|
||||||
# Eat the item
|
|
||||||
action_actor.items.remove(target_item)
|
|
||||||
action_actor.attributes["hunger"] = "full"
|
|
||||||
return f"You eat the {item}."
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
from adventure.context import get_current_context, get_dungeon_master
|
from adventure.context import action_context, get_dungeon_master
|
||||||
from adventure.utils.world import describe_entity
|
from adventure.utils.world import describe_entity
|
||||||
|
|
||||||
|
|
||||||
|
@ -7,15 +7,15 @@ def action_wash(unused: bool) -> str:
|
||||||
Wash yourself.
|
Wash yourself.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
_, action_room, action_actor = get_current_context()
|
with action_context() as (action_room, action_actor):
|
||||||
hygiene = action_actor.attributes.get("hygiene", "clean")
|
hygiene = action_actor.attributes.get("hygiene", "clean")
|
||||||
|
|
||||||
dungeon_master = get_dungeon_master()
|
dungeon_master = get_dungeon_master()
|
||||||
outcome = dungeon_master(
|
outcome = dungeon_master(
|
||||||
f"{action_actor.name} washes themselves in the {action_room.name}. {describe_entity(action_room)}. {describe_entity(action_actor)}"
|
f"{action_actor.name} washes themselves in the {action_room.name}. {describe_entity(action_room)}. {describe_entity(action_actor)}"
|
||||||
f"{action_actor.name} was {hygiene} to start with. How clean are they after washing? Respond with 'clean' or 'dirty'."
|
f"{action_actor.name} was {hygiene} to start with. How clean are they after washing? Respond with 'clean' or 'dirty'."
|
||||||
"If the room has a shower or running water, they should be cleaner. If the room is dirty, they should end up dirtier."
|
"If the room has a shower or running water, they should be cleaner. If the room is dirty, they should end up dirtier."
|
||||||
)
|
)
|
||||||
|
|
||||||
action_actor.attributes["clean"] = outcome.strip().lower()
|
action_actor.attributes["clean"] = outcome.strip().lower()
|
||||||
return f"You wash yourself in the {action_room.name} and feel {outcome}"
|
return f"You wash yourself in the {action_room.name} and feel {outcome}"
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
from adventure.context import get_current_context, get_dungeon_master
|
from adventure.context import action_context, get_dungeon_master
|
||||||
from adventure.utils.world import describe_entity
|
from adventure.utils.world import describe_entity
|
||||||
|
|
||||||
|
|
||||||
|
@ -7,13 +7,12 @@ def action_sleep(unused: bool) -> str:
|
||||||
Sleep until you are rested.
|
Sleep until you are rested.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
_, action_room, action_actor = get_current_context()
|
with action_context() as (action_room, action_actor):
|
||||||
|
dungeon_master = get_dungeon_master()
|
||||||
|
outcome = dungeon_master(
|
||||||
|
f"{action_actor.name} sleeps in the {action_room.name}. {describe_entity(action_room)}. {describe_entity(action_actor)}"
|
||||||
|
"How rested are they? Respond with 'rested' or 'tired'."
|
||||||
|
)
|
||||||
|
|
||||||
dungeon_master = get_dungeon_master()
|
action_actor.attributes["rested"] = outcome
|
||||||
outcome = dungeon_master(
|
return f"You sleep in the {action_room.name} and wake up feeling {outcome}"
|
||||||
f"{action_actor.name} sleeps in the {action_room.name}. {describe_entity(action_room)}. {describe_entity(action_actor)}"
|
|
||||||
"How rested are they? Respond with 'rested' or 'tired'."
|
|
||||||
)
|
|
||||||
|
|
||||||
action_actor.attributes["rested"] = outcome
|
|
||||||
return f"You sleep in the {action_room.name} and wake up feeling {outcome}"
|
|
||||||
|
|
|
@ -146,7 +146,8 @@ def simulate_world(
|
||||||
)
|
)
|
||||||
|
|
||||||
logger.debug(f"{actor.name} step result: {result}")
|
logger.debug(f"{actor.name} step result: {result}")
|
||||||
agent.memory.append(result)
|
if agent.memory:
|
||||||
|
agent.memory.append(result)
|
||||||
|
|
||||||
result_event = ResultEvent(result=result, room=room, actor=actor)
|
result_event = ResultEvent(result=result, room=room, actor=actor)
|
||||||
broadcast(result_event)
|
broadcast(result_event)
|
||||||
|
|
|
@ -54,7 +54,7 @@ def snapshot_world(world: World, step: int):
|
||||||
json_memory = {}
|
json_memory = {}
|
||||||
|
|
||||||
for actor, agent in get_all_actor_agents():
|
for actor, agent in get_all_actor_agents():
|
||||||
json_memory[actor.name] = list(agent.memory)
|
json_memory[actor.name] = list(agent.memory or [])
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"world": json_world,
|
"world": json_world,
|
||||||
|
|
Loading…
Reference in New Issue