Game Renderer Module

The Game Renderer module is the primary engine for visualizing the Property Tycoon game during active play. It acts as an interface between the game’s internal state (managed by Game, GameLogic, Board, Player, etc.) and the visual output presented to the user via the Pygame display surface. Its core responsibility is to accurately and efficiently draw all necessary game elements frame by frame, including static components like the board, dynamic elements like player tokens and dice, and context-sensitive UI panels for actions like buying, auctioning, or managing properties.

High-Level Design

Use Case Diagram (What it Renders)

This diagram outlines the key visual components and information that the GameRenderer is tasked with displaying during a typical game session. The “Game Loop” represents the main process invoking the renderer.

@startuml
actor "Game Loop" as GameLoop
rectangle "Game Rendering" {
  GameLoop -- (Render Game Board)
  GameLoop -- (Render Player Info Panel)
  GameLoop -- (Render Player Tokens/Animations)
  GameLoop -- (Render Dice)
  GameLoop -- (Render Property Cards/Tooltips)
  GameLoop -- (Render Action Buttons) : e.g., Roll, Buy, Pass, Bid
  GameLoop -- (Render Notifications/Popups)
  GameLoop -- (Render Auction UI)
  GameLoop -- (Render Jail Options UI)
  GameLoop -- (Render Development UI)
  GameLoop -- (Render Time Remaining/Warnings)
  GameLoop -- (Render Free Parking Pot)
  GameLoop -- (Render AI Emotion UI)
  (Render Game Board) ..> (Render Property Stars) : include
  (Render Player Info Panel) ..> (Render Player Money) : include
  (Render Player Info Panel) ..> (Render Owned Properties) : include
  (Render Player Info Panel) ..> (Render Player Status) : e.g., Jail, Bankrupt
}
@enduml

Game Renderer Responsibilities

Dependency Diagram

This diagram illustrates the main modules and libraries that the GameRenderer relies upon to function. It highlights its central role in accessing game state and utilizing utility modules for presentation.

@startuml
skinparam package {
  BackgroundColor white
  BorderColor black
  ArrowColor black
}

package "Game Renderer Module" {
  [GameRenderer]
}

package "Game Core" {
  [Game]
  [GameActions]
  [GameLogic]
  [Board]
  [Player]
  [DevelopmentManager]
}

package "UI Module" {
  [AIEmotionUI]
}

package "Utility Modules" {
  [Font_Manager]
}

package "External Libraries" {
  [pygame]
  [math]
  [os]
}

[GameRenderer] --> [Game]
[GameRenderer] --> [GameActions]
[GameRenderer] --> [GameLogic]
[GameRenderer] --> [Board]
[GameRenderer] --> [Player]
[GameRenderer] --> [DevelopmentManager]
[GameRenderer] --> [AIEmotionUI]
[GameRenderer] --> [Font_Manager]
[GameRenderer] --> [pygame]
[GameRenderer] --> [math]
@enduml

Game Renderer Dependencies

Simplified State-Based Rendering Logic

The main draw() method adapts its output based significantly on the current game.state. This state diagram shows the primary rendering paths determined by the game state.

@startuml
skinparam state {
  BackgroundColor white
  BorderColor black
  ArrowColor black
}

[*] --> Idle
Idle --> DrawingBase : Frame Start
DrawingBase --> DrawingPlayerInfo
DrawingPlayerInfo --> DrawingCommonUI

DrawingCommonUI --> CheckState

CheckState --> DrawRollState : state == "ROLL"
CheckState --> DrawBuyState : state == "BUY"
CheckState --> DrawAuctionState : state == "AUCTION"
CheckState --> DrawDevelopmentState : state == "DEVELOPMENT"
CheckState --> DrawOtherState : else

DrawRollState --> DrawingDice
DrawBuyState --> DrawingDice
DrawAuctionState --> DrawingDice
DrawDevelopmentState --> DrawingDice
DrawOtherState --> DrawingDice

DrawingDice --> DrawingOverlays
DrawingOverlays --> DrawingFinalFlip
DrawingFinalFlip --> [*]

state DrawingBase {
    [*] --> RenderBackground
    RenderBackground --> RenderBoard
    RenderBoard --> RenderPlayerAnimations
    RenderPlayerAnimations --> [*]
}

state DrawingPlayerInfo {
    [*] --> RenderPlayerStatusPanel
    RenderPlayerStatusPanel --> RenderPropertyTooltips
    RenderPropertyTooltips --> [*]
}

state DrawingCommonUI {
    [*] --> RenderTimeRemaining
    RenderTimeRemaining --> RenderFreeParkingPot
    RenderFreeParkingPot --> RenderAIEmotionUI
    RenderAIEmotionUI --> [*]
}
@enduml

