Beispiel #1
0
    def __init__(self, width, height, position, game_window):
        super().__init__(width, height, game_window)
        self.city_images = CityImages()

        self.menu = pgmen.Menu(title='BUILD - BUY',
                               width=width,
                               height=height,
                               position=position,
                               theme=self.get_theme(),
                               columns=11,
                               rows=1,
                               enabled=False,
                               mouse_enabled=True)

        # ZONING PANEL
        self.zone_building_panel = ZoningPanel(
            width=width,
            height=height,
            position=(100, 100 -
                      100 * height / game_window.window.get_height() - 0.3),
            game_window=game_window)

        # BUY A SPECIAL BUILDING PANEL
        self.special_building_panel = BuySpecialBuildingPanel(
            width=width,
            height=height,
            position=(100,
                      100 - 100 * height / game_window.window.get_height()),
            game_window=game_window)

        # BUTTONS
        scale = (0.075, 0.075)
        self.menu.add.image(self.city_images.get_icon('modern-city'),
                            scale=scale)
        self.zoning_button = self.menu.add.button("zone buildings",
                                                  self.zone_buildings)

        self.zoning_button.select(False)

        self.menu.add.label(' ')
        self.menu.add.label(' ')

        self.menu.add.image(self.city_images.get_icon('capitol'), scale=scale)
        self.buy_building_button = self.menu.add.button(
            "special buildings", self.special_buildings)

        self.menu.add.label(' ')
        self.menu.add.label(' ')

        self.menu.add.image(self.city_images.get_icon('bulldozer'),
                            scale=scale)
        self.bulldoze_button = self.menu.add.button("bulldoze", self.bulldoze)

        self.menu.add.label(' ')
