naman1102 commited on
Commit
b6409fe
·
1 Parent(s): f571ea2

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +89 -5
app.py CHANGED
@@ -26,9 +26,21 @@ class Character:
26
 
27
  def attack_target(self, target):
28
  """Performs an attack on a target."""
29
- # Add a little randomness to the attack damage
 
 
 
 
30
  damage = random.randint(self.attack - 2, self.attack + 2)
31
- message = f"{self.name} attacks {target.name} for {damage} damage!"
 
 
 
 
 
 
 
 
32
  message += "\n" + target.take_damage(damage)
33
  return message
34
 
@@ -39,7 +51,8 @@ class PlayerCharacter(Character):
39
  super().__init__(name, max_hp, attack, defense, character_type="player")
40
  self.xp = 0
41
  self.level = 1
42
- self.money = 0 # For future features like buying items
 
43
 
44
  def gain_xp(self, amount):
45
  """Awards experience points and checks for level up."""
@@ -109,6 +122,14 @@ class ScottPilgrimGame:
109
  ("The Subspace Highway", EvilEx("Young Neil", 160, 21, 8, 160, 75)) # Fictional final boss
110
  ]
111
 
 
 
 
 
 
 
 
 
112
  def reset_game(self):
113
  """Resets the game state for a new playthrough."""
114
  self.__init__() # Re-initialize the game object to its default state
@@ -155,6 +176,7 @@ class ScottPilgrimGame:
155
  if not self.player:
156
  return ""
157
  status = f"{self.player.name} (Lvl {self.player.level}) | HP: {self.player.current_hp}/{self.player.max_hp} | XP: {self.player.xp}/{self.player.level * 50}"
 
158
  if self.current_enemy:
159
  status += f"\n{self.current_enemy.name} | HP: {self.current_enemy.current_hp}/{self.current_enemy.max_hp}"
160
  return status
@@ -231,6 +253,50 @@ class ScottPilgrimGame:
231
  # Enemy gets a free hit as a penalty for a failed escape
232
  self.enemy_attack_turn()
233
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
234
  # --- Gradio Interface Logic ---
235
 
236
  # Create a single game instance to be managed by gr.State
@@ -271,7 +337,8 @@ def _format_status_html(game_state):
271
  status_html = [
272
  f"<h4 style='margin:4px 0'>{player.name} (Lvl {player.level})</h4>",
273
  f"HP: <progress value='{player.current_hp}' max='{player.max_hp}' style='width:160px;height:14px;'></progress> {player.current_hp}/{player.max_hp}<br>",
274
- f"XP: <progress value='{player.xp}' max='{player.level * 50}' style='width:160px;height:14px;'></progress> {player.xp}/{player.level * 50}<br><br>"
 
275
  ]
276
 
277
  if game_state.current_enemy:
@@ -309,6 +376,7 @@ def update_ui(game_state):
309
  char_buttons_visible = (phase == "start")
310
  action_buttons_visible = (phase == "combat")
311
  play_again_visible = (phase in ["game_over", "win"])
 
312
 
313
  return (
314
  log_html,
@@ -316,6 +384,7 @@ def update_ui(game_state):
316
  gr.update(visible=char_buttons_visible),
317
  gr.update(visible=action_buttons_visible),
318
  gr.update(visible=play_again_visible),
 
319
  game_state
320
  )
321
 
@@ -339,6 +408,15 @@ def play_again(game_state):
339
  game_state.reset_game()
340
  return update_ui(game_state)
341
 
 
 
 
 
 
 
 
 
 
342
  # --- Gradio UI Layout ---
343
 
344
  with gr.Blocks(theme=gr.themes.Soft()) as demo:
@@ -362,9 +440,14 @@ with gr.Blocks(theme=gr.themes.Soft()) as demo:
362
 
363
  play_again_btn = gr.Button("Play Again?", visible=False)
364
 
 
 
 
 
 
365
  # --- Event Handlers ---
366
  # Define a list of all UI components that need to be updated, including the state
367
- outputs = [game_output, status_output, character_selection_buttons, game_action_buttons, play_again_btn, game_state]
368
 
369
  # Connect buttons to their callback functions
