Spaces:
Running
Running
File size: 21,235 Bytes
e75654e 10de4ed 2703c4f 10de4ed 2703c4f 7346308 10de4ed 7346308 10de4ed 7346308 10de4ed 7346308 10de4ed 7346308 10de4ed 2703c4f 7346308 10de4ed 2703c4f 10de4ed 2703c4f 10de4ed 7346308 2703c4f 10de4ed 2703c4f 10de4ed 7346308 10de4ed 7346308 10de4ed |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 |
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")
|