Beispiel #2
0
    def __init__(self, city_space, width, height):
        self.city_space = city_space
        self.city_images = CityImages()

        self.road_graphics = RoadGraphics((width, height))
        self.lot_graphics = LotGraphics((width, height))

        RoadGraphics.reset()
        LotGraphics.reset()

        self.height = height  # amount of fields in height
        self.width = width  # amount of fields in width

        # pov - point of origin from which we're drawing
        self.pov_x = WINDOW_SIZE[0] // 2
        self.pov_y = WINDOW_SIZE[1] // 2
        self.scale = max([WINDOW_SIZE[1] // self.height + 1,
                          WINDOW_SIZE[0] // self.width + 1])  # defines the zoom level
        self.move_speed = (0, 0)  # added to pov in each frame

        self.selected_lot = None
class SpeedPanel(Panel):
    city_images = CityImages()

    def __init__(self, width, height, position, window, simulator):
        super().__init__(width, height, window)
        self.menu = pgmen.Menu(title='speed',
                               width=width, height=height,
                               position=position,
                               theme=self.get_theme(),
                               rows=2, columns=4,
                               mouse_enabled=True)
        self.simulator = simulator

        # icons
        scale = (0.06, 0.06)

        self.buttons = []
        # speed buttons
        self.menu.add.image(self.city_images.get_icon('halt'), scale=scale)
        self.buttons += [self.menu.add.button('0x', self.set_speed(0))]

        self.menu.add.image(self.city_images.get_icon('walk'), scale=scale)
        self.buttons += [self.menu.add.button('1x', self.set_speed(1))]

        self.menu.add.image(self.city_images.get_icon('run'), scale=scale)
        self.buttons += [self.menu.add.button('2x', self.set_speed(2))]

        self.menu.add.image(self.city_images.get_icon('running-ninja'), scale=scale)
        self.buttons += [self.menu.add.button('3x', self.set_speed(3))]

        self.buttons[1].select(update_menu=True)

    def set_speed(self, value):
        def change_simulation_speed():
            self.simulator.change_speed(value)

        return change_simulation_speed
class LotGraphics(metaclass=Singleton):
    zone_colors = {'residential': 0x60d394,
                   'commercial': 0x8fb8ed,
                   'industrial': 0xffd97d}
    animation_speed = 0.05
    map_dimensions = None
    zone_highlighting = False
    city_images = CityImages()

    def __init__(self, map_dimensions):
        self.frame = 0
        LotGraphics.map_dimensions = map_dimensions

    @classmethod
    def reset(cls):
        cls.animation_speed = 0.05

    def draw_background(self, lot, scale, pov, window):
        x, y = self.get_draw_position(lot, pov, scale)

        if not (-scale <= x < WINDOW_SIZE[0] and -scale <= y < WINDOW_SIZE[1]):
            return

        # lot type pictures
        for picture in self.city_images.get_images(lot.type, lot.seed):
            window.blit(picture, (x, y))

    def draw_construct(self, lot, scale, pov, window):
        x, y = self.get_draw_position(lot, pov, scale)

        if not (-scale <= x < WINDOW_SIZE[0] and -scale <= y < WINDOW_SIZE[1]):
            return

        # constructs
        if lot.construct:
            offset = int(ROAD_WIDTH_RATIO * scale)
            new_scale = int(scale * (1 - ROAD_WIDTH_RATIO))
            image = lot.construct.image
            width, height = image.get_width(), image.get_height()
            ratio = new_scale / width
            new_width, new_height = int(width * ratio), int(height * ratio)
            new_x = x + offset
            new_y = y - new_height + new_scale + offset
            pic = pg.transform.scale(
                lot.construct.image, (new_width, new_height))
            window.blit(pic, (new_x, new_y))

            # simulation effects
            self.draw_simulation_effects(lot, pov, scale, window)

        # zone highlighting
        if self.zone_highlighting and lot.zone_type:
            self.highlight_lot(lot, pov, scale, self.zone_colors[lot.zone_type], window)

    @classmethod
    def get_draw_position(cls, lot, pov, scale):
        return pov[0] - scale * cls.map_dimensions[0] // 2 + scale * lot.x, pov[1] - scale * cls.map_dimensions[
            1] // 2 + scale * lot.y

    def highlight_lot(self, lot, pov, scale, color, window):
        x, y = self.get_draw_position(lot, pov, scale)
        x += int(scale * ROAD_WIDTH_RATIO // 2)
        y += int(scale * ROAD_WIDTH_RATIO // 2)
        alpha = pg.Surface((scale, scale))
        alpha.set_alpha(128)
        alpha.fill(color)
        window.blit(alpha, (x, y))

    def draw_simulation_effects(self, lot, pov, scale, window):
        self.frame += self.animation_speed

        events = lot.current_events
        x, y = self.get_draw_position(lot, pov, scale)

        if 'burning' in events:
            size = scale
            image = self.city_images.get_animation_image(
                'fire', floor(self.frame) // 5, size)
            window.blit(image, (x + scale / 2 - size / 2, y))

        if 'unhappy' in events:
            size = int(scale / 5)
            image = self.city_images.get_animation_image(
                'unhappy', floor(self.frame) // 10, size)
            window.blit(image, (x + scale / 2 - size / 2, y))

        if 'pandemic' in events:
            size = int(scale / 2)
            image = self.city_images.get_animation_image(
                'pandemic', floor(self.frame), size)
            window.blit(
                image, (x + scale / 2 - size / 2, y + scale / 2 - size / 2))

        if 'burglary' in events:
            size = int(scale / 2)
            image = self.city_images.get_animation_image(
                'burglary', floor(self.frame), size)
            window.blit(
                image, (x + scale / 2 - size / 2, y + scale / 2 - size / 2))
Beispiel #5
0
class BuildModePanel(Panel):
    """ panel enabled after clicking the build mode button"""
    def __init__(self, width, height, position, game_window):
        super().__init__(width, height, game_window)
        self.city_images = CityImages()

        self.menu = pgmen.Menu(title='BUILD - BUY',
                               width=width,
                               height=height,
                               position=position,
                               theme=self.get_theme(),
                               columns=11,
                               rows=1,
                               enabled=False,
                               mouse_enabled=True)

        # ZONING PANEL
        self.zone_building_panel = ZoningPanel(
            width=width,
            height=height,
            position=(100, 100 -
                      100 * height / game_window.window.get_height() - 0.3),
            game_window=game_window)

        # BUY A SPECIAL BUILDING PANEL
        self.special_building_panel = BuySpecialBuildingPanel(
            width=width,
            height=height,
            position=(100,
                      100 - 100 * height / game_window.window.get_height()),
            game_window=game_window)

        # BUTTONS
        scale = (0.075, 0.075)
        self.menu.add.image(self.city_images.get_icon('modern-city'),
                            scale=scale)
        self.zoning_button = self.menu.add.button("zone buildings",
                                                  self.zone_buildings)

        self.zoning_button.select(False)

        self.menu.add.label(' ')
        self.menu.add.label(' ')

        self.menu.add.image(self.city_images.get_icon('capitol'), scale=scale)
        self.buy_building_button = self.menu.add.button(
            "special buildings", self.special_buildings)

        self.menu.add.label(' ')
        self.menu.add.label(' ')

        self.menu.add.image(self.city_images.get_icon('bulldozer'),
                            scale=scale)
        self.bulldoze_button = self.menu.add.button("bulldoze", self.bulldoze)

        self.menu.add.label(' ')

    def special_buildings(self):
        """runs after the button tagged "special building" is pressed"""

        enabled = self.special_building_panel.is_enabled()
        self.disable_subpanels()
        self.special_building_panel.unselect_selected_widget()

        if enabled:
            self.special_building_panel.disable()
            self.unselect_selected_widget()
        else:
            self.special_building_panel.enable()
            self.game_window.bulldozing = False
            self.game_window.zoning = False

    def zone_buildings(self):
        """runs after the button tagged "zone building" is pressed"""

        enabled = self.zone_building_panel.is_enabled()
        self.disable_subpanels()
        self.zone_building_panel.unselect_selected_widget()

        if enabled:
            self.zone_building_panel.disable()
            self.game_window.zoning = False
            self.unselect_selected_widget()
        else:
            self.zone_building_panel.enable()
            self.game_window.bulldozing = False
            self.game_window.zoning = False

    def bulldoze(self):
        """function called by bulldoze button"""
        self.disable_subpanels()
        if not self.game_window.bulldozing:
            self.game_window.bulldozing = True
            self.game_window.zoning = False
        else:
            self.game_window.bulldozing = False
            self.unselect_selected_widget()

    def get_subpanels(self):
        """overrides one from Panel"""
        return [self.special_building_panel, self.zone_building_panel]

    def disable(self):
        """overrides one from Panel"""
        super().disable()
        self.unselect_selected_widget()
        self.zone_building_panel.unselect_selected_widget()
        self.special_building_panel.unselect_selected_widget()
Beispiel #6
0
class CitySpaceGraphics(metaclass=Singleton):
    GRAY = (192, 192, 192)
    AFFECTS_COLOR = 0xf4acb7
    AFFECTED_COLOR = 0xffcad4

    def __init__(self, city_space, width, height):
        self.city_space = city_space
        self.city_images = CityImages()

        self.road_graphics = RoadGraphics((width, height))
        self.lot_graphics = LotGraphics((width, height))

        RoadGraphics.reset()
        LotGraphics.reset()

        self.height = height  # amount of fields in height
        self.width = width  # amount of fields in width

        # pov - point of origin from which we're drawing
        self.pov_x = WINDOW_SIZE[0] // 2
        self.pov_y = WINDOW_SIZE[1] // 2
        self.scale = max([WINDOW_SIZE[1] // self.height + 1,
                          WINDOW_SIZE[0] // self.width + 1])  # defines the zoom level
        self.move_speed = (0, 0)  # added to pov in each frame

        self.selected_lot = None

    def draw(self, mode, construct_to_buy, window):
        # rescale images
        self.city_images.rescale(self.scale)

        # draw lots
        for row in self.city_space.lots:
            for lot in row:
                self.lot_graphics.draw_background(lot, self.scale, (self.pov_x, self.pov_y), window)

        # faded picture of a construct to be placed and bought
        if construct_to_buy:
            lot = self.get_clicked_lot(pg.mouse.get_pos())
            image = pg.image.load(construct_to_buy.value['level'][0]['images'][0])
            x, y = self.lot_graphics.get_draw_position(lot, (self.pov_x, self.pov_y), self.scale)

            # calculating faded picture's dimensions and rescaling
            offset = int(ROAD_WIDTH_RATIO * self.scale)
            scale = int(self.scale * (1 - ROAD_WIDTH_RATIO))
            width, height = image.get_width(), image.get_height()
            ratio = scale / width
            new_width, new_height = int(width * ratio), int(height * ratio)
            x = x + offset
            y = y - new_height + scale + offset
            image = pg.transform.scale(image, (new_width, new_height))

            alpha = pg.Surface((scale, scale))
            alpha.set_alpha(128)

            if not lot.can_place(construct_to_buy):
                alpha.fill((255, 0, 0))
            else:
                alpha.fill((50, 50, 50))

            window.blit(image, (x, y))
            window.blit(alpha, (x, y))

        # roads
        self.road_graphics.draw(self.city_space.road_system, (self.pov_x, self.pov_y), self.scale, window)

        # constructs
        for row in self.city_space.lots:
            for lot in row:
                self.lot_graphics.draw_construct(lot, self.scale, (self.pov_x, self.pov_y), window)

        # cars
        self.road_graphics.animate_cars(self.city_space.road_system, (self.pov_x, self.pov_y), self.scale, window)

        # road placing drawing effect
        if mode == "road_placing":
            alpha = pg.Surface(WINDOW_SIZE)
            alpha.set_alpha(128)
            alpha.fill(self.GRAY)
            window.blit(alpha, (0, 0))
            self.road_graphics.highlight_roads(
                self.city_space.road_system, (self.pov_x, self.pov_y), self.scale, window)

        # access highlighting
        if mode == "access_highlighting":
            alpha = pg.Surface(WINDOW_SIZE)
            alpha.set_alpha(128)
            alpha.fill(self.GRAY)
            window.blit(alpha, (0, 0))
            self.highlight_access(window)

    def update(self):
        """moves the map in each frame"""

        self.pov_x += self.move_speed[0]
        self.pov_y += self.move_speed[1]

        # border:
        self.pov_x = max(self.pov_x, WINDOW_SIZE[0] -
                         self.scale * self.width // 2)
        self.pov_x = min(self.pov_x, self.scale * self.width // 2)
        self.pov_y = min(self.pov_y, self.scale * self.height // 2)
        self.pov_y = max(self.pov_y, WINDOW_SIZE[1] -
                         self.scale * self.height // 2)

    def hovered(self, pos, mode):
        """hovered lot highlighting"""
        if mode == "road_placing":
            self.city_space.road_system.hovered(self.get_clicked_road(pos))

    def add_move_speed(self, move_speed):
        self.move_speed = (
            self.move_speed[0] + move_speed[0], self.move_speed[1] + move_speed[1])

    def zoom(self, zoom_value):
        old_scale = self.scale
        self.scale = int(self.scale * zoom_value)
        self.scale = max([WINDOW_SIZE[1] // self.height + 1,
                          WINDOW_SIZE[0] // self.width + 1, self.scale])
        self.scale = min(self.scale, 500)

        mouse_x, mouse_y = pg.mouse.get_pos()
        self.pov_x -= int((mouse_x - self.pov_x) * (self.scale / old_scale - 1))
        self.pov_y -= int((mouse_y - self.pov_y) * (self.scale / old_scale - 1))

    def select_lot(self, mouse_pos):
        clicked_lot = self.get_clicked_lot(mouse_pos)
        self.selected_lot = clicked_lot

    def get_clicked_lot(self, mouse_pos):
        if mouse_pos is None:
            return None
        x = (mouse_pos[0] - self.pov_x +
             self.scale * self.width // 2) // self.scale
        y = (mouse_pos[1] - self.pov_y +
             self.scale * self.height // 2) // self.scale
        if 0 <= x < self.width and 0 <= y < self.height:
            return self.city_space.lots[x][y]

    def get_clicked_road(self, mouse_pos):
        if mouse_pos is None:
            return
        x = (mouse_pos[0] - self.pov_x +
             self.scale * (self.width / 2 - ROAD_WIDTH_RATIO / 2)) / self.scale
        y = (mouse_pos[1] - self.pov_y +
             self.scale * (self.height / 2 - ROAD_WIDTH_RATIO / 2)) / self.scale
        return x, y

    def highlight_access(self, window):
        if self.selected_lot is None or self.selected_lot.construct is None:
            return

        self.lot_graphics.highlight_lot(self.selected_lot, (self.pov_x, self.pov_y), self.scale, self.AFFECTS_COLOR, window)

        for lot in self.selected_lot.affects:
            if lot == self.selected_lot:
                continue
            self.lot_graphics.highlight_lot(lot, (self.pov_x, self.pov_y), self.scale, self.AFFECTED_COLOR, window)

    @staticmethod
    def set_speed(speed):
        LotGraphics.animation_speed = 0.05 * speed
        RoadGraphics.car_speed = 0.02 * speed
Beispiel #7
0
class GameWindowPanel(Panel):
    """main panel on the left side"""
    city_images = CityImages()

    def __init__(self, width, height, game_window, position=(100, 100)):
        super().__init__(width, height, game_window)
        self.menu = pgmen.Menu(title='CITY SIMULATION GAME',
                               width=width,
                               height=height,
                               position=(0, 0),
                               theme=self.get_theme(),
                               mouse_enabled=True)

        # BUILD MODE PANEL
        self.build_mode_panel = BuildModePanel(width=WINDOW_SIZE[0] - width,
                                               height=height // 15,
                                               position=position,
                                               game_window=game_window)

        # OPTIONS PANEL
        self.option_panel = OptionPanel(width=WINDOW_SIZE[0] // 2,
                                        height=WINDOW_SIZE[1] // 2,
                                        game_window=game_window)

        # STAT PANEL
        self.stat_panel = StatPanel(width=WINDOW_SIZE[0] // 2,
                                    height=WINDOW_SIZE[1] // 2,
                                    game_window=game_window)

        # BUTTONS
        scale = (0.1, 0.1)
        self.menu.add.image(self.city_images.get_icon('play-button'),
                            scale=scale,
                            onselect=self.play)
        self.play_button = self.menu.add.button("play", self.play)
        self.play_button.set_controls()
        self.menu.add.label(' ')

        self.menu.add.image(self.city_images.get_icon('road'), scale=scale)
        self.road_button = self.menu.add.button("add roads", self.add_road)
        self.menu.add.label(' ')

        self.menu.add.image(self.city_images.get_icon('crane'), scale=scale)
        self.build_mode_button = self.menu.add.button("build mode",
                                                      self.build_mode)
        self.menu.add.label(' ')

        self.menu.add.image(self.city_images.get_icon('mesh-network'),
                            scale=scale)
        self.build_mode_button = self.menu.add.button("access", self.access)
        self.menu.add.label(' ')

        self.menu.add.image(self.city_images.get_icon('settings-knobs'),
                            scale=scale)
        self.options_button = self.menu.add.button("options", self.options)
        self.menu.add.label(' ')

    def play(self):
        self.game_window.mode = "game_mode"
        self.disable_subpanels()

    def add_road(self):
        self.disable_subpanels()

        if self.game_window.mode != "road_placing":
            self.game_window.mode = "road_placing"
        else:
            self.game_window.mode = "game_mode"
            self.unselect_selected_widget()

    def access(self):
        self.disable_subpanels()

        if self.game_window.mode != "access_highlighting":
            self.game_window.mode = "access_highlighting"
            self.game_window.city_graphics.select_lot(None)
        else:
            self.game_window.mode = "game_mode"
            self.unselect_selected_widget()

    def options(self):
        enabled = self.option_panel.is_enabled()

        self.disable_subpanels()

        if enabled:
            self.option_panel.disable()
            self.unselect_selected_widget()
        else:
            self.game_window.mode = "game_mode"
            self.option_panel.enable()

    def build_mode(self):
        enabled = self.build_mode_panel.is_enabled()
        self.disable_subpanels()

        if enabled:
            self.build_mode_panel.disable()
            self.unselect_selected_widget()
            self.game_window.mode = "game_mode"
        else:
            self.game_window.mode = "build_mode"
            self.build_mode_panel.enable()
            self.game_window.toggle_zone_highlighting(True)
            self.build_mode_panel.unselect_selected_widget()

    def get_subpanels(self):
        return [
            self.build_mode_panel, self.option_panel, self.stat_panel,
            self.game_window.upgrade_panel
        ]

    def disable_subpanels(self):
        super().disable_subpanels()
        self.game_window.zoning = False
        self.game_window.bulldozing = False
        self.game_window.zoning_type = None
        self.game_window.toggle_zone_highlighting(False)
class RoadGraphics(metaclass=Singleton):
    HIGHLIGHT_COLOR = 0x6d597a
    PLACE_COLOR = 0xf7b267
    BLANK_COLOR = 0xeee2df

    CARS_TO_ROADS_RATIO = 0.1

    cars = set()
    car_speed = 0.04
    city_images = CityImages()
    map_dimensions = None

    def __init__(self, map_dimensions):
        self.map_dimensions = map_dimensions

    @classmethod
    def reset(cls):
        cls.cars = set()
        cls.car_speed = 0.04

    def draw(self, roads, pov, scale, window):
        picture = self.city_images.scaled_vertical
        for pos_x, pos_y in roads.vertical:
            self.draw_element(pos_x, pos_y, pov, scale, picture, window)

        picture = self.city_images.scaled_horizontal
        for pos_x, pos_y in roads.horizontal:
            self.draw_element(pos_x, pos_y, pov, scale, picture, window)

    def draw_element(self, pos_x, pos_y, pov, scale, element, window):
        x = pov[0] - scale * self.map_dimensions[0] // 2 + scale * pos_x
        y = pov[1] - scale * self.map_dimensions[1] // 2 + scale * pos_y
        if -scale < x < window.get_width() and -scale < y < window.get_height():
            window.blit(element, (x, y))

    @staticmethod
    def get_vertical_size(scale):
        return int(scale * ROAD_WIDTH_RATIO), int(scale + scale * ROAD_WIDTH_RATIO)

    @staticmethod
    def get_horizontal_size(scale):
        return int(scale + scale * ROAD_WIDTH_RATIO), int(scale * ROAD_WIDTH_RATIO)

    def highlight_roads(self, roads, pov, scale, window):
        # vertical
        alpha = pg.Surface(self.get_vertical_size(scale))
        alpha_level = 150
        alpha.set_alpha(alpha_level)

        # road outline
        alpha.fill(self.BLANK_COLOR)
        for pos_x, pos_y in product(range(self.map_dimensions[0]), range(self.map_dimensions[1])):
            if not (pos_x, pos_y) in roads.vertical:
                self.draw_element(pos_x, pos_y, pov, scale, alpha, window)

        alpha = pg.Surface(self.get_horizontal_size(scale))
        alpha.set_alpha(alpha_level)
        alpha.fill(self.BLANK_COLOR)
        for pos_x, pos_y in product(range(self.map_dimensions[0]), range(self.map_dimensions[1])):
            if not (pos_x, pos_y) in roads.horizontal:
                self.draw_element(pos_x, pos_y, pov, scale, alpha, window)

        # highlight
        alpha = pg.Surface(self.get_vertical_size(scale))
        alpha.set_alpha(alpha_level)
        alpha.fill(self.HIGHLIGHT_COLOR)
        for pos_x, pos_y in roads.vertical:
            self.draw_element(pos_x, pos_y, pov, scale, alpha, window)

        alpha = pg.Surface(self.get_horizontal_size(scale))
        alpha.set_alpha(alpha_level)

        alpha.fill(self.HIGHLIGHT_COLOR)
        for pos_x, pos_y in roads.horizontal:
            self.draw_element(pos_x, pos_y, pov, scale, alpha, window)

        # hovered
        if roads.hovered_road:
            if roads.hovered_direction == HORIZONTAL:
                alpha.fill(self.PLACE_COLOR)
                self.draw_element(
                    roads.hovered_road[0], roads.hovered_road[1], pov, scale, alpha, window)
            elif roads.hovered_direction == VERTICAL:
                alpha = pg.Surface(self.get_vertical_size(scale))
                alpha.set_alpha(alpha_level)
                alpha.fill(self.PLACE_COLOR)
                self.draw_element(
                    roads.hovered_road[0], roads.hovered_road[1], pov, scale, alpha, window)

    def animate_cars(self, roads, pov, scale, window):
        self.update_cars()
        if len(self.cars) < self.CARS_TO_ROADS_RATIO * (roads.get_road_count()):
            self.add_car(roads)

        if len(self.cars) > 2 * self.CARS_TO_ROADS_RATIO * (roads.get_road_count()):
            self.cars.pop()

        for car in self.cars:
            image = self.city_images.get_scaled_car_image(
                car.image_type, car.road_direction, car.car_direction)
            self.draw_element(car.x, car.y, pov, scale, image, window)

    def update_cars(self):
        for car in self.cars:
            car.move(self.car_speed)

    def add_car(self, roads, car_type=None, road=None, road_direction=None):
        if car_type is None:
            car_type = self.city_images.get_random_car_type()
        if road is None:
            if (road_direction is None or road_direction == VERTICAL) and roads.vertical:
                road = choice(list(roads.vertical))
                self.cars.add(Car(road, VERTICAL, roads, car_type))
            elif (road_direction is None or road_direction == HORIZONTAL) and roads.horizontal:
                road = choice(list(roads.horizontal))
                self.cars.add(Car(road, HORIZONTAL, roads, car_type))
        else:
            self.cars.add(Car(road, road_direction, roads, car_type))
class InfoPanel(Panel):
    ICON_SCALE = (0.048, 0.048)
    city_images = CityImages()

    def __init__(self, width, height, position, game_window, simulator):
        super().__init__(width, height, game_window)

        self.simulator = simulator

        # menu
        self.menu = pgmen.Menu('your city: ',
                               width=width,
                               height=height,
                               position=position,
                               theme=self.get_theme(),
                               columns=2,
                               rows=12)

        # info
        self.menu.add.label('---- r e s o u r c e s ----')
        funds_label = self.menu.add.label('             funds')
        happiness_label = self.menu.add.label('         happiness')
        population_label = self.menu.add.label('        population')
        water_label = self.menu.add.label('      water supply')
        waste_label = self.menu.add.label('      waste piled up')
        power_label = self.menu.add.label('      power supply')

        self.menu.add.label('')
        self.menu.add.label('----- d e m a n d -----')
        res_demand_label = self.menu.add.label('residential')
        comm_demand_label = self.menu.add.label(' commercial')
        indu_demand_label = self.menu.add.label(' industrial')

        self.menu.add.label('')

        self.images = {
            'funds':
            (self.menu.add.image(self.city_images.get_icon('banknote'),
                                 scale=self.ICON_SCALE), 'banknote'),
            'resident_happyness':
            (self.menu.add.image(self.city_images.get_icon('heart-inside'),
                                 scale=self.ICON_SCALE), 'heart-inside'),
            'population':
            (self.menu.add.image(self.city_images.get_icon('person'),
                                 scale=self.ICON_SCALE), 'person'),
            'water': (self.menu.add.image(self.city_images.get_icon('drop'),
                                          scale=self.ICON_SCALE), 'drop'),
            'waste':
            (self.menu.add.image(self.city_images.get_icon('trash-can'),
                                 scale=self.ICON_SCALE), 'trash-can'),
            'power': (self.menu.add.image(self.city_images.get_icon('plug'),
                                          scale=self.ICON_SCALE), 'plug')
        }

        self.menu.add.label('')
        self.menu.add.label('')
        self.images['residential demand'] = (self.menu.add.image(
            self.city_images.get_icon('house'),
            scale=self.ICON_SCALE), 'house')
        self.images['commercial demand'] = (self.menu.add.image(
            self.city_images.get_icon('shop'), scale=self.ICON_SCALE), 'shop')
        self.images['industrial demand'] = (self.menu.add.image(
            self.city_images.get_icon('factory'),
            scale=self.ICON_SCALE), 'factory')

        funds_label.add_draw_callback(self.update_label('funds'))
        population_label.add_draw_callback(self.update_label('population'))
        happiness_label.add_draw_callback(
            self.update_label('resident_happyness'))

        res_demand_label.add_draw_callback(
            self.update_label('residential demand'))
        comm_demand_label.add_draw_callback(
            self.update_label('commercial demand'))
        indu_demand_label.add_draw_callback(
            self.update_label('industrial demand'))
        water_label.add_draw_callback(self.update_label('water'))
        waste_label.add_draw_callback(self.update_label('waste'))
        power_label.add_draw_callback(self.update_label('power'))

        self.labels = [
            funds_label, happiness_label, res_demand_label, comm_demand_label,
            indu_demand_label, water_label, waste_label, power_label
        ]

        for image in self.images.values():
            image[0].red = False

    def update_label(self, key):
        def update(widget, menu):
            text = widget.get_title().split(':')[0]
            value = self.simulator.get_data(key)

            if text.strip() == 'happiness':
                widget.set_title(f'happiness: {value:.1%}')
            else:
                widget.set_title(f'{text}: {value}')

            image, image_key = self.images[key]
            if isinstance(value, str):
                if value == 'Very high' and not image.red:
                    image.red = True
                    image.set_image(
                        pgmen.baseimage.BaseImage(
                            self.city_images.get_warning_icon(
                                image_key)).scale(*self.ICON_SCALE))
                elif value != 'Very high' and image.red:
                    image.red = False
                    image.set_image(
                        pgmen.baseimage.BaseImage(
                            self.city_images.get_icon(image_key)).scale(
                                *self.ICON_SCALE))

            elif value < 0.1 and text.strip() != 'pollution' and not image.red:
                image.red = True
                image.set_image(
                    pgmen.baseimage.BaseImage(
                        self.city_images.get_warning_icon(image_key)).scale(
                            *self.ICON_SCALE))
            elif value >= 0.1 and image.red:
                image.red = False
                image.set_image(
                    pgmen.baseimage.BaseImage(
                        self.city_images.get_icon(image_key)).scale(
                            *self.ICON_SCALE))

        return update

    def force_update_labels(self):
        for label in self.labels:
            label.apply_draw_callbacks()

    def get_theme(self):
        theme = super().get_theme()
        theme.widget_padding = 2
        theme.widget_margin = (-10, 0)
        theme.widget_alignment = pgmen.locals.ALIGN_RIGHT
        return theme