370
  scott_btn.click(choose_character, inputs=[scott_btn, game_state], outputs=outputs)
@@ -372,6 +455,7 @@ with gr.Blocks(theme=gr.themes.Soft()) as demo:
372
  attack_btn.click(player_attack, inputs=[game_state], outputs=outputs)
373
  run_btn.click(player_run, inputs=[game_state], outputs=outputs)
374
  play_again_btn.click(play_again, inputs=[game_state], outputs=outputs)
 
375
 
376
  # Set the initial UI state when the app loads, ensuring the outputs match the function's return values
377
  demo.load(update_ui, inputs=[game_state], outputs=outputs)
 
26
 
27
  def attack_target(self, target):
28
  """Performs an attack on a target."""
29
+ # Add randomness, critical hits, and misses!
30
+ miss_chance = 0.05 # 5% chance to miss
31
+ if random.random() < miss_chance:
32
+ return f"{self.name} swings and MISSES {target.name}!"
33
+
34
  damage = random.randint(self.attack - 2, self.attack + 2)
35
+
36
+ critical_hit = random.random() < 0.1 # 10% chance for a crit
37
+ if critical_hit:
38
+ damage = int(damage * 1.5)
39
+ crit_text = " CRITICAL HIT!"
40
+ else:
41
+ crit_text = ""
42
+
43
+ message = f"{self.name} attacks {target.name} for {damage} damage!{crit_text}"
44
  message += "\n" + target.take_damage(damage)
45
  return message
46
 
 
51
  super().__init__(name, max_hp, attack, defense, character_type="player")
52
  self.xp = 0
53
  self.level = 1
54
+ # Start players with a little spending cash
55
+ self.money = 25
56
 
57
  def gain_xp(self, amount):
58
  """Awards experience points and checks for level up."""
 
122
  ("The Subspace Highway", EvilEx("Young Neil", 160, 21, 8, 160, 75)) # Fictional final boss
123
  ]
124
 
125
+ # --- In-game shop items ---
126
+ self.shop_items = {
127
+ "Energy Drink": {"cost": 10, "type": "heal", "amount": 20, "desc": "Restore 20 HP"},
128
+ "Protein Bar": {"cost": 15, "type": "attack", "amount": 2, "desc": "+2 Attack"},
129
+ "Armor Patch": {"cost": 15, "type": "defense", "amount": 1, "desc": "+1 Defense"},
130
+ "Max Potion": {"cost": 20, "type": "heal_full", "amount": 0, "desc": "Fully heal"},
131
+ }
132
+
133
  def reset_game(self):
134
  """Resets the game state for a new playthrough."""
135
  self.__init__() # Re-initialize the game object to its default state
 
176
  if not self.player:
177
  return ""
178
  status = f"{self.player.name} (Lvl {self.player.level}) | HP: {self.player.current_hp}/{self.player.max_hp} | XP: {self.player.xp}/{self.player.level * 50}"
179
+ status += f" | $: {self.player.money}"
180
  if self.current_enemy:
181
  status += f"\n{self.current_enemy.name} | HP: {self.current_enemy.current_hp}/{self.current_enemy.max_hp}"
182
  return status
 
253
  # Enemy gets a free hit as a penalty for a failed escape
254
  self.enemy_attack_turn()
255
 
