awacke1's picture
Update index.html
10de4ed verified
raw
history blame
21.2 kB
def update_game(choice, game_state_json):
"""Update game based on player choice"""
try: # Wrap major logic in try-except
if not choice: # Handle empty choice if dropdown value is cleared
# Return updates that don't change anything
return gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update()
# Parse game state from JSON
game_state_dict = json.loads(game_state_json)
game_state = GameState.from_dict(game_state_dict)
# Get current page data
current_page = game_state.current_page
current_page_str = str(current_page)
if current_page_str not in game_data:
# Handle invalid page number
error_content = f"<p>Error: Reached invalid page number {current_page}. Resetting game.</p>"
svg_error = create_svg_illustration("default")
new_game_state = GameState()
return (svg_error, error_content, gr.Dropdown(choices=["Restart"], label="Error", value=None), new_game_state.to_json(),
generate_stats_display(new_game_state), generate_inventory_display(new_game_state), "Error - Resetting")
page_data = game_data[current_page_str]
# Find the selected option
selected_option = None
current_options_list = page_data.get("options", [])
for opt in current_options_list:
# Ensure 'text' key exists in option dictionary
if isinstance(opt, dict) and opt.get("text") == choice:
selected_option = opt
break
# Check alternative option if it was displayed and chosen
alt_opt_data = page_data.get("alternativeOption")
if not selected_option and isinstance(alt_opt_data, dict) and alt_opt_data.get("text") == choice:
# Check if the alternative option should have been available
# This logic needs refinement: check if primary options were filtered out *and* alt condition met
alt_show_if = alt_opt_data.get("showIf")
current_inventory = game_state.inventory
primary_options_available = False
if current_options_list:
for opt in current_options_list:
is_available = True
req_item = opt.get("requireItem")
if req_item and req_item not in current_inventory:
is_available = False
req_any = opt.get("requireAnyItem")
if is_available and req_any and not any(i in current_inventory for i in req_any):
is_available = False
if is_available:
primary_options_available = True
break # Found at least one available primary option
# Select alternative only if no primary options were available AND showIf condition is met
if not primary_options_available and (not alt_show_if or any(item in current_inventory for item in alt_show_if)):
selected_option = alt_opt_data
if selected_option is None:
# Handle case where choice might be "Restart" or invalid
if choice == "Restart":
return initialize_game()
else:
# Stay on the same page if the choice wasn't found
options = [opt.get("text", "Invalid Option") for opt in current_options_list]
if alt_opt_data and alt_opt_data.get("text"): # Add alternative option text if it exists
# Check showIf condition for alternative option display
alt_show_if = alt_opt_data.get("showIf")
current_inventory = game_state.inventory
if not alt_show_if or any(item in current_inventory for item in alt_show_if):
# Check if primary options were filtered out
primary_options_available = False
if current_options_list:
# Re-check primary options availability
for opt in current_options_list:
is_available = True
req_item = opt.get("requireItem")
if req_item and req_item not in current_inventory: is_available = False
req_any = opt.get("requireAnyItem")
if is_available and req_any and not any(i in current_inventory for i in req_any): is_available = False
if is_available:
primary_options_available = True
break
if not primary_options_available:
options.append(alt_opt_data["text"])
if not options: options = ["Restart"]
svg_content = create_svg_illustration(page_data.get("illustration", "default"))
content = f"<h2>{page_data.get('title', 'Error')}</h2>" + page_data.get("content", "") + f"<p><i>Debug: Choice '{choice}' not matched to any available option.</i></p>"
stats_display = generate_stats_display(game_state)
inventory_display = generate_inventory_display(game_state)
story_path_display = f"You remain on page {current_page}: {page_data.get('title', 'Error')}"
return svg_content, content, gr.Dropdown(choices=options, value=None), game_state.to_json(), stats_display, inventory_display, story_path_display
# --- Option requirement checks ---
item_required = selected_option.get("requireItem")
any_item_required = selected_option.get("requireAnyItem")
current_inventory = game_state.inventory # Cache inventory
# Check specific item requirement
if item_required and item_required not in current_inventory:
content = f"<h2>{page_data['title']}</h2>" + page_data["content"]
content += f"<p style='color: red;'>You need the <strong>{item_required}</strong> for this option, but you don't have it.</p>"
options_texts = [opt.get("text") for opt in page_data.get("options", []) if opt.get("text")]
# Check if alt option should be added back
if alt_opt_data and alt_opt_data.get("text"):
alt_show_if = alt_opt_data.get("showIf")
if not alt_show_if or any(item in current_inventory for item in alt_show_if):
primary_available_check = any(
opt.get("text") and (not opt.get("requireItem") or opt.get("requireItem") in current_inventory) and \
(not opt.get("requireAnyItem") or any(i in current_inventory for i in opt.get("requireAnyItem")))
for opt in page_data.get("options", [])
)
if not primary_available_check: options_texts.append(alt_opt_data["text"])
if not options_texts: options_texts = ["Restart"]
svg_content = create_svg_illustration(page_data.get("illustration", "default"))
stats_display = generate_stats_display(game_state)
inventory_display = generate_inventory_display(game_state)
story_path_display = f"You remain on page {current_page}: {page_data['title']}"
return svg_content, content, gr.Dropdown(choices=list(set(options_texts)), value=None), game_state.to_json(), stats_display, inventory_display, story_path_display # Use set to remove duplicates
# Check 'any item' requirement
if any_item_required and not any(item in current_inventory for item in any_item_required):
item_list = ", ".join(f"'{item}'" for item in any_item_required)
content = f"<h2>{page_data['title']}</h2>" + page_data["content"]
content += f"<p style='color: red;'>You need one of the following items for this option: <strong>{item_list}</strong>, but you don't have any.</p>"
options_texts = [opt.get("text") for opt in page_data.get("options", []) if opt.get("text")]
# Check if alt option should be added back (similar logic as above)
if alt_opt_data and alt_opt_data.get("text"):
alt_show_if = alt_opt_data.get("showIf")
if not alt_show_if or any(item in current_inventory for item in alt_show_if):
primary_available_check = any(
opt.get("text") and (not opt.get("requireItem") or opt.get("requireItem") in current_inventory) and \
(not opt.get("requireAnyItem") or any(i in current_inventory for i in opt.get("requireAnyItem")))
for opt in page_data.get("options", [])
)
if not primary_available_check: options_texts.append(alt_opt_data["text"])
if not options_texts: options_texts = ["Restart"]
svg_content = create_svg_illustration(page_data.get("illustration", "default"))
stats_display = generate_stats_display(game_state)
inventory_display = generate_inventory_display(game_state)
story_path_display = f"You remain on page {current_page}: {page_data['title']}"
return svg_content, content, gr.Dropdown(choices=list(set(options_texts)), value=None), game_state.to_json(), stats_display, inventory_display, story_path_display
# --- Process valid choice ---
item_to_add = selected_option.get("addItem")
if item_to_add and item_to_add not in game_state.inventory:
game_state.inventory.append(item_to_add)
item_data = items_data.get(item_to_add, {})
if item_data.get("type") == "consumable" and item_data.get("useOnAdd"):
if "hpRestore" in item_data:
hp_before = game_state.current_hp
game_state.current_hp = min(game_state.max_hp, game_state.current_hp + item_data["hpRestore"])
# Maybe add feedback about using the item? Add later if needed.
game_state.inventory.remove(item_to_add) # Remove consumable after use
# Move to the next page (initial move before battle/challenge checks)
next_page = selected_option["next"]
next_page_str = str(next_page)
if next_page_str not in game_data:
error_content = f"<p>Error: Option leads to invalid page number {next_page}. Resetting game.</p>"
svg_error = create_svg_illustration("default")
new_game_state = GameState()
return (svg_error, error_content, gr.Dropdown(choices=["Restart"], label="Error", value=None), new_game_state.to_json(),
generate_stats_display(new_game_state), generate_inventory_display(new_game_state), "Error - Resetting")
# --- Battle Check ---
battle_occurred = False
battle_message = ""
battle_result = True
# Check random battle flag on the *current* page data (page_data before challenge potentially changes it)
if page_data.get("randomBattle", False) and random.random() < 0.2:
battle_result, battle_log = simulate_battle(game_state)
battle_occurred = True
if not battle_result:
content = "<h2>Game Over</h2><p>You have been defeated in battle!</p>" + battle_log
stats_display = generate_stats_display(game_state)
inventory_display = generate_inventory_display(game_state)
return create_svg_illustration("game-over"), content, gr.Dropdown(choices=["Restart"], label="Game Over", value=None), game_state.to_json(), stats_display, inventory_display, "Game Over - You were defeated in battle."
else:
battle_message = f"<div style='border: 1px solid green; padding: 10px; margin-top: 10px; background-color: #e8f5e9;'><strong>Battle Won!</strong>{battle_log}</div>"
# --- Set current page to the determined next page (before challenge check) ---
game_state.current_page = next_page
if next_page not in game_state.visited_pages:
game_state.visited_pages.append(next_page)
# Update journey progress
game_state.journey_progress += 5
if game_state.journey_progress > 100: game_state.journey_progress = 100
# Get data for the potentially final destination page (next_page_str)
page_data = game_data[next_page_str] # Now refers to the data of the page we are landing on
# --- HP Loss / Stat Increase on Landing Page ---
stat_increase = page_data.get("statIncrease")
if stat_increase:
stat = stat_increase.get("stat")
amount = stat_increase.get("amount")
if stat and amount and stat in game_state.stats:
game_state.stats[stat] += amount
hp_loss = page_data.get("hpLoss")
if hp_loss:
game_state.current_hp -= hp_loss
if game_state.current_hp <= 0:
game_state.current_hp = 0
content = "<h2>Game Over</h2><p>You have died from your wounds!</p>"
stats_display = generate_stats_display(game_state)
inventory_display = generate_inventory_display(game_state)
return create_svg_illustration("game-over"), content, gr.Dropdown(choices=["Restart"], label="Game Over", value=None), game_state.to_json(), stats_display, inventory_display, "Game Over - You died from your wounds."
# --- Challenge Check on Landing Page ---
challenge_log = ""
challenge_occurred = False
challenge = page_data.get("challenge")
if challenge:
challenge_occurred = True
req_stat = challenge.get("stat")
req_difficulty = challenge.get("difficulty")
req_success_page = challenge.get("success")
req_failure_page = challenge.get("failure")
if not all([req_stat, req_difficulty, req_success_page is not None, req_failure_page is not None]): # Check pages can be 0
challenge_log = "<div style='color: orange;'>Challenge data incomplete.</div>"
challenge = None # Skip challenge processing
else:
success, roll, total = perform_challenge(game_state, challenge)
challenge_log = f"<div style='border: 1px solid #ccc; padding: 10px; margin-top: 10px; background-color: #eee;'>"
challenge_log += f"<strong>Challenge: {challenge.get('title', 'Skill Check')}</strong><br>"
challenge_log += f"Target Stat: {req_stat}, Difficulty: {req_difficulty}<br>"
stat_val_before_roll = total - roll # Stat value used in the roll
challenge_log += f"You rolled a {roll} + ({stat_val_before_roll} {req_stat}) = {total}<br>"
if success:
challenge_log += "<strong style='color: green;'>Success!</strong></div>"
next_page = req_success_page
else:
# *** THIS IS THE CORRECTED LINE (around line 208 of app.py) ***
challenge_log += "<strong style='color: red;'>Failure!</strong></div>"
# **************************************************************
next_page = req_failure_page
# Update game state again based on challenge outcome
game_state.current_page = next_page
next_page_str = str(next_page)
if next_page_str not in game_data:
error_content = f"<p>Error: Challenge outcome leads to invalid page {next_page}. Resetting.</p>"
svg_error = create_svg_illustration("default")
new_game_state = GameState()
return (svg_error, error_content, gr.Dropdown(choices=["Restart"], label="Error", value=None), new_game_state.to_json(),
generate_stats_display(new_game_state), generate_inventory_display(new_game_state), "Error - Resetting")
if next_page not in game_state.visited_pages:
game_state.visited_pages.append(next_page)
# Load the *final* destination page data after challenge resolution
page_data = game_data[next_page_str]
# --- Prepare Final Output ---
final_page_title = page_data.get('title', 'Untitled')
svg_content = create_svg_illustration(page_data.get("illustration", "default"))
# Handle game over on the final destination page
if page_data.get("gameOver", False):
content = f"<h2>{final_page_title}</h2>"
content += page_data.get("content", "")
if battle_occurred and battle_result: content += battle_message
if challenge_occurred: content += challenge_log
if "ending" in page_data:
content += f"<div style='text-align: center; margin-top: 20px; font-weight: bold; color: #c00;'>THE END</div>"
content += f"<p style='font-style: italic;'>{page_data['ending']}</p>"
stats_display = generate_stats_display(game_state)
inventory_display = generate_inventory_display(game_state)
return svg_content, content, gr.Dropdown(choices=["Restart"], label="Game Over", value=None), game_state.to_json(), stats_display, inventory_display, "Game Over - " + final_page_title
# Build content for the final destination page (if not game over)
content = f"<h2>{final_page_title}</h2>"
content += page_data.get("content", "<p>No content.</p>")
if battle_occurred and battle_result: content += battle_message
if challenge_occurred: content += challenge_log
# Build options for the final destination page
options_texts = []
current_inventory = game_state.inventory # Cache inventory again
final_page_options = page_data.get("options", [])
if final_page_options:
for opt in final_page_options:
option_available = True
if isinstance(opt, dict): # Ensure opt is a dictionary
req_item = opt.get("requireItem")
if req_item and req_item not in current_inventory:
option_available = False
req_any_item = opt.get("requireAnyItem")
if option_available and req_any_item:
if not any(item in current_inventory for item in req_any_item):
option_available = False
if option_available and opt.get("text"):
options_texts.append(opt["text"])
# else: skip invalid option format
# Handle alternative option if necessary (only if no primary options were available/visible)
alt_opt_data = page_data.get("alternativeOption")
if isinstance(alt_opt_data, dict) and alt_opt_data.get("text") and not options_texts: # Check if text exists and no primary options shown
alt_show_if = alt_opt_data.get("showIf")
if not alt_show_if or any(item in current_inventory for item in alt_show_if):
options_texts.append(alt_opt_data["text"])
if not options_texts: # If still no options (dead end?)
options_texts = ["Restart"]
content += "<p><i>There are no further actions you can take from here.</i></p>"
# Update story path display text
story_path = f"You are on page {next_page}: {final_page_title}"
if game_state.journey_progress >= 80: story_path += " (Nearing the conclusion)"
elif game_state.journey_progress >= 50: story_path += " (Middle of your journey)"
elif game_state.journey_progress >= 25: story_path += " (Adventure beginning)"
# Generate final displays
stats_display = generate_stats_display(game_state)
inventory_display = generate_inventory_display(game_state)
# Return final state
return (svg_content, content, gr.Dropdown(choices=list(set(options_texts)), label="What will you do?", value=None),
game_state.to_json(), stats_display, inventory_display, story_path)
except Exception as e:
# Generic error handling for the entire update function
print(f"Error during update_game: {e}")
import traceback
traceback.print_exc() # Print detailed traceback to console (if possible in Gradio Lite)
error_content = f"<p>A critical error occurred: {e}. Resetting game state.</p>"
svg_error = create_svg_illustration("default")
new_game_state = GameState() # Reset state
return (svg_error, error_content, gr.Dropdown(choices=["Restart"], label="Critical Error", value=None), new_game_state.to_json(),
generate_stats_display(new_game_state), generate_inventory_display(new_game_state), "Critical Error - Reset")