import gradio as gr import json import os import tempfile import shutil import zipfile from relatively_constant_variables import finished_product_demo, all_states class Player: def __init__(self): self.inventory = [] self.money = 20 self.knowledge = {} def add_item(self, item): self.inventory.append(item) def has_item(self, item): return item in self.inventory def update_knowledge(self, topic): self.knowledge[topic] = True class GameSession: def __init__(self, starting_location='village', starting_state='start'): self.player = Player() self.current_location = starting_location self.current_state = starting_state self.game_log = [] def make_choice(self, choice_index): state = all_states[self.current_location][self.current_state] if 0 <= choice_index < len(state['choices']): choice = state['choices'][choice_index] next_state = state['transitions'][choice] self.game_log.append(f"You chose: {choice}") self.game_log.append(state['description']) if 'consequences' in state and choice in state['consequences']: if state['consequences'][choice]: state['consequences'][choice](self.player) else: # Handle empty consequence, e.g., log a message or provide a default action print(f"No consequence for choice: {choice}") # You can add any default action here if needed if '_' in next_state: self.current_location, self.current_state = next_state.split('_') else: self.current_state = next_state return self.get_current_state_info() else: return "Invalid choice. Please try again." def get_current_state_info(self): state = all_states[self.current_location][self.current_state] choices = [f"{idx + 1}. {choice}" for idx, choice in enumerate(state['choices'])] return state['description'], choices, "\n".join(self.game_log) def get_current_state_media(self): media = all_states[self.current_location][self.current_state]['media'] return media def start_game(starting_location='village', starting_state='start', new_states=all_states): global all_states game_session = GameSession(starting_location, starting_state) description, choices, game_log = game_session.get_current_state_info() all_states = new_states return description, choices, game_log, game_session def make_choice(choice, game_session, with_media=False): #Calls the nested make choice function in the game session class if not choice: description, choices, game_log = game_session.get_current_state_info() return description, choices, "Please select a choice before proceeding.", game_session choice_index = int(choice.split('.')[0]) - 1 result = game_session.make_choice(choice_index) if with_media: media = game_session.get_current_state_media() return result[0], gr.update(choices=result[1]), result[2], game_session, media else: return result[0], gr.update(choices=result[1]), result[2], game_session def validate_transitions(all_states): errors = [] for location, states in all_states.items(): for state_key, state in states.items(): for transition_key, transition_state in state['transitions'].items(): # Check if the transition is to another location if transition_state in all_states: trans_location, trans_state = transition_state, 'start' # Assuming 'start' state for new locations elif '_' in transition_state: trans_location, trans_state = transition_state.split('_') else: trans_location, trans_state = location, transition_state # Validate the transition state if trans_location not in all_states or trans_state not in all_states[trans_location]: errors.append(f"Invalid transition from {location}.{state_key} to {trans_location}.{trans_state}") return errors path_errors = validate_transitions(all_states) if path_errors: for error in path_errors: print(error) else: print("All transitions are valid.") def load_game(custom_config=None, with_media=False): global all_states if not custom_config: return gr.update(value="No custom configuration provided."), None, None, None, None, None, None try: new_config = json.loads(custom_config) all_states = new_config # Determine the starting location and state starting_location = next(iter(all_states.keys())) starting_state = next(iter(all_states[starting_location].keys())) print(f"Starting location: {starting_location}, Starting state: {starting_state}") game_session = GameSession(starting_location, starting_state) description, choices, game_log = game_session.get_current_state_info() new_path_errors = validate_transitions(all_states) output_media = [] if with_media: media_list = all_states[starting_location][starting_state].get('media', []) print(f"Media list: {media_list}") if media_list: for media_path in media_list: #media_component = create_media_component(media_path) output_media.append(media_path) print(f"Created {len(output_media)} media components") success_message = f"Custom configuration loaded successfully!\n{new_path_errors}" return ( gr.update(value=success_message), game_log, description, gr.update(choices=choices), gr.update(value=custom_config), game_session, output_media if with_media else None ) except json.JSONDecodeError as e: error_message = format_json_error(custom_config, e) return gr.update(value=error_message), None, None, None, gr.update(value=custom_config), None, None except Exception as e: error_message = f"Error loading custom configuration: {str(e)}" return gr.update(value=error_message), None, None, None, gr.update(value=custom_config), None, None def load_game_edit_version(custom_config=None, with_media=False, custom_starting_location=None, custom_starting_state=None): global all_states if not custom_config: return gr.update(value="No custom configuration provided."), None, None, None, None, None, None try: new_config = json.loads(custom_config) all_states = new_config # Determine the starting location and state if custom_starting_location and custom_starting_state: if custom_starting_location not in all_states or custom_starting_state not in all_states[custom_starting_location]: raise ValueError(f"Invalid custom starting point: {custom_starting_location}, {custom_starting_state}") starting_location = custom_starting_location starting_state = custom_starting_state else: starting_location = next(iter(all_states.keys())) starting_state = next(iter(all_states[starting_location].keys())) print(f"Starting location: {starting_location}, Starting state: {starting_state}") game_session = GameSession(starting_location, starting_state) description, choices, game_log = game_session.get_current_state_info() new_path_errors = validate_transitions(all_states) output_media = [] if with_media: media_list = all_states[starting_location][starting_state].get('media', []) print(f"Media list: {media_list}") if media_list: for media_path in media_list: output_media.append(media_path) print(f"Created {len(output_media)} media components") success_message = f"Custom configuration loaded successfully!\n{new_path_errors}" return ( gr.update(value=success_message), game_log, description, gr.update(choices=choices), gr.update(value=custom_config), game_session, output_media if with_media else None ) except json.JSONDecodeError as e: error_message = format_json_error(custom_config, e) return gr.update(value=error_message), None, None, None, gr.update(value=custom_config), None, None except Exception as e: error_message = f"Error loading custom configuration: {str(e)}" return gr.update(value=error_message), None, None, None, gr.update(value=custom_config), None, None media_folder = os.path.abspath("saved_media") #make sure same as SAVE_DIR below def export_config_with_media(config_json): global media_folder """ Export the config JSON and zip it along with any files referenced in the media fields. :param config_json: JSON string containing the config :param media_folder: Path to the folder containing media files :return: Path to the created zip file """ # Parse the JSON config = json.loads(config_json) # Create a temporary directory to store files for zipping with tempfile.TemporaryDirectory() as temp_dir: # Save the config JSON to the temp directory config_path = os.path.join(temp_dir, 'config.json') with open(config_path, 'w') as f: json.dump(config, f, indent=2) # Collect all media files media_files = set() for location in config.values(): if isinstance(location, dict): for sublocation in location.values(): if isinstance(sublocation, dict) and 'media' in sublocation: media_files.update(sublocation['media']) # Copy media files to the temp directory for media_file in media_files: src_path = os.path.join(media_folder, media_file) if os.path.exists(src_path): dst_path = os.path.join(temp_dir, media_file) shutil.copy2(src_path, dst_path) else: print(f"Warning: Media file not found: {media_file}") # Create a zip file zip_path = os.path.join(os.path.dirname(media_folder), 'config_with_media.zip') with zipfile.ZipFile(zip_path, 'w') as zipf: for root, _, files in os.walk(temp_dir): for file in files: file_path = os.path.join(root, file) arcname = os.path.relpath(file_path, temp_dir) zipf.write(file_path, arcname) return zip_path def format_json_error(config, error): lineno, colno = error.lineno, error.colno lines = config.split('\n') error_line = lines[lineno - 1] if lineno <= len(lines) else "" pointer = ' ' * (colno - 1) + '^' return f"""Invalid JSON format in custom configuration: Error at line {lineno}, column {colno}: {error_line} {pointer} Error details: {str(error)}""" def display_website(link): html = f"" gr.Info("If 404 then the space/page has probably been disabled - normally due to a better alternative") return html initgameinfo = start_game() fpeinitgameinfo = start_game(new_states=finished_product_demo)