State Diagram: GameRenderer Draw Logic by Game State

Detailed Design

Class Diagram

This diagram details the structure of the GameRenderer class itself, showing its attributes (dependencies and fonts) and its public methods responsible for drawing specific elements.

@startuml
skinparam class {
  BackgroundColor white
  BorderColor black
  ArrowColor black
}

class GameRenderer {
  - game: Game
  - game_actions: GameActions
  - screen: pygame.Surface
  - font: pygame.font.Font
  - small_font: pygame.font.Font
  - tiny_font: pygame.font.Font
  - button_font: pygame.font.Font
  - message_font: pygame.font.Font
  + draw_button(button: pygame.Rect, text: str, hover: bool, active: bool): void
  + draw_time_remaining(): void
  + draw(): void
  + draw_dice(dice1: int, dice2: int, is_rolling: bool): void
  + draw_property_card(property_data: dict): void
  + draw_buy_options(mouse_pos: tuple): void
  + draw_property_tooltip(property_data: dict, mouse_pos: tuple): void
  + draw_notification(): void
  + draw_card_alert(card: dict, player: dict): void
  + wrap_text(text: str, max_width: int): list<str>
  + draw_popup_message(): void
  + draw_auction(auction_data: dict): void
  + draw_jail_options(player: dict): void
  + draw_free_parking_pot(): void
  + draw_development_ui(property_data: dict): void
  + handle_development_click(pos: tuple, property_data: dict): any
}

note right of GameRenderer::draw
  Main rendering loop called each frame.
  Delegates to other draw methods based on game state.
end note

GameRenderer ..> Game : uses >
GameRenderer ..> GameActions : uses >
GameRenderer ..> Font_Manager : uses >
GameRenderer ..> "pygame.Surface" : uses >
GameRenderer ..> "pygame.Rect" : uses >
GameRenderer ..> "pygame.font.Font" : uses >
GameRenderer ..> AIEmotionUI : uses >
@enduml

GameRenderer Class Diagram

Activity Diagram: Drawing Dice

This diagram details the logic within the draw_dice method, including handling the rolling animation state and the special effect for doubles.

@startuml
skinparam activity {
  BackgroundColor white
  BorderColor black
  ArrowColor black
}

start
:Get window size;
:Calculate dice size and position;
:Draw Dice 1 Shadow;
:Draw Dice 1 Background;
if (Value 1 in dice_images?) then (yes)
  :Load and scale image 1;
  :Blit image 1;
else (no)
  :Render text value 1;
  :Blit text value 1;
endif
:Draw Dice 1 Border (color based on is_rolling);

:Draw Dice 2 Shadow;
:Draw Dice 2 Background;
 if (Value 2 in dice_images?) then (yes)
  :Load and scale image 2;
  :Blit image 2;
else (no)
  :Render text value 2;
  :Blit text value 2;
endif
:Draw Dice 2 Border (color based on is_rolling);

if (is_rolling == false AND dice1 == dice2?) then (yes)
  :Get current time;
  :Calculate sparkle positions and colors based on time;
  :Draw multiple sparkle effects around dice;
endif
stop
@enduml

Activity Diagram: draw_dice Method

Sequence Diagrams

Main Draw Sequence

Illustrates the high-level flow of execution when the main GameRenderer.draw() method is invoked by the game loop.

@startuml
skinparam sequence {
  ParticipantBackgroundColor white
  ParticipantBorderColor black
  ArrowColor black
  LifeLineBorderColor gray
  LifeLineBackgroundColor lightgray
}

participant "main_loop" as Loop
participant "renderer:GameRenderer" as Renderer
participant "game:Game" as Game
participant "board:Board" as Board
participant "dev_manager:DevelopmentManager" as DevMan
participant "emotion_ui:AIEmotionUI" as EmotionUI
participant "player:Player" as Player

Loop -> Renderer : draw()
activate Renderer
Renderer -> Game : check_time_limit()
Renderer -> Renderer : screen.fill() / draw gradient
Renderer -> Board : draw(screen)
Renderer -> Game : dev_manager.is_active
opt dev_manager is active
  Renderer -> DevMan : draw(mouse_pos)
  opt complete_button exists and player not AI
    Renderer -> Renderer : draw_button(complete_button, ...)
  end
end

Renderer -> Game : emotion_uis.values()
loop for each emotion_ui
  Renderer -> EmotionUI : draw()
end

Renderer -> Game : synchronize_player_positions()
Renderer -> Game : synchronize_player_money()
Renderer -> Game : synchronize_free_parking_pot()

