World Engine Tutorial Example 2

Zara Starling

On this page you will find the code for the "Zara Starling" chat world. Here is a brief description of this world:

Zara Starling, an interstellar archaeologist who discovers ancient alien civilizations and deciphers their lost histories.

You can modify the "# SETTINGS" section of the code to create your own characters.

from random import choice

# SETTINGS
# ------------------------------------------------------------
character_name = "Zara Starling"
character_appearance = "A Latina woman with dark hair, dressed in futuristic clothing."
character_personality = "Zara Starling is a passionate and curious interstellar archaeologist, known for her unwavering determination and sharp intellect. Her personality is a blend of adventurous spirit and scholarly rigor, as she eagerly dives into the mysteries of ancient alien civilizations with a zeal that borders on obsession. Zara possesses an innate ability to connect with the stories she uncovers, displaying empathy and respect for the long-gone cultures she studies. She's remarkably intuitive, often relying on her gut feelings to guide her through the most cryptic of ruins. Despite the solitary nature of her expeditions, Zara is personable and charismatic, capable of captivating audiences with tales of her cosmic adventures, and inspiring a sense of wonder about the universe in those she meets."
character_greetings = [
    '"You\'re interrupting. I do hope it\'s important."',
    '"Hello, and you are?"'
]
intro_guidelines = "Zara Starling is on board the Celestial Nomad, a ship that the main protagonist also happens to be on. Zara Starling and the protagonist don't know each other."


# REST OF CODE
# -------------------------------------------------------------
data = {
    "character_name": character_name,
    "character_appearance": character_appearance.strip(" \n"),
    "character_personality":character_personality.strip(" \n"),
    "character_greetings": character_greetings,
    "intro_guidelines": intro_guidelines,
    "is_first_message": True,
    "mode": "chat",
    "mode_switched": False,
    "num_steps": 0,
}

ui.story_info = ["Commands", '1. use the "/chat" command to enter "chat" mode. You can also specify the name of the character you want to chat with like so: "/chat Character Name".', '2. use the "/story" command to enter story mode.']


def handle_mode_switching():
    """
    This function handles switching between "/chat" mode and "/story" mode
    """
    if ui.action:
        if ui.action[:5] == "/chat":
            ai.perform_generation = False
            ui.action = ui.action.strip()

            # Get the character name after "/chat" if provided
            ws_index = ui.action.find(" ")
            if ws_index != -1:
                new_character_name = ui.action[ws_index + 1:]
            else:
                new_character_name = None
            
            # If player is already chatting with this character, display appropriate message
            if data["mode"] == "chat" and (ws_index == -1 or new_character_name == data["character_name"]):
                ui.display('You are already in "chat" mode.')
            
            # If starting chat with new character, switch mode
            else:
                if new_character_name:
                    data["character_name"] = new_character_name
                data["mode"] = "chat"
                data["mode_switched"] = True
                if new_character_name:
                    ui.display(f'You are now chatting with "{data["character_name"]}".')
                else:
                    ui.display(f'You have switched to "chat" mode and are now chatting with "{data["character_name"]}".')
        
        elif ui.action == "/story":
            ai.perform_generation = False

            # Check if the player already isn't in story mode
            if data["mode"] == "story":
                ui.display('You are already in "story" mode.')
            else:
                data["mode"] = "story"
                data["mode_switched"] = True
                ui.display('You have switched to "story" mode.')

        elif ui.action[0] == "/":
            ai.perform_generation = False
            ui.display('Invalid command. Available commands: "/chat", "/story".')


def pre_generate():
    # Only completion models work for chat worlds
    if "GPT" in ai.model_name:
        if "Completion" not in ai.model_name:
            ai.perform_generation = False
            ui.display('When in "chat" mode and using a ChatGPT model, please use the "Completion" version of the model.')
            return
    
    # Handle first message (which could be a greeting message depending on whether or not the player wrote something)
    if data["is_first_message"]:
        if not ui.action:
            ai.perform_generation = False
            ui.generated_text = f"{data['character_name']}: " + choice(data["character_greetings"])
            data["is_first_message"] = False
            return
        if ui.action[0] == "/":
            ai.perform_generation = False
            ui.display("Using commands is not permitted at the start of a chat.")
            return

    handle_mode_switching()

    if data["mode"] != "chat":
        ai.model_stop_text = ""
        ui.display_actions = True
        return
    
    # Add character info if the character is in scene or this is the beginning of the chat
    if data["character_name"] in ui.story_text or data["num_steps"] < 10:
        ai.enforce(f"Here is a description of {data['character_name']}'s appearance: " + data["character_appearance"])
        ai.enforce(f"Here is a description of {data['character_name']}'s personality: " + data["character_personality"])
        if data["num_steps"] < 5:
            ai.enforce(data["intro_guidelines"])
        ai.enforce(f"This whole story should be a dialogue between the main protagonist and {data['character_name']}, which goes on indefinitely. Make sure to keep the same format of the dialogue (\"{data['character_name']}: dialogue here\").")

    if not ui.action:
        return
    
    # We add quotes, if the action does not have any or does not contain an asterisk "*" indicating that it's an action
    if not "*" in ui.action and not '"' in ui.action:
        ui.action = f'"{ui.action}"'

    # We append the action in the chat format to the story text
    ui.story_text += "\n\nMe: " + ui.action + f"\n\n{data['character_name']}:"
    
    # We prevent the action from actually being performed
    ai.perform_action = False
    
    # We prevent the action from being displayed
    ui.display_actions = False
    
    # This makes the model stop generating more text once it generates two new line characters
    # This is useful to make text generation faster, since we don't need the text after the two new lines
    ai.model_stop_text = "\n\n"


def post_generate():
    if data["mode"] != "chat":
        return

    # The ai.model_stop_text may not work with all models, therefore
    # we additionaly check if 2 new line characters were generated. 
    # If yes, we cut the text to that point
    ui.generated_text = ui.generated_text.strip("\n")
    double_new_line_index = ui.generated_text.find("\n\n")
    if double_new_line_index != -1:
        ui.generated_text = ui.generated_text[:double_new_line_index]

    # We double check in case the AI generated "Me:" text, trying to say something for the user
    # If the AI did do so, we cut the text until that point.
    me_start = ui.generated_text.find("Me: ")
    if me_start != -1:
        ui.generated_text = ui.generated_text[:me_start].strip("\n")
    
    if data["mode_switched"] or data["is_first_message"]:
        me_prefix = "Me: " + ui.action + f"\n\n"
    else:
        me_prefix = "\n\nMe: " + ui.action + f"\n\n"
    
    character_prefix = f"{data['character_name']}:"

    # We must add the text the user provided as an action ti=o the generated text,
    # so that it appears in the UI
    if ui.generated_text[:len(character_prefix)] == character_prefix:
        prefix = me_prefix
    else:
        prefix = me_prefix + character_prefix
        if ui.generated_text[0] != " ":
            prefix += " "
        if ui.generated_text.count('"') % 2 != 0:
            if ui.generated_text[0] != '"':
                prefix += '"'
            else:
                ui.generated_text += '"'
    ui.generated_text = prefix + ui.generated_text

    # Make character chat prefix text bold
    ui.generated_text = ui.generated_text.replace("Me:", "Me:").replace(f"{data['character_name']}:", f"{data['character_name']}:")
    data["num_steps"] += 1

    # Rest mode switched to False
    data["mode_switched"] = False

    # If this was the first message, set is_first_message to False
    if data["is_first_message"]:
        data["is_first_message"] = False