mirror of
https://github.com/TheCommsChannel/TC2-BBS-mesh.git
synced 2025-03-05 20:51:53 -08:00
Merge 9f2b60b9f2
into 69d74e33dd
This commit is contained in:
commit
a5e65d7c9c
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -6,3 +6,4 @@ venv/
|
|||
.idea
|
||||
config.ini
|
||||
fortunes.txt
|
||||
offline_game_tester.py
|
||||
|
|
|
@ -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 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
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@ import configparser
|
|||
import logging
|
||||
import random
|
||||
import time
|
||||
import os
|
||||
|
||||
from meshtastic import BROADCAST_NUM
|
||||
|
||||
|
@ -46,6 +47,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':
|
||||
|
@ -665,3 +668,284 @@ 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."""
|
||||
|
||||
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
|
||||
|
||||
game_titles = list(games_available.keys()) # Display titles
|
||||
game_filenames = list(games_available.values()) # Actual filenames
|
||||
|
||||
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."""
|
||||
|
||||
# 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]
|
||||
|
||||
# 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})
|
||||
|
||||
# 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 = 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}")
|
||||
|
||||
# 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")
|
||||
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:] # 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"
|
||||
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)
|
||||
|
||||
# Ensure user state is properly tracked
|
||||
update_user_state(sender_id, {
|
||||
'command': 'IN_GAME',
|
||||
'step': 3,
|
||||
'game': game_name,
|
||||
'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."""
|
||||
|
||||
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
|
||||
|
||||
segment = game_map[game_position]
|
||||
|
||||
# Extract the storyline and choices
|
||||
storyline = segment[0]
|
||||
choices = segment[1:]
|
||||
|
||||
# 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)
|
||||
return
|
||||
|
||||
try:
|
||||
choice_index = int(message) - 1
|
||||
|
||||
if choice_index < 0 or choice_index * 2 + 1 >= len(choices):
|
||||
send_message("Invalid selection. Please enter a valid number.", sender_id, interface)
|
||||
return
|
||||
|
||||
target_position = int(choices[choice_index * 2 + 1])
|
||||
|
||||
if target_position not in game_map:
|
||||
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
|
||||
|
||||
# ✅ 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)
|
|
@ -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
|
||||
|
||||
|
||||
##########################
|
||||
|
|
62
game_line_offset_fix.py
Normal file
62
game_line_offset_fix.py
Normal file
|
@ -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()
|
71
gamefile_syntax.md
Normal file
71
gamefile_syntax.md
Normal file
|
@ -0,0 +1,71 @@
|
|||
# 📜 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**.
|
||||
- If present, the game will consider the title line as line 0.
|
||||
- Line 1 is always 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 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.**
|
||||
|
||||
---
|
||||
|
||||
## 📌 **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!
|
||||
|
471
games/forgotten_ruins_gpt.csv
Normal file
471
games/forgotten_ruins_gpt.csv
Normal file
|
@ -0,0 +1,471 @@
|
|||
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
|
||||
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
|
||||
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 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
|
||||
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
|
||||
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., 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., 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
|
||||
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!
|
Can't render this file because it contains an unexpected character in line 1 and column 7.
|
8
games/lost_forest.csv
Normal file
8
games/lost_forest.csv
Normal file
|
@ -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
|
Can't render this file because it contains an unexpected character in line 1 and column 7.
|
12
games/the_shortest_game.csv
Normal file
12
games/the_shortest_game.csv
Normal file
|
@ -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.
|
Can't render this file because it contains an unexpected character in line 1 and column 7.
|
|
@ -1,4 +1,5 @@
|
|||
import logging
|
||||
import os
|
||||
|
||||
from meshtastic import BROADCAST_NUM
|
||||
|
||||
|
@ -8,11 +9,17 @@ from command_handlers import (
|
|||
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 @@ bbs_menu_handlers = {
|
|||
|
||||
|
||||
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 @@ board_action_handlers = {
|
|||
"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,26 @@ 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:
|
||||
# ✅ **Fix: Ensure Games Menu Loads After Exiting a Game**
|
||||
if state and state['command'] == 'IN_GAME':
|
||||
logging.debug(f"🎮 User {sender_id} is in-game, processing game command...")
|
||||
|
||||
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
|
||||
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 +143,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 +157,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 +215,7 @@ 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':
|
||||
|
|
|
@ -14,6 +14,13 @@ other BBS servers listed in the config.ini file.
|
|||
|
||||
import logging
|
||||
import time
|
||||
import sys
|
||||
import io
|
||||
import locale
|
||||
|
||||
# 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
|
||||
|
@ -45,7 +52,7 @@ def display_banner():
|
|||
██║ ██║ ██╔═══╝ ╚════╝██╔══██╗██╔══██╗╚════██║
|
||||
██║ ╚██████╗███████╗ ██████╔╝██████╔╝███████║
|
||||
╚═╝ ╚═════╝╚══════╝ ╚═════╝ ╚═════╝ ╚══════╝
|
||||
Meshtastic Version
|
||||
Meshtastic Version WITH GAMES
|
||||
"""
|
||||
print(banner)
|
||||
|
||||
|
|
122
validate_game.py
Normal file
122
validate_game.py
Normal file
|
@ -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()
|
||||
|
Loading…
Reference in a new issue