Renderer -> Game : players
loop for each player
  Renderer -> Player : update_animation()
end

Renderer -> Board : draw(screen)
Renderer -> DevMan : _draw_property_stars()

Renderer -> Renderer : draw Player Info Panel
Renderer -> Renderer : draw_time_remaining()
Renderer -> Renderer : draw_free_parking_pot()
Renderer -> Renderer : draw_notification()

Renderer -> Game : state
alt game.state == "ROLL"
  Renderer -> Renderer : draw_button(roll_button, ...)
  opt can_develop
     Renderer -> Renderer : draw_button(develop_button, ...)
  end
  opt abridged mode
     Renderer -> Renderer : draw_button(pause_button, ...)
  end
  opt human players remain
     Renderer -> Renderer : draw_button(quit_button, ...)
  end
  opt AI turn
     Renderer -> Game : check_and_trigger_ai_turn()
  end
else game.state == "BUY"
  Renderer -> Renderer : draw_property_card(...)
  Renderer -> Renderer : draw_buy_options(...)
else game.state == "AUCTION"
  Renderer -> Renderer : draw_auction(...)
  Renderer -> Game : logic.check_auction_end()
  opt auction ended
     Renderer -> Game : handle_auction_end()
  end
else game.state == "DEVELOPMENT"
  Renderer -> Renderer : draw_development_ui(...)
end

opt game.dice_animation or recent roll
  Renderer -> Renderer : draw_dice(...)
end

opt game.show_card
  Renderer -> Renderer : draw_card_alert(...)
end

Renderer -> Board : camera.handle_camera_controls(...)
Renderer -> Board : update_board_positions()

Renderer -> Game : check_game_over()
opt game over
  Renderer -> Game : handle_game_over(...)
end

opt game.show_popup
  Renderer -> Renderer : draw_popup_message()
end

Renderer -> pygame.display : flip()
Renderer --> Loop
deactivate Renderer
@enduml

Sequence Diagram: Main Draw Call

Drawing Time Remaining with Warning

Shows the specific logic executed within the draw_time_remaining method, particularly when the game timer is low, triggering visual warnings.

@startuml
skinparam sequence {
  ParticipantBackgroundColor white
  ParticipantBorderColor black
  ArrowColor black
  LifeLineBorderColor gray
  LifeLineBackgroundColor lightgray
}

participant "renderer:GameRenderer" as Renderer
participant "game:Game" as Game
participant "pygame.time" as Time
participant "math" as Math

Renderer -> Renderer : draw_time_remaining()
activate Renderer
Renderer -> Game : game_mode, time_limit
opt game_mode == "abridged" and time_limit exists
  Renderer -> Time : get_ticks()
  Renderer -> Game : start_time, total_pause_time, game_paused, pause_start_time
  Renderer -> Renderer : calculate remaining time
  opt remaining <= 30
    Renderer -> Math : sin(...) # for flashing
    Renderer -> Renderer : calculate warning intensity, border width, color
    Renderer -> Renderer : draw warning border/overlay
  end
  opt time_limit_reached
    Renderer -> Renderer : draw "Finishing Lap" banner
  end
  opt remaining <= time_warning_start
     Renderer -> Math : sin(...) # for flashing
     Renderer -> Renderer : draw faint warning overlay
     opt remaining is 60, 30, or 10
         Renderer -> Game : add_message(...)
     end
  end
  Renderer -> Renderer : draw Time Panel (lap, time, progress bar)
end
Renderer --> Renderer : return
deactivate Renderer
@enduml

Sequence Diagram: Drawing Time Warning

Drawing Auction UI

Details the steps for rendering the auction interface when the game is in the “AUCTION” state.

@startuml
skinparam sequence {
  ParticipantBackgroundColor white
  ParticipantBorderColor black
  ArrowColor black
  LifeLineBorderColor gray
  LifeLineBackgroundColor lightgray
}

participant "renderer:GameRenderer" as Renderer
participant "game:Game" as Game

