From 73fd2d67f436d99462ca06e5e8e36131f24e4044 Mon Sep 17 00:00:00 2001 From: Jonathan Hite Date: Thu, 6 Feb 2025 22:33:14 -0500 Subject: [PATCH 01/15] Added game system. [G]ames are available in the Utilities menu. The readme was updated. A gamefile syntax readme was added. --- README.md | 2 + command_handlers.py | 288 ++++++++++++++++++++++++++++++++++++++++++ example_config.ini | 102 --------------- gamefile_syntax.md | 66 ++++++++++ games/lost_forest.csv | 8 ++ games/testing.txt | 1 + games/testnoname | 0 message_processing.py | 42 +++++- server.py | 2 +- text.py | 10 ++ validate_game.py | 122 ++++++++++++++++++ 11 files changed, 537 insertions(+), 106 deletions(-) delete mode 100644 example_config.ini create mode 100644 gamefile_syntax.md create mode 100644 games/lost_forest.csv create mode 100644 games/testing.txt create mode 100644 games/testnoname create mode 100644 text.py create mode 100644 validate_game.py diff --git a/README.md b/README.md index 9f67210..a08a828 100644 --- a/README.md +++ b/README.md @@ -221,6 +221,8 @@ The following device roles have been working: - **Statistics**: View statistics about nodes, hardware, and roles. - **Wall of Shame**: View devices with low battery levels. - **Fortune Teller**: Get a random fortune. Pulls from the fortunes.txt file. Feel free to edit this file remove or add more if you like. +- **GAMES**: GAMES!!! BBSs have to have games. You can make and add your own! Games should be written in python. The Games Menu gets the names from the GameTitle variable. (e.g. GameTitle="A Brave Adventure") in the game.py file. + ## Usage diff --git a/command_handlers.py b/command_handlers.py index 16695ba..9f9b8ce 100644 --- a/command_handlers.py +++ b/command_handlers.py @@ -2,6 +2,7 @@ import logging import random import time +import os from meshtastic import BROADCAST_NUM @@ -43,6 +44,8 @@ def build_menu(items, menu_name): menu_str += "[C]hannel Dir\n" elif item.strip() == 'J': menu_str += "[J]S8CALL\n" + elif item.strip() == 'G': + menu_str += "[G]ames\n" elif item.strip() == 'S': menu_str += "[S]tats\n" elif item.strip() == 'F': @@ -666,3 +669,288 @@ def handle_quick_help_command(sender_id, interface): response = ("✈️QUICK COMMANDS✈️\nSend command below for usage info:\nSM,, - Send " "Mail\nCM - Check Mail\nPB,, - Post Bulletin\nCB,, - Check Bulletins\n") send_message(response, sender_id, interface) + +def get_games_available(game_files): + """Returns a dictionary of available games with their filenames and titles. + + - If the first line contains `title="Game Title"`, it uses that as the display name. + - Otherwise, it uses the filename (without extension). + """ + + games = {} + + for file in game_files: + try: + file_path = os.path.join('./games', file) + with open(file_path, 'r', encoding='utf-8') as fp: + first_line = fp.readline().strip() + + # Check if the first line has a title definition + if first_line.lower().startswith("title="): + game_title = first_line.split("=", 1)[1].strip().strip('"') + else: + game_title = file # Use the filename as the title + + games[game_title] = file # Store the title with its correct filename + + except Exception as e: + print(f"Error loading game {file}: {e}") + + return games # Return a dictionary {Title: Filename} + + +def handle_games_command(sender_id, interface): + """Handles the Games Menu and lists available text-based games.""" + + # Find files in ./games that: + # - Have a .txt or .csv extension + # - OR have no extension + game_files = [ + f for f in os.listdir('./games') + if os.path.isfile(os.path.join('./games', f)) and (f.endswith('.txt') or f.endswith('.csv') or '.' not in f) + ] + + games_available = get_games_available(game_files) + + if not games_available: + send_message("No games available yet. Come back soon.", sender_id, interface) + update_user_state(sender_id, {'command': 'UTILITIES', 'step': 1}) + return None + + # Store game filenames in state to avoid title-related issues + game_titles = list(games_available.keys()) # Display titles + game_filenames = list(games_available.values()) # Actual filenames + + # Include exit option + numbered_games = "\n".join(f"{i+1}. {title}" for i, title in enumerate(game_titles)) + numbered_games += "\n[X] Exit" + + response = f"🎮 Games Menu 🎮\nWhich game would you like to play?\n{numbered_games}" + send_message(response, sender_id, interface) + + update_user_state(sender_id, {'command': 'GAMES', 'step': 1, 'games': game_filenames, 'titles': game_titles}) + + return response + + + + +def handle_game_menu_selection(sender_id, message, step, interface, state): + """Handles the user's selection of a game from the Games Menu, allowing exit with 'X' and starting immediately.""" + + # Allow users to exit with "X" like other menus + if message.lower() == "x": + handle_help_command(sender_id, interface) # Return to main menu + return + + games_available = state.get('games', []) + + try: + game_index = int(message) - 1 # Convert user input to zero-based index + if 0 <= game_index < len(games_available): + selected_game = games_available[game_index] + + # Update state to indicate the user is now in-game + update_user_state(sender_id, {'command': 'IN_GAME', 'step': 3, 'game': selected_game}) + + # Start the game immediately + start_selected_game(sender_id, interface, {'game': selected_game}) + else: + send_message("Invalid selection. Please enter a valid game number or 'X' to exit.", sender_id, interface) + + except ValueError: + send_message("Invalid input. Please enter a number corresponding to a game or 'X' to exit.", sender_id, interface) + +def start_selected_game(sender_id, interface, state): + """Starts the game selected by the user and ensures title detection.""" + + game_name = state.get('game', None) + if not game_name: + send_message("Unexpected error: No game found. Returning to game menu.", sender_id, interface) + update_user_state(sender_id, {'command': 'GAMES', 'step': 1}) + return + + # Construct the game file path + game_file_path = os.path.join('./games', game_name) + + # Final check if the file exists + if not os.path.exists(game_file_path): + send_message(f"Error: The game '{game_name}' could not be loaded.", sender_id, interface) + update_user_state(sender_id, {'command': 'GAMES', 'step': 1}) + return + + # Load the game map with title handling + try: + game_title, game_map = load_game_map(game_file_path) + except Exception as e: + send_message(f"Error loading game: {e}", sender_id, interface) + update_user_state(sender_id, {'command': 'GAMES', 'step': 1}) + return + + if not game_map: + send_message(f"Error: The game '{game_name}' could not be loaded.", sender_id, interface) + update_user_state(sender_id, {'command': 'GAMES', 'step': 1}) + return + + # Set up the user state for playing (ENSURE game_title is included) + new_state = { + 'command': 'IN_GAME', + 'step': 3, + 'game': game_name, + 'game_title': game_title, # ✅ Ensure title is stored + 'game_map': game_map, + 'game_position': 1 + } + update_user_state(sender_id, new_state) + + # Present the first segment + present_story_segment(sender_id, interface, new_state) # ✅ Pass updated state + + +def load_game_map(file_path): + """Loads a game map from a CSV file and returns its structured format.""" + + print(f"DEBUG: Inside load_game_map(), trying to open {file_path}") + + try: + with open(file_path, "r", encoding="utf-8") as f: + lines = f.readlines() + + print(f"DEBUG: Read {len(lines)} lines from file.") + + if not lines: + print("❌ ERROR: File is empty!") + return None + + # Check if the first line contains a title + first_line = lines[0].strip() + if first_line.lower().startswith("title="): + game_title = first_line.split("=", 1)[1].strip().strip('"') + game_lines = lines[1:] # Skip title + else: + game_title = file_path # Use filename if no title + game_lines = lines + + print(f"DEBUG: Game title detected -> {game_title}") + + # Parse game map + game_map = {} + for i, line in enumerate(game_lines, start=1): + game_map[i] = line.strip().split(",") + + print(f"DEBUG: Successfully loaded game map with {len(game_map)} entries.") + return game_title, game_map + + except Exception as e: + print(f"❌ ERROR inside load_game_map(): {e}") + return None + + +def present_story_segment(sender_id, interface, state): + """Presents the current segment of the game and available choices.""" + + game_name = state.get('game') + game_title = state.get('game_title', "Unknown Game") # ✅ Prevent KeyError + game_map = state.get('game_map', {}) + game_position = state.get('game_position', 1) + + if game_position not in game_map: + send_message("Error: Invalid game state.", sender_id, interface) + update_user_state(sender_id, {'command': 'GAMES', 'step': 1}) + return + + # Retrieve the current story segment + segment = game_map[game_position] + storyline = segment[0] + choices = segment[1:] + + # Build response message + response = f"🎮 {game_title} 🎮\n\n{storyline}\n\n" + for i in range(0, len(choices), 2): # Display numbered choices + response += f"{(i//2)+1}. {choices[i]}\n" + + response += "\n[X] Exit" + + send_message(response, sender_id, interface) + + # Update user state to track the current game progress + update_user_state(sender_id, { + 'command': 'IN_GAME', + 'step': 3, + 'game': game_name, + 'game_title': game_title, # ✅ Ensure it stays in state + 'game_map': game_map, + 'game_position': game_position + }) + +def process_game_choice(sender_id, message, interface, state): + """Processes the player's choice and advances the game.""" + + game_map = state.get('game_map', {}) + game_position = state.get('game_position', 1) + + if game_position not in game_map: + send_message("Error: Invalid game state.", sender_id, interface) + update_user_state(sender_id, {'command': 'GAMES', 'step': 1}) + return + + segment = game_map[game_position] + + # Extract the storyline and choices + storyline = segment[0] # First element is the story text + choices = segment[1:] # Remaining elements are choices + + # Ensure choices are properly formatted (must be in pairs) + if len(choices) % 2 != 0: + send_message("Error: Game data is corrupted.", sender_id, interface) + update_user_state(sender_id, {'command': 'GAMES', 'step': 1}) + return + + # Handle Exit + if message.lower() == "x": + send_message(f"Exiting '{state['game_title']}'... Returning to Games Menu.", sender_id, interface) + update_user_state(sender_id, {'command': 'GAMES', 'step': 1}) + handle_games_command(sender_id, interface) # Immediately display the game menu + return + + try: + # Convert user input to index (1-based to 0-based) + choice_index = int(message) - 1 + + # Validate choice selection + if choice_index < 0 or choice_index * 2 + 1 >= len(choices): + send_message("Invalid selection. Please enter a valid number.", sender_id, interface) + return + + # Retrieve the target position for the chosen option + target_position = int(choices[choice_index * 2 + 1]) + + # Check if the target position exists + if target_position not in game_map: + send_message("💀 Game Over! You fell into an abyss. 💀", sender_id, interface) + update_user_state(sender_id, {'command': 'GAMES', 'step': 1}) + handle_games_command(sender_id, interface) # Return to game menu + return + + # Update state with the new game position + update_user_state(sender_id, { + 'command': 'IN_GAME', + 'step': 3, + 'game': state['game'], + 'game_title': state['game_title'], + 'game_map': game_map, + 'game_position': target_position + }) + + # Present the new story segment + present_story_segment(sender_id, interface, { + 'command': 'IN_GAME', + 'step': 3, + 'game': state['game'], + 'game_title': state['game_title'], + 'game_map': game_map, + 'game_position': target_position + }) + + except (ValueError, IndexError): + send_message("Invalid selection. Please enter a valid number.", sender_id, interface) diff --git a/example_config.ini b/example_config.ini deleted file mode 100644 index 3066b36..0000000 --- a/example_config.ini +++ /dev/null @@ -1,102 +0,0 @@ -############################### -#### Select Interface type #### -############################### -# [type = serial] for USB connected devices -#If there are multiple serial devices connected, be sure to use the "port" option and specify a port -# Linux Example: -# port = /dev/ttyUSB0 -# -# Windows Example: -# port = COM3 -# [type = tcp] for network connected devices (ESP32 devices only - this does not work for WisBlock) -# If using tcp, remove the # from the beginning and replace 192.168.x.x with the IP address of your device -# Example: -# [interface] -# type = tcp -# hostname = 192.168.1.100 - -[interface] -type = serial -# port = /dev/ttyACM0 -# hostname = 192.168.x.x - - -############################ -#### BBS NODE SYNC LIST #### -############################ -# Provide a list of other nodes running TC²-BBS to sync mail messages and bulletins with -# Enter in a list of other BBS Nodes by their nodeID separated by commas (no spaces) -# Example: -# [sync] -# bbs_nodes = !17d7e4b7,!18e9f5a3,!1a2b3c4d - -# [sync] -# bbs_nodes = !17d7e4b7 - - -############################ -#### Allowed Node IDs #### -############################ -# Provide a list of node IDs that are allowed to post to the urgent board. -# If this section is commented out, anyone can post to the urgent board. -# Example: -# [allow_list] -# allowed_nodes = 12345678,87654321 -# -# [allow_list] -# allowed_nodes = !17d7e4b7 - - -#################### -#### Menu Items #### -#################### -# Remove any menu items you don't plan on using with your BBS below -# -[menu] -# Default Main Menu options for reference -# [Q]uick Commands -# [B]BS -# [U]tilities -# E[X]IT -# -# Remove any menu items from the list below that you want to exclude from the main menu -main_menu_items = Q, B, U, X - -# Default BBS Menu options for reference -# [M]ail -# [B]ulletins -# [C]hannel Dir -# [J]S8CALL -# E[X]IT -# -# Remove any menu items from the list below that you want to exclude from the BBS menu -bbs_menu_items = M, B, C, J, X - -# Default Utilities Menu option for reference -# [S]tats -# [F]ortune -# [W]all of Shame -# E[X]IT -# -# Remove any menu items from the list below that you want to exclude from the utilities menu -utilities_menu_items = S, F, W, X - - -########################## -#### JS8Call Settings #### -########################## -# If you would like messages from JS8Call to go into the BBS, uncomment and enter in info below: -# host = the IP address for your system running JS8Call -# port = TCP API port for JS8CALL - Default is 2442 -# db_file = this can be left as the default "js8call.db" unless you need to change for some reason -# js8groups = the JS8Call groups you're interested in receiving into the BBS -# store_messages = "true" will send messages that arent part of a group into the BBS (can be noisy). "false" will ignore these -# js8urgent = the JS8Call groups you consider to be urgent - anything sent to these will have a notice sent to the -# group chat (similar to how the urgent bulletin board works -# [js8call] -# host = 192.168.1.100 -# port = 2442 -# db_file = js8call.db -# js8groups = @GRP1,@GRP2,@GRP3 -# store_messages = True -# js8urgent = @URGNT diff --git a/gamefile_syntax.md b/gamefile_syntax.md new file mode 100644 index 0000000..d087a57 --- /dev/null +++ b/gamefile_syntax.md @@ -0,0 +1,66 @@ +# 📜 How to Format Game CSV Files + +This guide explains how to structure game files for the **text-based game system** using **CSV format**. + +--- + +## 📌 **Basic Structure** +Each line in the CSV file represents a **story segment** with: +- **Storyline text** (always first column) +- **Choices** (paired as `Choice Text, Target Line`) +- If no choices exist, it signals **GAME OVER** + +### **Example Format:** +```csv +What is your first choice?, East, 2, West, 3, North, 4, South, 5 +You chose East. A dense forest appears., Run forward, 6, Look around, 7 +You chose West. A river blocks your path., Try to swim, 8, Walk along the bank, 9 +You chose North. The path is blocked by rocks. +You chose South. A cave entrance looms ahead., Enter the cave, 10, Turn back, 11 +You went East and chose to run. The ground gives way and you fall. GAME OVER. +You went East and looked around. You see a hidden path., Follow it, 12, Stay put, 13 +``` + +--- + +## 📌 **Title Handling** +The **first line** of the file may contain a title in the format: +```csv +title="The Mysterious Forest" +``` +- If present, the game uses this as the **title**. +- If absent, the **filename** is used as the title. +- The second line will then be the **first story segment**. + +--- + +## 📌 **Rules for Formatting** +✅ **Each row starts with story text** +✅ **Choices appear as pairs** (`Choice Text, Target Line`) +✅ **Target Line must reference an existing line number** +✅ **A line without choices = GAME OVER** +✅ **Title (optional) must be in the first line as `title="Game Name"`** +✅ **File should be saved as `.csv` with UTF-8 encoding** + +--- + +## 📌 **Understanding the Flow** +1️⃣ The game **starts at line 1**. +2️⃣ The user selects a **numbered choice**. +3️⃣ The game jumps to the **corresponding line number**. +4️⃣ The game continues until: + - **No choices remain (GAME OVER)** + - **The player exits** (`X` command). + +--- + +## 📌 **Edge Cases & Notes** +⚠ **If an invalid line number is referenced**, the game may crash. +⚠ **A story segment must always be in column 1**, even if no choices follow. +⚠ **Extra spaces around text are automatically trimmed.** + +--- + +## 📌 **Final Notes** +By following this format, you can create **interactive, branching text adventures** without modifying code! 🎮🚀 + diff --git a/games/lost_forest.csv b/games/lost_forest.csv new file mode 100644 index 0000000..1665081 --- /dev/null +++ b/games/lost_forest.csv @@ -0,0 +1,8 @@ +title="The Lost Forest" +What is your first choice?, East, 2, West, 3, North, 4, South, 5 +You chose East. A dense forest appears., Run forward, 6, Look around, 7 +You chose West. A river blocks your path., Try to swim, 8, Walk along the bank, 9 +You chose North. The path is blocked by rocks. +You chose South. A cave entrance looms ahead., Enter the cave, 10, Turn back, 11 +You went East and chose to run. The ground gives way and you fall. GAME OVER. +You went East and looked around. You see a hidden path., Follow it, 12, Stay put, 13 diff --git a/games/testing.txt b/games/testing.txt new file mode 100644 index 0000000..ec6403e --- /dev/null +++ b/games/testing.txt @@ -0,0 +1 @@ +title="Test Game 9000" diff --git a/games/testnoname b/games/testnoname new file mode 100644 index 0000000..e69de29 diff --git a/message_processing.py b/message_processing.py index 80ece84..7288806 100644 --- a/message_processing.py +++ b/message_processing.py @@ -1,4 +1,5 @@ import logging +import os from meshtastic import BROADCAST_NUM @@ -8,11 +9,17 @@ handle_channel_directory_command, handle_channel_directory_steps, handle_send_mail_command, handle_read_mail_command, handle_check_mail_command, handle_delete_mail_confirmation, handle_post_bulletin_command, handle_check_bulletin_command, handle_read_bulletin_command, handle_read_channel_command, - handle_post_channel_command, handle_list_channels_command, handle_quick_help_command -) + handle_post_channel_command, handle_list_channels_command, handle_quick_help_command, handle_games_command, process_game_choice, + start_selected_game, handle_game_menu_selection + ) + +import command_handlers + +games_available = command_handlers.get_games_available(os.listdir('./games')) + from db_operations import add_bulletin, add_mail, delete_bulletin, delete_mail, get_db_connection, add_channel from js8call_integration import handle_js8call_command, handle_js8call_steps, handle_group_message_selection -from utils import get_user_state, get_node_short_name, get_node_id_from_num, send_message +from utils import get_user_state, get_node_short_name, get_node_id_from_num, send_message, update_user_state main_menu_handlers = { "q": handle_quick_help_command, @@ -31,6 +38,7 @@ utilities_menu_handlers = { + "g": handle_games_command, "s": handle_stats_command, "f": handle_fortune_command, "w": handle_wall_of_shame_command, @@ -53,6 +61,12 @@ "x": handle_help_command } +games_menu_handlers = { + "x": handle_help_command, +} +for i in range(1, len(games_available) + 1): + games_menu_handlers[str(i)] = lambda sender_id, interface, i=i: handle_game_menu_selection(sender_id, str(i), 1, interface, None) + def process_message(sender_id, message, interface, is_sync_message=False): state = get_user_state(sender_id) message_lower = message.lower().strip() @@ -90,6 +104,21 @@ def process_message(sender_id, message, interface, is_sync_message=False): channel_name, channel_url = parts[1], parts[2] add_channel(channel_name, channel_url) else: + # ✅ **Corrected IN_GAME Handling** + if state and state['command'] == 'IN_GAME': + message_lower = message.lower().strip() + + # Always check if the user wants to exit + if message_lower == "x": + send_message(f"Exiting '{state['game']}'... Returning to Games Menu.", sender_id, interface) + update_user_state(sender_id, {'command': 'GAMES', 'step': 1}) + return + + # Otherwise, process the game choice + process_game_choice(sender_id, message, interface, state) + return + + # 📌 Other menu processing remains unchanged if message_lower.startswith("sm,,"): handle_send_mail_command(sender_id, message_strip, interface, bbs_nodes) elif message_lower.startswith("cm"): @@ -109,6 +138,8 @@ def process_message(sender_id, message, interface, is_sync_message=False): handlers = bbs_menu_handlers elif menu_name == 'utilities': handlers = utilities_menu_handlers + elif menu_name == 'games': + handlers = games_menu_handlers else: handlers = main_menu_handlers elif state and state['command'] == 'BULLETIN_MENU': @@ -121,6 +152,9 @@ def process_message(sender_id, message, interface, is_sync_message=False): elif state and state['command'] == 'GROUP_MESSAGES': handle_group_message_selection(sender_id, message, state['step'], state, interface) return + elif state and state['command'] == 'GAMES': + handle_game_menu_selection(sender_id, message, state['step'], interface, state) + return else: handlers = main_menu_handlers @@ -176,6 +210,8 @@ def process_message(sender_id, message, interface, is_sync_message=False): handle_help_command(sender_id, interface) + + def on_receive(packet, interface): try: if 'decoded' in packet and packet['decoded']['portnum'] == 'TEXT_MESSAGE_APP': diff --git a/server.py b/server.py index 177f82f..444d23d 100644 --- a/server.py +++ b/server.py @@ -45,7 +45,7 @@ def display_banner(): ██║ ██║ ██╔═══╝ ╚════╝██╔══██╗██╔══██╗╚════██║ ██║ ╚██████╗███████╗ ██████╔╝██████╔╝███████║ ╚═╝ ╚═════╝╚══════╝ ╚═════╝ ╚═════╝ ╚══════╝ -Meshtastic Version +Meshtastic Version WITH GAMES """ print(banner) diff --git a/text.py b/text.py new file mode 100644 index 0000000..f992c61 --- /dev/null +++ b/text.py @@ -0,0 +1,10 @@ +import os + +print("Current Working Directory:", os.getcwd()) +file_path = "./games/lost_forest.csv" + +if os.path.exists(file_path): + print("✅ File exists! Python can detect it.") +else: + print("❌ File does NOT exist according to Python.") + diff --git a/validate_game.py b/validate_game.py new file mode 100644 index 0000000..857dd1f --- /dev/null +++ b/validate_game.py @@ -0,0 +1,122 @@ +import os + +def list_game_files(): + """Lists all game files in the ./games directory.""" + game_dir = "./games" + if not os.path.exists(game_dir): + print("❌ ERROR: 'games' directory does not exist.") + return [] + + game_files = [f for f in os.listdir(game_dir) if os.path.isfile(os.path.join(game_dir, f))] + + if not game_files: + print("❌ ERROR: No game files found in the './games' directory.") + return [] + + return game_files + + +def validate_game_file(file_path): + """Validates the format of a game CSV file.""" + + if not os.path.exists(file_path): + print(f"❌ ERROR: File '{file_path}' does not exist.") + return False + + with open(file_path, "r", encoding="utf-8") as file: + lines = file.readlines() + + if not lines: + print(f"❌ ERROR: File '{file_path}' is empty.") + return False + + # Check title format + first_line = lines[0].strip() + if first_line.lower().startswith("title="): + title = first_line.split("=", 1)[1].strip().strip('"') + print(f"✅ Title detected: {title}") + game_lines = lines[1:] # Skip title line + else: + print(f"⚠️ WARNING: No title detected. Using filename instead.") + game_lines = lines + + game_map = {} + valid_lines = set() + + for index, line in enumerate(game_lines, start=1): + parts = [p.strip() for p in line.strip().split(",")] + + if not parts or len(parts) < 1: + print(f"❌ ERROR: Line {index} is empty or improperly formatted.") + return False + + # First element is the story text + storyline = parts[0] + choices = parts[1:] + + # Validate choice pairs + if len(choices) % 2 != 0: + print(f"❌ ERROR: Line {index} has an uneven number of choices. Choices must be in pairs.") + return False + + # Validate choices mapping + for i in range(1, len(choices), 2): + choice_text = choices[i - 1] + try: + target_line = int(choices[i]) + valid_lines.add(target_line) + except ValueError: + print(f"❌ ERROR: Invalid mapping in line {index} ('{choice_text}' does not map to a valid number).") + return False + + # Store story segment + game_map[index] = (storyline, choices) + + # Validate that all mapped lines exist + missing_lines = valid_lines - set(game_map.keys()) + if missing_lines: + print(f"❌ ERROR: The following mapped lines do not exist: {sorted(missing_lines)}") + return False + + print(f"✅ Validation passed for '{file_path}'. No errors detected!") + return True + + +def main(): + """Lists games and asks user which to validate.""" + game_files = list_game_files() + + if not game_files: + return + + print("\nAvailable games for validation:") + for i, game in enumerate(game_files, start=1): + print(f"{i}. {game}") + print("A. Validate ALL games") + print("X. Exit") + + choice = input("\nSelect a game to validate (or 'A' for all, 'X' to exit): ").strip().lower() + + if choice == "x": + print("Exiting...") + return + elif choice == "a": + print("\n🔍 Validating all games...") + for game in game_files: + print(f"\n🔎 Validating {game}...") + validate_game_file(os.path.join("./games", game)) + else: + try: + game_index = int(choice) - 1 + if 0 <= game_index < len(game_files): + game_path = os.path.join("./games", game_files[game_index]) + print(f"\n🔎 Validating {game_files[game_index]}...") + validate_game_file(game_path) + else: + print("❌ ERROR: Invalid selection.") + except ValueError: + print("❌ ERROR: Invalid input. Please enter a number or 'A'/'X'.") + +if __name__ == "__main__": + main() + From 65c325a068648bba4114329bb800bb8e9c8b2620 Mon Sep 17 00:00:00 2001 From: Jonathan Hite Date: Thu, 6 Feb 2025 23:09:24 -0500 Subject: [PATCH 02/15] Updated Docker image --- docker/Dockerfile | 11 ++--- example_config.ini | 102 +++++++++++++++++++++++++++++++++++++++++++++ games/testing.txt | 1 - games/testnoname | 0 4 files changed, 108 insertions(+), 6 deletions(-) create mode 100644 example_config.ini delete mode 100644 games/testing.txt delete mode 100644 games/testnoname diff --git a/docker/Dockerfile b/docker/Dockerfile index 2f9f33e..4cf25b8 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -7,7 +7,8 @@ RUN apt-get update && \ && apt-get clean && rm -rf /var/lib/apt/lists/* # Clone the repository -RUN git clone https://github.com/TheCommsChannel/TC2-BBS-mesh.git +# RUN git clone https://github.com/TheCommsChannel/TC2-BBS-mesh.git +COPY ./ /home/mesh/bbs/ #### @@ -20,17 +21,17 @@ RUN mkdir -p /home/mesh/bbs WORKDIR /home/mesh/bbs # Install Python dependencies -COPY --from=build /TC2-BBS-mesh/requirements.txt ./ +COPY requirements.txt ./ RUN pip install --no-cache-dir --break-system-packages -r requirements.txt # Copy over app code -COPY --from=build /TC2-BBS-mesh/*.py ./ +COPY *.py ./ # Define config volume VOLUME /home/mesh/bbs/config WORKDIR /home/mesh/bbs/config -COPY --from=build /TC2-BBS-mesh/example_config.ini ./config.ini -COPY --from=build /TC2-BBS-mesh/fortunes.txt ./ +COPY example_config.ini ./config.ini +COPY fortunes.txt ./ # Define the command to run ENTRYPOINT [ "python3", "/home/mesh/bbs/server.py" ] diff --git a/example_config.ini b/example_config.ini new file mode 100644 index 0000000..3066b36 --- /dev/null +++ b/example_config.ini @@ -0,0 +1,102 @@ +############################### +#### Select Interface type #### +############################### +# [type = serial] for USB connected devices +#If there are multiple serial devices connected, be sure to use the "port" option and specify a port +# Linux Example: +# port = /dev/ttyUSB0 +# +# Windows Example: +# port = COM3 +# [type = tcp] for network connected devices (ESP32 devices only - this does not work for WisBlock) +# If using tcp, remove the # from the beginning and replace 192.168.x.x with the IP address of your device +# Example: +# [interface] +# type = tcp +# hostname = 192.168.1.100 + +[interface] +type = serial +# port = /dev/ttyACM0 +# hostname = 192.168.x.x + + +############################ +#### BBS NODE SYNC LIST #### +############################ +# Provide a list of other nodes running TC²-BBS to sync mail messages and bulletins with +# Enter in a list of other BBS Nodes by their nodeID separated by commas (no spaces) +# Example: +# [sync] +# bbs_nodes = !17d7e4b7,!18e9f5a3,!1a2b3c4d + +# [sync] +# bbs_nodes = !17d7e4b7 + + +############################ +#### Allowed Node IDs #### +############################ +# Provide a list of node IDs that are allowed to post to the urgent board. +# If this section is commented out, anyone can post to the urgent board. +# Example: +# [allow_list] +# allowed_nodes = 12345678,87654321 +# +# [allow_list] +# allowed_nodes = !17d7e4b7 + + +#################### +#### Menu Items #### +#################### +# Remove any menu items you don't plan on using with your BBS below +# +[menu] +# Default Main Menu options for reference +# [Q]uick Commands +# [B]BS +# [U]tilities +# E[X]IT +# +# Remove any menu items from the list below that you want to exclude from the main menu +main_menu_items = Q, B, U, X + +# Default BBS Menu options for reference +# [M]ail +# [B]ulletins +# [C]hannel Dir +# [J]S8CALL +# E[X]IT +# +# Remove any menu items from the list below that you want to exclude from the BBS menu +bbs_menu_items = M, B, C, J, X + +# Default Utilities Menu option for reference +# [S]tats +# [F]ortune +# [W]all of Shame +# E[X]IT +# +# Remove any menu items from the list below that you want to exclude from the utilities menu +utilities_menu_items = S, F, W, X + + +########################## +#### JS8Call Settings #### +########################## +# If you would like messages from JS8Call to go into the BBS, uncomment and enter in info below: +# host = the IP address for your system running JS8Call +# port = TCP API port for JS8CALL - Default is 2442 +# db_file = this can be left as the default "js8call.db" unless you need to change for some reason +# js8groups = the JS8Call groups you're interested in receiving into the BBS +# store_messages = "true" will send messages that arent part of a group into the BBS (can be noisy). "false" will ignore these +# js8urgent = the JS8Call groups you consider to be urgent - anything sent to these will have a notice sent to the +# group chat (similar to how the urgent bulletin board works +# [js8call] +# host = 192.168.1.100 +# port = 2442 +# db_file = js8call.db +# js8groups = @GRP1,@GRP2,@GRP3 +# store_messages = True +# js8urgent = @URGNT diff --git a/games/testing.txt b/games/testing.txt deleted file mode 100644 index ec6403e..0000000 --- a/games/testing.txt +++ /dev/null @@ -1 +0,0 @@ -title="Test Game 9000" diff --git a/games/testnoname b/games/testnoname deleted file mode 100644 index e69de29..0000000 From 416dec08222f1c72ffeb110fd104f438e520df79 Mon Sep 17 00:00:00 2001 From: Jonathan Hite Date: Thu, 6 Feb 2025 23:31:39 -0500 Subject: [PATCH 03/15] Attempt at Docker update --- docker/Dockerfile | 50 +++++++++++++++++++++++++++-------------------- 1 file changed, 29 insertions(+), 21 deletions(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index 4cf25b8..ee4ca42 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,37 +1,45 @@ -# From original Dockerfile at https://github.com/TheCommsChannel/TC2-BBS-mesh +# First stage: Base build environment FROM debian:stable-slim AS build +# Install required dependencies RUN apt-get update && \ - apt-get install -y \ - git \ - && apt-get clean && rm -rf /var/lib/apt/lists/* + apt-get install -y git && \ + apt-get clean && rm -rf /var/lib/apt/lists/* -# Clone the repository -# RUN git clone https://github.com/TheCommsChannel/TC2-BBS-mesh.git -COPY ./ /home/mesh/bbs/ +# Ensure the 'games' directory exists +RUN mkdir -p /home/mesh/bbs/games -#### +# Set working directory +WORKDIR /home/mesh/bbs + +# Copy project files including 'games' directory +COPY . /home/mesh/bbs/ -FROM --platform=$BUILDPLATFORM python:alpine +# Second stage: Final runtime environment +FROM python:alpine -# Switch to non-root user +# Add a non-root user for security RUN adduser --disabled-password mesh -USER mesh + +# Ensure working directory RUN mkdir -p /home/mesh/bbs WORKDIR /home/mesh/bbs +# Copy files from the build stage +COPY --from=build /home/mesh/bbs /home/mesh/bbs/ + # Install Python dependencies -COPY requirements.txt ./ RUN pip install --no-cache-dir --break-system-packages -r requirements.txt -# Copy over app code -COPY *.py ./ +# Ensure the 'games' directory exists in runtime container +RUN mkdir -p /home/mesh/bbs/games + +# Set user to mesh +USER mesh + +# Expose necessary ports +EXPOSE 8080 -# Define config volume -VOLUME /home/mesh/bbs/config -WORKDIR /home/mesh/bbs/config -COPY example_config.ini ./config.ini -COPY fortunes.txt ./ +# Start the server +CMD ["python", "server.py"] -# Define the command to run -ENTRYPOINT [ "python3", "/home/mesh/bbs/server.py" ] From 91d31fc6f89e650477437f655c4811907af1e544 Mon Sep 17 00:00:00 2001 From: Jonathan Hite Date: Fri, 7 Feb 2025 00:19:03 -0500 Subject: [PATCH 04/15] Fix Readmes --- README.md | 2 +- gamefile_syntax.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index a08a828..83b4ccf 100644 --- a/README.md +++ b/README.md @@ -221,7 +221,7 @@ The following device roles have been working: - **Statistics**: View statistics about nodes, hardware, and roles. - **Wall of Shame**: View devices with low battery levels. - **Fortune Teller**: Get a random fortune. Pulls from the fortunes.txt file. Feel free to edit this file remove or add more if you like. -- **GAMES**: GAMES!!! BBSs have to have games. You can make and add your own! Games should be written in python. The Games Menu gets the names from the GameTitle variable. (e.g. GameTitle="A Brave Adventure") in the game.py file. +- **GAMES**: GAMES!!! BBSs have to have games. You can make and add your own! Games should be written in a CSV format. The Games Menu gets the names from the Title variable. (e.g. Title="A Brave Adventure") in the game file. See gamefile_syntax.md for more information. ## Usage diff --git a/gamefile_syntax.md b/gamefile_syntax.md index d087a57..822824a 100644 --- a/gamefile_syntax.md +++ b/gamefile_syntax.md @@ -55,7 +55,7 @@ title="The Mysterious Forest" --- ## 📌 **Edge Cases & Notes** -⚠ **If an invalid line number is referenced**, the game may crash. +⚠ **If an invalid line number is referenced**, the game will crash by having the player fall into an abyss. ⚠ **A story segment must always be in column 1**, even if no choices follow. ⚠ **Extra spaces around text are automatically trimmed.** From 34704013bd2c516ae57a4a15ecc90bd14e74dfc0 Mon Sep 17 00:00:00 2001 From: Jonathan Hite Date: Fri, 7 Feb 2025 10:19:10 -0500 Subject: [PATCH 05/15] Adjusted the way game_title was displayed for games without a title line. Added clarification to the gamefile_syntax.md. --- .gitignore | 1 + command_handlers.py | 3 +-- gamefile_syntax.md | 5 +++++ games/lost_forest_no_title | 7 +++++++ 4 files changed, 14 insertions(+), 2 deletions(-) create mode 100644 games/lost_forest_no_title diff --git a/.gitignore b/.gitignore index 739b0d7..e451507 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ venv/ .idea config.ini fortunes.txt +offline_game_tester.py diff --git a/command_handlers.py b/command_handlers.py index 9f9b8ce..ce2259b 100644 --- a/command_handlers.py +++ b/command_handlers.py @@ -806,7 +806,6 @@ def start_selected_game(sender_id, interface, state): # Present the first segment present_story_segment(sender_id, interface, new_state) # ✅ Pass updated state - def load_game_map(file_path): """Loads a game map from a CSV file and returns its structured format.""" @@ -828,7 +827,7 @@ def load_game_map(file_path): game_title = first_line.split("=", 1)[1].strip().strip('"') game_lines = lines[1:] # Skip title else: - game_title = file_path # Use filename if no title + game_title = os.path.splitext(os.path.basename(file_path))[0] # Use filename without path/extension game_lines = lines print(f"DEBUG: Game title detected -> {game_title}") diff --git a/gamefile_syntax.md b/gamefile_syntax.md index 822824a..6c2d5cf 100644 --- a/gamefile_syntax.md +++ b/gamefile_syntax.md @@ -31,6 +31,8 @@ title="The Mysterious Forest" - If present, the game uses this as the **title**. - If absent, the **filename** is used as the title. - The second line will then be the **first story segment**. +- If present, the game will consider the title line as line 0. +- Line 1 is always the first story segment. --- @@ -63,4 +65,7 @@ title="The Mysterious Forest" ## 📌 **Final Notes** By following this format, you can create **interactive, branching text adventures** without modifying code! 🎮🚀 +Remember, if using a title in the first line you need to offset your line mapping by -1. It's easiest to write +the story and mapping first, then adding the title after you're done. If you forget to offset your line numbers, +a simple script to increment each choice number by -1 will fix it for you! diff --git a/games/lost_forest_no_title b/games/lost_forest_no_title new file mode 100644 index 0000000..8aa02c0 --- /dev/null +++ b/games/lost_forest_no_title @@ -0,0 +1,7 @@ +What is your first choice?, East, 2, West, 3, North, 4, South, 5 +You chose East. A dense forest appears., Run forward, 6, Look around, 7 +You chose West. A river blocks your path., Try to swim, 8, Walk along the bank, 9 +You chose North. The path is blocked by rocks. +You chose South. A cave entrance looms ahead., Enter the cave, 10, Turn back, 11 +You went East and chose to run. The ground gives way and you fall. GAME OVER. +You went East and looked around. You see a hidden path., Follow it, 12, Stay put, 13 \ No newline at end of file From dfeea2d36f592191b0fbefd93394410b8fa7cae5 Mon Sep 17 00:00:00 2001 From: Jonathan Hite Date: Fri, 7 Feb 2025 13:25:27 -0500 Subject: [PATCH 06/15] Fixed an issue with Game Over states not being correctly detected/handled. --- command_handlers.py | 89 ++++++++++++++++++------------------- games/lost_forest_no_title | 7 --- games/the_shortest_game.csv | 12 +++++ 3 files changed, 55 insertions(+), 53 deletions(-) delete mode 100644 games/lost_forest_no_title create mode 100644 games/the_shortest_game.csv diff --git a/command_handlers.py b/command_handlers.py index ce2259b..def5ec7 100644 --- a/command_handlers.py +++ b/command_handlers.py @@ -701,40 +701,34 @@ def get_games_available(game_files): def handle_games_command(sender_id, interface): """Handles the Games Menu and lists available text-based games.""" - - # Find files in ./games that: - # - Have a .txt or .csv extension - # - OR have no extension + game_files = [ f for f in os.listdir('./games') if os.path.isfile(os.path.join('./games', f)) and (f.endswith('.txt') or f.endswith('.csv') or '.' not in f) ] games_available = get_games_available(game_files) - if not games_available: send_message("No games available yet. Come back soon.", sender_id, interface) update_user_state(sender_id, {'command': 'UTILITIES', 'step': 1}) return None - # Store game filenames in state to avoid title-related issues game_titles = list(games_available.keys()) # Display titles game_filenames = list(games_available.values()) # Actual filenames - - # Include exit option + numbered_games = "\n".join(f"{i+1}. {title}" for i, title in enumerate(game_titles)) numbered_games += "\n[X] Exit" response = f"🎮 Games Menu 🎮\nWhich game would you like to play?\n{numbered_games}" send_message(response, sender_id, interface) + # ✅ Ensure `games` state is always reset when displaying the menu update_user_state(sender_id, {'command': 'GAMES', 'step': 1, 'games': game_filenames, 'titles': game_titles}) return response - def handle_game_menu_selection(sender_id, message, step, interface, state): """Handles the user's selection of a game from the Games Menu, allowing exit with 'X' and starting immediately.""" @@ -750,6 +744,9 @@ def handle_game_menu_selection(sender_id, message, step, interface, state): if 0 <= game_index < len(games_available): selected_game = games_available[game_index] + # Reset user state to ensure a clean start + update_user_state(sender_id, None) + # Update state to indicate the user is now in-game update_user_state(sender_id, {'command': 'IN_GAME', 'step': 3, 'game': selected_game}) @@ -761,6 +758,7 @@ def handle_game_menu_selection(sender_id, message, step, interface, state): except ValueError: send_message("Invalid input. Please enter a number corresponding to a game or 'X' to exit.", sender_id, interface) + def start_selected_game(sender_id, interface, state): """Starts the game selected by the user and ensures title detection.""" @@ -844,24 +842,37 @@ def load_game_map(file_path): print(f"❌ ERROR inside load_game_map(): {e}") return None - def present_story_segment(sender_id, interface, state): """Presents the current segment of the game and available choices.""" game_name = state.get('game') - game_title = state.get('game_title', "Unknown Game") # ✅ Prevent KeyError + game_title = state.get('game_title', "Unknown Game") game_map = state.get('game_map', {}) game_position = state.get('game_position', 1) if game_position not in game_map: send_message("Error: Invalid game state.", sender_id, interface) update_user_state(sender_id, {'command': 'GAMES', 'step': 1}) + handle_games_command(sender_id, interface) return # Retrieve the current story segment segment = game_map[game_position] storyline = segment[0] - choices = segment[1:] + choices = segment[1:] # Extract choices + + # 🛠️ **Check if this is a game-over state (no choices)** + if not choices: + send_message(f"🎮 {game_title} 🎮\n\n{storyline}\n\n💀 GAME OVER! Returning to the game menu...", sender_id, interface) + + # Reset user state before returning to menu + update_user_state(sender_id, {'command': 'GAMES', 'step': 1}) + + import time + time.sleep(1) # Ensure the message is processed before switching menus + + handle_games_command(sender_id, interface) + return # Build response message response = f"🎮 {game_title} 🎮\n\n{storyline}\n\n" @@ -872,16 +883,17 @@ def present_story_segment(sender_id, interface, state): send_message(response, sender_id, interface) - # Update user state to track the current game progress + # Ensure user state is properly tracked update_user_state(sender_id, { 'command': 'IN_GAME', 'step': 3, 'game': game_name, - 'game_title': game_title, # ✅ Ensure it stays in state + 'game_title': game_title, 'game_map': game_map, 'game_position': game_position }) + def process_game_choice(sender_id, message, interface, state): """Processes the player's choice and advances the game.""" @@ -891,65 +903,50 @@ def process_game_choice(sender_id, message, interface, state): if game_position not in game_map: send_message("Error: Invalid game state.", sender_id, interface) update_user_state(sender_id, {'command': 'GAMES', 'step': 1}) + handle_games_command(sender_id, interface) return segment = game_map[game_position] # Extract the storyline and choices - storyline = segment[0] # First element is the story text - choices = segment[1:] # Remaining elements are choices + storyline = segment[0] + choices = segment[1:] - # Ensure choices are properly formatted (must be in pairs) + # Ensure choices are properly formatted if len(choices) % 2 != 0: send_message("Error: Game data is corrupted.", sender_id, interface) update_user_state(sender_id, {'command': 'GAMES', 'step': 1}) + handle_games_command(sender_id, interface) return # Handle Exit if message.lower() == "x": send_message(f"Exiting '{state['game_title']}'... Returning to Games Menu.", sender_id, interface) update_user_state(sender_id, {'command': 'GAMES', 'step': 1}) - handle_games_command(sender_id, interface) # Immediately display the game menu + handle_games_command(sender_id, interface) return try: - # Convert user input to index (1-based to 0-based) choice_index = int(message) - 1 - # Validate choice selection if choice_index < 0 or choice_index * 2 + 1 >= len(choices): send_message("Invalid selection. Please enter a valid number.", sender_id, interface) return - # Retrieve the target position for the chosen option target_position = int(choices[choice_index * 2 + 1]) - # Check if the target position exists if target_position not in game_map: - send_message("💀 Game Over! You fell into an abyss. 💀", sender_id, interface) + send_message("💀 GAME OVER! No further choices available. 💀 Returning to the game menu...", sender_id, interface) update_user_state(sender_id, {'command': 'GAMES', 'step': 1}) - handle_games_command(sender_id, interface) # Return to game menu + handle_games_command(sender_id, interface) return - # Update state with the new game position - update_user_state(sender_id, { - 'command': 'IN_GAME', - 'step': 3, - 'game': state['game'], - 'game_title': state['game_title'], - 'game_map': game_map, - 'game_position': target_position - }) - - # Present the new story segment - present_story_segment(sender_id, interface, { - 'command': 'IN_GAME', - 'step': 3, - 'game': state['game'], - 'game_title': state['game_title'], - 'game_map': game_map, - 'game_position': target_position - }) - - except (ValueError, IndexError): - send_message("Invalid selection. Please enter a valid number.", sender_id, interface) + # ✅ FIX: Pass `state` instead of `update_user_state` + state['game_position'] = target_position + update_user_state(sender_id, state) + + # ✅ FIX: Pass the correct `state` variable, NOT `update_user_state` + present_story_segment(sender_id, interface, state) + + except ValueError: + send_message("Invalid input. Please enter a valid number.", sender_id, interface) diff --git a/games/lost_forest_no_title b/games/lost_forest_no_title deleted file mode 100644 index 8aa02c0..0000000 --- a/games/lost_forest_no_title +++ /dev/null @@ -1,7 +0,0 @@ -What is your first choice?, East, 2, West, 3, North, 4, South, 5 -You chose East. A dense forest appears., Run forward, 6, Look around, 7 -You chose West. A river blocks your path., Try to swim, 8, Walk along the bank, 9 -You chose North. The path is blocked by rocks. -You chose South. A cave entrance looms ahead., Enter the cave, 10, Turn back, 11 -You went East and chose to run. The ground gives way and you fall. GAME OVER. -You went East and looked around. You see a hidden path., Follow it, 12, Stay put, 13 \ No newline at end of file diff --git a/games/the_shortest_game.csv b/games/the_shortest_game.csv new file mode 100644 index 0000000..20665f8 --- /dev/null +++ b/games/the_shortest_game.csv @@ -0,0 +1,12 @@ +title="The Shortest Game" +You awaken at a crossroads. Which way will you go?, North, 2, East, 3, South, 4, West, 5 +You chose north. There was a cottage. Will you:, Explore it, 6, Keep going, 7 +You chose East. The way is dark. What will you do?, Keep going, 8, Light a fire, 9 +You chose South. You walked for a mile and found nothing. +You chose West. There is a man on the path., Ignore him, 10, Talk to him, 11 +You fell through the floor of the cottage. +Just beyond the cottage you see a village full of people. They seem to recognize you. You make this your new home. +You are brave. After a short walk through the dark forest you find a clearing you recognize. You know the way home. +Marauders spot your fire in the night. You don't wake again. +The man snickers as you pass. Later down the trail you are robbed. +The man tells you where you are and how to get back home. \ No newline at end of file From 5f1fe93c268e8760b15168a254dfcf40183867f7 Mon Sep 17 00:00:00 2001 From: Jonathan Hite Date: Fri, 7 Feb 2025 17:01:00 -0500 Subject: [PATCH 07/15] Added a GPT generated game. --- README.md | 2 +- games/forgotten_ruins_gpt.csv | 471 ++++++++++++++++++++++++++++++++++ 2 files changed, 472 insertions(+), 1 deletion(-) create mode 100644 games/forgotten_ruins_gpt.csv diff --git a/README.md b/README.md index 83b4ccf..15e36c4 100644 --- a/README.md +++ b/README.md @@ -221,7 +221,7 @@ The following device roles have been working: - **Statistics**: View statistics about nodes, hardware, and roles. - **Wall of Shame**: View devices with low battery levels. - **Fortune Teller**: Get a random fortune. Pulls from the fortunes.txt file. Feel free to edit this file remove or add more if you like. -- **GAMES**: GAMES!!! BBSs have to have games. You can make and add your own! Games should be written in a CSV format. The Games Menu gets the names from the Title variable. (e.g. Title="A Brave Adventure") in the game file. See gamefile_syntax.md for more information. +- **GAMES**: GAMES!!! BBSs have to have games. You can make and add your own! Games should be written in a CSV format. The Games Menu gets the names from the title variable. (e.g. title="A Brave Adventure") in the game file. See gamefile_syntax.md for more information. ## Usage diff --git a/games/forgotten_ruins_gpt.csv b/games/forgotten_ruins_gpt.csv new file mode 100644 index 0000000..dd00c78 --- /dev/null +++ b/games/forgotten_ruins_gpt.csv @@ -0,0 +1,471 @@ +title="The Forgotten Ruins - GPT Generated" +You stand before an ancient temple. Vines creep over its stone walls. The entrance is dark., Enter the temple, 2, Walk away, 3 +You step inside. A hallway stretches ahead. Torches flicker on damp stone walls., Move forward, 4, Examine the walls for clues, 5 +You decide it's not worth the risk. As you turn to leave a strong gust slams the entrance shut behind you., Look for another way out, 6, Call for help, 7 +The hallway splits into two paths—one veiled in shadow and the other dimly lit by glowing stones., Take the shadowed path, 8, Follow the glowing stones, 9 +The walls bear intricate carvings that depict a forgotten civilization. Some symbols seem familiar., Trace your fingers over the symbols, 10, Ignore them and keep moving, 11 +A spectral figure emerges from the darkness whispering in a long-lost tongue., Listen to its warning, 12, Run before it notices you, 13 +The whisper fades but the air thickens pressing against you. The temple will not let you leave! +The right path leads to a deep pit covered in strange moss., Try to jump over it, 14, Look for another way around, 15 +The left path descends into a spiral staircase leading to a subterranean chamber., Descend the staircase, 16, Return to the surface, 17 +A hidden door slides open revealing a passage filled with shifting shadows., Step inside, 18, Avoid it and continue forward, 19 +You move forward but the path loops back to the same chamber as before., Realize the loop, 20, Try a different passage, 21 +A pedestal holds an ancient rusted key covered in dried blood., Take the key, 22, Leave it alone, 23 +You hesitate too long and the pedestal collapses. The room trembles as the ceiling begins to fall! +You leap across the pit and land on the other side but a creaking sound echoes behind you., Move forward quickly, 24, Check the pit's edge, 25 +You find an ancient plank—its wood strangely intact., Place the plank over the pit, 26, Leave it and look for another path, 27 +The chamber ahead is lined with stone statues and their eyes seem to follow your every move., Examine them closely, 28, Walk quickly through the chamber, 29 +A narrow tunnel winds downward with an eerie blue light glowing from below., Enter carefully, 30, Retreat and find another way, 31 +The rusted key feels heavy in your hand as if resisting your grip., Hold onto it tightly, 32, Try using it on a nearby door, 33 +The chamber ahead holds an ornate idol embedded with gemstones., Take the idol, 34, Leave it untouched, 35 +As you touch the idol the statues surrounding you animate—their hollow eyes burning with blue fire., Try to reason with them, 36, Prepare to fight, 37 +The statues whisper an ancient vow and step aside allowing you passage., Move forward, 38, Ask them what they guard, 39 +The key fits perfectly into a locked door which swings open with an eerie groan., Step inside, 40, Hesitate and look around first, 41 +The idol shatters in your hands releasing a deep vibration through the chamber., Read the inscription on the pedestal, 42, Drop the pieces and flee, 43 +The statues kneel before you awaiting your command., Attempt to communicate, 44, Move cautiously past them, 45 +They lunge with inhuman speed—their stone hands crushing everything in their path! +The inscription tells of an ancient being seeking freedom., Speak the words aloud, 46, Ignore the warning and leave, 47 +The door leads to a grand hall filled with treasure but something watches from the darkness., Take a few treasures, 48, Search for an exit, 49 +The moment you touch the gold shadows rise and consume you! +The hall holds a hidden staircase leading upward., Ascend the staircase, 50, Stay and investigate further, 51 +A ghostly voice pleads with you to break a long-forgotten seal., Ask it why, 52, Refuse and move on, 53 +A chill fills the air as whispers swirl around you., Try to decipher the whispers, 54, Cover your ears and run, 55 +A massive stone tablet holds an inscription that pulses with magic., Read it aloud, 56, Ignore it and move forward, 57 +The air quakes as the ruins themselves begin to shake., Escape as quickly as possible, 58, Stay and see what happens, 59 +The ghost sighs and fades away leaving you in silence., Search for another path, 60, Exit the ruins, 61 +A distant voice echoes your call. Help is coming but can you trust them?, Wait for them, 62, Climb down the ruins yourself, 63 +You descend cautiously—each step sending dust spiraling into the air., Keep moving, 64, Rest for a moment, 65 +The ruins begin to crumble with large chunks of stone crashing down., Run for safety, 66, Hide in an alcove, 67 +You are crushed beneath the collapsing ruins! +You barely escape as the ruins collapse behind you., Celebrate your survival, 68, Look back one last time, 69 +The ruins settle into silence once more., Walk away, 70, Search the rubble for secrets, 71 +Beneath the rubble you discover a concealed staircase leading deeper underground., Descend further, 72, Turn away and leave, 73 +The hidden chamber is covered in murals of a lost empire—their eyes seem to watch you., Study the murals, 74, Search the chamber for artifacts, 75 +A hooded figure stands at the entrance of an old temple., Approach it, 76, Hide and observe, 77 +The figure whispers of a great power buried deep within., Follow it into the depths, 78, Demand answers, 79 +You enter a sacred sanctum filled with glowing relics., Examine the relics, 80, Search for a way out, 81 +The relics pulse with an eerie glow humming with power., Touch one, 82, Leave them untouched, 83 +A great stone guardian awakens from its slumber., Stand your ground, 84, Try to flee, 85 +The guardian watches you closely waiting., Speak to it, 86, Attack it, 87 +It nods slowly and gestures to an ancient door., Enter the door, 88, Stay where you are, 89 +Beyond the door lies an ancient throne room—its walls etched with glowing runes., Sit on the throne, 90, Inspect the runes, 91 +As you sit the room trembles. A deep voice echoes in your mind., Embrace your destiny, 92, Resist the power, 93 +The runes reveal the temple's hidden purpose—to trap those who seek its power., Try to break the cycle, 94, Accept your fate, 95 +A secret passage is revealed behind the throne., Enter the passage, 96, Remain in the throne room, 97 +The passage leads to an underground vault filled with ancient scrolls., Read the scrolls, 98, Search for an exit, 99 +The scrolls describe a powerful ritual to unseal the temple's magic., Perform the ritual, 100, Leave the vault, 101 +The ritual causes the ruins to quake violently., Escape immediately, 102, Stay and witness the change, 103 +A portal forms before you swirling with unknown energy., Step through the portal, 104, Destroy it, 105 +The portal transports you to a strange new world. Your journey is far from over! +The portal shatters and the ruins fall silent once more. You walk away forever changed! +The ruins tremble as the energy dissipates but something stirs within the darkness., Investigate the disturbance, 106, Flee while you still can, 107 +A towering figure emerges from the shadows—its form shifting like smoke and fire., Speak to the entity, 108, Attack it before it can strike, 109 +The entity's voice echoes within your mind offering knowledge in exchange for devotion., Accept the knowledge, 110, Refuse and demand answers, 111 +The knowledge floods your mind revealing ancient secrets lost to time., Use the knowledge to reshape the ruins, 112, Try to escape with what you have learned, 113 +The entity roars in anger at your defiance—the ruins collapsing around you., Run for your life, 114, Stand your ground, 115 +The ruins shake violently as the entity unleashes its wrath., Attempt to contain its power, 116, Let the destruction unfold, 117 +You harness the energy from the ruins—reforging them into something new., Ascend as the temple's new master, 118, Seal the magic away forever, 119 +The ground crumbles beneath your feet as the ruins give way., Leap to safety, 120, Try to stabilize the structure, 121 +You run as the temple collapses but the exit is far away., Make a final sprint, 122, Look for another escape route, 123 +The walls close in and the ceiling cracks above you., Dodge the falling debris, 124, Take shelter in an alcove, 125 +You stand firm against the chaos calling upon the temple’s power., Absorb the energy, 126, Resist and shield yourself, 127 +You barely manage to escape the collapse—finding yourself outside the temple as it sinks into the earth., Watch in awe, 128, Search for survivors, 129 +The energy you absorbed begins to change you., Embrace the transformation, 130, Fight against it, 131 +With great effort you stabilize the structure but at a great cost to yourself., Accept your fate and remain as guardian, 132, Collapse from exhaustion, 133 +The temple now reshaped by your will stands as a beacon of forgotten power., Welcome seekers of knowledge, 134, Guard the temple against intruders, 135 +The ruins finally settle with their magic fading into history., Leave the site behind, 136, Record your findings for future generations, 137 +The once-great temple is lost beneath the sands of time but its whispers remain., A new era begins, 138, The story ends here! +Your name is forever inscribed in history as the last one to witness the temple's power. Fade into legend! +The debris falls and you barely dodge out of the way., Sprint towards an opening, 139, Try to push through, 140 +The alcove provides momentary safety but the air grows thin., Search for a hidden passage, 141, Wait and conserve energy, 142 +The transformation consumes you—reshaping you into something no longer human., Accept your new form, 143, Fight against the corruption, 144 +The energy pulses violently causing instability in your body., Channel it into the ruins, 145, Attempt to purge it from yourself, 146 +You collapse from exhaustion but the ruins now stand strong once more., A new guardian rises, 147, The temple's power is sealed forever! +The seekers arrive drawn to the beacon of your presence., Welcome them as disciples, 148, Warn them to stay away, 149 +The world forgets the ruins leaving only myths behind. The story fades into obscurity! +History remembers your actions and the knowledge is passed on. A new era of discovery begins! +A great storm rises burying the ruins deeper under time itself. The cycle begins anew! +You sprint towards the opening but the path is unstable., Jump across a crumbling ledge, 150, Look for a safer route, 151 +You push through the collapsing debris but your strength is failing., Use a burst of energy, 152, Crawl through a narrow gap, 153 +You discover a hidden passage within the alcove., Enter the passage, 154, Try to reinforce the structure, 155 +The passage twists and turns leading deeper underground., Continue forward, 156, Mark your path and explore carefully, 157 +Your form is no longer recognizable as human., Embrace the chaos, 158, Search for a way to reverse the change, 159 +The corruption fights back as you struggle to retain control., Let go and evolve, 160, Use sheer willpower to resist, 161 +The energy stabilizes within you making you one with the temple., Ascend as a godlike being, 162, Banish yourself to prevent disaster, 163 +Your presence lingers in the ruins—an eternal guardian bound by fate. Watch over the temple for eternity! +The energy within the temple surges violently sealing all within. The ruins vanish from existence! +You leap across the crumbling ledge barely making it to the other side., Keep running, 164, Stop to catch your breath, 165 +The unstable ground beneath you gives way as you search for safety., Grab onto the nearest rock, 166, Try to roll with the collapse, 167 +The burst of energy propels you forward but your body weakens., Push forward despite the strain, 168, Stop and recover, 169 +Crawling through the narrow gap you emerge into an ancient chamber., Investigate the chamber, 170, Look for another exit, 171 +The passage winds deeper revealing an ancient mechanism buried in dust., Activate the mechanism, 172, Ignore it and keep moving, 173 +A mysterious light flickers ahead illuminating ancient symbols., Study the symbols, 174, Proceed cautiously, 175 +You feel the corruption spreading overtaking your thoughts., Let it consume you, 176, Resist with all your will, 177 +The temple itself seems to recognize your transformation., Embrace its gift, 178, Reject it and seek an escape, 179 +The power within you reaches its peak reshaping reality around you., Step into the new world, 180, Attempt to undo the transformation, 181 +You keep running as the ruins crumble behind you until you reach an open plateau., Look for a way down, 182, Rest and take in the sight, 183 +Pausing to catch your breath you feel the ground shaking beneath you., Brace for impact, 184, Attempt to run again, 185 +Grabbing onto the nearest rock you barely hold on as the ground collapses beneath you., Climb up, 186, Let go and drop down, 187 +Rolling with the collapse you find yourself in a deep underground cavern., Explore the cavern, 188, Search for a way out, 189 +The mechanism hums as ancient gears turn revealing a hidden door., Step through the door, 190, Disable the mechanism, 191 +Ignoring the mechanism you push forward into the unknown., Continue into darkness, 192, Try to map your path, 193 +The symbols glow brighter as you study them filling you with knowledge., Use the knowledge to navigate, 194, Step away before it's too late, 195 +Proceeding cautiously you feel an unseen force guiding you., Follow the force, 196, Resist and take another path, 197 +The corruption overtakes you merging your existence with the ruins. Become one with the temple! +You fight with all your will pushing back against the corruption., Succeed in purging it, 198, Fail and succumb! +The temple's gift fills you with power beyond mortal comprehension. Ascend as a godlike entity! +Rejecting the gift you break free from the temple's grasp., Escape into the outside world, 199 +Stepping into the new world you find yourself in a realm beyond time. Accept your new reality! +Attempting to undo the transformation you struggle as reality twists around you., Succeed and return to normal, 200, Fail and be lost forever! +You find a way down from the plateau arriving at a forgotten civilization., Discover their secrets, 201, Seek shelter and recover, 202 +Your body reverts the corruption fading away as you awaken in the ruins., Reflect on your journey, 203, Search for a way home, 204 +You remain trapped between worlds a forgotten specter wandering the temple's depths! +The civilization welcomes you as a lost traveler revealing ancient wisdom., Embrace their teachings, 205, Ask to leave and return to your world, 206 +You take shelter regaining your strength but the ruins still call to you., Return to explore, 207, Depart and leave it all behind, 208 +The ruins' stories now flow through you their mysteries forever imprinted in your soul! +You discover an ancient portal pulsing with strange energy., Step through, 209, Destroy it to sever the connection, 210 +The portal leads to a time before the ruins fell giving you a chance to alter history., Change the past, 211, Observe and learn without interference, 212 +Destroying the portal severs the temple's hold on reality causing it to collapse entirely! +Your choice rewrites history shaping a new path unknown to all! +Observing history reveals the inevitable cycle of rise and fall. Accept your place as a mere witness! +Reflecting on your journey you realize the temple's power must remain forgotten. Leave the ruins in silence! +Finding a way home you step back into the familiar world forever changed. Move forward with newfound wisdom! +Embracing the teachings of the civilization you become one of them shaping their future. A new legacy begins! +Returning to your world you find that nothing feels the same. Carry the burden of knowledge! +Exploring the ruins once more you uncover one final secret that should have remained buried. Release the ancient power! +Departing you turn your back on the past letting the ruins fade from memory. Some mysteries are best left unsolved! +Changing the past sets the course of time in motion once more but with unforeseen consequences. A new cycle begins! +Observing history teaches you that some things are inevitable. Accept fate and let time take its course! +With the final choices resolved the forgotten ruins fall silent their mysteries buried forever. The story ends! +You sprint towards the opening barely avoiding falling debris., Keep running, 213, Try to find stable ground, 214 +You push through feeling the ruins tremble beneath you. A final leap brings you to safety! +The ground steadies under your feet but a dark passage beckons ahead., Enter the passage, 215, Turn back, 216 +You find an ancient chamber untouched by time its walls lined with forgotten texts., Read the texts, 217, Search for another exit, 218 +You turn back but the temple collapses behind you cutting off your escape! +The texts reveal knowledge of the lost civilization forever changing your perspective. Preserve the knowledge! +You push deeper into the ruins discovering a hidden stairway., Descend the stairway, 219, Leave before it's too late, 220 +The stairway leads to a massive underground vault filled with ancient relics., Examine the relics, 221, Search for an exit, 222 +You retreat barely escaping as the ruins collapse behind you! +The relics glow faintly whispering forgotten secrets., Absorb their power, 223, Leave them untouched, 224 +A hidden door swings open revealing a path to the surface. Step through the door! +Power surges through you binding you to the ruins forever. Become the temple's guardian! +You resist temptation leaving the relics behind as you escape! +The transformation completes binding you to the temple as its eternal guardian! +You fight against the energy with sheer willpower. The temple trembles rejecting your presence., Escape before it’s too late, 225, Hold your ground and face the consequences, 226 +You flee as the temple collapses behind you barely making it out alive! +The temple’s wrath is swift. Darkness engulfs you and you become another forgotten soul within its halls! +The stairway twists endlessly leading deeper into the ruins., Continue descending, 227, Turn back, 228 +A vast underground lake stretches before you glowing faintly., Investigate the lake, 229, Search the cave for an exit, 230 +As you turn back the path behind you vanishes., Trapped you must press forward. Go to 229 +The water is impossibly still reflecting an unfamiliar sky., Step onto the surface, 231, Reach into the water, 232 +A narrow tunnel leads further into the ruins revealing ancient inscriptions., Study the inscriptions, 233, Ignore them and move on, 234 +The water solidifies beneath you and you find yourself walking across a new reality! +A hand grasps yours from the depths pulling you under! +The inscriptions glow as you decipher them revealing lost knowledge., Use the knowledge to navigate, 235, Close the book and leave, 236 +You press on blindly but the ruins shift around you. Lost you vanish into the temple’s depths! +The knowledge guides you safely to an exit allowing you to escape! +Without understanding the warnings you wander into a collapsing chamber! +The passage narrows forcing you to crawl forward., Keep moving, 237, Turn back and find another route, 238 +The tunnel opens into a grand chamber where a forgotten deity stirs., Approach the deity, 239, Find another exit, 240 +The tunnel collapses behind you forcing you to move forward., Go to 239 +The deity’s gaze pierces you unraveling your mind., Accept its gift, 241, Refuse and flee, 242 +You rush for another exit but the temple seals you within forever! +Power surges through you elevating you beyond mortality., Ascend as a godlike being, 243, Resist the transformation, 244 +The deity’s wrath consumes you leaving only whispers in the ruins! +The transformation is complete. You are no longer bound by mortal limits. Embrace your new existence! +You fight the transformation but the effort drains you completely! +You move cautiously sensing an unseen presence around you., Call out to it, 245, Stay silent and observe, 246 +A voice answers ancient and distant. It offers you a choice., Accept its guidance, 247, Reject it and run, 248 +Shadows coil around you pulling you into the abyss! +You are led to a hidden chamber where time itself bends., Step into the rift, 249, Attempt to understand the mechanism, 250 +The ruins react to your defiance collapsing around you! +Reality shifts and you emerge into a world beyond time. Embrace your new existence! +The mechanism hums with energy revealing forgotten knowledge., Use the knowledge to reshape the ruins, 251, Seal it away and leave, 252 +The ruins bend to your will reshaping into a monument of power. Ascend as its new master! +You turn away ensuring no one will ever access this power again. The temple fades into history! +The ground quakes beneath you as the temple resists your presence., Stand firm, 253, Try to escape, 254 +The force overwhelms you consuming you within the ruins! +You run as the ruins collapse but absorb a fragment of its power., Harness the energy, 255, Leave it behind, 256 +Dropping down you land in an ancient catacomb filled with forgotten remains., Search for an exit, 257, Examine the artifacts, 258 +A passage leads you back to the surface leaving the past undisturbed! +The artifacts whisper secrets of a long-lost era., Embrace the knowledge, 259, Destroy them to silence their voices, 260 +Your mind expands burdened with the weight of history. Accept your fate as a guardian of the ruins! +The voices fade but the temple crumbles around you sealing its secrets forever! +The darkness ahead conceals unseen dangers., Proceed blindly, 261, Light a torch to reveal the path, 262 +You step forward only to fall into an unseen abyss! +The torchlight reveals a hidden doorway leading to safety., Enter the doorway, 263, Turn back while you can, 264 +Inside you discover an ancient archive of lost knowledge. Preserve the knowledge for future generations! +You retreat but the ruins refuse to let you leave! +The path twists and turns disorienting you., Follow the carvings on the wall, 265, Keep moving without guidance, 266 +The carvings guide you to an exit leading you back to the surface! +Lost in the labyrinth you fade into obscurity! +Your body reverts the corruption fading away as you awaken in the ruins., Reflect on your journey, 267, Search for a way home, 268 +The ruins whisper to you their stories now part of your soul. Stay and guard the temple’s secrets! +You retrace your steps the path home now clearer than before., Step into the outside world, 269, Turn back for one last discovery, 270 +You emerge into the light forever changed by your experience! +The ruins call to you one final time offering an unanswered mystery., Delve deeper, 271, Leave the past behind, 272 +You uncover an ancient mechanism that hums with life., Activate it, 273, Destroy it to prevent its use, 274 +You walk away knowing some mysteries are best left unsolved! +The mechanism shifts revealing a gateway to an unknown world., Step through, 275, Shut it down before it’s too late, 276 +The device crumbles its secrets lost forever! +Beyond the gateway lies a world untouched by time. Explore this new realm! +The gateway pulses as you step through sealing shut behind you., Find yourself in a new world, 277, The story ends here, 278 +You awaken in a land beyond time with no way back., Accept your new existence! +The energy surge consumes everything leaving only silence.! +You barely escape as the ruins vanish leaving no trace. The ruins are lost forever! +The abandoned settlement holds no answers only echoes of the past., Move on, 279, Record your discoveries, 280 +The monolith hums but offers no further insight., Leave it behind, 281, Mark its location for future travelers, 282 +The ancient presence watches as you leave fading into obscurity. The past remains buried! +Your mind struggles to hold onto the vision before it fades., Accept what you have seen, 283, Try to remember everything, 284 +Despite your efforts the knowledge slips away like a dream., Move forward, 285, Let go of the past, 286 +The hidden chamber holds only dust and forgotten relics., Leave it undisturbed, 287, Take one as a memento, 288 +As you take the artifact a deep tremor shakes the temple., Run before it collapses, 289, Hold your ground, 290 +The ruins collapse trapping you forever in the dark.! +You barely escape as the last remnants of the temple fall behind you. The ruins are lost to time! +The mysterious figure gestures one last time before vanishing., Continue your journey, 291, Turn back one last time, 292 +You find no trace of the figure as the ruins fade into history. Accept that some mysteries are not meant to be solved! +A strange light flickers deep within the rubble., Investigate, 293, Walk away, 294 +The light fades revealing nothing but broken stone. The journey ends! +A final gust of wind carries whispers of the past as you leave. Let the ruins rest! +The vast desert stretches before you as you walk away., Keep moving forward, 295, Turn back for one last look, 296 +Looking back you see nothing but endless sand where the ruins once stood. Accept the past is gone! +You push forward leaving the ruins behind as the wind erases your footprints., The past is forgotten! +The desert stretches endlessly before you as you continue your journey., Seek shelter, 297, Follow the stars, 298 +You find a small cave offering momentary refuge but no answers., Rest and recover, 299, Move on before night falls, 300 +As you rest your dreams are filled with whispers of the ruins., Try to understand them, 301, Ignore them and focus on survival, 302 +The whispers fade into silence leaving you with only unanswered questions. The ruins are lost to time! +Ignoring the whispers you push forward into the unknown. Your journey continues! +The stars guide you to the edge of a long-forgotten path., Follow it, 303, Leave it behind, 304 +The path winds through barren lands before fading into the dunes. There is no going back! +You turn away from the path embracing the uncertainty of your journey. Some stories remain unfinished! +The night is silent as you press forward each step carrying you farther from the ruins., Keep moving, 305, Stop and reflect, 306 +Pausing to reflect you feel the weight of history fading from your mind., Let it go, 307, Hold onto what little remains, 308 +You release the past allowing the ruins to fade from memory. A new journey begins! +The echoes of the ruins linger in your thoughts but their meaning is lost. You move on! +The last remnants of the temple vanish beneath the shifting sands., Accept their fate, 309, Search for any surviving traces, 310 +Digging through the sand reveals nothing but broken stone. The ruins are truly lost! +The desert swallows all evidence of the past leaving no sign of what once was. The cycle continues! +You walk away feeling the weight of history lift from your shoulders. The past belongs to the past! +A distant figure appears on the horizon watching in silence., Approach them, 311, Ignore them and walk away, 312 +The figure vanishes before you can reach them leaving only questions behind. The journey ends! +You turn away without looking back choosing the unknown over unfinished mysteries. The ruins remain forgotten! +A sudden sandstorm rises erasing all traces of your path., Seek shelter, 313, Press forward blindly, 314 +You find shelter just in time as the storm rages outside., Wait for it to pass, 315, Venture out into the storm, 316 +The storm passes leaving only silence and a world forever changed. Move forward! +Lost in the storm you wander aimlessly until the desert claims you.! +You emerge from the storm to find the landscape unrecognizable., Search for a landmark, 317, Continue walking, 318 +A familiar rock formation guides you back to a known path., Follow it, 319, Ignore it and forge a new trail, 320 +Following the path leads you to the ruins' final remnants, now barely recognizable., Pay your respects, 321, Leave without looking back, 322 +You kneel before the ruins acknowledging the weight of forgotten history. The past is at peace! +You turn away from the ruins, choosing to move on without regret. The story fades! +Straying from the path you wander into an endless desert., Keep walking, 323, Search for shelter, 324 +You walk until exhaustion takes hold, collapsing beneath the vast sky. The journey ends! +A small oasis appears in the distance offering a final refuge., Approach it, 325, Ignore it and press on, 326 +The oasis is real, a haven untouched by time. You find peace in its solitude! +Pushing forward without rest, you are swallowed by the sands.! +The night is cold, the stars unfamiliar., Navigate by instinct, 327, Wait for dawn, 328 +Your instincts fail, leading you in circles until the desert takes you.! +As the sun rises, the desert seems less hostile, a new journey ahead., Embrace the unknown, 329, Try to retrace your steps, 330 +The unknown welcomes you, and you disappear into legend.! +Your steps bring you back to familiar ground, but the ruins are gone. The past cannot be reclaimed! +A faint glimmer of something buried in the sand catches your eye., Dig it up, 331, Leave it undisturbed, 332 +You uncover a single artifact, the last piece of a forgotten story., Carry it forward, 333, Bury it again, 334 +The artifact remains with you, a reminder of all that was lost. The past is never truly gone! +The artifact is returned to the earth, its secrets sealed forever. The ruins remain undisturbed! +A distant melody drifts through the air, pulling at your memory., Follow the sound, 335, Block it out and walk away, 336 +The melody fades as you approach, leaving only silence. Some things are not meant to be known! +Ignoring the sound, you continue onward, leaving behind the echoes of the past.! +The wind shifts, revealing an untouched section of the ruins., Investigate, 337, Turn away, 338 +The ruins hold no more secrets—only dust and echoes. There is nothing left to find! +Walking away, you choose the future over the past. Some mysteries must remain unsolved! +You dig deeper, uncovering a hidden vault beneath the sand., Enter the vault, 339, Leave it buried, 340 +Inside, the air is thick with age. The walls whisper of forgotten knowledge., Listen closely, 341, Seal the vault behind you, 342 +The voices reveal nothing but lost echoes. The ruins' story is already over! +You collapse the entrance, ensuring nothing will be disturbed again. Time reclaims the past! +Leaving the vault buried, you turn away, letting history rest. Some doors are best left closed! +A shadow looms over the horizon, shifting and unnatural., Confront it, 343, Walk in the opposite direction, 344 +The shadow engulfs you, its purpose unknowable. Some mysteries should remain unsolved! +You avoid the presence, pushing forward into the unknown, leaving the past behind! +The desert air grows still as if the ruins themselves are fading from existence., Accept it, 345, Try to stop it, 346 +You stand in silence, knowing the past cannot be held forever. Time moves on! +Your efforts are in vain—the ruins dissolve into dust, lost to history! +A final doorway stands before you, untouched by the collapse., Step through, 347, Turn away, 348 +Beyond the door is nothing but darkness. There is no more story to tell! +You walk away, never knowing what lay beyond. Some doors are better left closed! +A distant storm approaches, erasing the last traces of the ruins., Watch it unfold, 349, Seek shelter, 350 +The storm consumes everything, wiping away all remnants of the past. The cycle is complete! +You shelter from the storm, emerging into a world that has already moved on! +The sands shift beneath your feet, revealing something ancient., Examine it, 351, Walk away, 352 +The object is a simple stone, smoothed by time. No secrets remain! +You turn away, choosing the present over the past. The ruins are truly gone! +The last echoes of the ruins fade from your mind., Hold onto the memory, 353, Let it go, 354 +You remember, but memories are all that remain. Time moves on without them! +You release the past, feeling lighter than before. The ruins vanish from history! +The memory lingers, a fleeting echo of what once was., Accept the fleeting nature of time, 355, Try to reclaim what was lost, 356 +Time erodes all things, and you walk forward, carrying only the lessons learned! +You reach out to the past, but it slips through your fingers like sand. Nothing can be reclaimed! +The ruins are gone, yet a single pathway remains before you., Follow it, 357, Turn back one last time, 358 +The path leads to a vast, endless horizon. There is nothing left to find! +You turn back, but where once there were ruins, now only emptiness remains! +A faint whisper carries on the wind, a voice you once knew., Listen, 359, Ignore it and walk away, 360 +The whisper fades, leaving only silence. Some stories were never meant to be remembered! +You walk away, never looking back. The past is gone, and the present remains! +The sand beneath your feet shifts, revealing one final inscription., Read it, 361, Leave it untouched, 362 +The words are unreadable, eroded by time. Its meaning is lost forever! +You leave the inscription behind, knowing some secrets were never meant to be uncovered! +A distant figure stands at the horizon, watching., Approach, 363, Ignore it and keep walking, 364 +The figure vanishes as you draw near. Perhaps it was never there at all! +You continue walking, leaving everything behind. The ruins fade from memory! +A lone relic remains in your hands, the last piece of a forgotten age., Bury it, 365, Keep it as a reminder, 366 +You let the relic sink into the sands, allowing the past to rest! +You hold onto the relic, but with time, even it fades to dust. Nothing lasts forever! +The final grains of sand slip through your fingers., Accept the end, 367, Struggle against it, 368 +The end comes, as it always must. The ruins are forgotten, their tale complete! +You resist, but time is an unrelenting force. All things must fade! +The wind carries away the last traces of the temple., Stand and watch, 369, Turn and leave, 370 +You watch as history is erased, knowing its story will never be told again! +You walk away without looking back. The ruins existed once, but now they are gone! +The wind howls through the empty ruins, the last echoes of a forgotten age., Listen to the wind, 371, Walk away without a sound, 372 +The wind fades, leaving only silence. The ruins are gone, and so is their story! +You leave without a trace, your presence as fleeting as those who came before! +The sky above darkens as the last remnants of the temple disappear., Watch the sky, 373, Close your eyes and move on, 374 +You watch as the clouds swallow the ruins. In time, all things are erased! +You step forward into the unknown, leaving the ruins behind forever! +A single path remains before you, untouched by time., Follow it, 375, Turn back one last time, 376 +The path leads nowhere, stretching into eternity. You have reached the end! +You turn back, but the ruins have already vanished. There is nothing left to return to! +A faint whisper calls your name from the distance., Answer, 377, Ignore it and walk on, 378 +The whisper fades before you can understand it. The past holds no answers! +You do not turn back, allowing the ruins to be forgotten at last! +The ground beneath you shifts as the final remnants of the ruins crumble., Watch the collapse, 379, Keep moving forward, 380 +You witness the last stone fall, and with it, the last memory of the temple! +You do not look back. The past is gone, and the future calls you elsewhere! +A single stone remains where the temple once stood., Touch it, 381, Leave it undisturbed, 382 +The stone crumbles at your touch, scattering into dust! +You walk away, leaving the last remnant of the temple to time! +The air grows still, as if the world itself has forgotten this place., Breathe deeply, 383, Keep walking without hesitation, 384 +You take in the silence, knowing you are the last to stand here! +You walk forward, leaving no trace behind. The ruins are gone, and so are you! +The sky above clears, revealing an endless horizon., Step toward the horizon, 385, Close your eyes and rest, 386 +You walk toward the horizon, knowing there is nothing left to find! +You sit in the silence, letting time erase the past and the future alike! +The wind carries the last echoes of the ruins away., Listen one final time, 387, Turn and walk without hesitation, 388 +The whispering fades, leaving only the vast, empty world before you! +You move forward, never looking back, letting the ruins fade from memory! +A final shadow flickers at the edge of your vision., Acknowledge it, 389, Ignore it and move on, 390 +The shadow vanishes, just another trick of the past slipping away! +You continue forward, the ruins lost forever behind you! +The path you walk leads to an unknown place., Follow it without question, 391, Look back one last time, 392 +The unknown stretches ahead, vast and limitless. Your journey continues! +You hesitate, but the past has already been erased. The ruins no longer exist! +The sky above shimmers as the last remnants of magic dissolve., Watch the sky, 393, Keep walking, 394 +You witness the final trace of the temple fade into nothingness! +You do not stop, leaving the past where it belongs! +The ruins whisper a name—perhaps yours, perhaps another’s., Speak your own name, 395, Stay silent, 396 +The whisper fades, its meaning lost to time! +You leave in silence, carrying no answers, only the weight of what once was! +A single doorway stands in the vast emptiness ahead., Step through it, 397, Turn away, 398 +You step forward, into something new, leaving all else behind! +You turn away, knowing the past must remain buried! +The ground beneath you is firm, untouched by what once was., Stand still and reflect, 399, Walk toward the unknown, 400 +You reflect for a moment, then move on, never to return! +You take the first step into an uncertain future, free of the past! +A gust of wind stirs the dust where the temple once stood., Watch it settle, 401, Keep walking, 402 +The dust settles, the ruins gone, their secrets buried forever! +You do not stop. The ruins were only a moment in time, already forgotten! +The last traces of the ruins drift away on the wind., Breathe in the air of a new beginning, 403, Walk away without another thought, 404 +You inhale deeply, feeling the weight of the past lift from your shoulders! +You continue forward, leaving everything behind as if it never existed! +The stars above seem brighter now that the ruins are gone., Gaze up at them, 405, Keep your focus on the road ahead, 406 +You stare at the stars, feeling small yet free beneath their endless glow! +You do not waver. The future is yours to shape without the ruins’ shadow! +The wind shifts, carrying a faint, final sound., Listen closely, 407, Ignore it and move on, 408 +The sound fades, indistinguishable, and then silence remains! +You press on, knowing some things are best left unheard! +A single path stretches endlessly before you., Walk it without hesitation, 409, Pause and take one last look back, 410 +You follow the path into the unknown, with nothing left to hold you back! +You hesitate, but the ruins have already vanished from existence! +A faint glow shimmers on the horizon., Approach it, 411, Walk in the opposite direction, 412 +You move toward the light, stepping into the dawn of a new era! +You leave the glow behind, choosing your own way forward! +The echoes of the ruins are gone, but something lingers within you., Accept the feeling, 413, Try to shake it off, 414 +You carry the ruins within, a quiet memory that will never fade! +You push away the thoughts, determined to start anew! +A crossroads appears, though you do not remember it being there., Choose a direction and keep walking, 415, Stand still and wait, 416 +You walk a path unknown, yet somehow meant for you! +You wait, but nothing comes. The choice is yours alone to make! +The sky above is clear, free of the temple’s shadows., Take comfort in it, 417, Keep your gaze on the path ahead, 418 +You find peace in the open sky, no longer bound by the past! +You do not stop, moving forward without hesitation! +The ruins have vanished, but their memory remains., Carry the memory forward, 419, Let it fade into the past, 420 +You preserve the knowledge, ensuring the story is never forgotten! +You release the past, allowing yourself to move on without regret! +The road ahead is open, free from the burdens of the temple., Walk toward the horizon, 421, Take a moment to reflect, 422 +You embrace the unknown, stepping into a new journey! +You pause, realizing how much you have changed, then continue forward! +A gentle breeze stirs the dust where the ruins once stood., Feel the breeze and smile, 423, Turn away from it, 424 +You welcome the wind as a sign of renewal, ready for what comes next! +You turn your back on it, setting your own course from here! +A quiet feeling of closure settles within you., Accept it, 425, Question if it's truly over, 426 +You let peace take hold, knowing the story has reached its end! +You hesitate, but no more answers come. The temple is truly gone! +The night is calm, without the whispers of the past., Rest beneath the stars, 427, Keep walking, 428 +You fall into a deep, dreamless sleep, unburdened at last! +You walk until the horizon swallows you, the past left behind! +A single thought lingers—was it all real?, Dismiss the thought, 429, Hold onto it as a mystery, 430 +You shake your head and move on. It no longer matters! +You keep the mystery alive, a secret that only you will ever know! +A fork in the path awaits, though neither road is familiar., Choose one without looking back, 431, Stop and consider the options, 432 +You take the first step forward, unafraid of what lies ahead! +You weigh your choices, but in the end, all roads lead forward! +For the first time in a long while, you are free., Embrace your freedom, 433, Wonder if you will ever return, 434 +You walk forward with a light heart, unchained from the past! +You glance back one last time, but the ruins are gone forever! +The ruins exist now only in memory, their mysteries sealed forever., Let go of the past, 435, Keep the knowledge alive, 436 +You release the past and step into the future with no regrets! +You become a guardian of the forgotten, ensuring the story is never truly lost! +A quiet road stretches ahead, untouched by time., Walk forward without hesitation, 437, Look back one last time, 438 +You step into the unknown, ready for a new chapter! +You see nothing but empty land where the ruins once stood, and turn away! +The weight of the past finally lifts from your shoulders., Accept the peace, 439, Question what comes next, 440 +You find comfort in the quiet, knowing you have done all you could! +You wonder if the ruins will ever return, but that is a question for another time! +The whispers are gone, replaced by the sound of the wind., Let the wind guide you, 441, Stand still and listen, 442 +You walk forward, allowing fate to take you wherever it may! +You listen, but there is only silence. The story has ended! +A distant light flickers on the horizon., Follow it, 443, Ignore it and continue walking, 444 +You step toward the light, uncertain but unafraid! +You leave the light behind, choosing your own path instead! +The stars above seem brighter than before., Take it as a sign, 445, Ignore it and move on, 446 +You smile, knowing that even in endings, there is beauty! +You walk forward, leaving the past fully behind! +A lone traveler crosses your path., Speak to them, 447, Continue on your way, 448 +You exchange stories, but nothing will ever match what you have seen! +You pass by without a word, your journey yours alone! +The ruins may be gone, but something deep inside you has changed., Accept the change, 449, Resist it and try to return to normal, 450 +You embrace the person you have become and move forward! +You try to return to who you were, but some journeys cannot be undone! +The last echoes of the ruins fade from your mind., Walk away without looking back, 451, Try to remember every detail, 452 +You leave the past behind, knowing some mysteries are meant to be forgotten! +You cling to the memories, but time erodes even the deepest truths! +A cold wind brushes against your skin as you step forward., Accept the chill as a farewell, 453, Wrap yourself in your cloak and move on, 454 +You take a deep breath and walk into the unknown, ready for what comes next! +The warmth of your cloak brings comfort, but your journey is done! +The road stretches endlessly before you., Keep walking without pause, 455, Find a place to rest, 456 +You walk on, the past behind you and the future unwritten! +You rest, but the ruins still linger in your thoughts before sleep takes you! +The night sky glows with unfamiliar constellations., Marvel at the sight, 457, Keep your focus ahead, 458 +The stars remind you that the world is vast, and your story is but one of many! +You press forward, unshaken by what you have left behind! +A faint song echoes through the air., Follow the melody, 459, Ignore it and continue walking, 460 +You listen and follow, but the song fades, leaving only silence! +You walk on, ignoring the call of the unknown! +The road comes to a fork., Take the left path, 461, Take the right path, 462 +Each step takes you further from what once was, and that is enough! +No matter which way you turn, your journey is your own! +A small village appears in the distance., Approach it, 463, Walk past it and continue alone, 464 +You find peace among strangers, a quiet place to call home! +You choose solitude, letting the road take you wherever it may! +The wind carries the last remnants of the past away., Whisper a farewell, 465, Say nothing and move on, 466 +Your whispered goodbye is the final closure you needed! +You say nothing, but deep inside, you know it is over! \ No newline at end of file From 97f9cbf9ca4bb87706329fb3d3ee6986c52a57d8 Mon Sep 17 00:00:00 2001 From: Jonathan Hite Date: Fri, 7 Feb 2025 19:22:54 -0500 Subject: [PATCH 08/15] fixed example_config.ini Game menu integration --- example_config.ini | 3 ++- games/forgotten_ruins_gpt.csv | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/example_config.ini b/example_config.ini index 3066b36..b49c552 100644 --- a/example_config.ini +++ b/example_config.ini @@ -73,13 +73,14 @@ main_menu_items = Q, B, U, X bbs_menu_items = M, B, C, J, X # Default Utilities Menu option for reference +# [G]ames # [S]tats # [F]ortune # [W]all of Shame # E[X]IT # # Remove any menu items from the list below that you want to exclude from the utilities menu -utilities_menu_items = S, F, W, X +utilities_menu_items = G, S, F, W, X ########################## diff --git a/games/forgotten_ruins_gpt.csv b/games/forgotten_ruins_gpt.csv index dd00c78..4d1d2d7 100644 --- a/games/forgotten_ruins_gpt.csv +++ b/games/forgotten_ruins_gpt.csv @@ -1,4 +1,4 @@ -title="The Forgotten Ruins - GPT Generated" +title="Forgotten Ruins - GPT Made" You stand before an ancient temple. Vines creep over its stone walls. The entrance is dark., Enter the temple, 2, Walk away, 3 You step inside. A hallway stretches ahead. Torches flicker on damp stone walls., Move forward, 4, Examine the walls for clues, 5 You decide it's not worth the risk. As you turn to leave a strong gust slams the entrance shut behind you., Look for another way out, 6, Call for help, 7 @@ -468,4 +468,4 @@ You find peace among strangers, a quiet place to call home! You choose solitude, letting the road take you wherever it may! The wind carries the last remnants of the past away., Whisper a farewell, 465, Say nothing and move on, 466 Your whispered goodbye is the final closure you needed! -You say nothing, but deep inside, you know it is over! \ No newline at end of file +You say nothing, but deep inside, you know it is over! From 6a8811dca9b53bef201334dd50e035291356a15b Mon Sep 17 00:00:00 2001 From: Jonathan Hite Date: Fri, 7 Feb 2025 19:31:59 -0500 Subject: [PATCH 09/15] forced encoding so server.py would start on pi zero 2 w image --- server.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/server.py b/server.py index 444d23d..f551978 100644 --- a/server.py +++ b/server.py @@ -14,6 +14,11 @@ import logging import time +import sys +import io + +# Reconfigure stdout to use UTF-8 encoding +sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8') from config_init import initialize_config, get_interface, init_cli_parser, merge_config from db_operations import initialize_database From bcec01de886fedfab01e30250a41b0a40c6cf395 Mon Sep 17 00:00:00 2001 From: jhite1234 <46729349+jhite1234@users.noreply.github.com> Date: Fri, 7 Feb 2025 19:57:36 -0500 Subject: [PATCH 10/15] Delete text.py test file removed --- text.py | 10 ---------- 1 file changed, 10 deletions(-) delete mode 100644 text.py diff --git a/text.py b/text.py deleted file mode 100644 index f992c61..0000000 --- a/text.py +++ /dev/null @@ -1,10 +0,0 @@ -import os - -print("Current Working Directory:", os.getcwd()) -file_path = "./games/lost_forest.csv" - -if os.path.exists(file_path): - print("✅ File exists! Python can detect it.") -else: - print("❌ File does NOT exist according to Python.") - From 2525d761003d2a5686b26b7afc98d4bdf8522c98 Mon Sep 17 00:00:00 2001 From: Jonathan Hite Date: Fri, 7 Feb 2025 21:02:39 -0500 Subject: [PATCH 11/15] Revert Dockerfile changes --- docker/Dockerfile | 49 +++++++++++++++++++---------------------------- text.py | 10 ---------- 2 files changed, 20 insertions(+), 39 deletions(-) delete mode 100644 text.py diff --git a/docker/Dockerfile b/docker/Dockerfile index ee4ca42..2f9f33e 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,45 +1,36 @@ -# First stage: Base build environment +# From original Dockerfile at https://github.com/TheCommsChannel/TC2-BBS-mesh FROM debian:stable-slim AS build -# Install required dependencies RUN apt-get update && \ - apt-get install -y git && \ - apt-get clean && rm -rf /var/lib/apt/lists/* + apt-get install -y \ + git \ + && apt-get clean && rm -rf /var/lib/apt/lists/* -# Ensure the 'games' directory exists -RUN mkdir -p /home/mesh/bbs/games +# Clone the repository +RUN git clone https://github.com/TheCommsChannel/TC2-BBS-mesh.git -# Set working directory -WORKDIR /home/mesh/bbs - -# Copy project files including 'games' directory -COPY . /home/mesh/bbs/ +#### -# Second stage: Final runtime environment -FROM python:alpine +FROM --platform=$BUILDPLATFORM python:alpine -# Add a non-root user for security +# Switch to non-root user RUN adduser --disabled-password mesh - -# Ensure working directory +USER mesh RUN mkdir -p /home/mesh/bbs WORKDIR /home/mesh/bbs -# Copy files from the build stage -COPY --from=build /home/mesh/bbs /home/mesh/bbs/ - # Install Python dependencies +COPY --from=build /TC2-BBS-mesh/requirements.txt ./ RUN pip install --no-cache-dir --break-system-packages -r requirements.txt -# Ensure the 'games' directory exists in runtime container -RUN mkdir -p /home/mesh/bbs/games - -# Set user to mesh -USER mesh - -# Expose necessary ports -EXPOSE 8080 +# Copy over app code +COPY --from=build /TC2-BBS-mesh/*.py ./ -# Start the server -CMD ["python", "server.py"] +# Define config volume +VOLUME /home/mesh/bbs/config +WORKDIR /home/mesh/bbs/config +COPY --from=build /TC2-BBS-mesh/example_config.ini ./config.ini +COPY --from=build /TC2-BBS-mesh/fortunes.txt ./ +# Define the command to run +ENTRYPOINT [ "python3", "/home/mesh/bbs/server.py" ] diff --git a/text.py b/text.py deleted file mode 100644 index f992c61..0000000 --- a/text.py +++ /dev/null @@ -1,10 +0,0 @@ -import os - -print("Current Working Directory:", os.getcwd()) -file_path = "./games/lost_forest.csv" - -if os.path.exists(file_path): - print("✅ File exists! Python can detect it.") -else: - print("❌ File does NOT exist according to Python.") - From dc5efa2b47967a3099623fb9a5a5aae7d716c726 Mon Sep 17 00:00:00 2001 From: Jonathan Hite Date: Fri, 7 Feb 2025 23:08:04 -0500 Subject: [PATCH 12/15] fixed an issue with exiting midgame --- command_handlers.py | 2 +- message_processing.py | 12 ++++++++---- server.py | 6 ++++-- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/command_handlers.py b/command_handlers.py index def5ec7..5d91251 100644 --- a/command_handlers.py +++ b/command_handlers.py @@ -949,4 +949,4 @@ def process_game_choice(sender_id, message, interface, state): present_story_segment(sender_id, interface, state) except ValueError: - send_message("Invalid input. Please enter a valid number.", sender_id, interface) + send_message("Invalid input. Please enter a valid number.", sender_id, interface) \ No newline at end of file diff --git a/message_processing.py b/message_processing.py index 7288806..7a68a81 100644 --- a/message_processing.py +++ b/message_processing.py @@ -104,14 +104,19 @@ def process_message(sender_id, message, interface, is_sync_message=False): channel_name, channel_url = parts[1], parts[2] add_channel(channel_name, channel_url) else: - # ✅ **Corrected IN_GAME Handling** + # ✅ **Fix: Ensure Games Menu Loads After Exiting a Game** if state and state['command'] == 'IN_GAME': - message_lower = message.lower().strip() + logging.debug(f"🎮 User {sender_id} is in-game, processing game command...") - # Always check if the user wants to exit if message_lower == "x": + logging.debug(f"❌ User {sender_id} exited the game. Sending exit message...") send_message(f"Exiting '{state['game']}'... Returning to Games Menu.", sender_id, interface) update_user_state(sender_id, {'command': 'GAMES', 'step': 1}) + + logging.debug(f"🚀 Calling handle_games_command() for user {sender_id} after exit.") + handle_games_command(sender_id, interface) + logging.debug(f"✅ handle_games_command() execution completed for user {sender_id}!") + return # Otherwise, process the game choice @@ -211,7 +216,6 @@ def process_message(sender_id, message, interface, is_sync_message=False): - def on_receive(packet, interface): try: if 'decoded' in packet and packet['decoded']['portnum'] == 'TEXT_MESSAGE_APP': diff --git a/server.py b/server.py index f551978..3aa7839 100644 --- a/server.py +++ b/server.py @@ -16,9 +16,11 @@ import time import sys import io +import locale -# Reconfigure stdout to use UTF-8 encoding -sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8') +# Apply UTF-8 fix only if needed +if "utf" not in locale.getpreferredencoding().lower(): + sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8') from config_init import initialize_config, get_interface, init_cli_parser, merge_config from db_operations import initialize_database From 496638eb06bb4b56cc42c0178a26ec5ccac7f004 Mon Sep 17 00:00:00 2001 From: Jonathan Hite Date: Fri, 7 Feb 2025 23:42:58 -0500 Subject: [PATCH 13/15] validated the AI generated game --- games/forgotten_ruins_gpt.csv | 100 +++++++++++++++++----------------- removecomma.py | 28 ++++++++++ 2 files changed, 78 insertions(+), 50 deletions(-) create mode 100644 removecomma.py diff --git a/games/forgotten_ruins_gpt.csv b/games/forgotten_ruins_gpt.csv index 4d1d2d7..75ad2f7 100644 --- a/games/forgotten_ruins_gpt.csv +++ b/games/forgotten_ruins_gpt.csv @@ -74,13 +74,13 @@ The energy you absorbed begins to change you., Embrace the transformation, 130, With great effort you stabilize the structure but at a great cost to yourself., Accept your fate and remain as guardian, 132, Collapse from exhaustion, 133 The temple now reshaped by your will stands as a beacon of forgotten power., Welcome seekers of knowledge, 134, Guard the temple against intruders, 135 The ruins finally settle with their magic fading into history., Leave the site behind, 136, Record your findings for future generations, 137 -The once-great temple is lost beneath the sands of time but its whispers remain., A new era begins, 138, The story ends here! +The once-great temple is lost beneath the sands of time but its whispers remain., A new era begins, 138 Your name is forever inscribed in history as the last one to witness the temple's power. Fade into legend! The debris falls and you barely dodge out of the way., Sprint towards an opening, 139, Try to push through, 140 The alcove provides momentary safety but the air grows thin., Search for a hidden passage, 141, Wait and conserve energy, 142 The transformation consumes you—reshaping you into something no longer human., Accept your new form, 143, Fight against the corruption, 144 The energy pulses violently causing instability in your body., Channel it into the ruins, 145, Attempt to purge it from yourself, 146 -You collapse from exhaustion but the ruins now stand strong once more., A new guardian rises, 147, The temple's power is sealed forever! +You collapse from exhaustion but the ruins now stand strong once more., A new guardian rises, 147 The seekers arrive drawn to the beacon of your presence., Welcome them as disciples, 148, Warn them to stay away, 149 The world forgets the ruins leaving only myths behind. The story fades into obscurity! History remembers your actions and the knowledge is passed on. A new era of discovery begins! @@ -112,11 +112,11 @@ Ignoring the mechanism you push forward into the unknown., Continue into darknes The symbols glow brighter as you study them filling you with knowledge., Use the knowledge to navigate, 194, Step away before it's too late, 195 Proceeding cautiously you feel an unseen force guiding you., Follow the force, 196, Resist and take another path, 197 The corruption overtakes you merging your existence with the ruins. Become one with the temple! -You fight with all your will pushing back against the corruption., Succeed in purging it, 198, Fail and succumb! +You fight with all your will pushing back against the corruption., Succeed in purging it, 198 The temple's gift fills you with power beyond mortal comprehension. Ascend as a godlike entity! Rejecting the gift you break free from the temple's grasp., Escape into the outside world, 199 Stepping into the new world you find yourself in a realm beyond time. Accept your new reality! -Attempting to undo the transformation you struggle as reality twists around you., Succeed and return to normal, 200, Fail and be lost forever! +Attempting to undo the transformation you struggle as reality twists around you., Succeed and return to normal, 200 You find a way down from the plateau arriving at a forgotten civilization., Discover their secrets, 201, Seek shelter and recover, 202 Your body reverts the corruption fading away as you awaken in the ruins., Reflect on your journey, 203, Search for a way home, 204 You remain trapped between worlds a forgotten specter wandering the temple's depths! @@ -156,7 +156,7 @@ You flee as the temple collapses behind you barely making it out alive! The temple’s wrath is swift. Darkness engulfs you and you become another forgotten soul within its halls! The stairway twists endlessly leading deeper into the ruins., Continue descending, 227, Turn back, 228 A vast underground lake stretches before you glowing faintly., Investigate the lake, 229, Search the cave for an exit, 230 -As you turn back the path behind you vanishes., Trapped you must press forward. Go to 229 +As you turn back the path behind you vanishes., Trapped you must press forward., 229 The water is impossibly still reflecting an unfamiliar sky., Step onto the surface, 231, Reach into the water, 232 A narrow tunnel leads further into the ruins revealing ancient inscriptions., Study the inscriptions, 233, Ignore them and move on, 234 The water solidifies beneath you and you find yourself walking across a new reality! @@ -167,7 +167,7 @@ The knowledge guides you safely to an exit allowing you to escape! Without understanding the warnings you wander into a collapsing chamber! The passage narrows forcing you to crawl forward., Keep moving, 237, Turn back and find another route, 238 The tunnel opens into a grand chamber where a forgotten deity stirs., Approach the deity, 239, Find another exit, 240 -The tunnel collapses behind you forcing you to move forward., Go to 239 +The tunnel collapses behind you forcing you to move forward., You have no choice., 239 The deity’s gaze pierces you unraveling your mind., Accept its gift, 241, Refuse and flee, 242 You rush for another exit but the temple seals you within forever! Power surges through you elevating you beyond mortality., Ascend as a godlike being, 243, Resist the transformation, 244 @@ -210,7 +210,7 @@ The mechanism shifts revealing a gateway to an unknown world., Step through, 275 The device crumbles its secrets lost forever! Beyond the gateway lies a world untouched by time. Explore this new realm! The gateway pulses as you step through sealing shut behind you., Find yourself in a new world, 277, The story ends here, 278 -You awaken in a land beyond time with no way back., Accept your new existence! +You awaken in a land beyond time with no way back. Accept your new existence! The energy surge consumes everything leaving only silence.! You barely escape as the ruins vanish leaving no trace. The ruins are lost forever! The abandoned settlement holds no answers only echoes of the past., Move on, 279, Record your discoveries, 280 @@ -229,7 +229,7 @@ The light fades revealing nothing but broken stone. The journey ends! A final gust of wind carries whispers of the past as you leave. Let the ruins rest! The vast desert stretches before you as you walk away., Keep moving forward, 295, Turn back for one last look, 296 Looking back you see nothing but endless sand where the ruins once stood. Accept the past is gone! -You push forward leaving the ruins behind as the wind erases your footprints., The past is forgotten! +You push forward leaving the ruins behind as the wind erases your footprints. The past is forgotten! The desert stretches endlessly before you as you continue your journey., Seek shelter, 297, Follow the stars, 298 You find a small cave offering momentary refuge but no answers., Rest and recover, 299, Move on before night falls, 300 As you rest your dreams are filled with whispers of the ruins., Try to understand them, 301, Ignore them and focus on survival, 302 @@ -255,61 +255,61 @@ The storm passes leaving only silence and a world forever changed. Move forward! Lost in the storm you wander aimlessly until the desert claims you.! You emerge from the storm to find the landscape unrecognizable., Search for a landmark, 317, Continue walking, 318 A familiar rock formation guides you back to a known path., Follow it, 319, Ignore it and forge a new trail, 320 -Following the path leads you to the ruins' final remnants, now barely recognizable., Pay your respects, 321, Leave without looking back, 322 +Following the path leads you to the ruins' final remnants now barely recognizable., Pay your respects, 321, Leave without looking back, 322 You kneel before the ruins acknowledging the weight of forgotten history. The past is at peace! -You turn away from the ruins, choosing to move on without regret. The story fades! +You turn away from the ruins choosing to move on without regret. The story fades! Straying from the path you wander into an endless desert., Keep walking, 323, Search for shelter, 324 -You walk until exhaustion takes hold, collapsing beneath the vast sky. The journey ends! +You walk until exhaustion takes hold collapsing beneath the vast sky. The journey ends! A small oasis appears in the distance offering a final refuge., Approach it, 325, Ignore it and press on, 326 -The oasis is real, a haven untouched by time. You find peace in its solitude! -Pushing forward without rest, you are swallowed by the sands.! -The night is cold, the stars unfamiliar., Navigate by instinct, 327, Wait for dawn, 328 -Your instincts fail, leading you in circles until the desert takes you.! -As the sun rises, the desert seems less hostile, a new journey ahead., Embrace the unknown, 329, Try to retrace your steps, 330 -The unknown welcomes you, and you disappear into legend.! -Your steps bring you back to familiar ground, but the ruins are gone. The past cannot be reclaimed! +The oasis is real a haven untouched by time. You find peace in its solitude! +Pushing forward without rest you are swallowed by the sands.! +The night is cold the stars unfamiliar., Navigate by instinct, 327, Wait for dawn, 328 +Your instincts fail leading you in circles until the desert takes you.! +As the sun rises the desert seems less hostile a new journey ahead., Embrace the unknown, 329, Try to retrace your steps, 330 +The unknown welcomes you and you disappear into legend.! +Your steps bring you back to familiar ground but the ruins are gone. The past cannot be reclaimed! A faint glimmer of something buried in the sand catches your eye., Dig it up, 331, Leave it undisturbed, 332 -You uncover a single artifact, the last piece of a forgotten story., Carry it forward, 333, Bury it again, 334 -The artifact remains with you, a reminder of all that was lost. The past is never truly gone! -The artifact is returned to the earth, its secrets sealed forever. The ruins remain undisturbed! -A distant melody drifts through the air, pulling at your memory., Follow the sound, 335, Block it out and walk away, 336 -The melody fades as you approach, leaving only silence. Some things are not meant to be known! -Ignoring the sound, you continue onward, leaving behind the echoes of the past.! -The wind shifts, revealing an untouched section of the ruins., Investigate, 337, Turn away, 338 +You uncover a single artifact the last piece of a forgotten story., Carry it forward, 333, Bury it again, 334 +The artifact remains with you a reminder of all that was lost. The past is never truly gone! +The artifact is returned to the earth its secrets sealed forever. The ruins remain undisturbed! +A distant melody drifts through the air pulling at your memory., Follow the sound, 335, Block it out and walk away, 336 +The melody fades as you approach leaving only silence. Some things are not meant to be known! +Ignoring the sound you continue onward leaving behind the echoes of the past.! +The wind shifts revealing an untouched section of the ruins., Investigate, 337, Turn away, 338 The ruins hold no more secrets—only dust and echoes. There is nothing left to find! -Walking away, you choose the future over the past. Some mysteries must remain unsolved! -You dig deeper, uncovering a hidden vault beneath the sand., Enter the vault, 339, Leave it buried, 340 +Walking away you choose the future over the past. Some mysteries must remain unsolved! +You dig deeper uncovering a hidden vault beneath the sand., Enter the vault, 339, Leave it buried, 340 Inside, the air is thick with age. The walls whisper of forgotten knowledge., Listen closely, 341, Seal the vault behind you, 342 The voices reveal nothing but lost echoes. The ruins' story is already over! You collapse the entrance, ensuring nothing will be disturbed again. Time reclaims the past! -Leaving the vault buried, you turn away, letting history rest. Some doors are best left closed! -A shadow looms over the horizon, shifting and unnatural., Confront it, 343, Walk in the opposite direction, 344 -The shadow engulfs you, its purpose unknowable. Some mysteries should remain unsolved! -You avoid the presence, pushing forward into the unknown, leaving the past behind! +Leaving the vault buried you turn away, letting history rest. Some doors are best left closed! +A shadow looms over the horizon shifting and unnatural., Confront it, 343, Walk in the opposite direction, 344 +The shadow engulfs you its purpose unknowable. Some mysteries should remain unsolved! +You avoid the presence pushing forward into the unknown leaving the past behind! The desert air grows still as if the ruins themselves are fading from existence., Accept it, 345, Try to stop it, 346 -You stand in silence, knowing the past cannot be held forever. Time moves on! +You stand in silence knowing the past cannot be held forever. Time moves on! Your efforts are in vain—the ruins dissolve into dust, lost to history! A final doorway stands before you, untouched by the collapse., Step through, 347, Turn away, 348 Beyond the door is nothing but darkness. There is no more story to tell! -You walk away, never knowing what lay beyond. Some doors are better left closed! -A distant storm approaches, erasing the last traces of the ruins., Watch it unfold, 349, Seek shelter, 350 -The storm consumes everything, wiping away all remnants of the past. The cycle is complete! -You shelter from the storm, emerging into a world that has already moved on! -The sands shift beneath your feet, revealing something ancient., Examine it, 351, Walk away, 352 -The object is a simple stone, smoothed by time. No secrets remain! +You walk away never knowing what lay beyond. Some doors are better left closed! +A distant storm approaches erasing the last traces of the ruins., Watch it unfold, 349, Seek shelter, 350 +The storm consumes everything wiping away all remnants of the past. The cycle is complete! +You shelter from the storm emerging into a world that has already moved on! +The sands shift beneath your feet revealing something ancient., Examine it, 351, Walk away, 352 +The object is a simple stone smoothed by time. No secrets remain! You turn away, choosing the present over the past. The ruins are truly gone! The last echoes of the ruins fade from your mind., Hold onto the memory, 353, Let it go, 354 -You remember, but memories are all that remain. Time moves on without them! -You release the past, feeling lighter than before. The ruins vanish from history! -The memory lingers, a fleeting echo of what once was., Accept the fleeting nature of time, 355, Try to reclaim what was lost, 356 -Time erodes all things, and you walk forward, carrying only the lessons learned! -You reach out to the past, but it slips through your fingers like sand. Nothing can be reclaimed! -The ruins are gone, yet a single pathway remains before you., Follow it, 357, Turn back one last time, 358 -The path leads to a vast, endless horizon. There is nothing left to find! -You turn back, but where once there were ruins, now only emptiness remains! -A faint whisper carries on the wind, a voice you once knew., Listen, 359, Ignore it and walk away, 360 -The whisper fades, leaving only silence. Some stories were never meant to be remembered! -You walk away, never looking back. The past is gone, and the present remains! +You remember but memories are all that remain. Time moves on without them! +You release the past feeling lighter than before. The ruins vanish from history! +The memory lingers a fleeting echo of what once was., Accept the fleeting nature of time, 355, Try to reclaim what was lost, 356 +Time erodes all things and you walk forward carrying only the lessons learned! +You reach out to the past but it slips through your fingers like sand. Nothing can be reclaimed! +The ruins are gone yet a single pathway remains before you., Follow it, 357, Turn back one last time, 358 +The path leads to a vast endless horizon. There is nothing left to find! +You turn back but where once there were ruins now only emptiness remains! +A faint whisper carries on the wind a voice you once knew., Listen, 359, Ignore it and walk away, 360 +The whisper fades leaving only silence. Some stories were never meant to be remembered! +You walk away never looking back. The past is gone and the present remains! The sand beneath your feet shifts, revealing one final inscription., Read it, 361, Leave it untouched, 362 The words are unreadable, eroded by time. Its meaning is lost forever! You leave the inscription behind, knowing some secrets were never meant to be uncovered! @@ -468,4 +468,4 @@ You find peace among strangers, a quiet place to call home! You choose solitude, letting the road take you wherever it may! The wind carries the last remnants of the past away., Whisper a farewell, 465, Say nothing and move on, 466 Your whispered goodbye is the final closure you needed! -You say nothing, but deep inside, you know it is over! +You say nothing but deep inside you know it is over! diff --git a/removecomma.py b/removecomma.py new file mode 100644 index 0000000..c2c76d9 --- /dev/null +++ b/removecomma.py @@ -0,0 +1,28 @@ +import os +import re + +def process_file(input_filename, output_filename): + """Reads a file line by line, removing commas from lines that do not contain a number.""" + + if not os.path.exists(input_filename): + print(f"❌ Error: File '{input_filename}' not found. Please check the path.") + return # Exit the function + + with open(input_filename, 'r', encoding='utf-8') as infile, \ + open(output_filename, 'w', encoding='utf-8') as outfile: + + for line in infile: + if re.search(r'\d', line): # Checks if line contains any digit (0-9) + outfile.write(line) # Keep the line as is + else: + cleaned_line = line.replace(',', '') # Remove commas + outfile.write(cleaned_line) # Write the cleaned line + + print(f"✅ Processing complete! Cleaned file saved as '{output_filename}'.") + +# Example usage +input_file = "games/forgotten_ruins_gpt.csv" +output_file = "games/cleaned_forgotten_ruins_gpt.csv" + +process_file(input_file, output_file) + From 775ab0c688d03f11d2f13be9878151f26ccef6a3 Mon Sep 17 00:00:00 2001 From: Jonathan Hite Date: Fri, 7 Feb 2025 23:44:31 -0500 Subject: [PATCH 14/15] deleted file --- removecomma.py | 28 ---------------------------- 1 file changed, 28 deletions(-) delete mode 100644 removecomma.py diff --git a/removecomma.py b/removecomma.py deleted file mode 100644 index c2c76d9..0000000 --- a/removecomma.py +++ /dev/null @@ -1,28 +0,0 @@ -import os -import re - -def process_file(input_filename, output_filename): - """Reads a file line by line, removing commas from lines that do not contain a number.""" - - if not os.path.exists(input_filename): - print(f"❌ Error: File '{input_filename}' not found. Please check the path.") - return # Exit the function - - with open(input_filename, 'r', encoding='utf-8') as infile, \ - open(output_filename, 'w', encoding='utf-8') as outfile: - - for line in infile: - if re.search(r'\d', line): # Checks if line contains any digit (0-9) - outfile.write(line) # Keep the line as is - else: - cleaned_line = line.replace(',', '') # Remove commas - outfile.write(cleaned_line) # Write the cleaned line - - print(f"✅ Processing complete! Cleaned file saved as '{output_filename}'.") - -# Example usage -input_file = "games/forgotten_ruins_gpt.csv" -output_file = "games/cleaned_forgotten_ruins_gpt.csv" - -process_file(input_file, output_file) - From 9f2b60b9f2c3736c1e2ed8739ec3fad603f7cbcb Mon Sep 17 00:00:00 2001 From: Jonathan Hite Date: Sat, 8 Feb 2025 23:22:24 -0500 Subject: [PATCH 15/15] Added a repair tool for incorrectly formatted line mapping. --- game_line_offset_fix.py | 62 +++++++++ games/forgotten_ruins_gpt.csv | 250 +++++++++++++++++----------------- 2 files changed, 187 insertions(+), 125 deletions(-) create mode 100644 game_line_offset_fix.py diff --git a/game_line_offset_fix.py b/game_line_offset_fix.py new file mode 100644 index 0000000..63aba69 --- /dev/null +++ b/game_line_offset_fix.py @@ -0,0 +1,62 @@ +# This will fix a gamefile that was written with a title="Title" on line 1, but was not offset +# to account for that. If title="Title" is used on line 1, that line should then be treated +# as line 0. The gamefile processor assumes line 1 is the first line that doesn't start with title= +# This script will subtract one from every line number mapping in the gamefile. + +# Alternatively, you could add a "dummy line" to the second storyline shifting the rest of +# the file down by one line. + +import re +import os + +def list_game_files(directory): + return [f for f in os.listdir(directory) if os.path.isfile(os.path.join(directory, f))] + +def adjust_numbers_in_file(input_file, output_file): + with open(input_file, 'r', encoding='utf-8') as f: + lines = f.readlines() + + updated_lines = [] + for line in lines: + # Preserve title line + if line.startswith("title="): + updated_lines.append(line) + continue + + # Find numbers and subtract 1 from each + def adjust_number(match): + return f", {match.group(1)}, {int(match.group(2)) - 1}" + + updated_line = re.sub(r",\s*([^,]+)\s*,\s*(\d+)", adjust_number, line) + updated_lines.append(updated_line) + + with open(output_file, 'w', encoding='utf-8') as f: + f.writelines(updated_lines) + +def main(): + games_dir = "./games" + game_files = list_game_files(games_dir) + + if not game_files: + print("No files found in the ./games directory.") + return + + print("Select a file to process:") + for idx, filename in enumerate(game_files, start=1): + print(f"{idx}: {filename}") + + choice = int(input("Enter the number of the file: ")) - 1 + if choice < 0 or choice >= len(game_files): + print("Invalid selection.") + return + + input_filename = game_files[choice] + input_filepath = os.path.join(games_dir, input_filename) + output_filename = f"{os.path.splitext(input_filename)[0]}-linefix{os.path.splitext(input_filename)[1]}" + output_filepath = os.path.join(games_dir, output_filename) + + adjust_numbers_in_file(input_filepath, output_filepath) + print(f"Processed file saved as: {output_filename}") + +if __name__ == "__main__": + main() diff --git a/games/forgotten_ruins_gpt.csv b/games/forgotten_ruins_gpt.csv index 75ad2f7..aa24c39 100644 --- a/games/forgotten_ruins_gpt.csv +++ b/games/forgotten_ruins_gpt.csv @@ -279,17 +279,17 @@ The wind shifts revealing an untouched section of the ruins., Investigate, 337, The ruins hold no more secrets—only dust and echoes. There is nothing left to find! Walking away you choose the future over the past. Some mysteries must remain unsolved! You dig deeper uncovering a hidden vault beneath the sand., Enter the vault, 339, Leave it buried, 340 -Inside, the air is thick with age. The walls whisper of forgotten knowledge., Listen closely, 341, Seal the vault behind you, 342 +Inside the air is thick with age. The walls whisper of forgotten knowledge., Listen closely, 341, Seal the vault behind you, 342 The voices reveal nothing but lost echoes. The ruins' story is already over! -You collapse the entrance, ensuring nothing will be disturbed again. Time reclaims the past! -Leaving the vault buried you turn away, letting history rest. Some doors are best left closed! +You collapse the entrance ensuring nothing will be disturbed again. Time reclaims the past! +Leaving the vault buried you turn away letting history rest. Some doors are best left closed! A shadow looms over the horizon shifting and unnatural., Confront it, 343, Walk in the opposite direction, 344 The shadow engulfs you its purpose unknowable. Some mysteries should remain unsolved! You avoid the presence pushing forward into the unknown leaving the past behind! The desert air grows still as if the ruins themselves are fading from existence., Accept it, 345, Try to stop it, 346 You stand in silence knowing the past cannot be held forever. Time moves on! -Your efforts are in vain—the ruins dissolve into dust, lost to history! -A final doorway stands before you, untouched by the collapse., Step through, 347, Turn away, 348 +Your efforts are in vain—the ruins dissolve into dust lost to history! +A final doorway stands before you untouched by the collapse., Step through, 347, Turn away, 348 Beyond the door is nothing but darkness. There is no more story to tell! You walk away never knowing what lay beyond. Some doors are better left closed! A distant storm approaches erasing the last traces of the ruins., Watch it unfold, 349, Seek shelter, 350 @@ -297,7 +297,7 @@ The storm consumes everything wiping away all remnants of the past. The cycle is You shelter from the storm emerging into a world that has already moved on! The sands shift beneath your feet revealing something ancient., Examine it, 351, Walk away, 352 The object is a simple stone smoothed by time. No secrets remain! -You turn away, choosing the present over the past. The ruins are truly gone! +You turn away choosing the present over the past. The ruins are truly gone! The last echoes of the ruins fade from your mind., Hold onto the memory, 353, Let it go, 354 You remember but memories are all that remain. Time moves on without them! You release the past feeling lighter than before. The ruins vanish from history! @@ -310,162 +310,162 @@ You turn back but where once there were ruins now only emptiness remains! A faint whisper carries on the wind a voice you once knew., Listen, 359, Ignore it and walk away, 360 The whisper fades leaving only silence. Some stories were never meant to be remembered! You walk away never looking back. The past is gone and the present remains! -The sand beneath your feet shifts, revealing one final inscription., Read it, 361, Leave it untouched, 362 -The words are unreadable, eroded by time. Its meaning is lost forever! -You leave the inscription behind, knowing some secrets were never meant to be uncovered! -A distant figure stands at the horizon, watching., Approach, 363, Ignore it and keep walking, 364 +The sand beneath your feet shifts revealing one final inscription., Read it, 361, Leave it untouched, 362 +The words are unreadable eroded by time. Its meaning is lost forever! +You leave the inscription behind knowing some secrets were never meant to be uncovered! +A distant figure stands at the horizon watching., Approach, 363, Ignore it and keep walking, 364 The figure vanishes as you draw near. Perhaps it was never there at all! -You continue walking, leaving everything behind. The ruins fade from memory! -A lone relic remains in your hands, the last piece of a forgotten age., Bury it, 365, Keep it as a reminder, 366 -You let the relic sink into the sands, allowing the past to rest! -You hold onto the relic, but with time, even it fades to dust. Nothing lasts forever! +You continue walking leaving everything behind. The ruins fade from memory! +A lone relic remains in your hands the last piece of a forgotten age., Bury it, 365, Keep it as a reminder, 366 +You let the relic sink into the sands allowing the past to rest! +You hold onto the relic but with time even it fades to dust. Nothing lasts forever! The final grains of sand slip through your fingers., Accept the end, 367, Struggle against it, 368 -The end comes, as it always must. The ruins are forgotten, their tale complete! -You resist, but time is an unrelenting force. All things must fade! +The end comes as it always must. The ruins are forgotten their tale complete! +You resist but time is an unrelenting force. All things must fade! The wind carries away the last traces of the temple., Stand and watch, 369, Turn and leave, 370 -You watch as history is erased, knowing its story will never be told again! -You walk away without looking back. The ruins existed once, but now they are gone! -The wind howls through the empty ruins, the last echoes of a forgotten age., Listen to the wind, 371, Walk away without a sound, 372 -The wind fades, leaving only silence. The ruins are gone, and so is their story! -You leave without a trace, your presence as fleeting as those who came before! +You watch as history is erased knowing its story will never be told again! +You walk away without looking back. The ruins existed once but now they are gone! +The wind howls through the empty ruins the last echoes of a forgotten age., Listen to the wind, 371, Walk away without a sound, 372 +The wind fades leaving only silence. The ruins are gone and so is their story! +You leave without a trace your presence as fleeting as those who came before! The sky above darkens as the last remnants of the temple disappear., Watch the sky, 373, Close your eyes and move on, 374 -You watch as the clouds swallow the ruins. In time, all things are erased! -You step forward into the unknown, leaving the ruins behind forever! -A single path remains before you, untouched by time., Follow it, 375, Turn back one last time, 376 -The path leads nowhere, stretching into eternity. You have reached the end! -You turn back, but the ruins have already vanished. There is nothing left to return to! +You watch as the clouds swallow the ruins. In time all things are erased! +You step forward into the unknown leaving the ruins behind forever! +A single path remains before you untouched by time., Follow it, 375, Turn back one last time, 376 +The path leads nowhere stretching into eternity. You have reached the end! +You turn back but the ruins have already vanished. There is nothing left to return to! A faint whisper calls your name from the distance., Answer, 377, Ignore it and walk on, 378 The whisper fades before you can understand it. The past holds no answers! -You do not turn back, allowing the ruins to be forgotten at last! +You do not turn back allowing the ruins to be forgotten at last! The ground beneath you shifts as the final remnants of the ruins crumble., Watch the collapse, 379, Keep moving forward, 380 -You witness the last stone fall, and with it, the last memory of the temple! -You do not look back. The past is gone, and the future calls you elsewhere! +You witness the last stone fall and with it the last memory of the temple! +You do not look back. The past is gone and the future calls you elsewhere! A single stone remains where the temple once stood., Touch it, 381, Leave it undisturbed, 382 -The stone crumbles at your touch, scattering into dust! -You walk away, leaving the last remnant of the temple to time! -The air grows still, as if the world itself has forgotten this place., Breathe deeply, 383, Keep walking without hesitation, 384 -You take in the silence, knowing you are the last to stand here! -You walk forward, leaving no trace behind. The ruins are gone, and so are you! -The sky above clears, revealing an endless horizon., Step toward the horizon, 385, Close your eyes and rest, 386 -You walk toward the horizon, knowing there is nothing left to find! -You sit in the silence, letting time erase the past and the future alike! +The stone crumbles at your touch scattering into dust! +You walk away leaving the last remnant of the temple to time! +The air grows still as if the world itself has forgotten this place., Breathe deeply, 383, Keep walking without hesitation, 384 +You take in the silence knowing you are the last to stand here! +You walk forward leaving no trace behind. The ruins are gone and so are you! +The sky above clears revealing an endless horizon., Step toward the horizon, 385, Close your eyes and rest, 386 +You walk toward the horizon knowing there is nothing left to find! +You sit in the silence letting time erase the past and the future alike! The wind carries the last echoes of the ruins away., Listen one final time, 387, Turn and walk without hesitation, 388 -The whispering fades, leaving only the vast, empty world before you! -You move forward, never looking back, letting the ruins fade from memory! +The whispering fades leaving only the vast empty world before you! +You move forward never looking back letting the ruins fade from memory! A final shadow flickers at the edge of your vision., Acknowledge it, 389, Ignore it and move on, 390 -The shadow vanishes, just another trick of the past slipping away! -You continue forward, the ruins lost forever behind you! +The shadow vanishes just another trick of the past slipping away! +You continue forward the ruins lost forever behind you! The path you walk leads to an unknown place., Follow it without question, 391, Look back one last time, 392 -The unknown stretches ahead, vast and limitless. Your journey continues! -You hesitate, but the past has already been erased. The ruins no longer exist! +The unknown stretches ahead vast and limitless. Your journey continues! +You hesitate but the past has already been erased. The ruins no longer exist! The sky above shimmers as the last remnants of magic dissolve., Watch the sky, 393, Keep walking, 394 You witness the final trace of the temple fade into nothingness! -You do not stop, leaving the past where it belongs! -The ruins whisper a name—perhaps yours, perhaps another’s., Speak your own name, 395, Stay silent, 396 -The whisper fades, its meaning lost to time! -You leave in silence, carrying no answers, only the weight of what once was! +You do not stop leaving the past where it belongs! +The ruins whisper a name—perhaps yours perhaps another’s., Speak your own name, 395, Stay silent, 396 +The whisper fades its meaning lost to time! +You leave in silence carrying no answers only the weight of what once was! A single doorway stands in the vast emptiness ahead., Step through it, 397, Turn away, 398 -You step forward, into something new, leaving all else behind! -You turn away, knowing the past must remain buried! -The ground beneath you is firm, untouched by what once was., Stand still and reflect, 399, Walk toward the unknown, 400 -You reflect for a moment, then move on, never to return! -You take the first step into an uncertain future, free of the past! +You step forward into something new leaving all else behind! +You turn away knowing the past must remain buried! +The ground beneath you is firm untouched by what once was., Stand still and reflect, 399, Walk toward the unknown, 400 +You reflect for a moment then move on never to return! +You take the first step into an uncertain future free of the past! A gust of wind stirs the dust where the temple once stood., Watch it settle, 401, Keep walking, 402 -The dust settles, the ruins gone, their secrets buried forever! -You do not stop. The ruins were only a moment in time, already forgotten! +The dust settles the ruins gone their secrets buried forever! +You do not stop. The ruins were only a moment in time already forgotten! The last traces of the ruins drift away on the wind., Breathe in the air of a new beginning, 403, Walk away without another thought, 404 -You inhale deeply, feeling the weight of the past lift from your shoulders! -You continue forward, leaving everything behind as if it never existed! +You inhale deeply feeling the weight of the past lift from your shoulders! +You continue forward leaving everything behind as if it never existed! The stars above seem brighter now that the ruins are gone., Gaze up at them, 405, Keep your focus on the road ahead, 406 -You stare at the stars, feeling small yet free beneath their endless glow! +You stare at the stars feeling small yet free beneath their endless glow! You do not waver. The future is yours to shape without the ruins’ shadow! -The wind shifts, carrying a faint, final sound., Listen closely, 407, Ignore it and move on, 408 -The sound fades, indistinguishable, and then silence remains! -You press on, knowing some things are best left unheard! +The wind shifts carrying a faint final sound., Listen closely, 407, Ignore it and move on, 408 +The sound fades indistinguishable and then silence remains! +You press on knowing some things are best left unheard! A single path stretches endlessly before you., Walk it without hesitation, 409, Pause and take one last look back, 410 -You follow the path into the unknown, with nothing left to hold you back! -You hesitate, but the ruins have already vanished from existence! +You follow the path into the unknown with nothing left to hold you back! +You hesitate but the ruins have already vanished from existence! A faint glow shimmers on the horizon., Approach it, 411, Walk in the opposite direction, 412 -You move toward the light, stepping into the dawn of a new era! -You leave the glow behind, choosing your own way forward! -The echoes of the ruins are gone, but something lingers within you., Accept the feeling, 413, Try to shake it off, 414 -You carry the ruins within, a quiet memory that will never fade! -You push away the thoughts, determined to start anew! -A crossroads appears, though you do not remember it being there., Choose a direction and keep walking, 415, Stand still and wait, 416 -You walk a path unknown, yet somehow meant for you! -You wait, but nothing comes. The choice is yours alone to make! -The sky above is clear, free of the temple’s shadows., Take comfort in it, 417, Keep your gaze on the path ahead, 418 -You find peace in the open sky, no longer bound by the past! -You do not stop, moving forward without hesitation! -The ruins have vanished, but their memory remains., Carry the memory forward, 419, Let it fade into the past, 420 -You preserve the knowledge, ensuring the story is never forgotten! -You release the past, allowing yourself to move on without regret! -The road ahead is open, free from the burdens of the temple., Walk toward the horizon, 421, Take a moment to reflect, 422 -You embrace the unknown, stepping into a new journey! -You pause, realizing how much you have changed, then continue forward! +You move toward the light stepping into the dawn of a new era! +You leave the glow behind choosing your own way forward! +The echoes of the ruins are gone but something lingers within you., Accept the feeling, 413, Try to shake it off, 414 +You carry the ruins within a quiet memory that will never fade! +You push away the thoughts determined to start anew! +A crossroads appears though you do not remember it being there., Choose a direction and keep walking, 415, Stand still and wait, 416 +You walk a path unknown yet somehow meant for you! +You wait but nothing comes. The choice is yours alone to make! +The sky above is clear free of the temple’s shadows., Take comfort in it, 417, Keep your gaze on the path ahead, 418 +You find peace in the open sky no longer bound by the past! +You do not stop moving forward without hesitation! +The ruins have vanished but their memory remains., Carry the memory forward, 419, Let it fade into the past, 420 +You preserve the knowledge ensuring the story is never forgotten! +You release the past allowing yourself to move on without regret! +The road ahead is open free from the burdens of the temple., Walk toward the horizon, 421, Take a moment to reflect, 422 +You embrace the unknown stepping into a new journey! +You pause realizing how much you have changed then continue forward! A gentle breeze stirs the dust where the ruins once stood., Feel the breeze and smile, 423, Turn away from it, 424 -You welcome the wind as a sign of renewal, ready for what comes next! -You turn your back on it, setting your own course from here! +You welcome the wind as a sign of renewal ready for what comes next! +You turn your back on it setting your own course from here! A quiet feeling of closure settles within you., Accept it, 425, Question if it's truly over, 426 -You let peace take hold, knowing the story has reached its end! -You hesitate, but no more answers come. The temple is truly gone! -The night is calm, without the whispers of the past., Rest beneath the stars, 427, Keep walking, 428 -You fall into a deep, dreamless sleep, unburdened at last! -You walk until the horizon swallows you, the past left behind! +You let peace take hold knowing the story has reached its end! +You hesitate but no more answers come. The temple is truly gone! +The night is calm without the whispers of the past., Rest beneath the stars, 427, Keep walking, 428 +You fall into a deep dreamless sleep unburdened at last! +You walk until the horizon swallows you the past left behind! A single thought lingers—was it all real?, Dismiss the thought, 429, Hold onto it as a mystery, 430 You shake your head and move on. It no longer matters! -You keep the mystery alive, a secret that only you will ever know! -A fork in the path awaits, though neither road is familiar., Choose one without looking back, 431, Stop and consider the options, 432 -You take the first step forward, unafraid of what lies ahead! -You weigh your choices, but in the end, all roads lead forward! -For the first time in a long while, you are free., Embrace your freedom, 433, Wonder if you will ever return, 434 -You walk forward with a light heart, unchained from the past! -You glance back one last time, but the ruins are gone forever! -The ruins exist now only in memory, their mysteries sealed forever., Let go of the past, 435, Keep the knowledge alive, 436 +You keep the mystery alive a secret that only you will ever know! +A fork in the path awaits though neither road is familiar., Choose one without looking back, 431, Stop and consider the options, 432 +You take the first step forward unafraid of what lies ahead! +You weigh your choices but in the end all roads lead forward! +For the first time in a long while you are free., Embrace your freedom, 433, Wonder if you will ever return, 434 +You walk forward with a light heart unchained from the past! +You glance back one last time but the ruins are gone forever! +The ruins exist now only in memory their mysteries sealed forever., Let go of the past, 435, Keep the knowledge alive, 436 You release the past and step into the future with no regrets! -You become a guardian of the forgotten, ensuring the story is never truly lost! -A quiet road stretches ahead, untouched by time., Walk forward without hesitation, 437, Look back one last time, 438 -You step into the unknown, ready for a new chapter! -You see nothing but empty land where the ruins once stood, and turn away! +You become a guardian of the forgotten ensuring the story is never truly lost! +A quiet road stretches ahead untouched by time., Walk forward without hesitation, 437, Look back one last time, 438 +You step into the unknown ready for a new chapter! +You see nothing but empty land where the ruins once stood and turn away! The weight of the past finally lifts from your shoulders., Accept the peace, 439, Question what comes next, 440 -You find comfort in the quiet, knowing you have done all you could! -You wonder if the ruins will ever return, but that is a question for another time! -The whispers are gone, replaced by the sound of the wind., Let the wind guide you, 441, Stand still and listen, 442 -You walk forward, allowing fate to take you wherever it may! -You listen, but there is only silence. The story has ended! +You find comfort in the quiet knowing you have done all you could! +You wonder if the ruins will ever return but that is a question for another time! +The whispers are gone replaced by the sound of the wind., Let the wind guide you, 441, Stand still and listen, 442 +You walk forward allowing fate to take you wherever it may! +You listen but there is only silence. The story has ended! A distant light flickers on the horizon., Follow it, 443, Ignore it and continue walking, 444 -You step toward the light, uncertain but unafraid! -You leave the light behind, choosing your own path instead! +You step toward the light uncertain but unafraid! +You leave the light behind choosing your own path instead! The stars above seem brighter than before., Take it as a sign, 445, Ignore it and move on, 446 -You smile, knowing that even in endings, there is beauty! -You walk forward, leaving the past fully behind! +You smile knowing that even in endings there is beauty! +You walk forward leaving the past fully behind! A lone traveler crosses your path., Speak to them, 447, Continue on your way, 448 -You exchange stories, but nothing will ever match what you have seen! -You pass by without a word, your journey yours alone! -The ruins may be gone, but something deep inside you has changed., Accept the change, 449, Resist it and try to return to normal, 450 +You exchange stories but nothing will ever match what you have seen! +You pass by without a word your journey yours alone! +The ruins may be gone but something deep inside you has changed., Accept the change, 449, Resist it and try to return to normal, 450 You embrace the person you have become and move forward! -You try to return to who you were, but some journeys cannot be undone! +You try to return to who you were but some journeys cannot be undone! The last echoes of the ruins fade from your mind., Walk away without looking back, 451, Try to remember every detail, 452 -You leave the past behind, knowing some mysteries are meant to be forgotten! -You cling to the memories, but time erodes even the deepest truths! +You leave the past behind knowing some mysteries are meant to be forgotten! +You cling to the memories but time erodes even the deepest truths! A cold wind brushes against your skin as you step forward., Accept the chill as a farewell, 453, Wrap yourself in your cloak and move on, 454 -You take a deep breath and walk into the unknown, ready for what comes next! -The warmth of your cloak brings comfort, but your journey is done! +You take a deep breath and walk into the unknown ready for what comes next! +The warmth of your cloak brings comfort but your journey is done! The road stretches endlessly before you., Keep walking without pause, 455, Find a place to rest, 456 -You walk on, the past behind you and the future unwritten! -You rest, but the ruins still linger in your thoughts before sleep takes you! +You walk on the past behind you and the future unwritten! +You rest but the ruins still linger in your thoughts before sleep takes you! The night sky glows with unfamiliar constellations., Marvel at the sight, 457, Keep your focus ahead, 458 -The stars remind you that the world is vast, and your story is but one of many! -You press forward, unshaken by what you have left behind! +The stars remind you that the world is vast and your story is but one of many! +You press forward unshaken by what you have left behind! A faint song echoes through the air., Follow the melody, 459, Ignore it and continue walking, 460 -You listen and follow, but the song fades, leaving only silence! -You walk on, ignoring the call of the unknown! +You listen and follow but the song fades leaving only silence! +You walk on ignoring the call of the unknown! The road comes to a fork., Take the left path, 461, Take the right path, 462 -Each step takes you further from what once was, and that is enough! -No matter which way you turn, your journey is your own! +Each step takes you further from what once was and that is enough! +No matter which way you turn your journey is your own! A small village appears in the distance., Approach it, 463, Walk past it and continue alone, 464 -You find peace among strangers, a quiet place to call home! -You choose solitude, letting the road take you wherever it may! +You find peace among strangers a quiet place to call home! +You choose solitude letting the road take you wherever it may! The wind carries the last remnants of the past away., Whisper a farewell, 465, Say nothing and move on, 466 Your whispered goodbye is the final closure you needed! You say nothing but deep inside you know it is over!