2024-05-02 11:56:57 +00:00
|
|
|
from logging import getLogger
|
2024-05-26 22:03:39 +00:00
|
|
|
from random import choice
|
2024-05-19 00:48:18 +00:00
|
|
|
from typing import List, Tuple
|
2024-05-02 11:56:57 +00:00
|
|
|
|
|
|
|
from packit.agent import Agent
|
2024-05-04 22:17:56 +00:00
|
|
|
from packit.loops import loop_retry
|
2024-05-24 02:57:21 +00:00
|
|
|
from packit.results import enum_result, int_result
|
2024-05-16 04:12:06 +00:00
|
|
|
from packit.utils import could_be_json
|
|
|
|
|
2024-06-01 19:46:11 +00:00
|
|
|
from taleweave.context import (
|
|
|
|
broadcast,
|
|
|
|
get_game_config,
|
|
|
|
get_prompt,
|
|
|
|
set_current_world,
|
|
|
|
set_system_data,
|
|
|
|
)
|
2024-05-27 13:10:24 +00:00
|
|
|
from taleweave.game_system import GameSystem
|
|
|
|
from taleweave.models.effect import (
|
2024-05-24 02:57:21 +00:00
|
|
|
EffectPattern,
|
|
|
|
FloatEffectPattern,
|
|
|
|
IntEffectPattern,
|
|
|
|
StringEffectPattern,
|
2024-05-16 04:12:06 +00:00
|
|
|
)
|
2024-05-27 13:10:24 +00:00
|
|
|
from taleweave.models.entity import Character, Item, Portal, Room, World, WorldEntity
|
|
|
|
from taleweave.models.event import GenerateEvent
|
|
|
|
from taleweave.utils import try_parse_float, try_parse_int
|
|
|
|
from taleweave.utils.effect import resolve_int_range
|
|
|
|
from taleweave.utils.search import (
|
2024-05-27 01:32:03 +00:00
|
|
|
list_characters,
|
|
|
|
list_characters_in_room,
|
2024-05-19 18:09:52 +00:00
|
|
|
list_items,
|
2024-05-27 01:32:03 +00:00
|
|
|
list_items_in_character,
|
2024-05-19 18:09:52 +00:00
|
|
|
list_items_in_room,
|
|
|
|
list_rooms,
|
|
|
|
)
|
2024-05-27 13:10:24 +00:00
|
|
|
from taleweave.utils.string import normalize_name
|
2024-06-06 00:05:21 +00:00
|
|
|
from taleweave.utils.template import format_prompt
|
2024-05-02 11:56:57 +00:00
|
|
|
|
|
|
|
logger = getLogger(__name__)
|
|
|
|
|
2024-06-05 13:58:35 +00:00
|
|
|
MAX_NAME_LENGTH = 50
|
|
|
|
DISALLOWED_PUNCTUATION = ['"', ":"]
|
|
|
|
|
2024-06-01 19:46:11 +00:00
|
|
|
|
|
|
|
def get_world_config():
|
|
|
|
config = get_game_config()
|
|
|
|
return config.world
|
2024-05-04 22:57:24 +00:00
|
|
|
|
2024-05-02 11:56:57 +00:00
|
|
|
|
2024-05-13 04:33:47 +00:00
|
|
|
def duplicate_name_parser(existing_names: List[str]):
|
2024-05-16 04:12:06 +00:00
|
|
|
def name_parser(value: str, **kwargs):
|
2024-05-18 21:58:11 +00:00
|
|
|
logger.debug(f"validating generated name: {value}")
|
2024-05-16 04:12:06 +00:00
|
|
|
|
|
|
|
if value in existing_names:
|
2024-05-31 23:58:01 +00:00
|
|
|
raise ValueError(
|
|
|
|
format_prompt("world_generate_error_name_exists", name=value)
|
|
|
|
)
|
2024-05-14 01:08:19 +00:00
|
|
|
|
2024-05-16 04:12:06 +00:00
|
|
|
if could_be_json(value):
|
2024-05-31 23:58:01 +00:00
|
|
|
raise ValueError(
|
|
|
|
format_prompt("world_generate_error_name_json", name=value)
|
|
|
|
)
|
2024-05-13 04:33:47 +00:00
|
|
|
|
2024-06-05 13:58:35 +00:00
|
|
|
if any(p in value for p in DISALLOWED_PUNCTUATION):
|
2024-05-31 23:58:01 +00:00
|
|
|
raise ValueError(
|
|
|
|
format_prompt("world_generate_error_name_punctuation", name=value)
|
|
|
|
)
|
2024-05-13 04:33:47 +00:00
|
|
|
|
2024-06-05 13:58:35 +00:00
|
|
|
if len(value) > MAX_NAME_LENGTH:
|
2024-05-31 23:58:01 +00:00
|
|
|
raise ValueError(
|
|
|
|
format_prompt("world_generate_error_name_length", name=value)
|
|
|
|
)
|
2024-05-13 04:33:47 +00:00
|
|
|
|
2024-05-16 04:12:06 +00:00
|
|
|
return value
|
2024-05-13 04:33:47 +00:00
|
|
|
|
|
|
|
return name_parser
|
|
|
|
|
|
|
|
|
2024-05-18 21:58:11 +00:00
|
|
|
def broadcast_generated(
|
2024-05-13 04:33:47 +00:00
|
|
|
message: str | None = None,
|
|
|
|
entity: WorldEntity | None = None,
|
|
|
|
):
|
|
|
|
if message:
|
|
|
|
event = GenerateEvent.from_name(message)
|
|
|
|
elif entity:
|
|
|
|
event = GenerateEvent.from_entity(entity)
|
|
|
|
else:
|
|
|
|
raise ValueError("Either message or entity must be provided")
|
|
|
|
|
2024-05-18 21:58:11 +00:00
|
|
|
broadcast(event)
|
2024-05-13 04:33:47 +00:00
|
|
|
|
|
|
|
|
2024-05-19 04:30:17 +00:00
|
|
|
def generate_system_attributes(
|
|
|
|
agent: Agent, world: World, entity: WorldEntity, systems: List[GameSystem]
|
|
|
|
) -> None:
|
|
|
|
for system in systems:
|
|
|
|
if system.generate:
|
2024-06-05 03:07:26 +00:00
|
|
|
system.generate(agent, world, entity)
|
2024-05-19 04:30:17 +00:00
|
|
|
|
|
|
|
|
2024-05-04 20:35:42 +00:00
|
|
|
def generate_room(
|
2024-05-05 14:14:54 +00:00
|
|
|
agent: Agent,
|
2024-05-19 04:30:17 +00:00
|
|
|
world: World,
|
|
|
|
systems: List[GameSystem],
|
2024-06-05 13:58:35 +00:00
|
|
|
current_room: int | None = None,
|
|
|
|
total_rooms: int | None = None,
|
2024-05-04 20:35:42 +00:00
|
|
|
) -> Room:
|
2024-05-19 04:30:17 +00:00
|
|
|
existing_rooms = [room.name for room in list_rooms(world)]
|
|
|
|
|
2024-05-04 22:17:56 +00:00
|
|
|
name = loop_retry(
|
|
|
|
agent,
|
2024-05-31 23:58:01 +00:00
|
|
|
get_prompt("world_generate_room_name"),
|
2024-05-04 22:17:56 +00:00
|
|
|
context={
|
2024-05-19 04:30:17 +00:00
|
|
|
"world_theme": world.theme,
|
2024-05-04 22:17:56 +00:00
|
|
|
"existing_rooms": existing_rooms,
|
2024-06-05 13:58:35 +00:00
|
|
|
"current_room": current_room,
|
|
|
|
"total_rooms": total_rooms,
|
2024-05-04 22:17:56 +00:00
|
|
|
},
|
2024-05-13 04:33:47 +00:00
|
|
|
result_parser=duplicate_name_parser(existing_rooms),
|
2024-05-19 19:06:09 +00:00
|
|
|
toolbox=None,
|
2024-05-02 11:56:57 +00:00
|
|
|
)
|
2024-05-05 14:14:54 +00:00
|
|
|
|
2024-05-31 23:58:01 +00:00
|
|
|
broadcast_generated(format_prompt("world_generate_room_broadcast_room", name=name))
|
|
|
|
desc = agent(get_prompt("world_generate_room_description"), name=name)
|
2024-05-02 11:56:57 +00:00
|
|
|
|
2024-05-19 00:48:18 +00:00
|
|
|
actions = {}
|
2024-05-27 01:32:03 +00:00
|
|
|
room = Room(name=name, description=desc, items=[], characters=[], actions=actions)
|
2024-05-19 00:48:18 +00:00
|
|
|
|
2024-06-01 19:46:11 +00:00
|
|
|
world_config = get_world_config()
|
2024-05-26 22:03:39 +00:00
|
|
|
item_count = resolve_int_range(world_config.size.room_items) or 0
|
2024-05-31 23:58:01 +00:00
|
|
|
broadcast_generated(
|
|
|
|
format_prompt(
|
|
|
|
"world_generate_room_broadcast_items", item_count=item_count, name=name
|
|
|
|
)
|
|
|
|
)
|
2024-05-19 00:48:18 +00:00
|
|
|
|
2024-05-19 04:30:17 +00:00
|
|
|
for _ in range(item_count):
|
2024-05-19 00:48:18 +00:00
|
|
|
try:
|
|
|
|
item = generate_item(
|
|
|
|
agent,
|
2024-05-19 04:30:17 +00:00
|
|
|
world,
|
|
|
|
systems=systems,
|
|
|
|
dest_room=room,
|
2024-05-19 00:48:18 +00:00
|
|
|
)
|
|
|
|
broadcast_generated(entity=item)
|
|
|
|
|
2024-05-19 04:30:17 +00:00
|
|
|
room.items.append(item)
|
2024-05-19 00:48:18 +00:00
|
|
|
except Exception:
|
|
|
|
logger.exception("error generating item")
|
|
|
|
|
2024-05-27 01:32:03 +00:00
|
|
|
character_count = resolve_int_range(world_config.size.room_characters) or 0
|
|
|
|
broadcast_generated(
|
2024-05-31 23:58:01 +00:00
|
|
|
format_prompt(
|
|
|
|
"world_generate_room_broadcast_characters",
|
|
|
|
character_count=character_count,
|
|
|
|
name=name,
|
|
|
|
)
|
2024-05-27 01:32:03 +00:00
|
|
|
)
|
2024-05-19 00:48:18 +00:00
|
|
|
|
2024-05-27 01:32:03 +00:00
|
|
|
for _ in range(character_count):
|
2024-05-19 00:48:18 +00:00
|
|
|
try:
|
2024-05-27 01:32:03 +00:00
|
|
|
character = generate_character(
|
2024-05-19 00:48:18 +00:00
|
|
|
agent,
|
2024-05-19 04:30:17 +00:00
|
|
|
world,
|
|
|
|
systems=systems,
|
|
|
|
dest_room=room,
|
2024-05-19 00:48:18 +00:00
|
|
|
)
|
2024-05-27 01:32:03 +00:00
|
|
|
broadcast_generated(entity=character)
|
2024-05-19 00:48:18 +00:00
|
|
|
|
2024-05-27 01:32:03 +00:00
|
|
|
room.characters.append(character)
|
2024-05-19 00:48:18 +00:00
|
|
|
except Exception:
|
2024-05-27 01:32:03 +00:00
|
|
|
logger.exception("error generating character")
|
2024-05-19 00:48:18 +00:00
|
|
|
continue
|
2024-05-02 11:56:57 +00:00
|
|
|
|
2024-05-19 04:30:17 +00:00
|
|
|
return room
|
2024-05-02 11:56:57 +00:00
|
|
|
|
|
|
|
|
2024-05-19 00:48:18 +00:00
|
|
|
def generate_portals(
|
|
|
|
agent: Agent,
|
2024-05-19 04:30:17 +00:00
|
|
|
world: World,
|
2024-05-19 00:48:18 +00:00
|
|
|
source_room: Room,
|
|
|
|
dest_room: Room,
|
2024-05-19 04:30:17 +00:00
|
|
|
systems: List[GameSystem],
|
2024-05-31 23:58:01 +00:00
|
|
|
outgoing_name: str | None = None,
|
2024-05-19 00:48:18 +00:00
|
|
|
) -> Tuple[Portal, Portal]:
|
|
|
|
existing_source_portals = [portal.name for portal in source_room.portals]
|
|
|
|
existing_dest_portals = [portal.name for portal in dest_room.portals]
|
|
|
|
|
2024-05-31 23:58:01 +00:00
|
|
|
outgoing_name = outgoing_name or loop_retry(
|
2024-05-19 00:48:18 +00:00
|
|
|
agent,
|
2024-05-31 23:58:01 +00:00
|
|
|
get_prompt("world_generate_portal_name_outgoing"),
|
2024-05-19 00:48:18 +00:00
|
|
|
context={
|
|
|
|
"source_room": source_room.name,
|
|
|
|
"dest_room": dest_room.name,
|
|
|
|
"existing_portals": existing_source_portals,
|
2024-05-19 04:30:17 +00:00
|
|
|
"world_theme": world.theme,
|
2024-05-19 00:48:18 +00:00
|
|
|
},
|
|
|
|
result_parser=duplicate_name_parser(existing_source_portals),
|
2024-05-19 19:06:09 +00:00
|
|
|
toolbox=None,
|
2024-05-19 00:48:18 +00:00
|
|
|
)
|
2024-05-31 23:58:01 +00:00
|
|
|
broadcast_generated(
|
|
|
|
message=format_prompt(
|
|
|
|
"world_generate_portal_broadcast_outgoing", outgoing_name=outgoing_name
|
|
|
|
)
|
|
|
|
)
|
2024-05-19 00:48:18 +00:00
|
|
|
|
|
|
|
incoming_name = loop_retry(
|
|
|
|
agent,
|
2024-05-31 23:58:01 +00:00
|
|
|
get_prompt("world_generate_portal_name_incoming"),
|
2024-05-19 00:48:18 +00:00
|
|
|
context={
|
|
|
|
"source_room": source_room.name,
|
|
|
|
"dest_room": dest_room.name,
|
|
|
|
"existing_portals": existing_dest_portals,
|
2024-05-19 04:30:17 +00:00
|
|
|
"world_theme": world.theme,
|
2024-05-19 00:48:18 +00:00
|
|
|
"outgoing_name": outgoing_name,
|
|
|
|
},
|
|
|
|
result_parser=duplicate_name_parser(existing_dest_portals),
|
2024-05-19 19:06:09 +00:00
|
|
|
toolbox=None,
|
2024-05-19 00:48:18 +00:00
|
|
|
)
|
|
|
|
|
2024-05-31 23:58:01 +00:00
|
|
|
broadcast_generated(
|
|
|
|
message=format_prompt(
|
|
|
|
"world_generate_portal_broadcast_incoming",
|
|
|
|
incoming_name=incoming_name,
|
|
|
|
outgoing_name=outgoing_name,
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
# TODO: generate descriptions for the portals
|
2024-05-19 00:48:18 +00:00
|
|
|
|
|
|
|
outgoing_portal = Portal(
|
|
|
|
name=outgoing_name,
|
|
|
|
description=f"A {outgoing_name} leads to the {dest_room.name} room.",
|
|
|
|
destination=dest_room.name,
|
|
|
|
)
|
2024-05-19 04:30:17 +00:00
|
|
|
generate_system_attributes(agent, world, outgoing_portal, systems)
|
|
|
|
|
2024-05-19 00:48:18 +00:00
|
|
|
incoming_portal = Portal(
|
|
|
|
name=incoming_name,
|
|
|
|
description=f"A {incoming_name} leads to the {source_room.name} room.",
|
|
|
|
destination=source_room.name,
|
|
|
|
)
|
2024-05-19 04:30:17 +00:00
|
|
|
generate_system_attributes(agent, world, incoming_portal, systems)
|
2024-05-19 00:48:18 +00:00
|
|
|
|
|
|
|
return (outgoing_portal, incoming_portal)
|
|
|
|
|
|
|
|
|
2024-05-02 11:56:57 +00:00
|
|
|
def generate_item(
|
|
|
|
agent: Agent,
|
2024-05-19 04:30:17 +00:00
|
|
|
world: World,
|
|
|
|
systems: List[GameSystem],
|
|
|
|
dest_room: Room | None = None,
|
2024-05-27 01:32:03 +00:00
|
|
|
dest_character: Character | None = None,
|
2024-05-02 11:56:57 +00:00
|
|
|
) -> Item:
|
2024-05-19 04:30:17 +00:00
|
|
|
existing_items = [
|
|
|
|
item.name
|
|
|
|
for item in list_items(
|
2024-05-27 01:32:03 +00:00
|
|
|
world, include_character_inventory=True, include_item_inventory=True
|
2024-05-19 04:30:17 +00:00
|
|
|
)
|
|
|
|
]
|
2024-05-19 18:09:52 +00:00
|
|
|
|
2024-05-27 01:32:03 +00:00
|
|
|
if dest_character:
|
|
|
|
dest_note = f"The item will be held by the {dest_character.name} character"
|
|
|
|
existing_items += [
|
|
|
|
item.name for item in list_items_in_character(dest_character)
|
|
|
|
]
|
2024-05-02 11:56:57 +00:00
|
|
|
elif dest_room:
|
2024-05-19 04:30:17 +00:00
|
|
|
dest_note = f"The item will be placed in the {dest_room.name} room"
|
2024-05-19 18:09:52 +00:00
|
|
|
existing_items += [item.name for item in list_items_in_room(dest_room)]
|
2024-05-02 11:56:57 +00:00
|
|
|
else:
|
|
|
|
dest_note = "The item will be placed in the world"
|
|
|
|
|
2024-05-04 22:17:56 +00:00
|
|
|
name = loop_retry(
|
|
|
|
agent,
|
2024-05-31 23:58:01 +00:00
|
|
|
get_prompt("world_generate_item_name"),
|
2024-05-04 22:17:56 +00:00
|
|
|
context={
|
|
|
|
"dest_note": dest_note,
|
|
|
|
"existing_items": existing_items,
|
2024-05-19 04:30:17 +00:00
|
|
|
"world_theme": world.theme,
|
2024-05-04 22:17:56 +00:00
|
|
|
},
|
2024-05-13 04:33:47 +00:00
|
|
|
result_parser=duplicate_name_parser(existing_items),
|
2024-05-19 19:06:09 +00:00
|
|
|
toolbox=None,
|
2024-05-02 11:56:57 +00:00
|
|
|
)
|
2024-05-05 14:14:54 +00:00
|
|
|
|
2024-05-31 23:58:01 +00:00
|
|
|
broadcast_generated(
|
|
|
|
message=format_prompt("world_generate_item_broadcast_item", name=name)
|
2024-05-02 11:56:57 +00:00
|
|
|
)
|
2024-05-31 23:58:01 +00:00
|
|
|
desc = agent(get_prompt("world_generate_item_description"), name=name)
|
2024-05-02 11:56:57 +00:00
|
|
|
|
|
|
|
actions = {}
|
2024-05-16 04:12:06 +00:00
|
|
|
item = Item(name=name, description=desc, actions=actions)
|
2024-05-19 04:30:17 +00:00
|
|
|
generate_system_attributes(agent, world, item, systems)
|
2024-05-02 11:56:57 +00:00
|
|
|
|
2024-06-01 19:46:11 +00:00
|
|
|
world_config = get_world_config()
|
2024-05-26 22:03:39 +00:00
|
|
|
effect_count = resolve_int_range(world_config.size.item_effects) or 0
|
2024-05-31 23:58:01 +00:00
|
|
|
broadcast_generated(
|
|
|
|
message=format_prompt(
|
|
|
|
"world_generate_item_broadcast_effects",
|
|
|
|
effect_count=effect_count,
|
|
|
|
name=name,
|
|
|
|
)
|
|
|
|
)
|
2024-05-16 04:12:06 +00:00
|
|
|
|
2024-05-19 04:30:17 +00:00
|
|
|
for _ in range(effect_count):
|
2024-05-16 04:12:06 +00:00
|
|
|
try:
|
2024-05-19 04:30:17 +00:00
|
|
|
effect = generate_effect(agent, world, entity=item)
|
|
|
|
item.effects.append(effect)
|
2024-05-16 04:12:06 +00:00
|
|
|
except Exception:
|
|
|
|
logger.exception("error generating effect")
|
|
|
|
|
|
|
|
return item
|
2024-05-02 11:56:57 +00:00
|
|
|
|
|
|
|
|
2024-05-27 01:32:03 +00:00
|
|
|
def generate_character(
|
2024-05-05 14:14:54 +00:00
|
|
|
agent: Agent,
|
2024-05-19 04:30:17 +00:00
|
|
|
world: World,
|
|
|
|
systems: List[GameSystem],
|
|
|
|
dest_room: Room,
|
2024-05-27 02:33:44 +00:00
|
|
|
additional_prompt: str = "",
|
|
|
|
detail_prompt: str = "",
|
2024-05-27 17:03:39 +00:00
|
|
|
add_to_world_order: bool = True,
|
2024-05-27 01:32:03 +00:00
|
|
|
) -> Character:
|
|
|
|
existing_characters = [character.name for character in list_characters(world)] + [
|
|
|
|
character.name for character in list_characters_in_room(dest_room)
|
2024-05-19 18:09:52 +00:00
|
|
|
]
|
|
|
|
|
2024-05-04 22:17:56 +00:00
|
|
|
name = loop_retry(
|
|
|
|
agent,
|
2024-05-31 23:58:01 +00:00
|
|
|
get_prompt("world_generate_character_name"),
|
2024-05-04 22:17:56 +00:00
|
|
|
context={
|
2024-05-27 02:33:44 +00:00
|
|
|
"additional_prompt": additional_prompt,
|
2024-05-19 04:30:17 +00:00
|
|
|
"dest_room": dest_room.name,
|
2024-05-27 01:32:03 +00:00
|
|
|
"existing_characters": existing_characters,
|
2024-05-19 04:30:17 +00:00
|
|
|
"world_theme": world.theme,
|
2024-05-04 22:17:56 +00:00
|
|
|
},
|
2024-05-27 01:32:03 +00:00
|
|
|
result_parser=duplicate_name_parser(existing_characters),
|
2024-05-19 19:06:09 +00:00
|
|
|
toolbox=None,
|
2024-05-02 11:56:57 +00:00
|
|
|
)
|
2024-05-05 14:14:54 +00:00
|
|
|
|
2024-05-31 23:58:01 +00:00
|
|
|
broadcast_generated(
|
|
|
|
message=format_prompt("world_generate_character_broadcast_name", name=name)
|
|
|
|
)
|
2024-05-02 11:56:57 +00:00
|
|
|
description = agent(
|
2024-05-31 23:58:01 +00:00
|
|
|
get_prompt("world_generate_character_description"),
|
2024-05-27 02:33:44 +00:00
|
|
|
additional_prompt=additional_prompt,
|
|
|
|
detail_prompt=detail_prompt,
|
2024-05-02 11:56:57 +00:00
|
|
|
name=name,
|
|
|
|
)
|
|
|
|
backstory = agent(
|
2024-05-31 23:58:01 +00:00
|
|
|
get_prompt("world_generate_character_backstory"),
|
2024-05-27 02:33:44 +00:00
|
|
|
additional_prompt=additional_prompt,
|
|
|
|
detail_prompt=detail_prompt,
|
2024-05-02 11:56:57 +00:00
|
|
|
name=name,
|
|
|
|
)
|
|
|
|
|
2024-05-27 01:32:03 +00:00
|
|
|
character = Character(
|
2024-05-19 04:30:17 +00:00
|
|
|
name=name, backstory=backstory, description=description, actions={}, items=[]
|
|
|
|
)
|
2024-05-27 01:32:03 +00:00
|
|
|
generate_system_attributes(agent, world, character, systems)
|
2024-05-19 04:30:17 +00:00
|
|
|
|
2024-05-27 01:32:03 +00:00
|
|
|
# generate the character's inventory
|
2024-06-01 19:46:11 +00:00
|
|
|
world_config = get_world_config()
|
2024-05-27 01:32:03 +00:00
|
|
|
item_count = resolve_int_range(world_config.size.character_items) or 0
|
2024-05-31 23:58:01 +00:00
|
|
|
broadcast_generated(
|
|
|
|
message=format_prompt(
|
|
|
|
"world_generate_character_broadcast_items", item_count=item_count, name=name
|
|
|
|
)
|
|
|
|
)
|
2024-05-19 00:48:18 +00:00
|
|
|
|
|
|
|
for k in range(item_count):
|
|
|
|
try:
|
|
|
|
item = generate_item(
|
|
|
|
agent,
|
2024-05-19 04:30:17 +00:00
|
|
|
world,
|
|
|
|
systems,
|
2024-05-27 01:32:03 +00:00
|
|
|
dest_character=character,
|
2024-05-19 00:48:18 +00:00
|
|
|
)
|
2024-05-19 04:30:17 +00:00
|
|
|
generate_system_attributes(agent, world, item, systems)
|
2024-05-19 00:48:18 +00:00
|
|
|
broadcast_generated(entity=item)
|
|
|
|
|
2024-05-27 01:32:03 +00:00
|
|
|
character.items.append(item)
|
2024-05-19 00:48:18 +00:00
|
|
|
except Exception:
|
|
|
|
logger.exception("error generating item")
|
|
|
|
|
2024-05-27 17:03:39 +00:00
|
|
|
if add_to_world_order:
|
2024-05-31 23:58:01 +00:00
|
|
|
# TODO: make sure characters have an agent
|
2024-05-27 17:03:39 +00:00
|
|
|
logger.info(f"adding character {name} to end of world turn order")
|
|
|
|
world.order.append(name)
|
|
|
|
|
2024-05-27 01:32:03 +00:00
|
|
|
return character
|
2024-05-19 00:48:18 +00:00
|
|
|
|
2024-05-16 04:12:06 +00:00
|
|
|
|
2024-05-24 02:57:21 +00:00
|
|
|
def generate_effect(agent: Agent, world: World, entity: Item) -> EffectPattern:
|
2024-05-19 00:48:18 +00:00
|
|
|
entity_type = entity.type
|
2024-05-19 04:30:17 +00:00
|
|
|
existing_effects = [effect.name for effect in entity.effects]
|
2024-05-16 04:12:06 +00:00
|
|
|
|
|
|
|
name = loop_retry(
|
|
|
|
agent,
|
2024-05-31 23:58:01 +00:00
|
|
|
get_prompt("world_generate_effect_name"),
|
2024-05-16 04:12:06 +00:00
|
|
|
context={
|
2024-05-18 21:20:47 +00:00
|
|
|
"entity_name": entity.name,
|
2024-05-16 04:12:06 +00:00
|
|
|
"entity_type": entity_type,
|
|
|
|
"existing_effects": existing_effects,
|
2024-05-19 04:30:17 +00:00
|
|
|
"theme": world.theme,
|
2024-05-16 04:12:06 +00:00
|
|
|
},
|
|
|
|
result_parser=duplicate_name_parser(existing_effects),
|
2024-05-19 19:06:09 +00:00
|
|
|
toolbox=None,
|
2024-05-16 04:12:06 +00:00
|
|
|
)
|
2024-05-31 23:58:01 +00:00
|
|
|
broadcast_generated(
|
|
|
|
message=format_prompt("world_generate_effect_broadcast_effect", name=name)
|
|
|
|
)
|
2024-05-16 04:12:06 +00:00
|
|
|
|
|
|
|
description = agent(
|
2024-05-31 23:58:01 +00:00
|
|
|
get_prompt("world_generate_effect_description"),
|
2024-05-16 04:12:06 +00:00
|
|
|
name=name,
|
|
|
|
)
|
|
|
|
|
2024-05-27 02:33:44 +00:00
|
|
|
cooldown = loop_retry(
|
|
|
|
agent,
|
2024-05-31 23:58:01 +00:00
|
|
|
get_prompt("world_generate_effect_cooldown"),
|
2024-05-27 02:33:44 +00:00
|
|
|
context={
|
|
|
|
"name": name,
|
|
|
|
},
|
|
|
|
result_parser=int_result,
|
|
|
|
toolbox=None,
|
|
|
|
)
|
|
|
|
|
|
|
|
uses = loop_retry(
|
|
|
|
agent,
|
2024-05-31 23:58:01 +00:00
|
|
|
get_prompt("world_generate_effect_uses"),
|
2024-05-27 02:33:44 +00:00
|
|
|
context={
|
|
|
|
"name": name,
|
|
|
|
},
|
|
|
|
result_parser=int_result,
|
|
|
|
toolbox=None,
|
|
|
|
)
|
|
|
|
|
|
|
|
if uses == -1:
|
|
|
|
uses = None
|
|
|
|
|
2024-05-16 04:12:06 +00:00
|
|
|
attribute_names = agent(
|
2024-05-31 23:58:01 +00:00
|
|
|
get_prompt("world_generate_effect_attribute_names"),
|
2024-05-16 04:12:06 +00:00
|
|
|
name=name,
|
|
|
|
)
|
|
|
|
|
|
|
|
attributes = []
|
|
|
|
for attribute_name in attribute_names.split(","):
|
2024-05-19 18:09:52 +00:00
|
|
|
attribute_name = normalize_name(attribute_name)
|
2024-05-16 04:12:06 +00:00
|
|
|
if attribute_name:
|
|
|
|
value = agent(
|
2024-05-31 23:58:01 +00:00
|
|
|
get_prompt("world_generate_effect_attribute_value"),
|
2024-05-16 04:12:06 +00:00
|
|
|
name=name,
|
|
|
|
attribute_name=attribute_name,
|
|
|
|
)
|
|
|
|
value = value.strip()
|
2024-05-19 00:48:18 +00:00
|
|
|
|
2024-05-24 02:57:21 +00:00
|
|
|
# TODO: support more than just set: offset and multiply
|
2024-05-19 00:48:18 +00:00
|
|
|
int_value = try_parse_int(value)
|
|
|
|
if int_value is not None:
|
2024-05-24 02:57:21 +00:00
|
|
|
attribute_effect = IntEffectPattern(name=attribute_name, set=int_value)
|
2024-05-16 04:12:06 +00:00
|
|
|
else:
|
2024-05-19 00:48:18 +00:00
|
|
|
float_value = try_parse_float(value)
|
|
|
|
if float_value is not None:
|
2024-05-24 02:57:21 +00:00
|
|
|
attribute_effect = FloatEffectPattern(
|
|
|
|
name=attribute_name, set=float_value
|
2024-05-19 00:48:18 +00:00
|
|
|
)
|
|
|
|
else:
|
2024-05-24 02:57:21 +00:00
|
|
|
attribute_effect = StringEffectPattern(
|
|
|
|
name=attribute_name, set=value
|
2024-05-19 00:48:18 +00:00
|
|
|
)
|
2024-05-16 04:12:06 +00:00
|
|
|
|
|
|
|
attributes.append(attribute_effect)
|
|
|
|
|
2024-05-24 02:57:21 +00:00
|
|
|
duration = loop_retry(
|
|
|
|
agent,
|
2024-05-31 23:58:01 +00:00
|
|
|
get_prompt("world_generate_effect_duration"),
|
2024-05-24 02:57:21 +00:00
|
|
|
context={
|
|
|
|
"name": name,
|
|
|
|
},
|
|
|
|
result_parser=int_result,
|
2024-05-25 20:18:40 +00:00
|
|
|
toolbox=None,
|
2024-05-24 02:57:21 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
def parse_application(value: str, **kwargs) -> str:
|
|
|
|
value = enum_result(value, ["temporary", "permanent"])
|
|
|
|
if value:
|
|
|
|
return value
|
|
|
|
|
2024-05-31 23:58:01 +00:00
|
|
|
raise ValueError(get_prompt("world_generate_effect_error_application"))
|
2024-05-24 02:57:21 +00:00
|
|
|
|
|
|
|
application = loop_retry(
|
|
|
|
agent,
|
2024-05-31 23:58:01 +00:00
|
|
|
get_prompt("world_generate_effect_application"),
|
2024-05-24 02:57:21 +00:00
|
|
|
context={
|
|
|
|
"name": name,
|
|
|
|
},
|
|
|
|
result_parser=parse_application,
|
2024-05-25 20:18:40 +00:00
|
|
|
toolbox=None,
|
2024-05-24 02:57:21 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
return EffectPattern(
|
|
|
|
name,
|
|
|
|
description,
|
|
|
|
application,
|
|
|
|
attributes=attributes,
|
2024-05-27 02:33:44 +00:00
|
|
|
cooldown=cooldown,
|
|
|
|
duration=duration,
|
|
|
|
uses=uses,
|
2024-05-24 02:57:21 +00:00
|
|
|
)
|
2024-05-16 04:12:06 +00:00
|
|
|
|
|
|
|
|
2024-05-19 18:09:52 +00:00
|
|
|
def link_rooms(
|
2024-05-04 04:18:21 +00:00
|
|
|
agent: Agent,
|
2024-05-19 18:09:52 +00:00
|
|
|
world: World,
|
2024-05-19 04:30:17 +00:00
|
|
|
systems: List[GameSystem],
|
2024-05-19 18:09:52 +00:00
|
|
|
rooms: List[Room] | None = None,
|
|
|
|
) -> None:
|
|
|
|
rooms = rooms or world.rooms
|
2024-06-01 19:46:11 +00:00
|
|
|
world_config = get_world_config()
|
2024-05-02 11:56:57 +00:00
|
|
|
|
2024-05-19 18:09:52 +00:00
|
|
|
for room in rooms:
|
2024-05-26 22:03:39 +00:00
|
|
|
num_portals = resolve_int_range(world_config.size.portals) or 0
|
2024-05-12 20:47:18 +00:00
|
|
|
|
2024-05-19 00:48:18 +00:00
|
|
|
if len(room.portals) >= num_portals:
|
|
|
|
logger.info(f"room {room.name} already has enough portals")
|
|
|
|
continue
|
2024-05-02 11:56:57 +00:00
|
|
|
|
2024-05-18 21:58:11 +00:00
|
|
|
broadcast_generated(
|
2024-05-31 23:58:01 +00:00
|
|
|
format_prompt(
|
|
|
|
"world_generate_room_broadcast_portals",
|
2024-06-04 13:57:35 +00:00
|
|
|
portal_count=num_portals,
|
2024-06-05 02:15:36 +00:00
|
|
|
room=room,
|
2024-05-31 23:58:01 +00:00
|
|
|
)
|
2024-05-12 20:47:18 +00:00
|
|
|
)
|
2024-05-04 20:35:42 +00:00
|
|
|
|
2024-05-19 04:30:17 +00:00
|
|
|
for _ in range(num_portals):
|
2024-05-19 00:48:18 +00:00
|
|
|
previous_destinations = [portal.destination for portal in room.portals] + [
|
|
|
|
room.name
|
|
|
|
]
|
2024-05-19 04:30:17 +00:00
|
|
|
remaining_rooms = [
|
|
|
|
r for r in world.rooms if r.name not in previous_destinations
|
|
|
|
]
|
2024-05-19 00:48:18 +00:00
|
|
|
if len(remaining_rooms) == 0:
|
|
|
|
logger.info(f"no more rooms to link to from {room.name}")
|
|
|
|
break
|
|
|
|
|
|
|
|
# TODO: prompt the DM to choose a destination room
|
|
|
|
dest_room = choice(
|
2024-05-19 04:30:17 +00:00
|
|
|
[r for r in world.rooms if r.name not in previous_destinations]
|
2024-05-19 00:48:18 +00:00
|
|
|
)
|
|
|
|
|
2024-05-13 04:33:47 +00:00
|
|
|
try:
|
2024-05-19 00:48:18 +00:00
|
|
|
outgoing_portal, incoming_portal = generate_portals(
|
2024-05-19 04:30:17 +00:00
|
|
|
agent, world, room, dest_room, systems
|
2024-05-02 11:56:57 +00:00
|
|
|
)
|
2024-05-12 20:47:18 +00:00
|
|
|
|
2024-05-19 00:48:18 +00:00
|
|
|
room.portals.append(outgoing_portal)
|
|
|
|
dest_room.portals.append(incoming_portal)
|
2024-05-13 04:33:47 +00:00
|
|
|
except Exception:
|
2024-05-19 00:48:18 +00:00
|
|
|
logger.exception("error generating portal")
|
2024-05-02 11:56:57 +00:00
|
|
|
continue
|
|
|
|
|
2024-05-19 18:09:52 +00:00
|
|
|
|
|
|
|
def generate_world(
|
|
|
|
agent: Agent,
|
|
|
|
name: str,
|
|
|
|
theme: str,
|
|
|
|
systems: List[GameSystem],
|
|
|
|
room_count: int | None = None,
|
|
|
|
) -> World:
|
2024-06-01 19:46:11 +00:00
|
|
|
world_config = get_world_config()
|
2024-05-26 22:03:39 +00:00
|
|
|
room_count = room_count or resolve_int_range(world_config.size.rooms) or 0
|
2024-05-19 18:09:52 +00:00
|
|
|
|
2024-06-06 00:05:21 +00:00
|
|
|
broadcast_generated(
|
|
|
|
message=format_prompt(
|
|
|
|
"world_generate_world_broadcast_theme",
|
|
|
|
theme=theme,
|
|
|
|
room_count=room_count,
|
|
|
|
)
|
|
|
|
)
|
2024-05-19 18:09:52 +00:00
|
|
|
world = World(name=name, rooms=[], theme=theme, order=[])
|
|
|
|
set_current_world(world)
|
|
|
|
|
2024-05-25 20:18:40 +00:00
|
|
|
# initialize the systems
|
|
|
|
for system in systems:
|
|
|
|
if system.initialize:
|
|
|
|
data = system.initialize(world)
|
|
|
|
set_system_data(system.name, data)
|
|
|
|
|
2024-05-19 18:09:52 +00:00
|
|
|
# generate the rooms
|
2024-06-05 13:58:35 +00:00
|
|
|
for i in range(room_count):
|
2024-05-19 18:09:52 +00:00
|
|
|
try:
|
2024-06-05 13:58:35 +00:00
|
|
|
room = generate_room(
|
|
|
|
agent, world, systems, current_room=i, total_rooms=room_count
|
|
|
|
)
|
2024-05-19 18:09:52 +00:00
|
|
|
generate_system_attributes(agent, world, room, systems)
|
|
|
|
broadcast_generated(entity=room)
|
|
|
|
world.rooms.append(room)
|
|
|
|
except Exception:
|
|
|
|
logger.exception("error generating room")
|
|
|
|
continue
|
|
|
|
|
|
|
|
# generate portals to link the rooms together
|
|
|
|
link_rooms(agent, world, systems)
|
|
|
|
|
2024-05-27 01:32:03 +00:00
|
|
|
# ensure characters act in a stable order
|
|
|
|
world.order = [
|
|
|
|
character.name for room in world.rooms for character in room.characters
|
|
|
|
]
|
2024-05-19 04:30:17 +00:00
|
|
|
return world
|