Renderer -> Renderer : draw_auction(auction_data)
activate Renderer
Renderer -> Game : show_card
opt card is showing
    Renderer --> Renderer : return (don't draw auction)
end
Renderer -> Renderer : Validate auction_data keys
opt missing keys or invalid property
    Renderer -> Game : state = "ROLL"
    Renderer --> Renderer : return
end

Renderer -> Renderer : Draw overlay background
Renderer -> Renderer : Draw auction panel base (with shadow)
Renderer -> Renderer : Draw header (Current Bidder, Time Remaining)
Renderer -> Renderer : Draw Title ("AUCTION", Property Name)
Renderer -> Renderer : Draw Info (Current Bid, Minimum Bid)
opt highest_bidder exists
    Renderer -> Renderer : Draw Highest Bidder info
end

Renderer -> Renderer : Get current bidder data
opt bidder is human and can bid
    Renderer -> Renderer : Draw bid input field (auction_input)
    Renderer -> Renderer : Draw "Bid" button (using draw_button)
    Renderer -> Renderer : Draw "Pass" button (using draw_button)
end

opt passed_players exist
    Renderer -> Renderer : Draw list of passed players
end

Renderer --> Renderer : return
deactivate Renderer
@enduml

Sequence Diagram: Drawing Auction UI

Drawing Jail Options

Shows the process for displaying the available actions to a player currently in jail.

@startuml
skinparam sequence {
  ParticipantBackgroundColor white
  ParticipantBorderColor black
  ArrowColor black
  LifeLineBorderColor gray
  LifeLineBackgroundColor lightgray
}

participant "renderer:GameRenderer" as Renderer
participant "player:dict" as PlayerData

Renderer -> Renderer : draw_jail_options(player_data)
activate Renderer
opt player_data("in_jail") == False
    Renderer --> Renderer : return
end

Renderer -> Renderer : Draw overlay background
Renderer -> Renderer : Draw jail options panel base (with shadow)
Renderer -> Renderer : Draw Title ("Jail Options")

Renderer -> PlayerData : get("jail_card")
opt has jail card
    Renderer -> Renderer : draw_button("Use Get Out of Jail Free Card", ...)
end
Renderer -> PlayerData : get("money")
opt money >= 50
    Renderer -> Renderer : draw_button("Pay £50 Fine", ...)
end
Renderer -> Renderer : draw_button("Roll for Doubles", ...)

Renderer -> PlayerData : get("jail_turns")
Renderer -> Renderer : Draw Turns in Jail text

Renderer --> Renderer : return
deactivate Renderer
@enduml

Sequence Diagram: Drawing Jail Options

Drawing Notification/Popup

Illustrates how temporary messages (notifications or popups) are displayed over the main game view.

@startuml
skinparam sequence {
  ParticipantBackgroundColor white
  ParticipantBorderColor black
  ArrowColor black
  LifeLineBorderColor gray
  LifeLineBackgroundColor lightgray
}

participant "renderer:GameRenderer" as Renderer
participant "game:Game" as Game
participant "pygame.time" as Time

alt Notification
    Renderer -> Game : notification, notification_time
    opt notification exists
        Renderer -> Time : get_ticks()
        opt time < notification_time + DURATION
            Renderer -> Renderer : Draw notification background (top center)
            Renderer -> Renderer : Draw notification text
        else
            Renderer -> Game : notification = None
        end
    end
else Popup
    Renderer -> Game : show_popup, popup_title, popup_message
    opt show_popup == True
        Renderer -> Renderer : Draw overlay background
        Renderer -> Renderer : Draw popup panel background
        opt popup_title exists
            Renderer -> Renderer : Draw popup title
        end
        opt popup_message exists
            Renderer -> Renderer : wrap_text(popup_message, ...)
            loop for each line
                Renderer -> Renderer : Draw message line
            end
        end
        Renderer -> Renderer : Draw "OK" button
    end
end
@enduml

Sequence Diagram: Drawing Notification/Popup

Key Classes Overview

  • GameRenderer: The central class orchestrating the drawing process. It holds references to the game state (Game, GameActions), the display surface (screen), and various fonts (Font_Manager). It contains numerous draw_ methods, each responsible for rendering a specific visual component (e.g., draw_dice, draw_player_info_panel implicitly, draw_auction, draw_property_card). The main draw() method acts as the primary entry point, called each frame, which then delegates rendering tasks to the appropriate sub-methods based on the current game.state and other conditions.

API Documentation

class src.GameRenderer.GameRenderer(game, game_actions)[source]

Bases: object

draw()[source]
draw_auction(auction_data)[source]
draw_button(button, text, hover=False, active=True)[source]
draw_buy_options(mouse_pos)[source]
draw_card_alert(card, player)[source]
draw_development_ui(property_data)[source]
draw_dice(dice1, dice2, is_rolling)[source]
draw_free_parking_pot()[source]
draw_jail_options(player)[source]
draw_notification()[source]
draw_popup_message()[source]
draw_property_card(property_data)[source]
draw_property_tooltip(property_data, mouse_pos)[source]
draw_time_remaining()[source]
handle_development_click(pos, property_data)[source]
wrap_text(text, max_width)[source]