# Property Tycoon GameEventHandler.py
# Contains the classes for the game event handler, such as the handle input, the handle click, and the handle motion.
import pygame
import sys
from src.Sound_Manager import sound_manager
KEY_ROLL = [pygame.K_SPACE, pygame.K_RETURN]
KEY_BUY = [pygame.K_y, pygame.K_RETURN]
KEY_PASS = [pygame.K_n, pygame.K_ESCAPE]
[docs]
class GameEventHandler:
def __init__(self, game, game_actions):
self.game = game
self.game_actions = game_actions
[docs]
def handle_click(self, pos):
if self.game.game_over:
return False
if (
hasattr(self.game, "complete_button")
and self.game.dev_manager.is_active
and not self.game.current_player_is_ai
):
if self.game.complete_button.collidepoint(pos):
print("Complete button clicked - exiting development mode")
self.game.dev_manager.is_active = False
self.game.development_mode = False
self.game.state = "ROLL"
self.game.board.add_message(
"Development mode completed. Ready to roll dice."
)
return False
if self.game.dev_manager.is_active and self.game.state not in [
"AUCTION",
"BUY",
]:
dev_result = self.game.dev_manager.handle_click(pos)
if dev_result is not None:
if isinstance(dev_result, dict) and "winner" in dev_result:
return dev_result
elif dev_result == True:
return True
else:
return False
for emotion_ui in self.game.emotion_uis.values():
if emotion_ui.handle_click(pos):
return False
if self.game.show_popup:
if self.game.popup_message:
window_size = self.game.screen.get_size()
popup_width = int(window_size[0] * 0.4)
popup_height = int(window_size[1] * 0.3)
popup_x = (window_size[0] - popup_width) // 2
popup_y = (window_size[1] - popup_height) // 2
popup_rect = pygame.Rect(popup_x, popup_y, popup_width, popup_height)
if popup_rect.collidepoint(pos):
self.game.show_popup = False
return False
return False
if self.game.show_card:
self.game.show_card = False
self.game.current_card = None
self.game.current_card_player = None
return False
if self.game.development_mode:
result = self.game.dev_manager.handle_click(pos)
if result is not None:
return result
if self.game.state == "ROLL":
current_player = self.game.logic.players[
self.game.logic.current_player_index
]
player_obj = next(
(p for p in self.game.players if p.name == current_player["name"]),
None,
)
if not player_obj:
return False
if not player_obj.is_ai:
if self.game.develop_button.collidepoint(pos):
owned_properties = [
prop
for prop in self.game.logic.properties.values()
if prop.get("owner") == current_player["name"]
]
if owned_properties:
print(
f"Human player {current_player['name']} clicked development button"
)
self.game.development_mode = True
self.game.dev_manager.activate(current_player)
self.game.board.add_message(
"Development Mode: Click on your properties to manage them"
)
return False
if self.game.roll_button.collidepoint(pos):
if not any(player.is_moving for player in self.game.players):
self.game.development_mode = False
self.game.dev_manager.deactivate()
self.game_actions.play_turn()
return False
if (
not self.game.current_player_is_ai
and self.game.game_mode == "abridged"
and self.game.time_limit
and self.game.pause_button.collidepoint(pos)
):
current_time = pygame.time.get_ticks()
if self.game.game_paused:
pause_duration = current_time - self.game.pause_start_time
self.game.total_pause_time += pause_duration
self.game.game_paused = False
self.game.board.add_message("Game resumed")
else:
self.game.game_paused = True
self.game.pause_start_time = current_time
self.game.board.add_message("Game paused")
return False
if (
not self.game.current_player_is_ai
and self.game.roll_button.collidepoint(pos)
):
if (
self.game.game_mode == "abridged"
and self.game.time_limit
and self.game.game_paused
):
self.game.board.add_message(
"Game is paused. Click Continue to resume"
)
return False
else:
return self.game_actions.play_turn()
human_players_remaining = any(
not p.is_ai and not p.voluntary_exit and not p.bankrupt
for p in self.game.players
)
if (
not self.game.current_player_is_ai
and human_players_remaining
and self.game.quit_button.collidepoint(pos)
):
confirm_exit = self.game_actions.show_exit_confirmation()
if confirm_exit:
current_player = self.game.logic.players[
self.game.logic.current_player_index
]
final_assets = self.game_actions.calculate_player_assets(
current_player
)
result = self.game_actions.handle_voluntary_exit(
current_player["name"], final_assets
)
sound_manager.play_sound("player_exit")
if isinstance(result, dict):
return result
elif result:
self.game.board.add_message(
f"{current_player['name']} has voluntarily exited the game"
)
game_over_result = self.game_actions.check_game_over()
if game_over_result:
return game_over_result
if len(self.game.logic.players) > 0:
self.game.state = "ROLL"
self.game_actions.check_and_trigger_ai_turn()
else:
return self.game_actions.check_game_over()
return False
elif self.game.state == "BUY" and self.game.current_property is not None:
current_player = self.game.logic.players[
self.game.logic.current_player_index
]
if current_player.get("in_jail", False):
self.game.board.add_message(
f"{current_player['name']} cannot buy property while in jail!"
)
self.game.state = "ROLL"
self.game.renderer.draw()
pygame.display.flip()
return False
if self.game.yes_button.collidepoint(pos):
self.game_actions.handle_buy_decision(True)
self.game.dev_manager.deactivate()
self.game.renderer.draw()
pygame.display.flip()
return False
elif self.game.no_button.collidepoint(pos):
self.game_actions.handle_buy_decision(False)
self.game.dev_manager.deactivate()
self.game.renderer.draw()
pygame.display.flip()
return False
return False
elif self.game.state == "AUCTION":
print("\n=== Handling Auction Click ===")
auction_result = self.handle_auction_click(pos)
print(f"Auction click result: {auction_result}")
if auction_result == True and not hasattr(self.game, "auction_completed"):
print("Auction completed - changing state to ROLL")
self.game.state = "ROLL"
self.game.current_property = None
self.game.board.update_ownership(self.game.logic.properties)
self.game.update_current_player()
self.game.dev_manager.deactivate()
self.game_actions.check_and_trigger_ai_turn()
else:
print(
"Auction continues or completion in progress - maintaining AUCTION state"
)
return False
print(f"Final state after click: {self.game.state}")
return False
[docs]
def handle_motion(self, pos):
if self.game.game_over:
return False
for emotion_ui in self.game.emotion_uis.values():
emotion_ui.check_hover(pos)
if self.game.state == "ROLL":
hover_buttons = [
self.game.roll_button.collidepoint(pos),
self.game.quit_button.collidepoint(pos),
]
if self.game.game_mode == "abridged" and self.game.time_limit:
hover_buttons.append(self.game.pause_button.collidepoint(pos))
return any(hover_buttons)
elif self.game.state == "BUY":
return self.game.yes_button.collidepoint(
pos
) or self.game.no_button.collidepoint(pos)
elif self.game.state == "AUCTION":
return any(
btn.collidepoint(pos) for btn in self.game.auction_buttons.values()
)
return False
[docs]
def handle_key(self, event):
if self.game.dev_manager.is_active and self.game.state not in [
"AUCTION",
"BUY",
]:
if (
hasattr(self.game.dev_manager, "notification")
and self.game.dev_manager.notification
):
if self.game.dev_manager.notification.handle_key(event):
self.game.dev_manager.deactivate()
return False
dev_result = self.game.dev_manager.handle_key(event)
if dev_result is not None:
if isinstance(dev_result, dict) and "winner" in dev_result:
return dev_result
elif dev_result == True:
return True
else:
return False
if self.game.show_popup:
if event.key in [pygame.K_SPACE, pygame.K_RETURN, pygame.K_ESCAPE]:
self.game.show_popup = False
return False
if self.game.show_card:
if event.key in [pygame.K_SPACE, pygame.K_RETURN, pygame.K_ESCAPE]:
self.game.show_card = False
self.game.current_card = None
self.game.current_card_player = None
return False
any_player_moving = any(player.is_moving for player in self.game.players)
if any_player_moving and event.key not in [
pygame.K_LEFT,
pygame.K_RIGHT,
pygame.K_UP,
pygame.K_DOWN,
]:
print("Animations in progress, ignoring key input")
return False
print(f"\n=== Key Press Debug ===")
print(f"Key: {pygame.key.name(event.key)}")
print(f"Current state: {self.game.state}")
if self.game.state == "ROLL":
if not self.game.current_player_is_ai:
if event.key in KEY_ROLL:
if (
self.game.game_mode == "abridged"
and self.game.time_limit
and self.game.game_paused
):
self.game.board.add_message("Game is paused. Press P to resume")
return False
return self.game_actions.play_turn()
elif event.key == pygame.K_q:
confirm_exit = self.game_actions.show_exit_confirmation()
if confirm_exit:
current_player = self.game.logic.players[
self.game.logic.current_player_index
]
final_assets = self.game_actions.calculate_player_assets(
current_player
)
result = self.game_actions.handle_voluntary_exit(
current_player["name"], final_assets
)
sound_manager.play_sound("player_exit")
if result:
self.game.board.add_message(
f"{current_player['name']} has voluntarily exited the game"
)
if len(self.game.logic.players) > 0:
self.game.state = "ROLL"
self.game_actions.check_and_trigger_ai_turn()
else:
return self.game_actions.check_game_over()
return False
elif event.key == pygame.K_t and self.game.game_mode == "abridged":
self.game_actions.show_time_stats()
elif (
event.key == pygame.K_p
and self.game.game_mode == "abridged"
and self.game.time_limit
):
current_time = pygame.time.get_ticks()
if self.game.game_paused:
pause_duration = current_time - self.game.pause_start_time
self.game.total_pause_time += pause_duration
self.game.game_paused = False
self.game.board.add_message("Game resumed")
else:
self.game.game_paused = True
self.game.pause_start_time = current_time
self.game.board.add_message("Game paused")
return False
elif self.game.state == "BUY":
if event.key in KEY_BUY:
self.game_actions.handle_buy_decision(True)
return False
elif event.key in KEY_PASS:
self.game_actions.handle_buy_decision(False)
return False
elif self.game.state == "AUCTION":
print("Processing auction input")
self.handle_auction_input(event)
return False
if event.key in [pygame.K_LEFT, pygame.K_RIGHT, pygame.K_UP, pygame.K_DOWN]:
dx, dy = 0, 0
if event.key == pygame.K_LEFT:
dx = 10
elif event.key == pygame.K_RIGHT:
dx = -10
elif event.key == pygame.K_UP:
dy = 10
elif event.key == pygame.K_DOWN:
dy = -10
self.game.board.update_offset(dx, dy)
self.game.board.camera.handle_camera_controls(pygame.key.get_pressed())
return None
[docs]
def _process_auction_bid(self, current_bidder):
try:
bid_amount = int(self.game.auction_bid_amount or "0")
success, message = self.game.logic.process_auction_bid(
current_bidder, bid_amount
)
if message:
self.game.board.add_message(message)
if success:
self.game.auction_bid_amount = ""
print(f"Bid successful: £{bid_amount}")
sound_manager.play_sound("auction_bid")
else:
print(f"Bid failed: {message}")
except ValueError:
self.game.board.add_message("Please enter a valid number!")
print("Invalid bid amount")
[docs]
def handle_auction_click(self, pos):
print("\n=== Auction Click Debug ===")
if (
not hasattr(self.game.logic, "current_auction")
or self.game.logic.current_auction is None
):
print("Error: No active auction")
self.game.state = "ROLL"
return True
if self.game.show_card:
print("Card is showing - ignoring auction click")
return False
auction_data = self.game.logic.current_auction
if auction_data is None:
print("Error: Auction data is None in handle_auction_click")
self.game.state = "ROLL"
return True
if "active_players" not in auction_data or not auction_data["active_players"]:
print("No active players in auction")
self.game.state = "ROLL"
return True
if auction_data.get("completed", False):
print("Auction is already marked as completed - changing state to ROLL")
self.game.state = "ROLL"
return True
current_bidder = auction_data["active_players"][
auction_data["current_bidder_index"]
]
if current_bidder.get("exited", False):
print(f"Current bidder {current_bidder['name']} has exited - skipping")
auction_data["passed_players"].add(current_bidder["name"])
self.game.logic.move_to_next_bidder()
return False
current_bidder_obj = next(
(p for p in self.game.players if p.name == current_bidder["name"]), None
)
if not current_bidder_obj or (
hasattr(current_bidder_obj, "voluntary_exit")
and current_bidder_obj.voluntary_exit
):
print(
f"Current bidder {current_bidder['name']} doesn't have UI representation or has voluntarily exited"
)
auction_data["passed_players"].add(current_bidder["name"])
self.game.logic.move_to_next_bidder()
return False
if current_bidder.get("in_jail", False) and current_bidder.get("is_ai", False):
print(f"AI bidder {current_bidder['name']} is in jail - auto-passing")
auction_data["passed_players"].add(current_bidder["name"])
self.game.logic.move_to_next_bidder()
return False
print(f"Current bidder: {current_bidder['name']}")
print(f"Is AI: {current_bidder_obj.is_ai if current_bidder_obj else 'Unknown'}")
print(f"Current bid amount input: {self.game.auction_bid_amount}")
print(f"Bid button rect: {self.game.auction_buttons['bid']}")
print(f"Pass button rect: {self.game.auction_buttons['pass']}")
print(f"Click position: {pos}")
print(
f"Bid button collision: {self.game.auction_buttons['bid'].collidepoint(pos)}"
)
print(
f"Pass button collision: {self.game.auction_buttons['pass'].collidepoint(pos)}"
)
if not current_bidder_obj or current_bidder_obj.is_ai:
print("Current bidder is AI or not found - ignoring click")
return False
if self.game.auction_buttons["bid"].collidepoint(pos):
print(f"Bid button clicked by {current_bidder['name']}")
try:
bid_amount = int(self.game.auction_bid_amount or "0")
success, message = self.game.logic.process_auction_bid(
current_bidder, bid_amount
)
if message:
self.game.board.add_message(message)
if success:
self.game.auction_bid_amount = ""
print(f"Bid successful: £{bid_amount}")
sound_manager.play_sound("auction_bid")
else:
print(f"Bid failed: {message}")
except ValueError:
self.game.board.add_message("Please enter a valid number!")
print("Invalid bid amount")
elif self.game.auction_buttons["pass"].collidepoint(pos):
print(f"Pass button clicked by {current_bidder['name']}")
success, message = self.game.logic.process_auction_pass(current_bidder)
if message:
self.game.board.add_message(message)
if success:
print(f"{current_bidder['name']} passed successfully")
else:
print("Pass failed: {message}")
result_message = self.game.logic.check_auction_end()
print(f"Auction end check result: {result_message}")
if result_message == "auction_completed":
print("Auction is completed - showing results before changing state")
if (
hasattr(self.game.logic, "current_auction")
and self.game.logic.current_auction
):
auction_data = self.game.logic.current_auction
if auction_data and auction_data.get("highest_bidder"):
winner = auction_data["highest_bidder"]
property_name = auction_data["property"]["name"]
bid_amount = auction_data["current_bid"]
self.game.board.add_message(
f"{winner['name']} won {property_name} for £{bid_amount}"
)
sound_manager.play_sound("auction_win")
else:
property_name = auction_data["property"]["name"]
self.game.board.add_message(f"No one bid on {property_name}")
self.game.logic.current_auction = None
print("Cleared game.logic.current_auction")
self.game.auction_end_time = pygame.time.get_ticks()
self.game.auction_end_delay = 3000
self.game.auction_completed = True
self.game.board.update_ownership(self.game.logic.properties)
return False
print("Auction continues - returning False")
return False