rename step to turn
This commit is contained in:
parent
dd0e4ed573
commit
87ed47324c
|
@ -1,4 +1,4 @@
|
||||||
from adventure.context import action_context, get_agent_for_character, get_current_step
|
from adventure.context import action_context, get_agent_for_character, get_current_turn
|
||||||
from adventure.errors import ActionError
|
from adventure.errors import ActionError
|
||||||
from adventure.models.config import DEFAULT_CONFIG
|
from adventure.models.config import DEFAULT_CONFIG
|
||||||
from adventure.models.planning import CalendarEvent
|
from adventure.models.planning import CalendarEvent
|
||||||
|
@ -148,7 +148,7 @@ def check_calendar(count: int):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
count = min(count, character_config.event_limit)
|
count = min(count, character_config.event_limit)
|
||||||
current_turn = get_current_step()
|
current_turn = get_current_turn()
|
||||||
|
|
||||||
with action_context() as (_, action_character):
|
with action_context() as (_, action_character):
|
||||||
if len(action_character.planner.calendar.events) == 0:
|
if len(action_character.planner.calendar.events) == 0:
|
||||||
|
|
|
@ -25,7 +25,7 @@ from adventure.utils.string import normalize_name
|
||||||
logger = getLogger(__name__)
|
logger = getLogger(__name__)
|
||||||
|
|
||||||
# world context
|
# world context
|
||||||
current_step = 0
|
current_turn = 0
|
||||||
current_world: World | None = None
|
current_world: World | None = None
|
||||||
current_room: Room | None = None
|
current_room: Room | None = None
|
||||||
current_character: Character | None = None
|
current_character: Character | None = None
|
||||||
|
@ -140,8 +140,8 @@ def get_current_character() -> Character | None:
|
||||||
return current_character
|
return current_character
|
||||||
|
|
||||||
|
|
||||||
def get_current_step() -> int:
|
def get_current_turn() -> int:
|
||||||
return current_step
|
return current_turn
|
||||||
|
|
||||||
|
|
||||||
def get_dungeon_master() -> Agent:
|
def get_dungeon_master() -> Agent:
|
||||||
|
@ -180,9 +180,9 @@ def set_current_character(character: Character | None):
|
||||||
current_character = character
|
current_character = character
|
||||||
|
|
||||||
|
|
||||||
def set_current_step(step: int):
|
def set_current_turn(turn: int):
|
||||||
global current_step
|
global current_turn
|
||||||
current_step = step
|
current_turn = turn
|
||||||
|
|
||||||
|
|
||||||
def set_character_agent(name, character, agent):
|
def set_character_agent(name, character, agent):
|
||||||
|
|
|
@ -39,9 +39,9 @@ class SystemInitialize(Protocol):
|
||||||
|
|
||||||
|
|
||||||
class SystemSimulate(Protocol):
|
class SystemSimulate(Protocol):
|
||||||
def __call__(self, world: World, step: int, data: Any | None = None) -> None:
|
def __call__(self, world: World, turn: int, data: Any | None = None) -> None:
|
||||||
"""
|
"""
|
||||||
Simulate the world for the given step.
|
Simulate the world for the given turn. If this system has stored data, it will be passed in.
|
||||||
"""
|
"""
|
||||||
...
|
...
|
||||||
|
|
||||||
|
|
|
@ -32,7 +32,7 @@ load_dotenv(environ.get("ADVENTURE_ENV", ".env"), override=True)
|
||||||
if True:
|
if True:
|
||||||
from adventure.context import (
|
from adventure.context import (
|
||||||
get_system_data,
|
get_system_data,
|
||||||
set_current_step,
|
set_current_turn,
|
||||||
set_dungeon_master,
|
set_dungeon_master,
|
||||||
set_system_data,
|
set_system_data,
|
||||||
subscribe,
|
subscribe,
|
||||||
|
@ -142,10 +142,10 @@ def parse_args():
|
||||||
help="The file to save the world state to. Defaults to $world.state.json, if not set",
|
help="The file to save the world state to. Defaults to $world.state.json, if not set",
|
||||||
)
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--steps",
|
"--turns",
|
||||||
type=int_or_inf,
|
type=int_or_inf,
|
||||||
default=10,
|
default=10,
|
||||||
help="The number of simulation steps to run",
|
help="The number of simulation turns to run",
|
||||||
)
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--systems",
|
"--systems",
|
||||||
|
@ -223,7 +223,7 @@ def load_or_generate_world(
|
||||||
add_rooms = args.add_rooms
|
add_rooms = args.add_rooms
|
||||||
|
|
||||||
memory = {}
|
memory = {}
|
||||||
step = 0
|
turn = 0
|
||||||
|
|
||||||
# prepare an agent for the world builder
|
# prepare an agent for the world builder
|
||||||
llm = agent_easy_connect()
|
llm = agent_easy_connect()
|
||||||
|
@ -241,11 +241,11 @@ def load_or_generate_world(
|
||||||
with open(world_state_file, "r") as f:
|
with open(world_state_file, "r") as f:
|
||||||
state = WorldState(**load_yaml(f))
|
state = WorldState(**load_yaml(f))
|
||||||
|
|
||||||
set_current_step(state.step)
|
set_current_turn(state.turn)
|
||||||
load_or_initialize_system_data(args, systems, state.world)
|
load_or_initialize_system_data(args, systems, state.world)
|
||||||
|
|
||||||
memory = state.memory
|
memory = state.memory
|
||||||
step = state.step
|
turn = state.turn
|
||||||
world = state.world
|
world = state.world
|
||||||
elif path.exists(world_file):
|
elif path.exists(world_file):
|
||||||
logger.info(f"loading world from {world_file}")
|
logger.info(f"loading world from {world_file}")
|
||||||
|
@ -276,7 +276,7 @@ def load_or_generate_world(
|
||||||
link_rooms(world_builder, world, systems, new_rooms)
|
link_rooms(world_builder, world, systems, new_rooms)
|
||||||
|
|
||||||
create_agents(world, memory=memory, players=players)
|
create_agents(world, memory=memory, players=players)
|
||||||
return (world, world_state_file, step)
|
return (world, world_state_file, turn)
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
|
@ -356,20 +356,20 @@ def main():
|
||||||
|
|
||||||
# load or generate the world
|
# load or generate the world
|
||||||
world_prompt = get_world_prompt(args)
|
world_prompt = get_world_prompt(args)
|
||||||
world, world_state_file, world_step = load_or_generate_world(
|
world, world_state_file, world_turn = load_or_generate_world(
|
||||||
args, players, extra_systems, world_prompt=world_prompt
|
args, players, extra_systems, world_prompt=world_prompt
|
||||||
)
|
)
|
||||||
|
|
||||||
# make sure the snapshot system runs last
|
# make sure the snapshot system runs last
|
||||||
def snapshot_system(world: World, step: int, data: None = None) -> None:
|
def snapshot_system(world: World, turn: int, data: None = None) -> None:
|
||||||
logger.info("taking snapshot of world state")
|
logger.info("taking snapshot of world state")
|
||||||
save_world_state(world, step, world_state_file)
|
save_world_state(world, turn, world_state_file)
|
||||||
|
|
||||||
extra_systems.append(GameSystem(name="snapshot", simulate=snapshot_system))
|
extra_systems.append(GameSystem(name="snapshot", simulate=snapshot_system))
|
||||||
|
|
||||||
# hack: send a snapshot to the websocket server
|
# hack: send a snapshot to the websocket server
|
||||||
if args.server:
|
if args.server:
|
||||||
server_system(world, world_step)
|
server_system(world, world_turn)
|
||||||
|
|
||||||
# create the DM
|
# create the DM
|
||||||
llm = agent_easy_connect()
|
llm = agent_easy_connect()
|
||||||
|
@ -390,7 +390,7 @@ def main():
|
||||||
logger.debug("simulating world: %s", world)
|
logger.debug("simulating world: %s", world)
|
||||||
simulate_world(
|
simulate_world(
|
||||||
world,
|
world,
|
||||||
steps=args.steps,
|
turns=args.turns,
|
||||||
actions=extra_actions,
|
actions=extra_actions,
|
||||||
systems=extra_systems,
|
systems=extra_systems,
|
||||||
)
|
)
|
||||||
|
|
|
@ -58,7 +58,7 @@ class WorldSizeConfig:
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class WorldStepConfig:
|
class WorldTurnConfig:
|
||||||
action_retries: int
|
action_retries: int
|
||||||
planning_steps: int
|
planning_steps: int
|
||||||
planning_retries: int
|
planning_retries: int
|
||||||
|
@ -68,7 +68,7 @@ class WorldStepConfig:
|
||||||
class WorldConfig:
|
class WorldConfig:
|
||||||
character: WorldCharacterConfig
|
character: WorldCharacterConfig
|
||||||
size: WorldSizeConfig
|
size: WorldSizeConfig
|
||||||
step: WorldStepConfig
|
turn: WorldTurnConfig
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
|
@ -109,7 +109,7 @@ DEFAULT_CONFIG = Config(
|
||||||
room_characters=IntRange(min=1, max=3),
|
room_characters=IntRange(min=1, max=3),
|
||||||
room_items=IntRange(min=1, max=3),
|
room_items=IntRange(min=1, max=3),
|
||||||
),
|
),
|
||||||
step=WorldStepConfig(
|
turn=WorldTurnConfig(
|
||||||
action_retries=5,
|
action_retries=5,
|
||||||
planning_steps=3,
|
planning_steps=3,
|
||||||
planning_retries=3,
|
planning_retries=3,
|
||||||
|
|
|
@ -74,7 +74,7 @@ class World(BaseModel):
|
||||||
@dataclass
|
@dataclass
|
||||||
class WorldState(BaseModel):
|
class WorldState(BaseModel):
|
||||||
memory: Dict[str, List[str | Dict[str, str]]]
|
memory: Dict[str, List[str | Dict[str, str]]]
|
||||||
step: int
|
turn: int
|
||||||
world: World
|
world: World
|
||||||
id: str = Field(default_factory=uuid)
|
id: str = Field(default_factory=uuid)
|
||||||
type: Literal["world_state"] = "world_state"
|
type: Literal["world_state"] = "world_state"
|
||||||
|
|
|
@ -125,7 +125,7 @@ class SnapshotEvent(BaseModel):
|
||||||
|
|
||||||
world: Dict[str, Any]
|
world: Dict[str, Any]
|
||||||
memory: Dict[str, List[Any]]
|
memory: Dict[str, List[Any]]
|
||||||
step: int
|
turn: int
|
||||||
id: str = Field(default_factory=uuid)
|
id: str = Field(default_factory=uuid)
|
||||||
type: Literal["snapshot"] = "snapshot"
|
type: Literal["snapshot"] = "snapshot"
|
||||||
|
|
||||||
|
|
|
@ -301,11 +301,11 @@ async def server_main():
|
||||||
await asyncio.Future() # run forever
|
await asyncio.Future() # run forever
|
||||||
|
|
||||||
|
|
||||||
def server_system(world: World, step: int, data: Any | None = None):
|
def server_system(world: World, turn: int, data: Any | None = None):
|
||||||
global last_snapshot
|
global last_snapshot
|
||||||
id = uuid4().hex # TODO: should a server be allowed to generate event IDs?
|
id = uuid4().hex # TODO: should a server be allowed to generate event IDs?
|
||||||
json_state = {
|
json_state = {
|
||||||
**snapshot_world(world, step),
|
**snapshot_world(world, turn),
|
||||||
"id": id,
|
"id": id,
|
||||||
"type": "snapshot",
|
"type": "snapshot",
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,11 +34,11 @@ from adventure.context import (
|
||||||
broadcast,
|
broadcast,
|
||||||
get_character_agent_for_name,
|
get_character_agent_for_name,
|
||||||
get_character_for_agent,
|
get_character_for_agent,
|
||||||
get_current_step,
|
get_current_turn,
|
||||||
get_current_world,
|
get_current_world,
|
||||||
set_current_character,
|
set_current_character,
|
||||||
set_current_room,
|
set_current_room,
|
||||||
set_current_step,
|
set_current_turn,
|
||||||
set_current_world,
|
set_current_world,
|
||||||
set_game_systems,
|
set_game_systems,
|
||||||
)
|
)
|
||||||
|
@ -55,7 +55,7 @@ from adventure.utils.world import describe_entity, format_attributes
|
||||||
logger = getLogger(__name__)
|
logger = getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
step_config = DEFAULT_CONFIG.world.step
|
turn_config = DEFAULT_CONFIG.world.turn
|
||||||
|
|
||||||
|
|
||||||
def world_result_parser(value, agent, **kwargs):
|
def world_result_parser(value, agent, **kwargs):
|
||||||
|
@ -154,7 +154,7 @@ def prompt_character_action(
|
||||||
toolbox=action_toolbox,
|
toolbox=action_toolbox,
|
||||||
)
|
)
|
||||||
|
|
||||||
logger.debug(f"{character.name} step result: {result}")
|
logger.debug(f"{character.name} action result: {result}")
|
||||||
if agent.memory:
|
if agent.memory:
|
||||||
# TODO: make sure this is not duplicating memories and wasting space
|
# TODO: make sure this is not duplicating memories and wasting space
|
||||||
agent.memory.append(result)
|
agent.memory.append(result)
|
||||||
|
@ -173,9 +173,9 @@ def get_notes_events(character: Character, current_turn: int):
|
||||||
notes_prompt = "You have no recent notes.\n"
|
notes_prompt = "You have no recent notes.\n"
|
||||||
|
|
||||||
if len(upcoming_events) > 0:
|
if len(upcoming_events) > 0:
|
||||||
current_step = get_current_step()
|
current_turn = get_current_turn()
|
||||||
events = [
|
events = [
|
||||||
f"{event.name} in {event.turn - current_step} turns"
|
f"{event.name} in {event.turn - current_turn} turns"
|
||||||
for event in upcoming_events
|
for event in upcoming_events
|
||||||
]
|
]
|
||||||
events = "\n".join(events)
|
events = "\n".join(events)
|
||||||
|
@ -194,7 +194,7 @@ def prompt_character_think(
|
||||||
current_turn: int,
|
current_turn: int,
|
||||||
max_steps: int | None = None,
|
max_steps: int | None = None,
|
||||||
) -> str:
|
) -> str:
|
||||||
max_steps = max_steps or step_config.planning_steps
|
max_steps = max_steps or turn_config.planning_steps
|
||||||
|
|
||||||
notes_prompt, events_prompt = get_notes_events(character, current_turn)
|
notes_prompt, events_prompt = get_notes_events(character, current_turn)
|
||||||
|
|
||||||
|
@ -242,7 +242,7 @@ def prompt_character_think(
|
||||||
|
|
||||||
def simulate_world(
|
def simulate_world(
|
||||||
world: World,
|
world: World,
|
||||||
steps: float | int = inf,
|
turns: float | int = inf,
|
||||||
actions: Sequence[Callable[..., str]] = [],
|
actions: Sequence[Callable[..., str]] = [],
|
||||||
systems: Sequence[GameSystem] = [],
|
systems: Sequence[GameSystem] = [],
|
||||||
):
|
):
|
||||||
|
@ -279,8 +279,8 @@ def simulate_world(
|
||||||
|
|
||||||
# simulate each character
|
# simulate each character
|
||||||
for i in count():
|
for i in count():
|
||||||
current_step = get_current_step()
|
current_turn = get_current_turn()
|
||||||
logger.info(f"simulating step {i} of {steps} (world step {current_step})")
|
logger.info(f"simulating turn {i} of {turns} (world turn {current_turn})")
|
||||||
|
|
||||||
for character_name in world.order:
|
for character_name in world.order:
|
||||||
character, agent = get_character_agent_for_name(character_name)
|
character, agent = get_character_agent_for_name(character_name)
|
||||||
|
@ -299,13 +299,13 @@ def simulate_world(
|
||||||
|
|
||||||
# decrement effects on the character and remove any that have expired
|
# decrement effects on the character and remove any that have expired
|
||||||
expire_effects(character)
|
expire_effects(character)
|
||||||
expire_events(character, current_step)
|
expire_events(character, current_turn)
|
||||||
|
|
||||||
# give the character a chance to think and check their planner
|
# give the character a chance to think and check their planner
|
||||||
if agent.memory and len(agent.memory) > 0:
|
if agent.memory and len(agent.memory) > 0:
|
||||||
try:
|
try:
|
||||||
thoughts = prompt_character_think(
|
thoughts = prompt_character_think(
|
||||||
room, character, agent, planner_toolbox, current_step
|
room, character, agent, planner_toolbox, current_turn
|
||||||
)
|
)
|
||||||
logger.debug(f"{character.name} thinks: {thoughts}")
|
logger.debug(f"{character.name} thinks: {thoughts}")
|
||||||
except Exception:
|
except Exception:
|
||||||
|
@ -313,17 +313,22 @@ def simulate_world(
|
||||||
f"error during planning for character {character.name}"
|
f"error during planning for character {character.name}"
|
||||||
)
|
)
|
||||||
|
|
||||||
result = prompt_character_action(
|
try:
|
||||||
room, character, agent, action_names, action_tools, current_step
|
result = prompt_character_action(
|
||||||
)
|
room, character, agent, action_names, action_tools, current_turn
|
||||||
result_event = ResultEvent(result=result, room=room, character=character)
|
)
|
||||||
broadcast(result_event)
|
result_event = ResultEvent(
|
||||||
|
result=result, room=room, character=character
|
||||||
|
)
|
||||||
|
broadcast(result_event)
|
||||||
|
except Exception:
|
||||||
|
logger.exception(f"error during action for character {character.name}")
|
||||||
|
|
||||||
for system in systems:
|
for system in systems:
|
||||||
if system.simulate:
|
if system.simulate:
|
||||||
system.simulate(world, current_step)
|
system.simulate(world, current_turn)
|
||||||
|
|
||||||
set_current_step(current_step + 1)
|
set_current_turn(current_turn + 1)
|
||||||
if i >= steps:
|
if i >= turns:
|
||||||
logger.info("reached step limit at world step %s", current_step + 1)
|
logger.info("reached turn limit at world turn %s", current_turn + 1)
|
||||||
break
|
break
|
||||||
|
|
|
@ -34,10 +34,10 @@ def create_agents(
|
||||||
set_character_agent(character.name, character, agent)
|
set_character_agent(character.name, character, agent)
|
||||||
|
|
||||||
|
|
||||||
def graph_world(world: World, step: int):
|
def graph_world(world: World, turn: int):
|
||||||
import graphviz
|
import graphviz
|
||||||
|
|
||||||
graph_name = f"{path.basename(world.name)}-{step}"
|
graph_name = f"{path.basename(world.name)}-{turn}"
|
||||||
graph = graphviz.Digraph(graph_name, format="png")
|
graph = graphviz.Digraph(graph_name, format="png")
|
||||||
for room in world.rooms:
|
for room in world.rooms:
|
||||||
characters = [character.name for character in room.characters]
|
characters = [character.name for character in room.characters]
|
||||||
|
@ -50,8 +50,8 @@ def graph_world(world: World, step: int):
|
||||||
graph.render(directory=graph_path)
|
graph.render(directory=graph_path)
|
||||||
|
|
||||||
|
|
||||||
def snapshot_world(world: World, step: int):
|
def snapshot_world(world: World, turn: int):
|
||||||
# save the world itself, along with the step number of the memory of each agent
|
# save the world itself, along with the turn number and the memory of each agent
|
||||||
json_world = RootModel[World](world).model_dump()
|
json_world = RootModel[World](world).model_dump()
|
||||||
|
|
||||||
json_memory = {}
|
json_memory = {}
|
||||||
|
@ -62,7 +62,7 @@ def snapshot_world(world: World, step: int):
|
||||||
return {
|
return {
|
||||||
"world": json_world,
|
"world": json_world,
|
||||||
"memory": json_memory,
|
"memory": json_memory,
|
||||||
"step": step,
|
"turn": turn,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -94,9 +94,9 @@ def save_world(world, filename):
|
||||||
f.write(json_world)
|
f.write(json_world)
|
||||||
|
|
||||||
|
|
||||||
def save_world_state(world, step, filename):
|
def save_world_state(world, turn, filename):
|
||||||
graph_world(world, step)
|
graph_world(world, turn)
|
||||||
json_state = snapshot_world(world, step)
|
json_state = snapshot_world(world, turn)
|
||||||
with open(filename, "w") as f:
|
with open(filename, "w") as f:
|
||||||
dump(json_state, f, default=world_json, indent=2)
|
dump(json_state, f, default=world_json, indent=2)
|
||||||
|
|
||||||
|
|
|
@ -130,7 +130,7 @@ def update_attributes(
|
||||||
|
|
||||||
def update_logic(
|
def update_logic(
|
||||||
world: World,
|
world: World,
|
||||||
step: int,
|
turn: int,
|
||||||
data: Any | None = None,
|
data: Any | None = None,
|
||||||
*,
|
*,
|
||||||
rules: LogicTable,
|
rules: LogicTable,
|
||||||
|
@ -190,7 +190,7 @@ def load_logic(filename: str):
|
||||||
logger.info("initialized logic system")
|
logger.info("initialized logic system")
|
||||||
system_format = wraps(format_logic)(partial(format_logic, rules=logic_rules))
|
system_format = wraps(format_logic)(partial(format_logic, rules=logic_rules))
|
||||||
system_initialize = wraps(update_logic)(
|
system_initialize = wraps(update_logic)(
|
||||||
partial(update_logic, step=0, rules=logic_rules, triggers=logic_triggers)
|
partial(update_logic, turn=0, rules=logic_rules, triggers=logic_triggers)
|
||||||
)
|
)
|
||||||
system_simulate = wraps(update_logic)(
|
system_simulate = wraps(update_logic)(
|
||||||
partial(update_logic, rules=logic_rules, triggers=logic_triggers)
|
partial(update_logic, rules=logic_rules, triggers=logic_triggers)
|
||||||
|
|
|
@ -187,7 +187,7 @@ def generate_quests(agent: Agent, theme: str, entity: WorldEntity) -> None:
|
||||||
# TODO: generate one new quest
|
# TODO: generate one new quest
|
||||||
|
|
||||||
|
|
||||||
def simulate_quests(world: World, step: int, data: QuestData | None = None) -> None:
|
def simulate_quests(world: World, turn: int, data: QuestData | None = None) -> None:
|
||||||
"""
|
"""
|
||||||
1. Check for any completed quests.
|
1. Check for any completed quests.
|
||||||
2. Update any active quests.
|
2. Update any active quests.
|
||||||
|
|
|
@ -80,7 +80,7 @@ export function App(props: AppProps) {
|
||||||
});
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const { setClientId, setActiveTurn, setPlayers, appendEvent, setWorld, world, clientId, setPlayerCharacter: setCharacter } = store.getState();
|
const { setClientId, setActiveTurn, setPlayers, appendEvent, setTurn, setWorld, world, clientId, setPlayerCharacter: setCharacter } = store.getState();
|
||||||
if (doesExist(lastMessage)) {
|
if (doesExist(lastMessage)) {
|
||||||
const event = JSON.parse(lastMessage.data);
|
const event = JSON.parse(lastMessage.data);
|
||||||
|
|
||||||
|
@ -107,6 +107,7 @@ export function App(props: AppProps) {
|
||||||
return;
|
return;
|
||||||
case 'snapshot':
|
case 'snapshot':
|
||||||
setWorld(event.world);
|
setWorld(event.world);
|
||||||
|
setTurn(event.turn);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
// this is not concerning, other events are kept in history and displayed
|
// this is not concerning, other events are kept in history and displayed
|
||||||
|
|
|
@ -92,12 +92,12 @@ export function ActionEventItem(props: EventItemProps) {
|
||||||
|
|
||||||
export function SnapshotEventItem(props: EventItemProps) {
|
export function SnapshotEventItem(props: EventItemProps) {
|
||||||
const { event } = props;
|
const { event } = props;
|
||||||
const { step, world } = event;
|
const { turn, world } = event;
|
||||||
const { name, theme } = world;
|
const { name, theme } = world;
|
||||||
|
|
||||||
return <ListItem alignItems="flex-start" ref={props.focusRef}>
|
return <ListItem alignItems="flex-start" ref={props.focusRef}>
|
||||||
<ListItemAvatar>
|
<ListItemAvatar>
|
||||||
<Avatar>{step}</Avatar>
|
<Avatar>{turn}</Avatar>
|
||||||
</ListItemAvatar>
|
</ListItemAvatar>
|
||||||
<ListItemText
|
<ListItemText
|
||||||
primary={name}
|
primary={name}
|
||||||
|
@ -109,7 +109,7 @@ export function SnapshotEventItem(props: EventItemProps) {
|
||||||
variant="body2"
|
variant="body2"
|
||||||
color="text.primary"
|
color="text.primary"
|
||||||
>
|
>
|
||||||
Step: {step}
|
Turn: {turn}
|
||||||
</Typography>
|
</Typography>
|
||||||
World Theme: {theme}
|
World Theme: {theme}
|
||||||
</Fragment>
|
</Fragment>
|
||||||
|
|
|
@ -22,7 +22,7 @@ export function formatResult(data: any) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function formatWorld(data: any) {
|
export function formatWorld(data: any) {
|
||||||
return `${data.world.theme} - ${data.step}`;
|
return `${data.world.theme} - ${data.turn}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const formatters: Record<string, any> = {
|
export const formatters: Record<string, any> = {
|
||||||
|
|
|
@ -50,6 +50,7 @@ export interface World {
|
||||||
order: Array<string>;
|
order: Array<string>;
|
||||||
rooms: Array<Room>;
|
rooms: Array<Room>;
|
||||||
theme: string;
|
theme: string;
|
||||||
|
turn: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: copy event types from server
|
// TODO: copy event types from server
|
||||||
|
|
|
@ -35,10 +35,12 @@ export interface ClientState {
|
||||||
|
|
||||||
export interface WorldState {
|
export interface WorldState {
|
||||||
players: Record<string, string>;
|
players: Record<string, string>;
|
||||||
|
turn: Maybe<number>;
|
||||||
world: Maybe<World>;
|
world: Maybe<World>;
|
||||||
|
|
||||||
// setters
|
// setters
|
||||||
setPlayers: (players: Record<string, string>) => void;
|
setPlayers: (players: Record<string, string>) => void;
|
||||||
|
setTurn: (turn: Maybe<number>) => void;
|
||||||
setWorld: (world: Maybe<World>) => void;
|
setWorld: (world: Maybe<World>) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -105,8 +107,10 @@ export function createClientStore(): StateCreator<ClientState> {
|
||||||
export function createWorldStore(): StateCreator<WorldState> {
|
export function createWorldStore(): StateCreator<WorldState> {
|
||||||
return (set) => ({
|
return (set) => ({
|
||||||
players: {},
|
players: {},
|
||||||
|
turn: undefined,
|
||||||
world: undefined,
|
world: undefined,
|
||||||
setPlayers: (players) => set({ players }),
|
setPlayers: (players) => set({ players }),
|
||||||
|
setTurn: (turn) => set({ turn }),
|
||||||
setWorld: (world) => set({ world }),
|
setWorld: (world) => set({ world }),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { Maybe, doesExist } from '@apextoaster/js-utils';
|
import { Maybe, doesExist } from '@apextoaster/js-utils';
|
||||||
import { Card, CardContent, Typography } from '@mui/material';
|
import { Card, CardContent, Divider, Stack, Typography } from '@mui/material';
|
||||||
import { SimpleTreeView } from '@mui/x-tree-view/SimpleTreeView';
|
import { SimpleTreeView } from '@mui/x-tree-view/SimpleTreeView';
|
||||||
import { TreeItem } from '@mui/x-tree-view/TreeItem';
|
import { TreeItem } from '@mui/x-tree-view/TreeItem';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
@ -40,6 +40,7 @@ export function characterStateSelector(s: StoreState) {
|
||||||
|
|
||||||
export function worldStateSelector(s: StoreState) {
|
export function worldStateSelector(s: StoreState) {
|
||||||
return {
|
return {
|
||||||
|
turn: s.turn,
|
||||||
world: s.world,
|
world: s.world,
|
||||||
setDetailEntity: s.setDetailEntity,
|
setDetailEntity: s.setDetailEntity,
|
||||||
};
|
};
|
||||||
|
@ -121,7 +122,7 @@ export function RoomItem(props: { room: Room } & BaseEntityItemProps) {
|
||||||
export function WorldPanel(props: BaseEntityItemProps) {
|
export function WorldPanel(props: BaseEntityItemProps) {
|
||||||
const { setPlayer } = props;
|
const { setPlayer } = props;
|
||||||
const state = useStore(store, worldStateSelector);
|
const state = useStore(store, worldStateSelector);
|
||||||
const { world, setDetailEntity } = state;
|
const { turn, world, setDetailEntity } = state;
|
||||||
|
|
||||||
// eslint-disable-next-line no-restricted-syntax
|
// eslint-disable-next-line no-restricted-syntax
|
||||||
if (!doesExist(world)) {
|
if (!doesExist(world)) {
|
||||||
|
@ -136,14 +137,20 @@ export function WorldPanel(props: BaseEntityItemProps) {
|
||||||
|
|
||||||
return <Card style={{ minHeight: 200, overflow: 'auto' }}>
|
return <Card style={{ minHeight: 200, overflow: 'auto' }}>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
<Typography gutterBottom variant="h5" component="div">{world.name}</Typography>
|
<Stack direction="column" spacing={2}>
|
||||||
<Typography variant="body1">
|
<Typography gutterBottom variant="h5" component="div">{world.name}</Typography>
|
||||||
Theme: {world.theme}
|
<Typography variant="body1">
|
||||||
</Typography>
|
Theme: {world.theme}
|
||||||
<SimpleTreeView>
|
</Typography>
|
||||||
<TreeItem itemId="world-graph" label="Graph" onClick={() => setDetailEntity(world)} />
|
<Typography variant="body2">
|
||||||
{world.rooms.map((room) => <RoomItem key={room.name} room={room} setPlayer={setPlayer} />)}
|
Turn: {turn}
|
||||||
</SimpleTreeView>
|
</Typography>
|
||||||
|
<Divider />
|
||||||
|
<SimpleTreeView>
|
||||||
|
<TreeItem itemId="world-graph" label="Graph" onClick={() => setDetailEntity(world)} />
|
||||||
|
{world.rooms.map((room) => <RoomItem key={room.name} room={room} setPlayer={setPlayer} />)}
|
||||||
|
</SimpleTreeView>
|
||||||
|
</Stack>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>;
|
</Card>;
|
||||||
}
|
}
|
||||||
|
|
|
@ -154,7 +154,7 @@ The snapshot event is fired at the end of each turn and contains a complete snap
|
||||||
type: "snapshot"
|
type: "snapshot"
|
||||||
world: Dict
|
world: Dict
|
||||||
memory: Dict
|
memory: Dict
|
||||||
step: int
|
turn: int
|
||||||
```
|
```
|
||||||
|
|
||||||
This is primarily used to save the world state, but can also be used to sync clients and populate the world menu.
|
This is primarily used to save the world state, but can also be used to sync clients and populate the world menu.
|
||||||
|
|
Loading…
Reference in New Issue