Compare commits

...

10 Commits

41 changed files with 269 additions and 111 deletions

1
.gitignore vendored
View File

@@ -1,3 +1,4 @@
*.dem *.dem
__pycache__/ __pycache__/
venv venv
.venv

Binary file not shown.

View File

Before

Width:  |  Height:  |  Size: 7.2 KiB

After

Width:  |  Height:  |  Size: 7.2 KiB

BIN
assets/images/bomb.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 377 KiB

View File

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

View File

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 10 KiB

View File

Before

Width:  |  Height:  |  Size: 9.4 KiB

After

Width:  |  Height:  |  Size: 9.4 KiB

View File

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 19 KiB

View File

@@ -1,6 +1,6 @@
{ {
"material": "maps/cs_office.png", "material": "assets/maps/overview/cs_office.png",
"pos_x": -1838, "pos_x": -1838,
"pos_y": 1858, "pos_y": 1858,
"scale": 4.1, "scale": 4.1,

View File

@@ -0,0 +1,8 @@
{
"material": "assets/maps/overview/de_ancient.png",
"pos_x": -2953,
"pos_y": 2164,
"scale": 5
}

View File

@@ -1,5 +1,5 @@
{ {
"material": "maps/de_anubis.png", "material": "assets/maps/overview/de_anubis.png",
"pos_x": -2796.000000, "pos_x": -2796.000000,
"pos_y": 3328.000000, "pos_y": 3328.000000,
"scale": 5.220000 "scale": 5.220000

View File

@@ -1,6 +1,6 @@
{ {
"material": "maps/de_dust2.png" , "material": "assets/maps/overview/de_dust2.png" ,
"pos_x": -2476 , "pos_x": -2476 ,
"pos_y": 3239, "pos_y": 3239,
"scale": 4.4 , "scale": 4.4 ,

View File

@@ -1,6 +1,6 @@
{ {
"material": "maps/de_inferno.png", "material": "assets/maps/overview/de_inferno.png",
"pos_x": -2087 , "pos_x": -2087 ,
"pos_y" : 3870, "pos_y" : 3870,
"scale": 4.9, "scale": 4.9,

View File

@@ -1,5 +1,5 @@
{ {
"material": "maps/de_mirage.png", "material": "assets/maps/overview/de_mirage.png",
"pos_x": -3230, "pos_x": -3230,
"pos_y": 1713, "pos_y": 1713,
"scale": 5.00, "scale": 5.00,

View File

@@ -0,0 +1,8 @@
{
"material": "assets/maps/overview/de_overpass.png",
"pos_x": -4831,
"pos_y": 1781,
"scale": 5.2
}

View File

Before

Width:  |  Height:  |  Size: 195 KiB

After

Width:  |  Height:  |  Size: 195 KiB

View File

Before

Width:  |  Height:  |  Size: 88 KiB

After

Width:  |  Height:  |  Size: 88 KiB

View File

Before

Width:  |  Height:  |  Size: 126 KiB

After

Width:  |  Height:  |  Size: 126 KiB

View File

Before

Width:  |  Height:  |  Size: 172 KiB

After

Width:  |  Height:  |  Size: 172 KiB

View File

Before

Width:  |  Height:  |  Size: 129 KiB

After

Width:  |  Height:  |  Size: 129 KiB

View File

Before

Width:  |  Height:  |  Size: 37 KiB

After

Width:  |  Height:  |  Size: 37 KiB

View File

Before

Width:  |  Height:  |  Size: 200 KiB

After

Width:  |  Height:  |  Size: 200 KiB

View File

@@ -1,5 +1,4 @@
import pygame import pygame
import copy
class ControlController: class ControlController:
def __init__(self, control_renderer, box_top: tuple[int, int]): def __init__(self, control_renderer, box_top: tuple[int, int]):

View File

@@ -22,13 +22,15 @@ def main():
}, },
"styling": { "styling": {
"font": pygame.font.Font("assets/fonts/Metropolis-Regular.ttf", 30), "font": pygame.font.Font("assets/fonts/Metropolis-Regular.ttf", 30),
"small_font": pygame.font.Font("assets/fonts/Metropolis-Regular.ttf", 15), "small_font": pygame.font.Font("assets/fonts/Metropolis-Regular.ttf", 18),
"underline_bold_font": (lambda f: (f.set_underline(True), f)[1])(pygame.font.Font("assets/fonts/Metropolis-Bold.ttf", 30)),
"button_colour": (200, 200, 200), "button_colour": (200, 200, 200),
"pressed_button_colour": (150, 150, 150), "pressed_button_colour": (150, 150, 150),
"text_colour": (255, 255, 255), "text_colour": (255, 255, 255),
"background_colour": (30, 30, 30), "background_colour": (30, 30, 30),
"foreground_colour": (100, 100, 100), "foreground_colour": (100, 100, 100),
"player_selected_colour": (255, 255, 0), "player_selected_colour": (255, 255, 0),
"bomb_image": pygame.image.load("assets/images/bomb.png"),
} }
} }

View File

@@ -1,8 +0,0 @@
{
"material": "maps/de_ancient.png",
"pos_x": -2953,
"pos_y": 2164,
"scale": 5
}

View File

@@ -1,8 +0,0 @@
{
"material": "maps/de_overpass.png",
"pos_x": -4831,
"pos_y": 1781,
"scale": 5.2
}

View File

@@ -2,11 +2,13 @@ from models.player import Player
from models.team import Team from models.team import Team
class Match: class Match:
def __init__(self, map_name, game_info, team_1: Team, team_2: Team, tick_rate=64): def __init__(self, map_name, game_info, team_1: Team, team_2: Team, game_events, tick_rate=64):
self.team_1 = team_1 self.team_1 = team_1
self.team_2 = team_2 self.team_2 = team_2
self.map_name = map_name self.map_name = map_name
self.game_events = game_events
self.round_start_times = list(self.game_events[0][1]["tick"])
self.tick = 1 self.tick = 1
self.current_tick = game_info[game_info["tick"] == self.tick] self.current_tick = game_info[game_info["tick"] == self.tick]
@@ -17,28 +19,46 @@ class Match:
self.game_info = game_info.sort_values(by=["tick", "player_steamid"]) # pd dataframe sorted by tick self.game_info = game_info.sort_values(by=["tick", "player_steamid"]) # pd dataframe sorted by tick
self.tick_rate = tick_rate self.tick_rate = tick_rate
def _update_player_positions(self) -> None: def _update_player(self, player: Player) -> None:
# inefficient, might need to change player_row = self.current_tick[self.current_tick["player_steamid"] == player.steam_id]
if player_row.empty:
return
row = player_row.iloc[0]
player.x = row["X"]
player.y = row["Y"]
player.z = row["Z"]
player.pitch = row["pitch"]
player.yaw = row["yaw"]
player.dead = row["is_alive"] == 0
player.is_shooting = row["shots_fired"]
player.health = int(row["health"])
player.armour = int(row["armor_value"])
player.current_weapon = row["active_weapon_name"]
player.kills = int(row["kills_total"])
player.deaths = int(row["deaths_total"])
player.assists = int(row["assists_total"])
player.inventory = row["inventory"]
if "C4 Explosive" in player.inventory:
player.has_bomb = True
else:
player.has_bomb = False
if "Defuse Kit" in player.inventory:
player.has_defuser = True
else:
player.has_defuser = False
def _update_players(self) -> None:
# empty tick # empty tick
if self.current_tick.empty: if self.current_tick.empty:
return return
# # check if current tick has NaN values
# if self.current_tick.isnull().values.any():
# return
for player in self.get_players(): for player in self.get_players():
player.x = self.current_tick[self.current_tick["player_steamid"] == player.steam_id]["X"].values[0] self._update_player(player)
player.y = self.current_tick[self.current_tick["player_steamid"] == player.steam_id]["Y"].values[0]
player.z = self.current_tick[self.current_tick["player_steamid"] == player.steam_id]["Z"].values[0]
player.pitch = self.current_tick[self.current_tick["player_steamid"] == player.steam_id]["pitch"].values[0]
player.yaw = self.current_tick[self.current_tick["player_steamid"] == player.steam_id]["yaw"].values[0]
player.dead = self.current_tick[self.current_tick["player_steamid"] == player.steam_id]["is_alive"].values[0] == 0
player.is_shooting = self.current_tick[self.current_tick["player_steamid"] == player.steam_id]["shots_fired"].values[0]
player.health = int(self.current_tick[self.current_tick["player_steamid"] == player.steam_id]["health"].values[0])
player.armour = int(self.current_tick[self.current_tick["player_steamid"] == player.steam_id]["armor_value"].values[0])
player.current_weapon = self.current_tick[self.current_tick["player_steamid"] == player.steam_id]["active_weapon_name"].values[0]
def _update_round(self) -> None: def _update_round(self) -> None:
if self.current_tick.empty: if self.current_tick.empty:
@@ -46,27 +66,34 @@ class Match:
self.round = self.current_tick["total_rounds_played"].values[0] self.round = self.current_tick["total_rounds_played"].values[0]
if self.round >= 12: self.team_1.score = int(self.current_tick[self.current_tick["team_num"] == self.team_1.id]["team_rounds_total"].values[0])
self.team_1.set_t() self.team_2.score = int(self.current_tick[self.current_tick["team_num"] == self.team_2.id]["team_rounds_total"].values[0])
self.team_2.set_ct()
self.team_1.score = int(self.current_tick[self.current_tick["team_num"] == 3]["team_rounds_total"].values[0]) def _update_team_ids(self) -> None:
self.team_2.score = int(self.current_tick[self.current_tick["team_num"] == 2]["team_rounds_total"].values[0]) # get random player from each team
else: if self.current_tick.empty:
self.team_1.set_ct() return
self.team_2.set_t()
self.team_1.score = int(self.current_tick[self.current_tick["team_num"] == 2]["team_rounds_total"].values[0]) for team in self.get_teams():
self.team_2.score = int(self.current_tick[self.current_tick["team_num"] == 3]["team_rounds_total"].values[0]) random_player = team.players[0]
player_team_id = self.current_tick[self.current_tick["player_steamid"] == random_player.steam_id]["team_num"].values[0]
if player_team_id == 2:
team.set_t()
elif player_team_id == 3:
team.set_ct()
def next_tick(self) -> None: def next_tick(self) -> None:
self.tick += 1 self.tick += 1
self.current_tick = self.game_info[self.game_info["tick"] == self.tick] self.current_tick = self.game_info[self.game_info["tick"] == self.tick]
self._update_player_positions() self._update_players()
self._update_team_ids()
self._update_round() self._update_round()
def set_tick(self, tick: int) -> None: def set_tick(self, tick: int) -> None:
self.tick = tick self.tick = tick
self.current_tick = self.game_info[self.game_info["tick"] == self.tick] self.current_tick = self.game_info[self.game_info["tick"] == self.tick]
self._update_player_positions() self._update_players()
self._update_round() self._update_round()
def get_players(self) -> list[Player]: def get_players(self) -> list[Player]:

View File

@@ -13,6 +13,15 @@ class Player:
self.dead = False self.dead = False
self.is_shooting = False self.is_shooting = False
self.current_weapon = None self.current_weapon = None
self.has_bomb = False
self.is_planting = False
self.has_defuser = False
self.is_defusing = False
self.inventory = []
self.kills = 0
self.deaths = 0
self.assists = 0
## UI-related state ## UI-related state
self.is_selected = False self.is_selected = False

View File

@@ -7,7 +7,8 @@ class Team:
def __init__(self): def __init__(self):
self.players = [] self.players = []
self.score = 0 self.score = 0
self.set_t() self.id = 1
self.set_ct()
def add_player(self, player: Player): def add_player(self, player: Player):
self.players.append(player) self.players.append(player)
@@ -16,9 +17,9 @@ class Team:
self.players.remove(player) self.players.remove(player)
def set_ct(self): def set_ct(self):
self.is_ct = True self.id = 3
self.colour = CT_COLOUR self.colour = CT_COLOUR
def set_t(self): def set_t(self):
self.is_ct = False self.id = 2
self.colour = T_COLOUR self.colour = T_COLOUR

View File

@@ -1,5 +1,5 @@
import pygame import pygame
from widgets.slider import HorizontalSlider from widgets.break_slider import BreakSlider
class ControlRenderer: class ControlRenderer:
def __init__(self, screen, match): def __init__(self, screen, match):
@@ -8,7 +8,10 @@ class ControlRenderer:
self.match = match self.match = match
self.colour = (255, 255, 255) self.colour = (255, 255, 255)
self.slider = HorizontalSlider(self.screen, 50, 0, self.screen.get_width()-50, 20, 1, self.match.max_tick) self.slider = BreakSlider(self.screen, 50, 0, self.screen.get_width()-50, 20, 1, self.match.max_tick)
self.slider.fill = True
self.slider.set_breakpoints(self.match.round_start_times)
self.slider.set_fill_colour((0,0,0))
def _render_slider(self): def _render_slider(self):
# Update slider value # Update slider value

View File

@@ -10,10 +10,10 @@ class GUIRenderer:
# Buttons # Buttons
self.settings_button = Button(self.screen.get_width()-40, 10, 30, 30, None) self.settings_button = Button(self.screen.get_width()-40, 10, 30, 30, None)
self.settings_button.set_image("assets/setting.png") self.settings_button.set_image("assets/images/setting.png")
self.back_button = Button(self.screen.get_width()-80, 10, 30, 30, None) self.back_button = Button(self.screen.get_width()-80, 10, 30, 30, None)
self.back_button.set_image("assets/arrow.png") self.back_button.set_image("assets/images/arrow.png")
self.colour = (255, 255, 255) self.colour = (255, 255, 255)

View File

@@ -1,5 +1,3 @@
import pygame
class InfoRenderer: class InfoRenderer:
def __init__(self, screen, styling, match): def __init__(self, screen, styling, match):
self.screen = screen self.screen = screen
@@ -8,25 +6,45 @@ class InfoRenderer:
self.colour = self.styling["text_colour"] self.colour = self.styling["text_colour"]
self.font = self.styling["font"] self.font = self.styling["font"]
self.small_font = self.styling["small_font"] self.small_font = self.styling["small_font"]
self.underline_bold_font = self.styling["underline_bold_font"]
self.selected_player = None self.selected_player = None
self.player_info_start_y = 100
self.match_info_start_y = 500
# Private methods # Private methods
def _render_player_info(self): def _render_player_info(self):
"""Draws the player info on the screen.""" """Draws the player info on the screen."""
if self.selected_player is None: if self.selected_player is None:
return player_info_title = "No player selected\n"
player_info = ""
else:
player_info_title = f"Player: {self.selected_player.name}\n"
player_info = f"Active Weapon: {self.selected_player.current_weapon}\n"
player_info += f"Inventory: \n - {"\n - ".join(self.selected_player.inventory)}\n"
player_info += f"Health: {self.selected_player.health}\n"
player_info += f"Armour: {self.selected_player.armour}\n"
player_info += f"Kills: {self.selected_player.kills}\n"
player_info += f"Deaths: {self.selected_player.deaths}\n"
player_info += f"Assists: {self.selected_player.assists}\n"
player_info_title = f"{self.selected_player.name}\n" text_surface = self.underline_bold_font.render(player_info_title, True, self.styling["text_colour"])
player_info = f"Active Weapon: {self.selected_player.current_weapon}\n" self.screen.blit(text_surface, (10, self.player_info_start_y))
player_info += f"Health: {self.selected_player.health}\n"
player_info += f"Armour: {self.selected_player.armour}\n"
text_surface = self.font.render(player_info_title, True, self.styling["text_colour"])
self.screen.blit(text_surface, (10, 100))
text_surface = self.small_font.render(player_info, True, self.styling["text_colour"]) text_surface = self.small_font.render(player_info, True, self.styling["text_colour"])
self.screen.blit(text_surface, (10, 150)) self.screen.blit(text_surface, (10, self.player_info_start_y + 50))
def _render_match_info(self):
"""Draws the match info on the screen."""
match_info_title = "Match Info\n"
match_info = f"Map: {self.match.map_name}\n"
match_info += f"Score: {self.match.team_1.score}-{self.match.team_2.score}\n"
text_surface = self.underline_bold_font.render(match_info_title, True, self.styling["text_colour"])
self.screen.blit(text_surface, (10, self.match_info_start_y))
text_surface = self.small_font.render(match_info, True, self.styling["text_colour"])
self.screen.blit(text_surface, (10, self.match_info_start_y + 50))
def _render_current_tick(self, match_tick, max_tick): def _render_current_tick(self, match_tick, max_tick):
text = self.font.render(f"Tick: {match_tick}/{max_tick}", True, self.colour) text = self.font.render(f"Tick: {match_tick}/{max_tick}", True, self.colour)
@@ -41,4 +59,4 @@ class InfoRenderer:
"""Renders the info on the screen.""" """Renders the info on the screen."""
self._render_player_info() self._render_player_info()
self._render_current_tick(self.match.tick, self.match.max_tick) self._render_current_tick(self.match.tick, self.match.max_tick)
self._render_team_scores(self.match.team_1.score, self.match.team_2.score) self._render_match_info()

View File

@@ -13,9 +13,14 @@ class PlayerRenderer:
self.styling = styling self.styling = styling
self.player_font = pygame.font.Font(None, 15) self.player_font = pygame.font.Font(None, 15)
self.bomb_image = self.styling["bomb_image"]
self.player_radius = 5 self.player_radius = 5
self.hovered_radius = 10 self.hovered_radius = 10
self.bomb_radius = 10
self.bomb_hovered_radius = 15
self.health_bar_foreground = (0, 255, 0) self.health_bar_foreground = (0, 255, 0)
self.health_bar_background = (255, 0, 0) self.health_bar_background = (255, 0, 0)
@@ -41,6 +46,10 @@ class PlayerRenderer:
## Private Methods ## Private Methods
def _render_circle(self, player: Player, team): def _render_circle(self, player: Player, team):
if player.has_bomb:
self._render_bomb(player)
return
if player.is_hovered: if player.is_hovered:
radius = self.hovered_radius radius = self.hovered_radius
else: else:
@@ -49,6 +58,16 @@ class PlayerRenderer:
x, y = self.map_coord_converter.map_to_screen(player.x, player.y) x, y = self.map_coord_converter.map_to_screen(player.x, player.y)
pygame.draw.circle(self.screen, team.colour, (x, y), radius) pygame.draw.circle(self.screen, team.colour, (x, y), radius)
def _render_bomb(self, player: Player):
if player.is_hovered:
radius = self.bomb_hovered_radius
else:
radius = self.bomb_radius
x, y = self.map_coord_converter.map_to_screen(player.x, player.y)
bomb_image = pygame.transform.scale(self.bomb_image, (radius*2, radius*2))
self.screen.blit(bomb_image, (x-radius, y-radius))
def _render_text(self, player): def _render_text(self, player):
if player.is_selected: if player.is_selected:
text = self.player_font.render(player.name, True, self.styling["player_selected_colour"]) text = self.player_font.render(player.name, True, self.styling["player_selected_colour"])

View File

@@ -1,4 +1,3 @@
import pygame
from widgets.button import Button from widgets.button import Button
from widgets.switch import Switch from widgets.switch import Switch
@@ -21,7 +20,7 @@ class SettingsMenuRenderer:
self.back_button.set_colour(self.styling["button_colour"]) self.back_button.set_colour(self.styling["button_colour"])
self.back_button.set_font(self.styling["font"]) self.back_button.set_font(self.styling["font"])
self.back_button.set_pressed_colour(self.styling["pressed_button_colour"]) self.back_button.set_pressed_colour(self.styling["pressed_button_colour"])
self.back_button.set_image("assets/arrow.png") self.back_button.set_image("assets/images/arrow.png")
# Switches # Switches
self.show_yaw_button = Switch(self.widget_start_x, 100, 100, self.show_yaw_text.get_rect().height, self.options["show_yaw"]) self.show_yaw_button = Switch(self.widget_start_x, 100, 100, self.show_yaw_text.get_rect().height, self.options["show_yaw"])

View File

@@ -1,15 +1,13 @@
demoparser2==0.38.0 demoparser2==0.38.0
numpy==2.2.4 numpy==2.2.5
pandas==2.2.3 pandas==2.2.3
polars==1.25.2 polars==1.29.0
pyarrow==19.0.1 pyarrow==20.0.0
pygame==2.6.1 pygame-ce==2.5.3
pygame_gui==0.6.13
python-dateutil==2.9.0.post0 python-dateutil==2.9.0.post0
pytz==2025.1 python-i18n==0.3.9
pytz==2025.2
six==1.17.0 six==1.17.0
tqdm==4.67.1 tqdm==4.67.1
tzdata==2025.1 tzdata==2025.2
pygame_gui==0.6.13
pygame-ce==2.5.3
pygame-pgu==0.21
python-i18n==0.3.9

View File

@@ -1,3 +1,4 @@
import time
from states.game_state import GameState from states.game_state import GameState
from controllers.player_controller import PlayerController from controllers.player_controller import PlayerController
from render.map_renderer import MapRenderer from render.map_renderer import MapRenderer
@@ -15,10 +16,19 @@ class Game(GameState):
def __init__(self, switch_state_callback, context): def __init__(self, switch_state_callback, context):
super().__init__(switch_state_callback, context) super().__init__(switch_state_callback, context)
match_data_path = f"maps/{self.match.map_name}.json" self.match_data_path = f"assets/maps/config/{self.match.map_name}.json"
match_image_path = f"maps/{self.match.map_name}.png" self.match_image_path = f"assets/maps/overview/{self.match.map_name}.png"
# Screen Areas self.game_update_interval = 1 / self.match.tick_rate
self.elapsed_time = 0
self.last_time = time.perf_counter()
self.__init_screen_areas()
self.__init_utils()
self.__init_renderers()
self.__init_controllers()
def __init_screen_areas(self):
self.info_box = pygame.Surface((350, self.screen.get_height()), pygame.SRCALPHA) self.info_box = pygame.Surface((350, self.screen.get_height()), pygame.SRCALPHA)
self.info_box_top_left = (0, 0) self.info_box_top_left = (0, 0)
@@ -28,17 +38,17 @@ class Game(GameState):
self.control_box = pygame.Surface((650, 120), pygame.SRCALPHA) self.control_box = pygame.Surface((650, 120), pygame.SRCALPHA)
self.control_box_top_left = (350, 650) self.control_box_top_left = (350, 650)
# Helper Classes def __init_utils(self):
self.map_coord_controller = MapCoordConverter(self.game_box.get_width(), self.game_box.get_height(), match_data_path, match_image_path) self.map_coord_controller = MapCoordConverter(self.game_box.get_width(), self.game_box.get_height(), self.match_data_path, self.match_image_path)
# Renderers def __init_renderers(self):
self.map_renderer = MapRenderer(self.game_box, match_data_path, match_image_path) self.map_renderer = MapRenderer(self.game_box, self.match_data_path, self.match_image_path)
self.player_renderer = PlayerRenderer(self.game_box, self.match, self.map_coord_controller, self.options, self.styling) self.player_renderer = PlayerRenderer(self.game_box, self.match, self.map_coord_controller, self.options, self.styling)
self.gui_render = GUIRenderer(self.screen, self.match) self.gui_render = GUIRenderer(self.screen, self.match)
self.info_render = InfoRenderer(self.info_box, self.styling, self.match) self.info_render = InfoRenderer(self.info_box, self.styling, self.match)
self.control_render = ControlRenderer(self.control_box, self.match) self.control_render = ControlRenderer(self.control_box, self.match)
# Controllers def __init_controllers(self):
self.player_controller = PlayerController(self.player_renderer, self.match, self.game_box_top_left) self.player_controller = PlayerController(self.player_renderer, self.match, self.game_box_top_left)
self.gui_controller = GUIController(self.gui_render, self.switch_state) self.gui_controller = GUIController(self.gui_render, self.switch_state)
self.info_controller = InfoController(self.info_render, self.player_controller) self.info_controller = InfoController(self.info_render, self.player_controller)
@@ -55,8 +65,15 @@ class Game(GameState):
self.control_controller.update(event) self.control_controller.update(event)
def update(self): def update(self):
"""Updates game objects.""" """Fixed-timestep update decoupled from frame rate."""
self.match.next_tick() now = time.perf_counter()
delta = now - self.last_time
self.last_time = now
self.elapsed_time += delta
while self.elapsed_time >= self.game_update_interval:
self.match.next_tick()
self.elapsed_time -= self.game_update_interval
def draw(self): def draw(self):
"""Draws everything on screen.""" """Draws everything on screen."""

View File

@@ -1,9 +1,6 @@
from states.game_state import GameState from states.game_state import GameState
from widgets.button import Button
from widgets.switch import Switch
from controllers.settings_controller import SettingsController from controllers.settings_controller import SettingsController
from render.settings_menu_renderer import SettingsMenuRenderer from render.settings_menu_renderer import SettingsMenuRenderer
import pygame
class SettingsMenu(GameState): class SettingsMenu(GameState):
def __init__(self, switch_state_callback, context): def __init__(self, switch_state_callback, context):

View File

@@ -3,7 +3,6 @@ from widgets.button import Button
from models.match import Match from models.match import Match
from models.player import Player from models.player import Player
from models.team import Team from models.team import Team
from pgu import gui
import demoparser2 import demoparser2
import pygame import pygame
import pygame_gui import pygame_gui
@@ -17,7 +16,7 @@ class StartMenu(GameState):
self.default_button_width = self.screen.get_width() * 0.8 self.default_button_width = self.screen.get_width() * 0.8
# logo # logo
self.logo = pygame.image.load("assets/logo.png").convert_alpha() self.logo = pygame.image.load("assets/images/logo.png").convert_alpha()
self.logo_scale = 0.7 self.logo_scale = 0.7
self.logo = pygame.transform.smoothscale(self.logo, (self.logo.get_rect().size[0] * self.logo_scale, self.logo.get_rect().size[1] * self.logo_scale)) self.logo = pygame.transform.smoothscale(self.logo, (self.logo.get_rect().size[0] * self.logo_scale, self.logo.get_rect().size[1] * self.logo_scale))
@@ -84,15 +83,19 @@ class StartMenu(GameState):
demo_parser = demoparser2.DemoParser(demo_file) demo_parser = demoparser2.DemoParser(demo_file)
game_info = demo_parser.parse_ticks(["X", "Y", "Z", "pitch", "yaw", "is_alive", "team", "player_steamid", game_info = demo_parser.parse_ticks(["X", "Y", "Z", "pitch", "yaw", "is_alive", "team", "player_steamid",
"team_rounds_total", "team_num", "total_rounds_played", "shots_fired", "team_rounds_total", "team_num", "total_rounds_played", "shots_fired",
"kills_total", "deaths_total", "assists_total", "inventory",
"health", "armor_value", "active_weapon_name"]) "health", "armor_value", "active_weapon_name"])
round_changes = demo_parser.parse_events(["round_start", "is_ct_timeout"])
header_info = demo_parser.parse_header() header_info = demo_parser.parse_header()
map_name = header_info['map_name'] map_name = header_info['map_name']
players = demo_parser.parse_player_info() players = demo_parser.parse_player_info()
print(demo_parser.list_game_events())
team_1 = Team() team_1 = Team()
team_1.set_ct()
team_2 = Team() team_2 = Team()
m = Match(map_name, game_info, team_1, team_2, self.options) team_2.set_ct()
m = Match(map_name, game_info, team_1, team_2, round_changes)
for index, row in players.iterrows(): for index, row in players.iterrows():
if row["team_number"] == 2: if row["team_number"] == 2:
team_1.add_player(Player(row["name"], row["steamid"])) team_1.add_player(Player(row["name"], row["steamid"]))

55
widgets/break_slider.py Normal file
View File

@@ -0,0 +1,55 @@
from widgets.slider import HorizontalSlider
import pygame
class BreakSlider(HorizontalSlider):
def __init__(self, screen, x, y, width, height, min_value, max_value):
super().__init__(screen, x, y, width, height, min_value, max_value)
self.breakpoints = []
# defaults
self.breakpoint_colour = (0, 0, 0)
self.breakpoint_line_width = 2
def add_breakpoint(self, breakpoint):
if breakpoint < self.min_value or breakpoint > self.max_value:
raise ValueError("Breakpoint must fit between min and max values")
self.breakpoints.append(breakpoint)
def set_breakpoints(self, breakpoints):
self.breakpoints = breakpoints
def _draw_breakpoints(self):
for breakpoint in self.breakpoints:
# create line
break_x = self._value_to_knob(breakpoint)
pygame.draw.line(self.screen,
self.breakpoint_colour,
(break_x, self.y),
(break_x, self.y + self.height),
width=self.breakpoint_line_width)
def draw(self):
self._draw_slider()
self._draw_breakpoints()
self._draw_knob()
if __name__ == "__main__":
pygame.init()
screen = pygame.display.set_mode((800, 600))
slider = BreakSlider(screen, 50, 50, 700, 20, 0, 1000)
slider.add_breakpoint(100)
slider.add_breakpoint(500)
slider.add_breakpoint(700)
running = True
while running:
screen.fill((0, 0, 0))
slider.draw()
pygame.display.flip()
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
slider.handle_event(event)

View File

@@ -41,7 +41,7 @@ class Button:
def set_font_size(self, font_size: int) -> None: def set_font_size(self, font_size: int) -> None:
self.font_size = font_size self.font_size = font_size
def set_font(self, font: pygame.Font) -> None: def set_font(self, font: pygame.font.Font) -> None:
self.font = font self.font = font
def set_text(self, text: str) -> None: def set_text(self, text: str) -> None:

View File

@@ -15,7 +15,7 @@ class HorizontalSlider:
self.knob_colour = (255, 0, 0) self.knob_colour = (255, 0, 0)
self.rect_radius = 5 self.rect_radius = 5
self.knob_radius = 10 self.knob_radius = 5
self.knob_x = self.x + self.knob_radius self.knob_x = self.x + self.knob_radius
self.dragging = False self.dragging = False
@@ -54,16 +54,23 @@ class HorizontalSlider:
self.knob_x = event.pos[0] self.knob_x = event.pos[0]
self.value = self._knob_to_value(self.knob_x) self.value = self._knob_to_value(self.knob_x)
def draw(self): def _draw_slider(self):
"""
Draw the slider on the screen
"""
if self.fill: if self.fill:
pygame.draw.rect(self.screen, self.fill_colour, (self.x, self.y, self.knob_x, self.height), border_radius=self.rect_radius) pygame.draw.rect(self.screen, self.fill_colour, (self.x, self.y, self.knob_x, self.height), border_radius=self.rect_radius)
pygame.draw.rect(self.screen, self.background_colour, (self.knob_x, self.y, self.width - (self.knob_x - self.x), self.height), border_radius=self.rect_radius) pygame.draw.rect(self.screen, self.background_colour, (self.knob_x, self.y, self.width - (self.knob_x - self.x), self.height), border_radius=self.rect_radius)
else: else:
pygame.draw.rect(self.screen, self.background_colour, (self.x, self.y, self.width, self.height), border_radius=self.rect_radius) pygame.draw.rect(self.screen, self.background_colour, (self.x, self.y, self.width, self.height), border_radius=self.rect_radius)
pygame.draw.circle(self.screen, self.knob_colour, (int(self.knob_x), self.y + self.height // 2), self.knob_radius)
def _draw_knob(self):
pygame.draw.line(self.screen, self.knob_colour, (self.knob_x, self.y), (self.knob_x, self.y + self.height), width=self.knob_radius)
def draw(self):
"""
Draw the slider on the screen
"""
self._draw_slider()
self._draw_knob()
def set_value(self, value): def set_value(self, value):
""" """
@@ -89,6 +96,9 @@ class HorizontalSlider:
def set_knob_colour(self, colour): def set_knob_colour(self, colour):
self.knob_colour = colour self.knob_colour = colour
def set_fill_colour(self, colour):
self.fill_colour = colour
if __name__ == "__main__": if __name__ == "__main__":
pygame.init() pygame.init()
screen = pygame.display.set_mode((800, 600)) screen = pygame.display.set_mode((800, 600))