diff --git a/llm_config.yaml b/llm_config.yaml
index 745334be..5b915c40 100644
--- a/llm_config.yaml
+++ b/llm_config.yaml
@@ -16,7 +16,7 @@ DUNGEON_LOCATION_TEMPLATE: '{"index": (int), "name": "", "description": 25 words
CHARACTER_TEMPLATE: '{"name":"", "description": "50 words", "appearance": "25 words", "personality": "50 words", "money":(int), "level":"", "gender":"m/f/n", "age":(int), "race":"", "occupation":""}'
FOLLOW_TEMPLATE: '{{"response":"yes or no", "reason":"50 words"}}'
ITEM_TYPES: ["Weapon", "Wearable", "Health", "Money", "Trash"]
-PRE_PROMPT: 'You are a creative game keeper for an interactive fiction story telling session. You craft detailed worlds and interesting characters with unique and deep personalities for the player to interact with. Do not acknowledge the task or speak directly to the user, or respond with anything besides the request..'
+PRE_PROMPT: 'You are a creative game keeper for an interactive fiction story telling session. You craft detailed worlds and interesting characters with unique and deep personalities for the player to interact with. Always follow the instructions given, never acknowledge the task or speak directly to the user or respond with anything besides the request.'
BASE_PROMPT: '{context}\n[USER_START] Rewrite [{input_text}] in your own words. The information inside the tags should be used to ensure it fits the story. Use about {max_words} words.'
DIALOGUE_PROMPT: '{context}\nThe following is a conversation between {character1} and {character2}; {character2}s sentiment towards {character1}: {sentiment}. Write a single response as {character2} in third person pov, using {character2} description and other information found inside the tags. If {character2} has a quest active, they will discuss it based on its status. Respond in JSON using this template: """{dialogue_template}""". [USER_START]Continue the following conversation as {character2}: {previous_conversation}'
COMBAT_PROMPT: '{context}\nThe following is a combat scene between {attackers} and {defenders} in {location}. [USER_START] Describe the following combat result in about 150 words in vivid language, using the characters weapons and their health status: 1.0 is highest, 0.0 is lowest. Combat Result: {input_text}'
@@ -46,3 +46,4 @@ REQUEST_FOLLOW_PROMPT: '{context}\n[USER_START]Act as as {cha
DAY_CYCLE_EVENT_PROMPT: '{context}\n[USER_START] Write up to two sentences describing the transition from {from_time} to {to_time} in {location_name}, using the information supplied inside the tags.'
NARRATIVE_EVENT_PROMPT: '{context}\n[USER_START] Write a narrative event that occurs in {location_name} using the information supplied inside the tags. The event should be related to the location and the characters present. Use up to 50 words.'
RANDOM_SPAWN_PROMPT: '{context}\n[USER_START] An npc or a mob has entered {location_name}. Select either and fill in one of the following templates using the information supplied inside the tags. Respond using JSON in the following format: {npc_template}'
+ADVANCE_STORY_PROMPT: '{context}\n[USER_START] Advance the high-level plot outline in the current story, hidden to the player of this roleplaying game. The story progress is a value between 0 and 10. Consider the pacing of the plot when writing the section. A value below 3 is considered early. An inciting event should happen during this phase. Between 3 and 8 is the middle of the story. A climactic event should happen at 8 or 9. Above 9 is considered to be the ending. Use the information supplied inside the tags to write a short paragraph that progresses the plot outline. Use up to 100 words.'
\ No newline at end of file
diff --git a/stories/combat_sandbox/story.py b/stories/combat_sandbox/story.py
index 4a5e9473..ae37676e 100644
--- a/stories/combat_sandbox/story.py
+++ b/stories/combat_sandbox/story.py
@@ -7,7 +7,7 @@
from tale.base import Location
from tale.driver import Driver
from tale.json_story import JsonStory
-from tale.llm.llm_ext import DynamicStory
+from tale.llm.dynamic_story import DynamicStory
from tale.main import run_from_cmdline
from tale.player import Player, PlayerConnection
from tale.charbuilder import PlayerNaming
diff --git a/stories/demo/story.py b/stories/demo/story.py
index ceda5d69..31f294ee 100644
--- a/stories/demo/story.py
+++ b/stories/demo/story.py
@@ -5,10 +5,12 @@
Copyright by Irmen de Jong (irmen@razorvine.net)
"""
import datetime
+import pathlib
import sys
from typing import Optional, Generator
from tale.driver import Driver
+from tale.main import run_from_cmdline
from tale.player import Player, PlayerConnection
from tale.charbuilder import PlayerNaming
from tale.story import *
@@ -85,5 +87,9 @@ def goodbye(self, player: Player) -> None:
if __name__ == "__main__":
# story is invoked as a script, start it.
- from tale.main import run_from_cmdline
- run_from_cmdline(["--game", sys.path[0]])
+ gamedir = pathlib.Path(__file__).parent
+ if gamedir.is_dir() or gamedir.is_file():
+ cmdline_args = sys.argv[1:]
+ cmdline_args.insert(0, "--game")
+ cmdline_args.insert(1, str(gamedir))
+ run_from_cmdline(cmdline_args)
\ No newline at end of file
diff --git a/stories/prancingllama/story.py b/stories/prancingllama/story.py
index a73bb0db..e9b6acd2 100644
--- a/stories/prancingllama/story.py
+++ b/stories/prancingllama/story.py
@@ -6,7 +6,7 @@
from tale.base import Location
from tale.cmds import spells
from tale.driver import Driver
-from tale.llm.llm_ext import DynamicStory
+from tale.llm.dynamic_story import DynamicStory
from tale.skills.magic import MagicType
from tale.main import run_from_cmdline
from tale.player import Player, PlayerConnection
@@ -14,6 +14,7 @@
from tale.skills.skills import SkillType
from tale.story import *
from tale.skills.weapon_type import WeaponType
+from tale.story import StoryContext
from tale.zone import Zone
class Story(DynamicStory):
@@ -34,7 +35,7 @@ class Story(DynamicStory):
config.startlocation_player = "prancingllama.entrance"
config.startlocation_wizard = "prancingllama.entrance"
config.zones = ["prancingllama"]
- config.context = "The final outpost high up in a cold, craggy mountain range. It's frequented by adventurers and those seeking to avoid attention."
+ config.context = StoryContext(base_story="The final outpost high up in a cold, craggy mountain range. A drama unfolds between those avoiding the cold and those seeking the cold. And what is lurking underneath the snow covered peaks and uncharted valleys?")
config.type = "A low level fantasy adventure with focus of character building and interaction."
config.custom_resources = True
@@ -42,7 +43,7 @@ class Story(DynamicStory):
def init(self, driver: Driver) -> None:
"""Called by the game driver when it is done with its initial initialization."""
self.driver = driver
- self._zones = dict() # type: dict(str, Zone)
+ self._zones = dict() # type: {str, Zone}
self._zones["The Prancing Llama"] = Zone("The Prancing Llama", description="A cold, craggy mountain range. Snow covered peaks and uncharted valleys hide and attract all manners of creatures.")
import zones.prancingllama
for location in zones.prancingllama.all_locations:
@@ -96,13 +97,13 @@ def goodbye(self, player: Player) -> None:
player.tell("Goodbye, %s. Please come back again soon." % player.title)
player.tell("\n")
- def races_for_zone(self, zone: str) -> [str]:
+ def races_for_zone(self, zone: str) -> list[str]:
return self._catalogue._creatures
- def items_for_zone(self, zone: str) -> [str]:
+ def items_for_zone(self, zone: str) -> list[str]:
return self._catalogue._items
- def zone_info(self, zone_name: str, location: str) -> dict():
+ def zone_info(self, zone_name: str, location: str) -> dict:
zone_info = super.zone_info(zone_name, location)
zone_info['races'] = self.races_for_zone(zone_name)
zone_info['items'] = self.items_for_zone(zone_name)
diff --git a/tale/cmds/normal.py b/tale/cmds/normal.py
index 8105bbcc..c4db5ed5 100644
--- a/tale/cmds/normal.py
+++ b/tale/cmds/normal.py
@@ -11,7 +11,7 @@
from typing import Iterable, List, Dict, Generator, Union, Optional
from tale.llm.LivingNpc import LivingNpc
-from tale.llm.llm_ext import DynamicStory
+from tale.llm.dynamic_story import DynamicStory
from tale.skills.skills import SkillType
from . import abbreviations, cmd, disabled_in_gamemode, disable_notify_action, overrides_soul, no_soul_parse
diff --git a/tale/driver.py b/tale/driver.py
index a41f1292..dc53759b 100644
--- a/tale/driver.py
+++ b/tale/driver.py
@@ -31,12 +31,12 @@
from . import __version__ as tale_version_str, _check_required_libraries
from . import mud_context, errors, util, cmds, player, pubsub, charbuilder, lang, verbdefs, vfs, base
-from .story import TickMethod, GameMode, MoneyType, StoryBase
+from .story import StoryContext, TickMethod, GameMode, MoneyType, StoryBase
from .tio import DEFAULT_SCREEN_WIDTH
from .races import playable_races
from .errors import StoryCompleted
from tale.load_character import CharacterLoader, CharacterV2
-from tale.llm.llm_ext import DynamicStory
+from tale.llm.dynamic_story import DynamicStory
from tale.llm.llm_utils import LlmUtil
from tale.web.web_utils import clear_resources, copy_web_resources
@@ -535,6 +535,9 @@ def _server_tick(self) -> None:
events, idle_time, subbers = topicinfo[topicname]
if events == 0 and not subbers and idle_time > 30:
pubsub.topic(topicname).destroy()
+ progress = self.story.increase_progress(0.0001)
+ if progress:
+ self.llm_util.advance_story_section(self.story)
def disconnect_idling(self, conn: player.PlayerConnection) -> None:
raise NotImplementedError
@@ -933,7 +936,7 @@ def build_location(self, targetLocation: base.Location, zone: Zone, player: play
# try to add location, and if it fails, remove exit to it
result = dynamic_story.add_location(location, zone=zone.name)
if not result:
- for exit in exits: # type: Exit
+ for exit in exits:
if exit.name == location.name:
exits.remove(exit)
for exit in exits:
diff --git a/tale/dungeon/dungeon_generator.py b/tale/dungeon/dungeon_generator.py
index 4b0a10aa..429a2316 100644
--- a/tale/dungeon/dungeon_generator.py
+++ b/tale/dungeon/dungeon_generator.py
@@ -5,7 +5,7 @@
from tale.coord import Coord
from tale.item_spawner import ItemSpawner
from tale.items.basic import Money
-from tale.llm.llm_ext import DynamicStory
+from tale.llm.dynamic_story import DynamicStory
from tale.mob_spawner import MobSpawner
from tale.zone import Zone
diff --git a/tale/json_story.py b/tale/json_story.py
index fe786755..2d3e53b9 100644
--- a/tale/json_story.py
+++ b/tale/json_story.py
@@ -2,7 +2,7 @@
from tale.day_cycle.day_cycle import DayCycle
from tale.day_cycle.llm_day_cycle_listener import LlmDayCycleListener
from tale.items import generic
-from tale.llm.llm_ext import DynamicStory
+from tale.llm.dynamic_story import DynamicStory
from tale.player import Player
from tale.random_event import RandomEvent
from tale.story import GameMode, StoryConfig
diff --git a/tale/llm/contexts/AdvanceStoryContext.py b/tale/llm/contexts/AdvanceStoryContext.py
new file mode 100644
index 00000000..7792ee59
--- /dev/null
+++ b/tale/llm/contexts/AdvanceStoryContext.py
@@ -0,0 +1,12 @@
+
+
+from tale.llm.contexts.BaseContext import BaseContext
+
+
+class AdvanceStoryContext(BaseContext):
+
+ def __init__(self, story_context: str):
+ super().__init__(story_context)
+
+ def to_prompt_string(self) -> str:
+ return f"{self.story_context}"
\ No newline at end of file
diff --git a/tale/llm/contexts/BaseContext.py b/tale/llm/contexts/BaseContext.py
index c455454c..8f3ba10b 100644
--- a/tale/llm/contexts/BaseContext.py
+++ b/tale/llm/contexts/BaseContext.py
@@ -1,12 +1,16 @@
-
-
from abc import ABC, abstractmethod
+from typing import Union
+
+from tale.story import StoryContext
class BaseContext(ABC):
- def __init__(self, story_context: str) -> None:
- self.story_context = story_context
+ def __init__(self, story_context: Union[StoryContext, str]) -> None:
+ if isinstance(story_context, StoryContext):
+ self.story_context = story_context.to_context_with_past()
+ else:
+ self.story_context = story_context
@abstractmethod
def to_prompt_string(self) -> str:
diff --git a/tale/llm/llm_ext.py b/tale/llm/dynamic_story.py
similarity index 98%
rename from tale/llm/llm_ext.py
rename to tale/llm/dynamic_story.py
index a1061016..e3e2f2d7 100644
--- a/tale/llm/llm_ext.py
+++ b/tale/llm/dynamic_story.py
@@ -10,7 +10,7 @@
from tale.llm.LivingNpc import LivingNpc
from tale.quest import Quest, QuestType
from tale.mob_spawner import MobSpawner
-from tale.story import StoryBase
+from tale.story import StoryBase, StoryContext
from tale.zone import Zone
import tale.llm.llm_cache as llm_cache
@@ -21,6 +21,8 @@ def __init__(self) -> None:
self._zones = dict() # type: dict[str, Zone]
self._world = WorldInfo()
self._catalogue = Catalogue()
+ if isinstance(self.config.context, str):
+ self.config.context = StoryContext(self.config.context)
def get_zone(self, name: str) -> Zone:
""" Find a zone by name."""
diff --git a/tale/llm/llm_cache.py b/tale/llm/llm_cache.py
index f420fc59..945cccbb 100644
--- a/tale/llm/llm_cache.py
+++ b/tale/llm/llm_cache.py
@@ -21,7 +21,7 @@ def cache_event(event: str, event_hash: int = -1) -> int:
event_cache[event_hash] = event
return event_hash
-def get_events(event_hashes: [int]) -> str:
+def get_events(event_hashes: list[int]) -> str:
""" Gets events from the cache. """
return "".join([event_cache.get(event_hash, '') for event_hash in event_hashes])
@@ -37,7 +37,7 @@ def cache_look(look: str, look_hash: int = -1) -> int:
look_cache[look_hash] = look
return look_hash
-def get_looks(look_hashes: [int]) -> str:
+def get_looks(look_hashes: list[int]) -> str:
""" Gets an event from the cache. """
return ", ".join([look_cache.get(look_hash, '') for look_hash in look_hashes])
diff --git a/tale/llm/llm_utils.py b/tale/llm/llm_utils.py
index 92cb11e2..dd0b4ca3 100644
--- a/tale/llm/llm_utils.py
+++ b/tale/llm/llm_utils.py
@@ -14,7 +14,7 @@
from tale.llm.contexts.EvokeContext import EvokeContext
from tale.llm.contexts.FollowContext import FollowContext
from tale.llm.contexts.WorldGenerationContext import WorldGenerationContext
-from tale.llm.llm_ext import DynamicStory
+from tale.llm.dynamic_story import DynamicStory
from tale.llm.llm_io import IoUtil
from tale.llm.contexts.DialogueContext import DialogueContext
from tale.llm.quest_building import QuestBuilding
@@ -342,6 +342,10 @@ def set_story(self, story: DynamicStory):
if story.config.image_gen:
self._init_image_gen(story.config.image_gen)
+ def advance_story_section(self, story: DynamicStory) -> str:
+ """ Increase the story progress"""
+ return self._story_building.advance_story_section(story or self.__story)
+
def _init_image_gen(self, image_gen: str):
""" Initialize the image generator"""
clazz = getattr(sys.modules['tale.image_gen.' + image_gen.lower()], image_gen)
diff --git a/tale/llm/story_building.py b/tale/llm/story_building.py
index b76a6f49..f9290215 100644
--- a/tale/llm/story_building.py
+++ b/tale/llm/story_building.py
@@ -1,6 +1,8 @@
# This file contains the StoryBuilding class, which is responsible for generating the story background
from tale import parse_utils
from tale.llm import llm_config
+from tale.llm.contexts.AdvanceStoryContext import AdvanceStoryContext
+from tale.llm.dynamic_story import DynamicStory
from tale.llm.llm_io import IoUtil
@@ -11,12 +13,21 @@ def __init__(self, io_util: IoUtil, default_body: dict, backend: str = 'kobold_c
self.io_util = io_util
self.default_body = default_body
self.story_background_prompt = llm_config.params['STORY_BACKGROUND_PROMPT'] # Type: str
+ self.advance_story_prompt = llm_config.params['ADVANCE_STORY_PROMPT'] # Type: str
- def generate_story_background(self, world_mood: int, world_info: str, story_type: str):
+ def generate_story_background(self, world_mood: int, world_info: str, story_type: str) -> str:
prompt = self.story_background_prompt.format(
story_type=story_type,
world_mood=parse_utils.mood_string_from_int(world_mood),
world_info=world_info)
request_body = self.default_body
return self.io_util.synchronous_request(request_body, prompt=prompt)
+
+ def advance_story_section(self, story: DynamicStory) -> str:
+ story_context = AdvanceStoryContext(story.config.context)
+ prompt = self.advance_story_prompt.format(context=story_context.to_prompt_string())
+ request_body = self.default_body
+ result = self.io_util.synchronous_request(request_body, prompt=prompt)
+ story.config.context.set_current_section(result)
+ return result
\ No newline at end of file
diff --git a/tale/llm/world_building.py b/tale/llm/world_building.py
index 21c00518..8257d6a8 100644
--- a/tale/llm/world_building.py
+++ b/tale/llm/world_building.py
@@ -10,7 +10,7 @@
from tale.llm import llm_config
from tale.llm.contexts.DungeonLocationsContext import DungeonLocationsContext
from tale.llm.contexts.WorldGenerationContext import WorldGenerationContext
-from tale.llm.llm_ext import DynamicStory
+from tale.llm.dynamic_story import DynamicStory
from tale.llm.llm_io import IoUtil
from tale.llm.requests.generate_zone import GenerateZone
from tale.llm.requests.start_location import StartLocation
diff --git a/tale/parse_utils.py b/tale/parse_utils.py
index f165a994..5854481b 100644
--- a/tale/parse_utils.py
+++ b/tale/parse_utils.py
@@ -8,19 +8,16 @@
from tale.equip_npcs import equip_npc
from tale.item_spawner import ItemSpawner
from tale.items import generic
-from tale.items.basic import Boxlike, Drink, Food, Health, Money, Note
from tale.llm.LivingNpc import LivingNpc
from tale.load_items import load_item
from tale.skills.magic import MagicType
from tale.npc_defs import StationaryMob, StationaryNpc, Trader
from tale.races import BodyType, UnarmedAttack
from tale.mob_spawner import MobSpawner
-from tale.story import GameMode, MoneyType, TickMethod, StoryConfig
-from tale.skills.weapon_type import WeaponSkills, WeaponType
-from tale.wearable import WearLocation
+from tale.story import GameMode, MoneyType, StoryContext, TickMethod, StoryConfig
+from tale.skills.weapon_type import WeaponType
import json
import re
-import sys
import os
@@ -236,7 +233,11 @@ def load_story_config(json_file: dict):
config.server_mode = GameMode[json_file['server_mode']]
config.npcs = json_file.get('npcs', '')
config.items = json_file.get('items', '')
- config.context = json_file.get('context', '')
+ context = json_file.get('context', '')
+ if isinstance(context, dict):
+ config.context = StoryContext().from_json(context)
+ else:
+ config.context = context
config.type = json_file.get('type', '')
config.world_info = json_file.get('world_info', '')
config.world_mood = json_file.get('world_mood', config.world_mood)
@@ -275,7 +276,7 @@ def save_story_config(config: StoryConfig) -> dict:
json_file['type'] = config.type
json_file['world_info'] = config.world_info
json_file['world_mood'] = config.world_mood
- json_file['context'] = config.context
+ json_file['context'] = config.context if isinstance(config.context, str) else config.context.to_json()
json_file['custom_resources'] = config.custom_resources
json_file['image_gen'] = config.image_gen
json_file['epoch'] = 0 # TODO: fix later
@@ -295,7 +296,7 @@ def _insert(new_item: Item, locations, location: str):
loc.insert(new_item, None)
def remove_special_chars(message: str):
- re.sub('[^A-Za-z0-9 .,_\-\'\"]+', '', message)
+ re.sub('[^A-Za-z0-9 .,_\'\"]+', '', message)
return message
def trim_response(message: str):
@@ -646,7 +647,7 @@ def load_stats(json_stats: dict) -> Stats:
stats.skills[WeaponType(int_skill)] = json
return stats
-def save_items(items: List[Item]) -> []:
+def save_items(items: List[Item]) -> list[dict]:
json_items = []
for item in items:
json_item = item.to_dict()
diff --git a/tale/random_event.py b/tale/random_event.py
index e3875d04..8e979d9f 100644
--- a/tale/random_event.py
+++ b/tale/random_event.py
@@ -3,7 +3,7 @@
import random
from tale import _MudContext
from tale.driver import Driver
-from tale.llm.llm_ext import DynamicStory
+from tale.llm.dynamic_story import DynamicStory
from tale.player import PlayerConnection
from tale.util import call_periodically
diff --git a/tale/story.py b/tale/story.py
index 2ccac42b..4b2916d5 100644
--- a/tale/story.py
+++ b/tale/story.py
@@ -7,7 +7,9 @@
import datetime
import enum
-from typing import Optional, Any, List, Set, Generator
+import math
+import random
+from typing import Optional, Any, List, Set, Generator, Union
from packaging.version import Version
from . import __version__ as tale_version_str
@@ -72,7 +74,7 @@ def __init__(self) -> None:
self.server_mode = GameMode.IF # the actual game mode the server is operating in (will be set at startup time)
self.items = "" # items to populate the world with. only used by json loading
self.npcs = "" # npcs to populate the world with. only used by json loading
- self.context = "" # context to giving background for the story.
+ self.context = "" # type: Union[str, StoryContext] # context to giving background for the story.
self.type = "" # brief description of the setting and type of story, for LLM context
self.world_info = "" # brief description of the world, for LLM context
self.world_mood = 0 # how safe is the world? 5 is a happy place, -5 is nightmare mode.
@@ -148,6 +150,11 @@ def story_failure(self, player) -> None:
player.tell("You have failed to complete the story.")
player.tell("\n")
+ def increase_progress(self, amount: float = 1.0) -> bool:
+ if isinstance(self.config.context, StoryContext):
+ return self.config.context.increase_progress(amount)
+ return False
+
def _verify(self, driver) -> None:
"""verify correctness and compatibility of the story configuration"""
if not isinstance(self.config, StoryConfig):
@@ -164,3 +171,50 @@ def _verify(self, driver) -> None:
tale_version_required = Version(self.config.requires_tale)
if tale_version < tale_version_required:
raise StoryConfigError("This game requires tale " + self.config.requires_tale + ", but " + tale_version_str + " is installed.")
+
+
+class StoryContext:
+
+ def __init__(self, base_story: str = "") -> None:
+ self.base_story = base_story
+ self.current_section = ""
+ self.past_sections = []
+ self.progress = 0.0
+ self.length = 10.0
+ self.speed = 1.0
+
+ def increase_progress(self, amount: float = 1.0) -> bool:
+ """ increase the progress by the given amount, return True if the progress has changed past the integer value """
+ start_progess = math.floor(self.progress)
+ self.progress += random.random() * amount * self.speed
+ if self.progress >= self.length:
+ self.progress = self.length
+ return start_progess != math.floor(self.progress)
+
+ def set_current_section(self, section: str) -> None:
+ if self.current_section:
+ self.past_sections.append(self.current_section)
+ self.current_section = section
+
+ def to_context(self) -> str:
+ return f" Base plot: {self.base_story}; Active section: {self.current_section}"
+
+ def to_context_with_past(self) -> str:
+ return f" Base plot: {self.base_story}; Past: {' '.join(self.past_sections) if self.past_sections else 'This is the beginning of the story'}; Active section:{self.current_section}; Progress: {self.progress}/{self.length};"
+
+ def from_json(self, data: dict) -> 'StoryContext':
+ self.base_story = data.get("base_story", "")
+ self.current_section = data.get("current_section", "")
+ self.past_sections = data.get("past_sections", [])
+ self.progress = data.get("progress", 0.0)
+ self.length = data.get("length", 10.0)
+ self.speed = data.get("speed", 1.0)
+ return self
+
+ def to_json(self) -> dict:
+ return {"base_story": self.base_story,
+ "current_section": self.current_section,
+ "past_sections": self.past_sections,
+ "progress": self.progress,
+ "length": self.length,
+ "speed": self.speed}
\ No newline at end of file
diff --git a/tale/story_builder.py b/tale/story_builder.py
index 560216c4..4847d9ed 100644
--- a/tale/story_builder.py
+++ b/tale/story_builder.py
@@ -4,10 +4,11 @@
from tale import lang, load_items
from tale.base import Location
-from tale.llm.llm_ext import DynamicStory
+from tale.llm.dynamic_story import DynamicStory
from tale.llm.llm_utils import LlmUtil
from tale.player import PlayerConnection
from tale.quest import Quest, QuestType
+from tale.story import StoryContext
from tale.zone import Zone
@@ -148,10 +149,11 @@ def apply_to_story(self, story: DynamicStory, llm_util: LlmUtil):
story.config.world_info = self.story_info.world_info
story.config.world_mood = self.story_info.world_mood
self.connection.output("Generating story background...")
- story.config.context = llm_util.generate_story_background(world_info=story.config.world_info,
+ background = llm_util.generate_story_background(world_info=story.config.world_info,
world_mood=story.config.world_mood,
story_type=story.config.type)
-
+ story_context = StoryContext(base_story=background)
+ story.config.context = story_context
items = self.generate_world_items(llm_util, story.config.context, story.config.type, story.config.world_info, story.config.world_mood)
for item in items:
story._catalogue.add_item(item)
diff --git a/tale/web/resources/test.jpg b/tale/web/resources/test.jpg
deleted file mode 100644
index 35af9819..00000000
Binary files a/tale/web/resources/test.jpg and /dev/null differ
diff --git a/tale/web/web_utils.py b/tale/web/web_utils.py
index b47b4cc5..631f7509 100644
--- a/tale/web/web_utils.py
+++ b/tale/web/web_utils.py
@@ -71,7 +71,7 @@ def _find_image(image_name: str) -> str:
def copy_web_resources(gamepath: str):
# copy the resources folder to the resources folder in the web folder
- shutil.copytree(os.path.join(gamepath, "resources"), os.path.join(web_resources_path, resource_folder), dirs_exist_ok=True)
+ shutil.copytree(os.path.join(gamepath, resource_folder), os.path.join(web_resources_path, resource_folder), dirs_exist_ok=True)
def clear_resources():
resource_path = os.path.join(web_resources_path, resource_folder)
@@ -84,8 +84,13 @@ def clear_resources():
if os.path.isfile(item_path):
os.remove(item_path)
-def copy_single_image(gamepath: str, image_name: str):
- shutil.copy(os.path.join(gamepath, "resources", image_name), os.path.join(web_resources_path, resource_folder))
+def copy_single_image(gamepath: str, image_name: str) -> str:
+ from_path = os.path.join(gamepath, resource_folder, image_name)
+ if not os.path.exists(from_path):
+ return
+ to_path = os.path.join(web_resources_path, resource_folder)
+ return shutil.copy(from_path, to_path)
def _check_file_exists(filename: str) -> bool:
- return os.path.exists(os.path.join(web_resources_path, "resources", filename))
\ No newline at end of file
+ check_path = os.path.join(web_resources_path, resource_folder, filename)
+ return os.path.exists(check_path)
\ No newline at end of file
diff --git a/tale/zone.py b/tale/zone.py
index 48488d53..ecc6319d 100644
--- a/tale/zone.py
+++ b/tale/zone.py
@@ -1,4 +1,3 @@
-import random
from tale.base import Location
from tale.coord import Coord
@@ -17,6 +16,7 @@ def __init__(self, name: str, description: str = '') -> None:
self.neighbors = dict() # type: dict[str, Zone] # north, east, south or west
self.center = Coord(0,0,0)
self.name = name
+ self.lore = ""
def add_location(self, location: Location) -> bool:
""" Add a location to the zone. Skip if location already exists."""
@@ -42,7 +42,8 @@ def get_info(self) -> dict:
"races":self.races,
"items":self.items,
"size":self.size,
- "center":self.center.as_tuple()
+ "center":self.center.as_tuple(),
+ "lore":self.lore,
}
def get_neighbor(self, direction: str) -> 'Zone':
@@ -86,4 +87,5 @@ def from_json(data: dict) -> 'Zone':
if data.get("center", None) is not None:
center = data.get("center")
zone.center = Coord(center[0], center[1], center[2])
+ zone.lore = data.get("lore", "")
return zone
diff --git a/tests/files/test.jpg b/tests/files/test.jpg
new file mode 100644
index 00000000..81aad162
Binary files /dev/null and b/tests/files/test.jpg differ
diff --git a/tests/files/test_cache.json b/tests/files/test_cache.json
index c9e3a8c4..d64fc0be 100644
--- a/tests/files/test_cache.json
+++ b/tests/files/test_cache.json
@@ -1,11 +1,12 @@
{
"events": {
- "-8722945376858612228": "test event"
+ "-8722945376858612228": "test event",
+ "249076626384170890539394815371839499036": "test event",
+ "249076626384170890539394815371839499036": "test event"
},
"looks": {
- "5170880201559623883": "test look"
- },
- "tells": {
- "-3872932153149350210": "test tell"
+ "5170880201559623883": "test look",
+ "59748369602019872689181092956633477846": "test look",
+ "59748369602019872689181092956633477846": "test look"
}
}
\ No newline at end of file
diff --git a/tests/test_dynamic_story.py b/tests/test_dynamic_story.py
new file mode 100644
index 00000000..26409bf3
--- /dev/null
+++ b/tests/test_dynamic_story.py
@@ -0,0 +1,80 @@
+from tale.base import Location
+from tale.coord import Coord
+from tale.llm.dynamic_story import DynamicStory
+from tale.zone import Zone
+
+
+class TestDynamicStory():
+
+ def test_add_location(self):
+ story = DynamicStory()
+ test_location = Location('test')
+ test_location.world_location = Coord(0,0,0)
+ story.add_location(test_location)
+ assert(story._world._locations['test'] == test_location)
+ assert(story._world._grid[(0,0,0)] == test_location)
+
+ def test_add_location_duplicate(self):
+ story = DynamicStory()
+ test_location = Location('test')
+ test_location.world_location = Coord(0,0,0)
+ story.add_location(test_location)
+ assert(story._world._locations['test'] == test_location)
+ assert(story._world._grid[(0,0,0)] == test_location)
+ assert(not story.add_location(test_location))
+
+ def test_add_location_to_zone(self):
+ story = DynamicStory()
+ test_location = Location('test')
+ test_location.world_location = Coord(0,0,0)
+ test_zone = Zone('zone')
+ story.add_zone(test_zone)
+ story.add_location(test_location, 'zone')
+ assert(story._world._locations['test'] == test_location)
+ assert(story._world._grid[(0,0,0)] == test_location)
+ assert(story._zones['zone'].locations['test'] == test_location)
+ assert(story.get_location(zone='zone', name='test') == test_location)
+
+ def test_find_location_in_zone(self):
+ story = DynamicStory()
+ test_location = Location('test')
+ test_location.world_location = Coord(0,0,0)
+ test_zone = Zone('zone')
+ story.add_zone(test_zone)
+ story.add_location(test_location, 'zone')
+ assert(story.find_location('test') == test_location)
+
+ def test_neighbors_for_location(self):
+ story = DynamicStory()
+ story._locations = dict()
+ test_location = Location('test')
+ test_location.world_location = Coord(0,0,0)
+ north_location = Location('north')
+ north_location.world_location = Coord(0,1,0)
+ south_location = Location('south')
+ south_location.world_location = Coord(0,-1,0)
+ east_location = Location('east')
+ east_location.world_location = Coord(1,0,0)
+ west_location = Location('west')
+ west_location.world_location = Coord(-1,0,0)
+
+ story.add_location(test_location)
+ story.add_location(north_location)
+ story.add_location(south_location)
+ story.add_location(east_location)
+ story.add_location(west_location)
+
+ neighbors = story.neighbors_for_location(test_location)
+ assert(neighbors['north'] == north_location)
+ assert(neighbors['south'] == south_location)
+ assert(neighbors['east'] == east_location)
+ assert(neighbors['west'] == west_location)
+
+ def test_check_setting(self):
+ story = DynamicStory()
+ assert(story.check_setting('fantasy') == 'fantasy')
+ assert(story.check_setting('modern') == 'modern')
+ assert(story.check_setting('sci-fi') == 'scifi')
+ assert(story.check_setting('steampunk') == '')
+ assert(story.check_setting('cyberpunk') == '')
+ assert(story.check_setting('western') == '')
diff --git a/tests/test_llm_ext.py b/tests/test_living_npc.py
similarity index 83%
rename from tests/test_llm_ext.py
rename to tests/test_living_npc.py
index 3ba8f320..ee656d25 100644
--- a/tests/test_llm_ext.py
+++ b/tests/test_living_npc.py
@@ -1,16 +1,13 @@
-from datetime import timedelta
import json
import responses
from tale import mud_context
-import tale
from tale.llm import llm_cache
from tale.base import Exit, Item, Living, Location, ParseResult, Weapon
-from tale.coord import Coord
from tale.llm.LivingNpc import LivingNpc
from tale.llm.item_handling_result import ItemHandlingResult
-from tale.llm.llm_ext import DynamicStory
+from tale.llm.dynamic_story import DynamicStory
from tale.llm.llm_io import IoUtil
from tale.llm.llm_utils import LlmUtil
from tale.player import Player
@@ -18,8 +15,8 @@
from tale.skills.skills import SkillType
from tale.skills.weapon_type import WeaponType
from tale.wearable import WearLocation
-from tale.zone import Zone
from tests.supportstuff import FakeDriver, MsgTraceNPC
+
class TestLivingNpc():
drink = Item("ale", "jug of ale", descr="Looks and smells like strong ale.")
@@ -356,78 +353,3 @@ def test_search(self):
assert(actions == '')
assert not self.npc2.hidden
assert ["test searches for something.", "test reveals actor"] == self.msg_trace_npc.messages
-
-class TestDynamicStory():
-
- def test_add_location(self):
- story = DynamicStory()
- test_location = Location('test')
- test_location.world_location = Coord(0,0,0)
- story.add_location(test_location)
- assert(story._world._locations['test'] == test_location)
- assert(story._world._grid[(0,0,0)] == test_location)
-
- def test_add_location_duplicate(self):
- story = DynamicStory()
- test_location = Location('test')
- test_location.world_location = Coord(0,0,0)
- story.add_location(test_location)
- assert(story._world._locations['test'] == test_location)
- assert(story._world._grid[(0,0,0)] == test_location)
- assert(not story.add_location(test_location))
-
- def test_add_location_to_zone(self):
- story = DynamicStory()
- test_location = Location('test')
- test_location.world_location = Coord(0,0,0)
- test_zone = Zone('zone')
- story.add_zone(test_zone)
- story.add_location(test_location, 'zone')
- assert(story._world._locations['test'] == test_location)
- assert(story._world._grid[(0,0,0)] == test_location)
- assert(story._zones['zone'].locations['test'] == test_location)
- assert(story.get_location(zone='zone', name='test') == test_location)
-
- def test_find_location_in_zone(self):
- story = DynamicStory()
- test_location = Location('test')
- test_location.world_location = Coord(0,0,0)
- test_zone = Zone('zone')
- story.add_zone(test_zone)
- story.add_location(test_location, 'zone')
- assert(story.find_location('test') == test_location)
-
- def test_neighbors_for_location(self):
- story = DynamicStory()
- story._locations = dict()
- test_location = Location('test')
- test_location.world_location = Coord(0,0,0)
- north_location = Location('north')
- north_location.world_location = Coord(0,1,0)
- south_location = Location('south')
- south_location.world_location = Coord(0,-1,0)
- east_location = Location('east')
- east_location.world_location = Coord(1,0,0)
- west_location = Location('west')
- west_location.world_location = Coord(-1,0,0)
-
- story.add_location(test_location)
- story.add_location(north_location)
- story.add_location(south_location)
- story.add_location(east_location)
- story.add_location(west_location)
-
- neighbors = story.neighbors_for_location(test_location)
- assert(neighbors['north'] == north_location)
- assert(neighbors['south'] == south_location)
- assert(neighbors['east'] == east_location)
- assert(neighbors['west'] == west_location)
-
- def test_check_setting(self):
- story = DynamicStory()
- assert(story.check_setting('fantasy') == 'fantasy')
- assert(story.check_setting('modern') == 'modern')
- assert(story.check_setting('sci-fi') == 'scifi')
- assert(story.check_setting('steampunk') == '')
- assert(story.check_setting('cyberpunk') == '')
- assert(story.check_setting('western') == '')
\ No newline at end of file
diff --git a/tests/test_llm_utils.py b/tests/test_llm_utils.py
index 8128a017..4080d9af 100644
--- a/tests/test_llm_utils.py
+++ b/tests/test_llm_utils.py
@@ -5,6 +5,7 @@
from tale.llm.contexts.CharacterContext import CharacterContext
from tale.llm.contexts.FollowContext import FollowContext
from tale.llm.contexts.WorldGenerationContext import WorldGenerationContext
+from tale.llm.dynamic_story import DynamicStory
import tale.llm.llm_cache as llm_cache
from tale import mud_context
from tale import zone
@@ -17,7 +18,7 @@
from tale.llm.responses.FollowResponse import FollowResponse
from tale.player import Player, PlayerConnection
from tale.races import UnarmedAttack
-from tale.story import MoneyType
+from tale.story import MoneyType, StoryConfig, StoryContext
from tale.tio.console_io import ConsoleIo
from tale.zone import Zone
from tests.supportstuff import FakeIoUtil
@@ -543,4 +544,23 @@ def test_generate_note_quest(self):
assert(quest.reason == 'A test quest')
assert(quest.target == 'Arto')
-
\ No newline at end of file
+class TestStoryBuilding():
+
+ driver = IFDriver(screen_delay=99, gui=False, web=True, wizard_override=True)
+ driver.game_clock = util.GameDateTime(datetime.datetime(year=2023, month=1, day=1), 1)
+
+ llm_util = LlmUtil(FakeIoUtil()) # type: LlmUtil
+
+ story = JsonStory('tests/files/world_story/', parse_utils.load_story_config(parse_utils.load_json('tests/files/test_story_config_empty.json')))
+
+ story.init(driver)
+
+ def test_advance_story_section(self):
+ self.llm_util._story_building.io_util.response = "Chapter 2"
+
+ self.story.config.context = StoryContext(base_story='test context')
+
+ result = self.llm_util._story_building.advance_story_section(self.story)
+
+ assert(result == 'Chapter 2')
+ assert(self.story.config.context.current_section == 'Chapter 2')
\ No newline at end of file
diff --git a/tests/test_mudobjects.py b/tests/test_mudobjects.py
index f3d91bad..67b031fd 100644
--- a/tests/test_mudobjects.py
+++ b/tests/test_mudobjects.py
@@ -12,7 +12,7 @@
from tale.base import Location, Exit, Item, MudObject, Living, _limbo, Container, Weapon, Door, Key, ParseResult, MudObjRegistry
from tale.demo.story import Story as DemoStory
from tale.errors import ActionRefused, LocationIntegrityError, UnknownVerbException, TaleError
-from tale.llm.llm_ext import DynamicStory
+from tale.llm.dynamic_story import DynamicStory
from tale.player import Player
from tale.story import MoneyType, StoryBase
from tale.shop import Shopkeeper
diff --git a/tests/test_normal_commands.py b/tests/test_normal_commands.py
index a63a84f4..e8f0fd0e 100644
--- a/tests/test_normal_commands.py
+++ b/tests/test_normal_commands.py
@@ -7,7 +7,7 @@
from tale.cmds import normal
from tale.errors import ActionRefused, ParseError
from tale.llm.LivingNpc import LivingNpc
-from tale.llm.llm_ext import DynamicStory
+from tale.llm.dynamic_story import DynamicStory
from tale.llm.llm_utils import LlmUtil
from tale.player import Player
from tale.skills.skills import SkillType
diff --git a/tests/test_parse_utils.py b/tests/test_parse_utils.py
index a9ed7606..48195ff2 100644
--- a/tests/test_parse_utils.py
+++ b/tests/test_parse_utils.py
@@ -10,7 +10,7 @@
from tale.mob_spawner import MobSpawner
from tale.npc_defs import Trader
from tale.races import BodyType
-from tale.story import GameMode, MoneyType
+from tale.story import GameMode, MoneyType, StoryContext
from tale.skills.weapon_type import WeaponType
from tale.wearable import WearLocation
from tale.zone import Zone
@@ -46,6 +46,13 @@ def test_load_story_config(self):
assert(config.money_type == MoneyType.NOTHING)
assert(config.supported_modes == {GameMode.IF})
assert(config.zones == ["test zone"])
+
+ config.context = StoryContext().from_json({'base_story': 'Base context', 'current_context': 'Current context'})
+
+ stored_config = parse_utils.save_story_config(config)
+
+ new_config = parse_utils.load_story_config(stored_config)
+ assert(isinstance(new_config.context, StoryContext))
def test_connect_location_to_exit(self):
""" This simulates a room having been generated before"""
diff --git a/tests/test_quests.py b/tests/test_quests.py
index 714729f5..9b89ba32 100644
--- a/tests/test_quests.py
+++ b/tests/test_quests.py
@@ -2,7 +2,7 @@
from tale.base import Item, ParseResult
from tale.driver import Driver
from tale.llm.LivingNpc import LivingNpc
-from tale.llm.llm_ext import DynamicStory
+from tale.llm.dynamic_story import DynamicStory
from tale.llm.llm_utils import LlmUtil
from tale.quest import Quest, QuestStatus, QuestType
from tale.story import StoryConfig
diff --git a/tests/test_spells.py b/tests/test_spells.py
index dde09566..5f73150e 100644
--- a/tests/test_spells.py
+++ b/tests/test_spells.py
@@ -4,7 +4,7 @@
from tale.cmds import spells
from tale.errors import ActionRefused
from tale.llm.LivingNpc import LivingNpc
-from tale.llm.llm_ext import DynamicStory
+from tale.llm.dynamic_story import DynamicStory
from tale.llm.llm_utils import LlmUtil
from tale.skills.magic import MagicType
from tale.player import Player
diff --git a/tests/test_story.py b/tests/test_story.py
new file mode 100644
index 00000000..268bb1cb
--- /dev/null
+++ b/tests/test_story.py
@@ -0,0 +1,78 @@
+import pytest
+from tale.story import StoryContext
+
+class TestStoryContext:
+
+ def test_initialization(self):
+ context = StoryContext(base_story="A hero's journey")
+ assert context.base_story == "A hero's journey"
+ assert context.current_section == ""
+ assert context.past_sections == []
+
+ def test_set_current_section(self):
+ context = StoryContext(base_story="A hero's journey")
+ context.set_current_section("Chapter 1")
+ assert context.current_section == "Chapter 1"
+ assert context.past_sections == []
+
+ context.set_current_section("Chapter 2")
+ assert context.current_section == "Chapter 2"
+ assert context.past_sections == ["Chapter 1"]
+
+ def test_increase_progress(self):
+ context = StoryContext(base_story="A hero's journey")
+ result = context.increase_progress(0.01)
+ assert result == False
+ assert context.progress < 0.011
+ context.increase_progress(0.01)
+ assert context.progress < 0.021
+
+ context.progress = 0.99999999999
+ result = context.increase_progress(1)
+ assert result == True
+ assert context.progress > 1.0
+
+ context.progress = 9.9999999999999999999
+ context.increase_progress(1)
+ assert context.progress == 10.0
+
+ def test_to_context(self):
+ context = StoryContext(base_story="A hero's journey")
+ context.set_current_section("Chapter 1")
+ assert context.to_context() == " Base plot: A hero's journey; Active section: Chapter 1"
+
+ def test_to_context_with_past(self):
+ context = StoryContext(base_story="A hero's journey")
+ context.set_current_section("Chapter 1")
+ context.set_current_section("Chapter 2")
+ assert context.to_context_with_past() == " Base plot: A hero's journey; Past: Chapter 1; Active section:Chapter 2; Progress: 0.0/10.0;"
+
+ def test_from_json(self):
+ data = {
+ "base_story": "A hero's journey",
+ "current_section": "Chapter 1",
+ "past_sections": ["Prologue"],
+ "progress": 0.5,
+ "length": 10.0,
+ }
+ context = StoryContext().from_json(data)
+ assert context.base_story == "A hero's journey"
+ assert context.current_section == "Chapter 1"
+ assert context.past_sections == ["Prologue"]
+ assert context.progress == 0.5
+ assert context.length == 10.0
+
+ def test_to_json(self):
+ context = StoryContext(base_story="A hero's journey")
+ context.set_current_section("Prologue")
+ context.set_current_section("Chapter 1")
+ context.set_current_section("Chapter 2")
+ data = context.to_json()
+ assert data == {
+ "base_story": "A hero's journey",
+ "current_section": "Chapter 2",
+ "past_sections": ["Prologue", "Chapter 1"],
+ "progress": 0.0,
+ "length": 10.0,
+ "speed": 1.0
+ }
\ No newline at end of file
diff --git a/tests/test_story_builder.py b/tests/test_story_builder.py
index 62601162..7a0419d9 100644
--- a/tests/test_story_builder.py
+++ b/tests/test_story_builder.py
@@ -4,10 +4,10 @@
import pytest
from tale import util, mud_context
from tale.driver_if import IFDriver
-from tale.llm.llm_ext import DynamicStory
+from tale.llm.dynamic_story import DynamicStory
from tale.llm.llm_utils import LlmUtil
from tale.player import PlayerConnection
-from tale.story import StoryConfig
+from tale.story import StoryConfig, StoryContext
from tale.story_builder import StoryBuilder, StoryInfo
from tests.supportstuff import FakeIoUtil
@@ -74,7 +74,9 @@ def test_apply_to_story(self):
builder.story_info.world_mood = 3
builder.story_info.start_location = "on a small road outside a village"
- responses = ['In the peaceful land of Whimsy, where the sun always shines bright and the creatures are forever young, a darkness has begun to stir beneath the surface. A great evil, born from the twisted imagination of the Mad Hatter, threatens to consume the entire realm. This malevolent force seeks to plunge Whimsy into eternal night, and all who oppose it must band together if they hope to save their home from destruction. The players\' village is the first to fall under attack, forcing them to embark on a perilous journey across this magical landscape to rally allies for the impending war against the shadowy foe. Along the way, they discover hidden secrets about the nature of their world and their own roles within its grand narrative, ultimately deciding the fate of an enchanted civilization hanging precariously upon their shoulders.',
+ story_background = "In the peaceful land of Whimsy, where the sun always shines bright and the creatures are forever young, a darkness has begun to stir beneath the surface. A great evil, born from the twisted imagination of the Mad Hatter, threatens to consume the entire realm. This malevolent force seeks to plunge Whimsy into eternal night, and all who oppose it must band together if they hope to save their home from destruction. The players\' village is the first to fall under attack, forcing them to embark on a perilous journey across this magical landscape to rally allies for the impending war against the shadowy foe. Along the way, they discover hidden secrets about the nature of their world and their own roles within its grand narrative, ultimately deciding the fate of an enchanted civilization hanging precariously upon their shoulders."
+
+ responses = [story_background,
'{"items":[{"name":"Enchanted Petals", "type":"Health", "value": 20, "description": "A handful of enchanted petals that can be used to heal wounds and cure ailments."}]}',
'{"creatures": [ { "name": "Whimsy Woozle", "description": "A gentle, ethereal creature with a penchant for gardening and poetry. They tend to the area\'s lush fields and forests, filling the air with sweet melodies and the scent of blooming wildflowers. They are friendly and welcoming to all visitors.", "level": 1 }, { "name": "Lunar Lopster", "description": "A mysterious crustacean with an affinity for the moon\'s gentle light. They roam the area at night, their glowing shells lighting the way through the darkness. They are neutral towards visitors, but may offer cryptic advice or guidance to those who seek it.", "level": 2 }, { "name": "Shadow Stag", "description": "A sleek and elusive creature with a mischievous grin. They roam the area\'s forests, their dark forms blending into the shadows. They are hostile towards intruders, and will not hesitate to attack those who threaten their home.", "level": 3 }, { "name": "Moonflower", "description": "A rare and beautiful flower that blooms only under the light of the full moon. They can be found in the area\'s forests, and are said to have powerful healing properties. They are very friendly towards visitors, and will offer their petals to those who show kindness and respect.", "level": 4 }, { "name": "Moonstone", "description": "A rare and valuable mineral that can be found in the area\'s mountains. It glows with a soft, ethereal light, and is said to have powerful magical properties. It is highly sought after by collectors, and can be found in both the earth and the water.", "level": 5 }]}',
'{ "name": "Moonlit Meadows", "description": "A serene and mystical area nestled between two great mountains, where the moon\'s gentle light illuminates the lush fields and forests. The air is filled with the sweet scent of blooming wildflowers, and the gentle chirping of nocturnal creatures can be heard in the distance. The area is home to a diverse array of magical creatures, including the Whimsy Woozle, the Lunar Lopster, and the Shadow Stag."}',
@@ -105,6 +107,8 @@ def test_apply_to_story(self):
assert(story.config.world_info == "a mix between alice in wonderland and the wizard of oz with a dark element")
assert(story.config.world_mood == 3)
assert(story.config.startlocation_player == "Moonlit Meadows.Greenhaven")
+ assert(isinstance(story.config.context, StoryContext))
+ assert(story.config.context.to_json() == StoryContext(base_story=story_background).to_json())
quest = None
assert(start_location.livings)
diff --git a/tests/test_web_utils.py b/tests/test_web_utils.py
index 4a5bd15c..ff687e66 100644
--- a/tests/test_web_utils.py
+++ b/tests/test_web_utils.py
@@ -5,7 +5,6 @@
class TestWebUtils():
-
def test_create_chat_container(self):
result = create_chat_container("Bloated Murklin <:> Hello World!")
@@ -13,7 +12,8 @@ def test_create_chat_container(self):
assert '' in result
assert 'Hello World!
' in result
- def test_copy_single_image(self):
- web_utils.web_resources_path = "tale/web"
- copy_single_image("./tests/files", "test.jpg")
- assert _check_file_exists("test.jpg")
\ No newline at end of file
+ # def test_copy_single_image(self):
+ # web_utils.web_resources_path = "tale/web"
+ # copy_single_image("./tests/files", "test.jpg")
+ # assert _check_file_exists("test.jpg")
+ # web_utils.clear_resources()
\ No newline at end of file
diff --git a/tests/test_wizard_commands.py b/tests/test_wizard_commands.py
index a37df8d4..98f3f11c 100644
--- a/tests/test_wizard_commands.py
+++ b/tests/test_wizard_commands.py
@@ -8,7 +8,7 @@
from tale.errors import ParseError
from tale.items.basic import Food
from tale.llm.LivingNpc import LivingNpc
-from tale.llm.llm_ext import DynamicStory
+from tale.llm.dynamic_story import DynamicStory
from tale.llm.llm_utils import LlmUtil
from tale.player import Player
from tale.story import StoryConfig
diff --git a/tests/test_zone.py b/tests/test_zone.py
index b67eb796..fb737e17 100644
--- a/tests/test_zone.py
+++ b/tests/test_zone.py
@@ -52,7 +52,8 @@ def test_get_info(self):
"items":['sword', 'shield', 'armor'],
"center":(0, 0, 0),
"level":2,
- "size":5}
+ "size":5,
+ "lore":''}
def test_get_neighbor(self):
zone = Zone('test')