256
+ # --------- Shop Mechanics ---------
257
+ def get_shop_choices(self):
258
+ """Return list of item names with price for UI dropdown."""
259
+ return [f"{name} - ${data['cost']}" for name, data in self.shop_items.items()]
260
+
261
+ def buy_item(self, item_display_name):
262
+ """Process purchasing an item given the dropdown display string."""
263
+ if not self.player:
264
+ return
265
+
266
+ # Extract item key (before ' - $')
267
+ item_name = item_display_name.split(" - $")[0]
268
+ if item_name not in self.shop_items:
269
+ self.add_message("That item doesn't exist!")
270
+ return
271
+
272
+ item = self.shop_items[item_name]
273
+ cost = item["cost"]
274
+
275
+ if self.player.money < cost:
276
+ self.add_message("Not enough money!")
277
+ return
278
+
279
+ # Deduct money
280
+ self.player.money -= cost
281
+
282
+ # Apply item effect
283
+ if item["type"] == "heal":
284
+ self.player.current_hp = min(self.player.max_hp, self.player.current_hp + item["amount"])
285
+ self.add_message(f"You used {item_name} and healed {item['amount']} HP!")
286
+ elif item["type"] == "heal_full":
287
+ healed = self.player.max_hp - self.player.current_hp
288
+ self.player.current_hp = self.player.max_hp
289
+ self.add_message(f"{item_name} fully restored your HP (+{healed})!")
290
+ elif item["type"] == "attack":
291
+ self.player.attack += item["amount"]
292
+ self.add_message(f"{item_name} consumed! Attack increased by {item['amount']}.")
293
+ elif item["type"] == "defense":
294
+ self.player.defense += item["amount"]
295
+ self.add_message(f"{item_name} equipped! Defense increased by {item['amount']}.")
296
+
297
+ # Confirm purchase
298
+ self.add_message(f"You bought {item_name} for ${cost}. Remaining money: ${self.player.money}.")
299
+
300
  # --- Gradio Interface Logic ---
301
 
302
  # Create a single game instance to be managed by gr.State
 
337
  status_html = [
338
  f"<h4 style='margin:4px 0'>{player.name} (Lvl {player.level})</h4>",
339
  f"HP: <progress value='{player.current_hp}' max='{player.max_hp}' style='width:160px;height:14px;'></progress> {player.current_hp}/{player.max_hp}<br>",
340
+ f"XP: <progress value='{player.xp}' max='{player.level * 50}' style='width:160px;height:14px;'></progress> {player.xp}/{player.level * 50}<br>",
341
+ f"Money: ${player.money}<br><br>"
342
  ]
343
 
344
  if game_state.current_enemy:
 
376
  char_buttons_visible = (phase == "start")
377
  action_buttons_visible = (phase == "combat")
378
  play_again_visible = (phase in ["game_over", "win"])
379
+ shop_visible = (phase == "combat")
380
 
381
  return (
382
  log_html,
 
384
  gr.update(visible=char_buttons_visible),
385
  gr.update(visible=action_buttons_visible),
386
  gr.update(visible=play_again_visible),
387
+ gr.update(visible=shop_visible),
388
  game_state
389
  )
390
 
 
408
  game_state.reset_game()
409
  return update_ui(game_state)
410
 
411
+ # --- Shop callback ---
412
+ def buy_item_action(item_choice, game_state):
413
+ """Callback for Buy button in the shop."""
414
+ if item_choice:
415
+ game_state.buy_item(item_choice)
416
+ else:
417
+ game_state.add_message("Select an item first!")
418
+ return update_ui(game_state)
419
+
420
  # --- Gradio UI Layout ---
421
 
422
  with gr.Blocks(theme=gr.themes.Soft()) as demo:
 
440
 
441
  play_again_btn = gr.Button("Play Again?", visible=False)
442
 
443
+ # --- Shop UI Components ---
444
+ with gr.Row(visible=False) as shop_row:
445
+ item_dropdown = gr.Dropdown(choices=game_instance.get_shop_choices(), label="Shop Items")
446
+ buy_btn = gr.Button("Buy")
447
+
448
  # --- Event Handlers ---
449
  # Define a list of all UI components that need to be updated, including the state
450
+ outputs = [game_output, status_output, character_selection_buttons, game_action_buttons, play_again_btn, shop_row, game_state]
451
 
452
  # Connect buttons to their callback functions
453
  scott_btn.click(choose_character, inputs=[scott_btn, game_state], outputs=outputs)
 
455
  attack_btn.click(player_attack, inputs=[game_state], outputs=outputs)
456
  run_btn.click(player_run, inputs=[game_state], outputs=outputs)
457
  play_again_btn.click(play_again, inputs=[game_state], outputs=outputs)
458
+ buy_btn.click(buy_item_action, inputs=[item_dropdown, game_state], outputs=outputs)
459
 
460
  # Set the initial UI state when the app loads, ensuring the outputs match the function's return values
461
  demo.load(update_ui, inputs=[game_state], outputs=outputs)