From 21a955e7095cf70ade171af2e4729cd5a89f5698 Mon Sep 17 00:00:00 2001 From: Ren Date: Sun, 15 Jun 2025 09:43:31 +0200 Subject: [PATCH 1/3] spawn --- .../shelved.patch | 135 +++++++ .../shelved.patch | 0 ...heckout_at_12_06_2025__20_36__Changes_.xml | 4 + .idea/workspace.xml | 267 +++++++++++++ bullet.py | 41 +- game.py | 374 ++++++++++++------ npc.py | 108 +++-- player.py | 165 +++++--- 8 files changed, 862 insertions(+), 232 deletions(-) create mode 100644 .idea/shelf/Uncommitted_changes_before_Checkout_at_12_06_2025,_20_36_[Changes]/shelved.patch create mode 100644 .idea/shelf/Uncommitted_changes_before_Checkout_at_12_06_2025,_20_36_[Changes]1/shelved.patch create mode 100644 .idea/shelf/Uncommitted_changes_before_Checkout_at_12_06_2025__20_36__Changes_.xml create mode 100644 .idea/workspace.xml diff --git a/.idea/shelf/Uncommitted_changes_before_Checkout_at_12_06_2025,_20_36_[Changes]/shelved.patch b/.idea/shelf/Uncommitted_changes_before_Checkout_at_12_06_2025,_20_36_[Changes]/shelved.patch new file mode 100644 index 0000000..d66649e --- /dev/null +++ b/.idea/shelf/Uncommitted_changes_before_Checkout_at_12_06_2025,_20_36_[Changes]/shelved.patch @@ -0,0 +1,135 @@ +Index: game.py +IDEA additional info: +Subsystem: com.intellij.openapi.diff.impl.patch.BaseRevisionTextPatchEP +<+>\"\"\"Game module\"\"\"\n\nimport queue\nimport threading\nimport time\nfrom datetime import datetime\n\n\nimport uuid\n\nimport psutil\nimport pygame\nimport socketio\n\nfrom bullet import Bullet\nfrom inputs import InputManager, PLAYER_KEYMAPS\nfrom player import Player\nfrom npc import NPC\n\nfrom config import GAME_FPS, CLIENT_REFRESH_COEF, INFO_TIMER, GATEWAY_ADDRESS\n\n\nclass AbstractGame:\n \"\"\"Abstract game ancestor\"\"\"\n\n def __init__(self, **kwargs):\n self.input_manager = InputManager()\n\n self.screen = kwargs[\"screen\"]\n self.clock = pygame.time.Clock()\n self.running = True\n self.tick = 1 * GAME_FPS\n self.players = {}\n self.npcs = [] # todo DICT by id????\n\n self.bullets = []\n self.player_bullets = []\n self.npc_bullets = []\n self.last_shot_times = {}\n self.shoot_cooldown = 250\n self.npc_last_shot_times = {}\n self.npc_shoot_cooldown = 150\n\n def try_shoot(self, uid, target_pos):\n now = pygame.time.get_ticks()\n last_shot = self.last_shot_times.get(uid, 0)\n if now - last_shot >= self.shoot_cooldown:\n player = self.players[uid]\n bullet = player.shoot(target_pos, color=player.color)\n self.player_bullets.append(bullet)\n self.last_shot_times[uid] = now\n\n def try_npc_shoot(self, npc):\n now = pygame.time.get_ticks()\n npc_id = id(npc)\n last_shot = self.npc_last_shot_times.get(npc_id, 0)\n if now - last_shot >= self.npc_shoot_cooldown:\n target = npc.get_shot_target(self.players.values())\n if target:\n bullet = Bullet(npc.rect.centerx, npc.rect.centery,\n target.rect.centerx, target.rect.centery,\n color=(255, 0, 0))\n self.npc_bullets.append(bullet)\n self.npc_last_shot_times[npc_id] = now\n\n\n def short_uid(self, length=8):\n \"\"\"Helper uid function\"\"\"\n return uuid.uuid4().hex[:length]\n\n def handle_events(self):\n \"\"\"Handles events and interruptions\"\"\"\n for event in pygame.event.get():\n if event.type == pygame.QUIT:\n print(\"Pressed quitting!\")\n self.running = False\n\n def handle_key_events(self):\n \"\"\"Handles key presses\"\"\"\n raise NotImplementedError\n\n\n def update_players(self, **kwargs):\n \"\"\"Handles players updates\"\"\"\n\n for player in self.players.values():\n player.update(inputs = self.input_manager.get_inputs(player.uid), **kwargs)\n self.input_manager.clear_inputs(player.uid)\n\n\n def update_npcs(self, **kwargs):\n \"\"\"Handles npcs updates\"\"\"\n\n for npc in self.npcs:\n npc.update(players = self.players, **kwargs)\n self.try_npc_shoot(npc)\n\n\n def render_all(self):\n \"\"\"Draws itself\"\"\"\n self.screen.fill((0, 0, 0))\n\n for player in self.players.values():\n player.draw(self.screen)\n\n for npc in self.npcs:\n npc.draw(self.screen)\n\n for bullet in self.player_bullets:\n bullet.draw(self.screen)\n\n for bullet in self.npc_bullets:\n bullet.draw(self.screen)\n\n def update_bullets(self):\n \"\"\"Update bullets position\"\"\"\n for bullet_list in [self.player_bullets, self.npc_bullets]:\n for bullet in bullet_list[:]:\n bullet.update()\n if bullet.is_off_screen():\n bullet_list.remove(bullet)\n\n\n def run(self):\n \"\"\"Running loop\"\"\"\n while self.running:\n self.handle_events()\n self.handle_key_events()\n\n self.update_players()\n self.update_npcs()\n self.update_bullets()\n\n self.render_all()\n pygame.display.flip()\n self.clock.tick(self.tick)\n\n print(\"Closing game ....\")\n\n\nclass SingleGame(AbstractGame):\n \"\"\"Single player game\"\"\"\n\n PLAYER1 = \"Player1\"\n #PLAYER2 = \"Player2\"\n\n def __init__(self, **kwargs):\n super().__init__(**kwargs)\n\n pl1 = Player(self.PLAYER1) # todo some better init\n pl1.set_coords(20,20)\n self.players[self.PLAYER1] = pl1\n\n self.input_manager.add_keymap(self.PLAYER1, PLAYER_KEYMAPS[\"wasd\"])\n\n #pl2 = Player(self.PLAYER2) # experimental player 2\n #pl2.set_coords(100, 20)\n #pl2.color = (0,0,255)\n #self.players[self.PLAYER2] = pl2\n\n #self.input_manager.add_keymap(self.PLAYER2, PLAYER_KEYMAPS[\"arrows\"])\n\n self.npcs.append(NPC(400, 400))\n\n def handle_key_events(self):\n \"\"\"Handles key presses\"\"\"\n\n keys = pygame.key.get_pressed()\n mouse_buttons = pygame.mouse.get_pressed()\n\n for key in PLAYER_KEYMAPS[\"wasd\"].keys():\n if keys[key]:\n self.input_manager.add_input(self.PLAYER1, key)\n\n #for key in PLAYER_KEYMAPS[\"arrows\"].keys():\n #if keys[key]:\n #self.input_manager.add_input(self.PLAYER2, key)\n\n if mouse_buttons[0]:\n self.try_shoot(self.PLAYER1, pygame.mouse.get_pos())\n\n\nclass MultiGameHost(AbstractGame):\n \"\"\"Multi-player game, role host\"\"\"\n\n PLAYER1 = \"HOST\" # todo some short id\n PLAYER1_NAME = \"Libor\" # todo config?\n\n def __init__(self, **kwargs):\n super().__init__(**kwargs)\n\n pl1 = Player(self.PLAYER1)\n pl1.set_coords(20,20)\n pl1.set_name(self.PLAYER1_NAME)\n self.players[self.PLAYER1] = pl1\n\n self.input_manager.add_keymap(self.PLAYER1, PLAYER_KEYMAPS[\"wasd\"])\n\n self.npcs.append(NPC(400, 400))\n\n self.client_thread = None\n self.info_thread = None\n self.socket_thread = None\n self.socket_queue = queue.Queue()\n\n self.sio = socketio.Client()\n self.sio.on('message', self.message)\n self.sio.on('info', self.info)\n\n self.sio.on('move', self.move)\n\n def run(self):\n \"\"\"Running loop\"\"\"\n\n # sio běží v neblokujícím vlákně\n self.client_thread = threading.Thread(target=self.start_client, daemon=True)\n self.client_thread.start()\n\n self.info_thread = threading.Thread(target=self.start_info, daemon=True)\n self.info_thread.start()\n\n self.socket_thread = threading.Thread(target=self.start_socket, daemon=True)\n self.socket_thread.start()\n\n while self.running:\n\n if not self.sio.connected:\n print(\"Connecting to server\") # todo better gui!\n else:\n self.handle_events()\n self.handle_key_events()\n\n self.update_players()\n self.update_npcs()\n self.update_bullets()\n self.send_all_positions()\n\n self.render_all()\n pygame.display.flip()\n\n self.clock.tick(self.tick)\n\n print(\"Closing game!\")\n self.info_thread.join()\n self.socket_thread.join()\n self.sio.disconnect()\n self.client_thread.join()\n\n\n\n\n def handle_key_events(self):\n \"\"\"Handles key presses\"\"\"\n\n keys = pygame.key.get_pressed()\n mouse_buttons = pygame.mouse.get_pressed()\n\n for key in PLAYER_KEYMAPS[\"wasd\"].keys():\n if keys[key]:\n self.input_manager.add_input(self.PLAYER1, key)\n\n if mouse_buttons[0]:\n self.try_shoot(self.PLAYER1, pygame.mouse.get_pos())\n\n\n def start_socket(self):\n \"\"\" Vlákno pro komunikaci přes socket, které bude odebírat data z fronty \"\"\"\n while self.running:\n try:\n while not self.socket_queue.empty(): # Zpracuj všechny dostupné zprávy\n name, data = self.socket_queue.get_nowait()\n # print(\"emitted\", name )\n self.sio.emit(name, data)\n\n except queue.Empty:\n pass # Pokud fronta je prázdná, čekáme\n\n time.sleep(0.01)\n\n def start_info(self):\n \"\"\"Info heartbeat thread\"\"\"\n while self.running:\n time.sleep(INFO_TIMER) # Pauza 1 sekunda\n if self.sio.connected:\n print(\"heartbeat!\")\n now = datetime.now()\n print(now.strftime(\"%H:%M:%S.%f\")[:-3])\n self.sio.emit(\"info\")\n\n def start_client(self):\n \"\"\"SIO connection thread\"\"\"\n headers = {\"role\": \"host\", \"uid\": self.PLAYER1, \"name\": self.PLAYER1_NAME }\n self.sio.connect(GATEWAY_ADDRESS, headers=headers, transports=[\"websocket\"])\n self.sio.wait()\n\n def send_all_positions(self):\n \"\"\"Position synchronizing method\"\"\"\n\n player_data = [player.get_transport_data() for player in self.players.values()]\n\n npc_data = [] # todo :)\n\n self.socket_queue.put((\"game_state\", {\"players\": player_data, \"npcs\": npc_data}))\n\n\n def message(self, msg):\n \"\"\"Message handler\"\"\"\n print(\"Got new message!\", msg)\n\n def info(self, msg):\n \"\"\"Info handler\"\"\"\n now = datetime.now()\n print(now.strftime(\"%H:%M:%S.%f\")[:-3])\n print(\"Got info!\", msg)\n\n for client in msg.get(\"clientList\", []): # add missing players\n uid = client[\"uid\"]\n if uid not in self.players:\n\n print(\"New player\", client.get(\"name\"))\n new_player = Player(uid)\n new_player.set_coords(20, 20)\n new_player.set_name(client.get(\"name\"))\n self.players[uid] = new_player\n\n def move(self, data):\n \"\"\"Incoming move handler\"\"\"\n # print(data, \"Got move!\", data)\n self.input_manager.add_inputs(data[\"uid\"], data[\"inputs\"])\n\n\nclass MultiGameClient(AbstractGame):\n \"\"\"Multi-player game, role client\"\"\"\n\n def __init__(self, **kwargs):\n super().__init__(**kwargs)\n\n self.player1_id = self.short_uid()\n self.player1_name = \"Some player\" # todo config?\n pl1 = Player(self.player1_id)\n pl1.set_coords(20,20)\n pl1.set_name(self.player1_name)\n self.players[self.player1_id] = pl1\n\n self.input_manager.add_keymap(self.player1_id, PLAYER_KEYMAPS[\"wasd\"])\n\n self.npcs.append(NPC(400, 400))\n\n self.client_thread = None\n self.socket_thread = None\n self.socket_queue = queue.Queue()\n\n self.sio = socketio.Client(\n reconnection=True,\n reconnection_attempts=20,\n reconnection_delay=0.1,\n reconnection_delay_max=5\n )\n\n # sio handlers\n self.sio.on('message', self.message)\n self.sio.on('info', self.info)\n self.sio.on('game_state', self.game_state)\n\n # sio info block\n self.sio.on(\"connect\", lambda: print(\"connected\"))\n self.sio.on(\"disconnect\", lambda x: print(\"disconnected\", x))\n self.sio.on(\"reconnect\", lambda x: print(\"reconnected\", x))\n self.sio.on(\"reconnect_error\", lambda x: print(\"reconnect-error\", x))\n\n\n def run(self):\n \"\"\"Running loop\"\"\"\n\n # sio běží v neblokujícím vlákně\n self.client_thread = threading.Thread(target=self.start_client, daemon=True)\n self.client_thread.start()\n\n self.socket_thread = threading.Thread(target=self.start_socket, daemon=True)\n self.socket_thread.start()\n\n cnt = 0\n\n while self.running:\n\n if not self.sio.connected:\n self.screen.fill((0, 0, 0))\n pygame.display.flip()\n print(\"Connecting to server\") # todo some nice screen, add some waiting for host\n else:\n self.handle_events()\n self.handle_key_events()\n self.update_bullets()\n self.send_key_events()\n\n # no drawing, only from game_state event!\n\n cnt += 1\n if cnt > (200 * CLIENT_REFRESH_COEF):\n cpu = psutil.cpu_percent()\n print(f\"CPU: {cpu}\")\n print(\"heartbeat!\")\n now = datetime.now()\n print(now.strftime(\"%H:%M:%S.%f\")[:-3])\n self.sio.emit(\"info\")\n cnt = 0\n\n # todo: rychlejsi tick, protoze se updatuje eventem\n # ale pozor na zahlceni socketu\n self.clock.tick(self.tick * CLIENT_REFRESH_COEF)\n\n print(\"Closing game!\")\n self.sio.disconnect()\n self.client_thread.join()\n\n def start_socket(self):\n \"\"\" Vlákno pro komunikaci přes socket, které bude odebírat data z fronty \"\"\"\n while self.running:\n try:\n while not self.socket_queue.empty(): # Zpracuj všechny dostupné zprávy\n name, data = self.socket_queue.get_nowait()\n # print(\"emitted\", name )\n if self.sio.connected:\n self.sio.emit(name, data)\n\n except queue.Empty:\n pass # Pokud fronta je prázdná, čekáme\n\n time.sleep(0.02)\n\n def handle_key_events(self):\n \"\"\"Handles key presses\"\"\"\n\n keys = pygame.key.get_pressed()\n mouse_buttons = pygame.mouse.get_pressed()\n\n for key in PLAYER_KEYMAPS[\"wasd\"].keys():\n if keys[key]:\n self.input_manager.add_input(self.player1_id, key)\n\n if mouse_buttons[0]:\n self.try_shoot(self.PLAYER1, pygame.mouse.get_pos())\n\n def send_key_events(self):\n \"\"\"Send key presses to socket\"\"\"\n\n inputs = self.input_manager.get_inputs(self.player1_id)\n self.input_manager.clear_inputs(self.player1_id)\n if len(inputs) > 0:\n# self.sio.emit(\"move\", {\"uid\": self.PLAYER1, \"inputs\": inputs})\n self.socket_queue.put((\"move\", {\"uid\": self.player1_id, \"inputs\": inputs}))\n\n def start_client(self):\n \"\"\"Sio connect thread\"\"\"\n\n # todo posílat color v hexa\n\n headers = {\"role\": \"client\", \"uid\": self.player1_id, \"name\": self.player1_name}\n self.sio.connect(GATEWAY_ADDRESS, wait_timeout=5, headers=headers, transports=[\"websocket\"])\n\n self.sio.wait()\n\n def message(self, msg):\n \"\"\"Message handler\"\"\"\n print(\"Got new message!\", msg)\n\n def info(self, msg):\n \"\"\"Info handler\"\"\"\n now = datetime.now()\n print(now.strftime(\"%H:%M:%S.%f\")[:-3])\n print(\"Got info!\", msg)\n\n for client in msg.get(\"clientList\", []): # add missing players\n uid = client[\"uid\"]\n if uid not in self.players:\n\n print(\"New player\", client.get(\"name\"))\n new_player = Player(uid)\n new_player.set_coords(20, 20)\n new_player.set_name(client.get(\"name\"))\n self.players[uid] = new_player\n\n def game_state(self, data):\n \"\"\"Incoming game state handler\"\"\"\n # print(data, \"Got new game state!\", data)\n\n for player_data in data[\"players\"]:\n if player_data[\"uid\"] in self.players:\n pl = self.players.get(player_data[\"uid\"])\n pl.update_data(player_data)\n # self.update_npcs()\n\n self.render_all()\n\n pygame.display.flip()\n +Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP +<+>UTF-8 +=================================================================== +diff --git a/game.py b/game.py +--- a/game.py (revision 66a2f5de9ffb6ec19bf108b79b51e22c5f47eeb4) ++++ b/game.py (date 1749753078762) +@@ -41,6 +41,25 @@ + self.npc_last_shot_times = {} + self.npc_shoot_cooldown = 150 + ++ def check_bullet_collisions(self): ++ for bullet in self.npc_bullets[:]: ++ for player in self.players.values(): ++ if bullet.rect.colliderect(player.rect): ++ player.health -= 10 ++ if player.health < 0: ++ player.health = 0 ++ self.npc_bullets.remove(bullet) ++ break ++ ++ for bullet in self.player_bullets[:]: ++ for npc in self.npcs[:]: ++ if bullet.rect.colliderect(npc.rect): ++ npc.health -= 10 ++ if npc.health < 0: ++ npc.health = 0 ++ self.player_bullets.remove(bullet) ++ break ++ + def try_shoot(self, uid, target_pos): + now = pygame.time.get_ticks() + last_shot = self.last_shot_times.get(uid, 0) +@@ -130,6 +149,7 @@ + self.update_players() + self.update_npcs() + self.update_bullets() ++ self.check_bullet_collisions() + + self.render_all() + pygame.display.flip() +@@ -222,6 +242,7 @@ + self.socket_thread = threading.Thread(target=self.start_socket, daemon=True) + self.socket_thread.start() + ++ + while self.running: + + if not self.sio.connected: +@@ -233,6 +254,7 @@ + self.update_players() + self.update_npcs() + self.update_bullets() ++ self.check_bullet_collisions() + self.send_all_positions() + + self.render_all() +Index: npc.py +IDEA additional info: +Subsystem: com.intellij.openapi.diff.impl.patch.BaseRevisionTextPatchEP +<+>\"\"\"NPC module\"\"\"\nimport math\n\nimport pygame\n\nfrom config import NPC_SPEED\nfrom bullet import Bullet\n\nclass NPC:\n \"\"\"NPC common class\"\"\"\n def __init__(self, x, y):\n self.rect = pygame.Rect(x, y, 20, 20)\n\n def update(self, **kwargs):\n \"\"\"Updates self position according to players\"\"\"\n nearest_player = self.find_closest_player(self, kwargs[\"players\"].values())\n\n\n if self.rect.x < nearest_player.rect.x:\n self.rect.x += NPC_SPEED\n elif self.rect.x > nearest_player.rect.x:\n self.rect.x -= NPC_SPEED\n if self.rect.y < nearest_player.rect.y:\n self.rect.y += NPC_SPEED\n elif self.rect.y > nearest_player.rect.y:\n self.rect.y -= NPC_SPEED\n\n def get_shot_target(self, players):\n return self.find_closest_player(self, players)\n\n # def shoot(self, target):\n # bullet = Bullet(self.rect.centerx, self.rect.centery,\n # target.rect.centerx, target.rect.centery,\n # color=(255, 0, 0))\n # self.bullets.append(bullet)\n\n def draw(self, screen):\n \"\"\"Draws itself\"\"\"\n pygame.draw.rect(screen, (255, 0, 0), self.rect)\n\n\n def distance(self, rect1, rect2):\n \"\"\"Helper function for distance computing\"\"\"\n return math.hypot(rect1.x - rect2.x, rect1.y - rect2.y)\n\n def find_closest_player(self, npc, players):\n \"\"\"Function for NPC to find which player to chase, not optimized\"\"\"\n closest = None\n min_dist = float('inf')\n\n for player in players:\n dist = self.distance(npc.rect, player.rect)\n if dist < min_dist:\n min_dist = dist\n closest = player\n\n return closest\n +Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP +<+>UTF-8 +=================================================================== +diff --git a/npc.py b/npc.py +--- a/npc.py (revision 66a2f5de9ffb6ec19bf108b79b51e22c5f47eeb4) ++++ b/npc.py (date 1749751148158) +@@ -11,6 +11,9 @@ + def __init__(self, x, y): + self.rect = pygame.Rect(x, y, 20, 20) + ++ self.health = 100 ++ self.max_health = 100 ++ + def update(self, **kwargs): + """Updates self position according to players""" + nearest_player = self.find_closest_player(self, kwargs["players"].values()) +@@ -34,9 +37,18 @@ + # color=(255, 0, 0)) + # self.bullets.append(bullet) + ++ def draw_lifebar(self,screen): ++ bar_width = self.rect.width ++ bar_heigth = 5 ++ fill = (self.health/self.max_health) * bar_width ++ ++ pygame.draw.rect(screen,(255,0,0),(self.rect.x,self.rect.y - 10,bar_width, bar_heigth)) ++ pygame.draw.rect(screen, (0, 255, 0), (self.rect.x, self.rect.y - 10, fill, bar_heigth)) ++ + def draw(self, screen): + """Draws itself""" + pygame.draw.rect(screen, (255, 0, 0), self.rect) ++ self.draw_lifebar(screen) + + + def distance(self, rect1, rect2): +Index: player.py +IDEA additional info: +Subsystem: com.intellij.openapi.diff.impl.patch.BaseRevisionTextPatchEP +<+>\"\"\"Player module\"\"\"\n\nimport pygame\nfrom config import PLAYER_WIDTH, PLAYER_HEIGHT, PLAYER_SPEED, SCREEN_WIDTH, SCREEN_HEIGHT\nfrom enums import KeyType\nfrom bullet import Bullet\nimport random\n\nclass Player:\n \"\"\"Player class init\"\"\"\n def __init__(self, uid):\n\n self.uid = uid\n self.rect = pygame.Rect(0, 0, PLAYER_WIDTH, PLAYER_HEIGHT)\n self.color = (0, 255, 0)\n self.name = \"Player1\"\n self.speed = PLAYER_SPEED\n\n\n def set_coords(self, x, y):\n \"\"\"Coords setter\"\"\"\n self.rect = pygame.Rect(x, y, PLAYER_WIDTH, PLAYER_HEIGHT)\n\n def set_name(self, name):\n \"\"\"Name setter\"\"\"\n self.name = name\n\n\n def get_transport_data(self):\n \"\"\"Prepare data for data event\"\"\"\n return {\"uid\": self.uid, \"x\": self.rect.x, \"y\": self.rect.y}\n\n def update_data(self, transport_data):\n \"\"\"Update self position from data event\"\"\"\n self.rect.x = transport_data[\"x\"]\n self.rect.y = transport_data[\"y\"]\n\n def update(self, **kwargs):\n \"\"\"Update self position from move intention\"\"\"\n\n inp = kwargs[\"inputs\"]\n mouse_pos = kwargs.get(\"mouse_pos\",(self.rect.centerx,self.rect.centery))\n\n if KeyType.LEFT.name in inp:\n self.rect.x -= self.speed\n if KeyType.RIGHT.name in inp:\n self.rect.x += self.speed\n if KeyType.UP.name in inp:\n self.rect.y -= self.speed\n if KeyType.DOWN.name in inp:\n self.rect.y += self.speed\n\n # overflow corrections\n self.rect.x = max(self.rect.x, 0)\n self.rect.y = max(self.rect.y, 0)\n self.rect.x = min(self.rect.x, SCREEN_WIDTH - PLAYER_WIDTH)\n self.rect.y = min(self.rect.y, SCREEN_HEIGHT - PLAYER_HEIGHT)\n\n\n\n def shoot(self, target_pos=None, color=None):\n if target_pos is None:\n target_pos = (400, 300)\n\n if color is None:\n color = self.color\n\n bullet = Bullet(self.rect.centerx, self.rect.centery, *target_pos, color=color)\n return bullet\n\n def draw(self, screen):\n \"\"\"Draws itself\"\"\"\n pygame.draw.rect(screen, self.color, self.rect)\n +Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP +<+>UTF-8 +=================================================================== +diff --git a/player.py b/player.py +--- a/player.py (revision 66a2f5de9ffb6ec19bf108b79b51e22c5f47eeb4) ++++ b/player.py (date 1749751094222) +@@ -16,6 +16,9 @@ + self.name = "Player1" + self.speed = PLAYER_SPEED + ++ self.health = 100 ++ self.max_health = 100 ++ + + def set_coords(self, x, y): + """Coords setter""" +@@ -68,6 +71,15 @@ + bullet = Bullet(self.rect.centerx, self.rect.centery, *target_pos, color=color) + return bullet + ++ def draw_lifebar(self,screen): ++ bar_width = self.rect.width ++ bar_heigth = 5 ++ fill = (self.health/self.max_health) * bar_width ++ ++ pygame.draw.rect(screen,(255,0,0),(self.rect.x,self.rect.y - 10,bar_width, bar_heigth)) ++ pygame.draw.rect(screen,(0, 255, 0),(self.rect.x, self.rect.y - 10, fill, bar_heigth)) ++ + def draw(self, screen): + """Draws itself""" + pygame.draw.rect(screen, self.color, self.rect) ++ self.draw_lifebar(screen) diff --git a/.idea/shelf/Uncommitted_changes_before_Checkout_at_12_06_2025,_20_36_[Changes]1/shelved.patch b/.idea/shelf/Uncommitted_changes_before_Checkout_at_12_06_2025,_20_36_[Changes]1/shelved.patch new file mode 100644 index 0000000..e69de29 diff --git a/.idea/shelf/Uncommitted_changes_before_Checkout_at_12_06_2025__20_36__Changes_.xml b/.idea/shelf/Uncommitted_changes_before_Checkout_at_12_06_2025__20_36__Changes_.xml new file mode 100644 index 0000000..d52213d --- /dev/null +++ b/.idea/shelf/Uncommitted_changes_before_Checkout_at_12_06_2025__20_36__Changes_.xml @@ -0,0 +1,4 @@ + + \ No newline at end of file diff --git a/.idea/workspace.xml b/.idea/workspace.xml new file mode 100644 index 0000000..703f46f --- /dev/null +++ b/.idea/workspace.xml @@ -0,0 +1,267 @@ + + + + + + + + + + + + + + + + + + + { + "lastFilter": { + "state": "OPEN", + "assignee": "Ren6436" + } +} + { + "selectedUrlAndAccountId": { + "url": "https://github.com/dracek/uuPyGame.git", + "accountId": "bbc4e1cb-aa98-4684-86cc-3d43099966ad" + } +} + { + "associatedIndex": 4 +} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1745221181550 + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/bullet.py b/bullet.py index c13f5c9..4b621bf 100644 --- a/bullet.py +++ b/bullet.py @@ -3,21 +3,28 @@ from config import SCREEN_WIDTH, SCREEN_HEIGHT, BULLET_SIZE, BULLET_SPEED + + class Bullet: - def __init__(self, x, y, target_x, target_y,color=(0, 0, 0)): - self.rect = pygame.Rect(x, y, BULLET_SIZE, BULLET_SIZE) - angle = math.atan2(target_y - y, target_x - x) - self.dx = math.cos(angle) * BULLET_SPEED - self.dy = math.sin(angle) * BULLET_SPEED - self.color = color - - def update(self): - self.rect.x += self.dx - self.rect.y += self.dy - - def draw(self, screen): - pygame.draw.rect(screen,self.color, self.rect) - - def is_off_screen(self): - return (self.rect.x < 0 or self.rect.x > SCREEN_WIDTH or - self.rect.y < 0 or self.rect.y > SCREEN_HEIGHT) \ No newline at end of file + def __init__(self, x, y, target_x, target_y,color=(0, 0, 0),shooter=None): + self.rect = pygame.Rect(x, y, BULLET_SIZE, BULLET_SIZE) + angle = math.atan2(target_y - y, target_x - x) + self.dx = math.cos(angle) * BULLET_SPEED + self.dy = math.sin(angle) * BULLET_SPEED + self.color = color + self.shooter = shooter + + + def update(self): + self.rect.x += self.dx + self.rect.y += self.dy + + + def draw(self, screen): + pygame.draw.rect(screen,self.color, self.rect) + + + def is_off_screen(self): + return (self.rect.x < 0 or self.rect.x > SCREEN_WIDTH or + self.rect.y < 0 or self.rect.y > SCREEN_HEIGHT) + diff --git a/game.py b/game.py index f0dc673..1dd7126 100644 --- a/game.py +++ b/game.py @@ -1,6 +1,9 @@ """Game module""" + import pygame +import random + from bullet import Bullet from enums import KeyType @@ -8,182 +11,315 @@ from player import Player from npc import NPC + from config import GAME_FPS + + class AbstractGame: - """Abstract game ancestor""" + """Abstract game ancestor""" + + + def __init__(self, **kwargs): + self.input_manager = InputManager() + + + self.screen = kwargs["screen"] + self.clock = pygame.time.Clock() + self.running = True + self.tick = 1 * GAME_FPS + self.players = {} + self.npcs = [] # todo DICT by id???? + + + self.bullets = [] + self.player_bullets = [] + self.npc_bullets = [] + + + self.npc_last_shot_times = {} + self.npc_shoot_cooldown = 500 + self.score = 0 + self.last_spawn_time = pygame.time.get_ticks() + self.spawn_interval_range = (3000, 5000) + + + def spawn_random_npc(self): + npc_type = random.choices( + ["easy", "medium", "hard"], + weights=[70, 25, 5], + k=1 + )[0] + x = random.randint(50, self.screen.get_width() - 50) + y = random.randint(50, self.screen.get_height() - 50) + self.npcs.append(NPC(x, y, npc_type=npc_type)) + + + def try_spawn_npc(self): + now = pygame.time.get_ticks() + if now - self.last_spawn_time > self.next_spawn_interval: + self.spawn_random_npc() + self.last_spawn_time = now + # Nastav nový random interval pro další spawn + self.next_spawn_interval = random.randint(*self.spawn_interval_range) + + + def try_npc_shoot(self, npc): + now = pygame.time.get_ticks() + npc_id = id(npc) + last_shot = self.npc_last_shot_times.get(npc_id, 0) + if now - last_shot >= self.npc_shoot_cooldown: + target = npc.get_shot_target(self.players.values()) + if target: + bullet = Bullet( + npc.rect.centerx, npc.rect.centery, + target.rect.centerx, target.rect.centery, + color=(255, 0, 0), + shooter=npc # tady přidáš referenci na střílejícího NPC + ) + self.npc_bullets.append(bullet) + self.npc_last_shot_times[npc_id] = now + + + def check_bullet_collisions(self): + # NPC střely na hráče + for bullet in self.npc_bullets[:]: + for npc in self.npcs: + if bullet.shooter == npc: + for player in self.players.values(): + if bullet.rect.colliderect(player.rect): + player.health -= npc.damage + self.npc_bullets.remove(bullet) + break + break + + + for bullet in self.player_bullets[:]: + for npc in self.npcs[:]: + if bullet.rect.colliderect(npc.rect): + npc.health -= 10 + if npc.health <= 0: + self.npcs.remove(npc) + self.score += npc.score + self.player_bullets.remove(bullet) + break + + + def handle_events(self): + """Handles events and interruptions""" + for event in pygame.event.get(): + if event.type == pygame.QUIT: + print("Pressed quitting!") + self.running = False + + + def handle_key_events(self): + """Handles key presses""" + raise NotImplementedError + + + + + def update_players(self, **kwargs): + """Handles players updates""" + + + for player in self.players.values(): + + + inputs = self.input_manager.get_inputs(player.uid) - def __init__(self, **kwargs): - self.input_manager = InputManager() - self.screen = kwargs["screen"] - self.clock = pygame.time.Clock() - self.running = True - self.tick = 1 * GAME_FPS - self.players = {} - self.npcs = [] # todo DICT by id???? + player.update(inputs = inputs, **kwargs) - self.bullets = [] - self.player_bullets = [] - self.npc_bullets = [] - self.npc_last_shot_times = {} - self.npc_shoot_cooldown = 500 + if KeyType.SHOOT.name in inputs: + bullet = player.shoot(self.npcs) + if bullet is not None: + self.player_bullets.append(bullet) - def try_npc_shoot(self, npc): - now = pygame.time.get_ticks() - npc_id = id(npc) - last_shot = self.npc_last_shot_times.get(npc_id, 0) - if now - last_shot >= self.npc_shoot_cooldown: - target = npc.get_shot_target(self.players.values()) - if target: - bullet = Bullet(npc.rect.centerx, npc.rect.centery, - target.rect.centerx, target.rect.centery, - color=(255, 0, 0)) - self.npc_bullets.append(bullet) - self.npc_last_shot_times[npc_id] = now + self.input_manager.clear_inputs(player.uid) - def handle_events(self): - """Handles events and interruptions""" - for event in pygame.event.get(): - if event.type == pygame.QUIT: - print("Pressed quitting!") - self.running = False - def handle_key_events(self): - """Handles key presses""" - raise NotImplementedError - def update_players(self, **kwargs): - """Handles players updates""" + def update_npcs(self, **kwargs): + """Handles npcs updates""" - for player in self.players.values(): - inputs = self.input_manager.get_inputs(player.uid) + for npc in self.npcs: + npc.update(players = self.players, **kwargs) + self.try_npc_shoot(npc) - player.update(inputs = inputs, **kwargs) - if KeyType.SHOOT.name in inputs: - bullet = player.shoot(self.npcs) - if bullet is not None: - self.player_bullets.append(bullet) - self.input_manager.clear_inputs(player.uid) + def render_all(self): + """Draws itself""" + self.screen.fill((0, 0, 0)) - def update_npcs(self, **kwargs): - """Handles npcs updates""" - for npc in self.npcs: - npc.update(players = self.players, **kwargs) - self.try_npc_shoot(npc) + for player in self.players.values(): + player.draw(self.screen) - def render_all(self): - """Draws itself""" - self.screen.fill((0, 0, 0)) + for npc in self.npcs: + npc.draw(self.screen) - for player in self.players.values(): - player.draw(self.screen) - for npc in self.npcs: - npc.draw(self.screen) + for bullet in self.player_bullets: + bullet.draw(self.screen) - for bullet in self.player_bullets: - bullet.draw(self.screen) - for bullet in self.npc_bullets: - bullet.draw(self.screen) + for bullet in self.npc_bullets: + bullet.draw(self.screen) - def update_bullets(self): - """Update bullets position""" - for bullet_list in [self.player_bullets, self.npc_bullets]: - for bullet in bullet_list[:]: - bullet.update() - if bullet.is_off_screen(): - bullet_list.remove(bullet) + font = pygame.font.SysFont(None, 30) + score_surface = font.render(f"Score: {self.score}", True, (255, 255, 255)) + self.screen.blit(score_surface, (10, 10)) - def run(self): - """Running loop""" - while self.running: - self.handle_events() - self.handle_key_events() - self.update_players() - self.update_npcs() - self.update_bullets() + def update_bullets(self): + """Update bullets position""" + for bullet_list in [self.player_bullets, self.npc_bullets]: + for bullet in bullet_list[:]: + bullet.update() + if bullet.is_off_screen(): + bullet_list.remove(bullet) + + def check_game_end(self): + alive_players = [p for p in self.players.values() if p.health > 0] + if not alive_players: + self.running = False + if self.score >= 200: + self.display_end_message("You win!") + else: + self.display_end_message("You lost.") + + def display_end_message(self, message): + font = pygame.font.SysFont(None, 60) + text = font.render(message, True, (255, 255, 255)) + rect = text.get_rect(center=(self.screen.get_width() // 2, self.screen.get_height() // 2)) + self.screen.fill((0, 0, 0)) + self.screen.blit(text, rect) + pygame.display.flip() + pygame.time.wait(3000) + + + def run(self): + """Running loop""" + self.next_spawn_interval = random.randint(*self.spawn_interval_range) + while self.running: + self.handle_events() + self.handle_key_events() + + + self.try_spawn_npc() + + + self.update_players() + self.update_npcs() + self.update_bullets() + + + self.render_all() + pygame.display.flip() + self.clock.tick(self.tick) + self.check_bullet_collisions() + self.check_game_end() + + + print("Closing game ....") - self.render_all() - pygame.display.flip() - self.clock.tick(self.tick) - print("Closing game ....") class SingleGame(AbstractGame): - """Single player game""" + """Single player game""" + + + PLAYER1 = "Player1" + + + def __init__(self, **kwargs): + super().__init__(**kwargs) + + + pl1 = Player(self.PLAYER1) # todo some better init + pl1.set_coords(20,20) + self.players[self.PLAYER1] = pl1 - PLAYER1 = "Player1" - def __init__(self, **kwargs): - super().__init__(**kwargs) + self.input_manager.add_keymap(self.PLAYER1, PLAYER_KEYMAPS["wasd"]) - pl1 = Player(self.PLAYER1) # todo some better init - pl1.set_coords(20,20) - self.players[self.PLAYER1] = pl1 - self.input_manager.add_keymap(self.PLAYER1, PLAYER_KEYMAPS["wasd"]) + self.spawn_random_npc() - self.npcs.append(NPC(400, 400)) - def handle_key_events(self): - """Handles key presses""" + def handle_key_events(self): + """Handles key presses""" + + + keys = pygame.key.get_pressed() + + + for key in PLAYER_KEYMAPS["wasd"].keys(): + if keys[key]: + self.input_manager.add_input(self.PLAYER1, key) - keys = pygame.key.get_pressed() - for key in PLAYER_KEYMAPS["wasd"].keys(): - if keys[key]: - self.input_manager.add_input(self.PLAYER1, key) class CoopGame(AbstractGame): - """Single player game""" + """Single player game""" + + + PLAYER1 = "Player1" + PLAYER2 = "Player2" + + + def __init__(self, **kwargs): + super().__init__(**kwargs) + + + pl1 = Player(self.PLAYER1) + pl1.set_coords(20,20) + self.players[self.PLAYER1] = pl1 + + + self.input_manager.add_keymap(self.PLAYER1, PLAYER_KEYMAPS["wasd"]) + + + pl2 = Player(self.PLAYER2) # experimental player 2 + pl2.set_coords(100, 20) + pl2.color = (0,0,255) + self.players[self.PLAYER2] = pl2 + + + self.input_manager.add_keymap(self.PLAYER2, PLAYER_KEYMAPS["arrows"]) + - PLAYER1 = "Player1" - PLAYER2 = "Player2" + self.npcs.append(NPC(400, 400)) - def __init__(self, **kwargs): - super().__init__(**kwargs) - pl1 = Player(self.PLAYER1) - pl1.set_coords(20,20) - self.players[self.PLAYER1] = pl1 - self.input_manager.add_keymap(self.PLAYER1, PLAYER_KEYMAPS["wasd"]) - pl2 = Player(self.PLAYER2) # experimental player 2 - pl2.set_coords(100, 20) - pl2.color = (0,0,255) - self.players[self.PLAYER2] = pl2 + def handle_key_events(self): + """Handles key presses""" - self.input_manager.add_keymap(self.PLAYER2, PLAYER_KEYMAPS["arrows"]) - self.npcs.append(NPC(400, 400)) + keys = pygame.key.get_pressed() - def handle_key_events(self): - """Handles key presses""" + for key in PLAYER_KEYMAPS["wasd"].keys(): + if keys[key]: + self.input_manager.add_input(self.PLAYER1, key) - keys = pygame.key.get_pressed() - for key in PLAYER_KEYMAPS["wasd"].keys(): - if keys[key]: - self.input_manager.add_input(self.PLAYER1, key) + for key in PLAYER_KEYMAPS["arrows"].keys(): + if keys[key]: + self.input_manager.add_input(self.PLAYER2, key) - for key in PLAYER_KEYMAPS["arrows"].keys(): - if keys[key]: - self.input_manager.add_input(self.PLAYER2, key) diff --git a/npc.py b/npc.py index 948b93f..9e84516 100644 --- a/npc.py +++ b/npc.py @@ -2,50 +2,94 @@ import math import pygame + from config import NPC_SPEED + class NPC: - """NPC common class""" - def __init__(self, x, y): - self.rect = pygame.Rect(x, y, 20, 20) + """NPC common class""" + + + TYPES = { + "easy": {"health": 50, "damage": 5, "color": (100, 255, 100), "score": 10}, + "medium": {"health": 100, "damage": 10, "color": (255, 255, 0), "score": 25}, + "hard": {"health": 150, "damage": 20, "color": (255, 100, 100), "score": 50}, + } + + + def __init__(self, x, y,npc_type="medium"): + self.rect = pygame.Rect(x, y, 20, 20) + + + self.type = npc_type + npc_config = self.TYPES[self.type] + self.score = npc_config["score"] + self.health = npc_config["health"] + self.max_health = npc_config["health"] + self.damage = npc_config["damage"] + self.color = npc_config["color"] + + + + + def update(self, **kwargs): + """Updates self position according to players""" + nearest_player = self.find_closest_player(self, kwargs["players"].values()) + + + + + if self.rect.x < nearest_player.rect.x: + self.rect.x += NPC_SPEED + elif self.rect.x > nearest_player.rect.x: + self.rect.x -= NPC_SPEED + if self.rect.y < nearest_player.rect.y: + self.rect.y += NPC_SPEED + elif self.rect.y > nearest_player.rect.y: + self.rect.y -= NPC_SPEED + + + def get_shot_target(self, players): + return self.find_closest_player(self, players) + + + def draw_lifebar(self, screen): + bar_width = self.rect.width + bar_heigth = 5 + fill = (self.health / self.max_health) * bar_width + + + pygame.draw.rect(screen, (255, 0, 0), (self.rect.x, self.rect.y - 10, bar_width, bar_heigth)) + pygame.draw.rect(screen, (0, 255, 0), (self.rect.x, self.rect.y - 10, fill, bar_heigth)) + + + + def draw(self, screen): + """Draws itself""" + pygame.draw.rect(screen, (255, 0, 0), self.rect) + self.draw_lifebar(screen) - def update(self, **kwargs): - """Updates self position according to players""" - nearest_player = self.find_closest_player(self, kwargs["players"].values()) - if self.rect.x < nearest_player.rect.x: - self.rect.x += NPC_SPEED - elif self.rect.x > nearest_player.rect.x: - self.rect.x -= NPC_SPEED - if self.rect.y < nearest_player.rect.y: - self.rect.y += NPC_SPEED - elif self.rect.y > nearest_player.rect.y: - self.rect.y -= NPC_SPEED - def get_shot_target(self, players): - return self.find_closest_player(self, players) + def distance(self, rect1, rect2): + """Helper function for distance computing""" + return math.hypot(rect1.x - rect2.x, rect1.y - rect2.y) - def draw(self, screen): - """Draws itself""" - pygame.draw.rect(screen, (255, 0, 0), self.rect) + def find_closest_player(self, npc, players): + """Function for NPC to find which player to chase, not optimized""" + closest = None + min_dist = float('inf') - def distance(self, rect1, rect2): - """Helper function for distance computing""" - return math.hypot(rect1.x - rect2.x, rect1.y - rect2.y) + for player in players: + dist = self.distance(npc.rect, player.rect) + if dist < min_dist: + min_dist = dist + closest = player - def find_closest_player(self, npc, players): - """Function for NPC to find which player to chase, not optimized""" - closest = None - min_dist = float('inf') - for player in players: - dist = self.distance(npc.rect, player.rect) - if dist < min_dist: - min_dist = dist - closest = player + return closest - return closest diff --git a/player.py b/player.py index 314c7b9..301e712 100644 --- a/player.py +++ b/player.py @@ -2,96 +2,133 @@ import math import pygame + from config import PLAYER_WIDTH, PLAYER_HEIGHT, PLAYER_SPEED, SCREEN_WIDTH, SCREEN_HEIGHT from enums import KeyType, Facing from bullet import Bullet + movement_keys = {KeyType.LEFT.name, KeyType.RIGHT.name, KeyType.UP.name, KeyType.DOWN.name} + class Player: - """Player class init""" - def __init__(self, uid): + """Player class init""" + def __init__(self, uid): + + + self.uid = uid + self.rect = pygame.Rect(0, 0, PLAYER_WIDTH, PLAYER_HEIGHT) + self.color = (0, 255, 0) + self.name = "Player1" + self.speed = PLAYER_SPEED + self.is_moving = False + self.facing = Facing.RIGHT + self.health = 100 + self.max_health = 100 + + + + + self.shoot_cooldown = 250 + self.last_shot_time = pygame.time.get_ticks() + + + + + def set_coords(self, x, y): + """Coords setter""" + self.rect = pygame.Rect(x, y, PLAYER_WIDTH, PLAYER_HEIGHT) + + + def set_name(self, name): + """Name setter""" + self.name = name + + + def update(self, **kwargs): + """Update self position from move intention""" + + + inp = kwargs["inputs"] + + + self.is_moving = any(key in inp for key in movement_keys) + + + if KeyType.UP.name in inp: + self.rect.y -= self.speed + self.facing = Facing.UP + elif KeyType.DOWN.name in inp: + self.rect.y += self.speed + self.facing = Facing.DOWN + + + if KeyType.LEFT.name in inp: + self.rect.x -= self.speed + self.facing = Facing.LEFT + elif KeyType.RIGHT.name in inp: + self.rect.x += self.speed + self.facing = Facing.RIGHT + + + # overflow corrections - todo možná udělat přes kolize s okrajem! + self.rect.x = max(self.rect.x, 0) + self.rect.y = max(self.rect.y, 0) + self.rect.x = min(self.rect.x, SCREEN_WIDTH - PLAYER_WIDTH) + self.rect.y = min(self.rect.y, SCREEN_HEIGHT - PLAYER_HEIGHT) + - self.uid = uid - self.rect = pygame.Rect(0, 0, PLAYER_WIDTH, PLAYER_HEIGHT) - self.color = (0, 255, 0) - self.name = "Player1" - self.speed = PLAYER_SPEED - self.is_moving = False - self.facing = Facing.RIGHT + def distance(self, rect1, rect2): + """Helper function for distance computing""" + return math.hypot(rect1.x - rect2.x, rect1.y - rect2.y) - self.shoot_cooldown = 250 - self.last_shot_time = pygame.time.get_ticks() + def find_closest_npc(self, npcs): + closest = None + min_dist = float('inf') - def set_coords(self, x, y): - """Coords setter""" - self.rect = pygame.Rect(x, y, PLAYER_WIDTH, PLAYER_HEIGHT) + for npc in npcs: + dist = self.distance(self.rect, npc.rect) + if dist < min_dist: + min_dist = dist + closest = npc - def set_name(self, name): - """Name setter""" - self.name = name - def update(self, **kwargs): - """Update self position from move intention""" + return closest - inp = kwargs["inputs"] - self.is_moving = any(key in inp for key in movement_keys) - if KeyType.UP.name in inp: - self.rect.y -= self.speed - self.facing = Facing.UP - elif KeyType.DOWN.name in inp: - self.rect.y += self.speed - self.facing = Facing.DOWN - if KeyType.LEFT.name in inp: - self.rect.x -= self.speed - self.facing = Facing.LEFT - elif KeyType.RIGHT.name in inp: - self.rect.x += self.speed - self.facing = Facing.RIGHT + def shoot(self, npcs): + """Try to shoot, does not fire in cooldown""" - # overflow corrections - todo možná udělat přes kolize s okrajem! - self.rect.x = max(self.rect.x, 0) - self.rect.y = max(self.rect.y, 0) - self.rect.x = min(self.rect.x, SCREEN_WIDTH - PLAYER_WIDTH) - self.rect.y = min(self.rect.y, SCREEN_HEIGHT - PLAYER_HEIGHT) - def distance(self, rect1, rect2): - """Helper function for distance computing""" - return math.hypot(rect1.x - rect2.x, rect1.y - rect2.y) + now = pygame.time.get_ticks() + if now - self.last_shot_time >= self.shoot_cooldown: + target = self.find_closest_npc(npcs) + if target is None: + return None - def find_closest_npc(self, npcs): - closest = None - min_dist = float('inf') - for npc in npcs: - dist = self.distance(self.rect, npc.rect) - if dist < min_dist: - min_dist = dist - closest = npc + target_pos = (target.rect.centerx, target.rect.centery) + bullet = Bullet(self.rect.centerx, self.rect.centery, *target_pos, color=self.color) + self.last_shot_time = now + return bullet - return closest + def draw_lifebar(self, screen): + bar_width = self.rect.width + bar_heigth = 5 + fill = (self.health / self.max_health) * bar_width - def shoot(self, npcs): - """Try to shoot, does not fire in cooldown""" - now = pygame.time.get_ticks() - if now - self.last_shot_time >= self.shoot_cooldown: - target = self.find_closest_npc(npcs) - if target is None: - return None + pygame.draw.rect(screen, (255, 0, 0), (self.rect.x, self.rect.y - 10, bar_width, bar_heigth)) + pygame.draw.rect(screen, (0, 255, 0), (self.rect.x, self.rect.y - 10, fill, bar_heigth)) - target_pos = (target.rect.centerx, target.rect.centery) - bullet = Bullet(self.rect.centerx, self.rect.centery, *target_pos, color=self.color) - self.last_shot_time = now - return bullet + def draw(self, screen): + """Draws itself""" + pygame.draw.rect(screen, self.color, self.rect) + self.draw_lifebar(screen) - def draw(self, screen): - """Draws itself""" - pygame.draw.rect(screen, self.color, self.rect) From ea96469889c0744f90d24b8c45752aa5cf80f98b Mon Sep 17 00:00:00 2001 From: Ren Date: Sun, 15 Jun 2025 09:44:37 +0200 Subject: [PATCH 2/3] spawn --- .../shelved.patch | 135 --------- .../shelved.patch | 0 ...heckout_at_12_06_2025__20_36__Changes_.xml | 4 - .idea/workspace.xml | 267 ------------------ 4 files changed, 406 deletions(-) delete mode 100644 .idea/shelf/Uncommitted_changes_before_Checkout_at_12_06_2025,_20_36_[Changes]/shelved.patch delete mode 100644 .idea/shelf/Uncommitted_changes_before_Checkout_at_12_06_2025,_20_36_[Changes]1/shelved.patch delete mode 100644 .idea/shelf/Uncommitted_changes_before_Checkout_at_12_06_2025__20_36__Changes_.xml delete mode 100644 .idea/workspace.xml diff --git a/.idea/shelf/Uncommitted_changes_before_Checkout_at_12_06_2025,_20_36_[Changes]/shelved.patch b/.idea/shelf/Uncommitted_changes_before_Checkout_at_12_06_2025,_20_36_[Changes]/shelved.patch deleted file mode 100644 index d66649e..0000000 --- a/.idea/shelf/Uncommitted_changes_before_Checkout_at_12_06_2025,_20_36_[Changes]/shelved.patch +++ /dev/null @@ -1,135 +0,0 @@ -Index: game.py -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.BaseRevisionTextPatchEP -<+>\"\"\"Game module\"\"\"\n\nimport queue\nimport threading\nimport time\nfrom datetime import datetime\n\n\nimport uuid\n\nimport psutil\nimport pygame\nimport socketio\n\nfrom bullet import Bullet\nfrom inputs import InputManager, PLAYER_KEYMAPS\nfrom player import Player\nfrom npc import NPC\n\nfrom config import GAME_FPS, CLIENT_REFRESH_COEF, INFO_TIMER, GATEWAY_ADDRESS\n\n\nclass AbstractGame:\n \"\"\"Abstract game ancestor\"\"\"\n\n def __init__(self, **kwargs):\n self.input_manager = InputManager()\n\n self.screen = kwargs[\"screen\"]\n self.clock = pygame.time.Clock()\n self.running = True\n self.tick = 1 * GAME_FPS\n self.players = {}\n self.npcs = [] # todo DICT by id????\n\n self.bullets = []\n self.player_bullets = []\n self.npc_bullets = []\n self.last_shot_times = {}\n self.shoot_cooldown = 250\n self.npc_last_shot_times = {}\n self.npc_shoot_cooldown = 150\n\n def try_shoot(self, uid, target_pos):\n now = pygame.time.get_ticks()\n last_shot = self.last_shot_times.get(uid, 0)\n if now - last_shot >= self.shoot_cooldown:\n player = self.players[uid]\n bullet = player.shoot(target_pos, color=player.color)\n self.player_bullets.append(bullet)\n self.last_shot_times[uid] = now\n\n def try_npc_shoot(self, npc):\n now = pygame.time.get_ticks()\n npc_id = id(npc)\n last_shot = self.npc_last_shot_times.get(npc_id, 0)\n if now - last_shot >= self.npc_shoot_cooldown:\n target = npc.get_shot_target(self.players.values())\n if target:\n bullet = Bullet(npc.rect.centerx, npc.rect.centery,\n target.rect.centerx, target.rect.centery,\n color=(255, 0, 0))\n self.npc_bullets.append(bullet)\n self.npc_last_shot_times[npc_id] = now\n\n\n def short_uid(self, length=8):\n \"\"\"Helper uid function\"\"\"\n return uuid.uuid4().hex[:length]\n\n def handle_events(self):\n \"\"\"Handles events and interruptions\"\"\"\n for event in pygame.event.get():\n if event.type == pygame.QUIT:\n print(\"Pressed quitting!\")\n self.running = False\n\n def handle_key_events(self):\n \"\"\"Handles key presses\"\"\"\n raise NotImplementedError\n\n\n def update_players(self, **kwargs):\n \"\"\"Handles players updates\"\"\"\n\n for player in self.players.values():\n player.update(inputs = self.input_manager.get_inputs(player.uid), **kwargs)\n self.input_manager.clear_inputs(player.uid)\n\n\n def update_npcs(self, **kwargs):\n \"\"\"Handles npcs updates\"\"\"\n\n for npc in self.npcs:\n npc.update(players = self.players, **kwargs)\n self.try_npc_shoot(npc)\n\n\n def render_all(self):\n \"\"\"Draws itself\"\"\"\n self.screen.fill((0, 0, 0))\n\n for player in self.players.values():\n player.draw(self.screen)\n\n for npc in self.npcs:\n npc.draw(self.screen)\n\n for bullet in self.player_bullets:\n bullet.draw(self.screen)\n\n for bullet in self.npc_bullets:\n bullet.draw(self.screen)\n\n def update_bullets(self):\n \"\"\"Update bullets position\"\"\"\n for bullet_list in [self.player_bullets, self.npc_bullets]:\n for bullet in bullet_list[:]:\n bullet.update()\n if bullet.is_off_screen():\n bullet_list.remove(bullet)\n\n\n def run(self):\n \"\"\"Running loop\"\"\"\n while self.running:\n self.handle_events()\n self.handle_key_events()\n\n self.update_players()\n self.update_npcs()\n self.update_bullets()\n\n self.render_all()\n pygame.display.flip()\n self.clock.tick(self.tick)\n\n print(\"Closing game ....\")\n\n\nclass SingleGame(AbstractGame):\n \"\"\"Single player game\"\"\"\n\n PLAYER1 = \"Player1\"\n #PLAYER2 = \"Player2\"\n\n def __init__(self, **kwargs):\n super().__init__(**kwargs)\n\n pl1 = Player(self.PLAYER1) # todo some better init\n pl1.set_coords(20,20)\n self.players[self.PLAYER1] = pl1\n\n self.input_manager.add_keymap(self.PLAYER1, PLAYER_KEYMAPS[\"wasd\"])\n\n #pl2 = Player(self.PLAYER2) # experimental player 2\n #pl2.set_coords(100, 20)\n #pl2.color = (0,0,255)\n #self.players[self.PLAYER2] = pl2\n\n #self.input_manager.add_keymap(self.PLAYER2, PLAYER_KEYMAPS[\"arrows\"])\n\n self.npcs.append(NPC(400, 400))\n\n def handle_key_events(self):\n \"\"\"Handles key presses\"\"\"\n\n keys = pygame.key.get_pressed()\n mouse_buttons = pygame.mouse.get_pressed()\n\n for key in PLAYER_KEYMAPS[\"wasd\"].keys():\n if keys[key]:\n self.input_manager.add_input(self.PLAYER1, key)\n\n #for key in PLAYER_KEYMAPS[\"arrows\"].keys():\n #if keys[key]:\n #self.input_manager.add_input(self.PLAYER2, key)\n\n if mouse_buttons[0]:\n self.try_shoot(self.PLAYER1, pygame.mouse.get_pos())\n\n\nclass MultiGameHost(AbstractGame):\n \"\"\"Multi-player game, role host\"\"\"\n\n PLAYER1 = \"HOST\" # todo some short id\n PLAYER1_NAME = \"Libor\" # todo config?\n\n def __init__(self, **kwargs):\n super().__init__(**kwargs)\n\n pl1 = Player(self.PLAYER1)\n pl1.set_coords(20,20)\n pl1.set_name(self.PLAYER1_NAME)\n self.players[self.PLAYER1] = pl1\n\n self.input_manager.add_keymap(self.PLAYER1, PLAYER_KEYMAPS[\"wasd\"])\n\n self.npcs.append(NPC(400, 400))\n\n self.client_thread = None\n self.info_thread = None\n self.socket_thread = None\n self.socket_queue = queue.Queue()\n\n self.sio = socketio.Client()\n self.sio.on('message', self.message)\n self.sio.on('info', self.info)\n\n self.sio.on('move', self.move)\n\n def run(self):\n \"\"\"Running loop\"\"\"\n\n # sio běží v neblokujícím vlákně\n self.client_thread = threading.Thread(target=self.start_client, daemon=True)\n self.client_thread.start()\n\n self.info_thread = threading.Thread(target=self.start_info, daemon=True)\n self.info_thread.start()\n\n self.socket_thread = threading.Thread(target=self.start_socket, daemon=True)\n self.socket_thread.start()\n\n while self.running:\n\n if not self.sio.connected:\n print(\"Connecting to server\") # todo better gui!\n else:\n self.handle_events()\n self.handle_key_events()\n\n self.update_players()\n self.update_npcs()\n self.update_bullets()\n self.send_all_positions()\n\n self.render_all()\n pygame.display.flip()\n\n self.clock.tick(self.tick)\n\n print(\"Closing game!\")\n self.info_thread.join()\n self.socket_thread.join()\n self.sio.disconnect()\n self.client_thread.join()\n\n\n\n\n def handle_key_events(self):\n \"\"\"Handles key presses\"\"\"\n\n keys = pygame.key.get_pressed()\n mouse_buttons = pygame.mouse.get_pressed()\n\n for key in PLAYER_KEYMAPS[\"wasd\"].keys():\n if keys[key]:\n self.input_manager.add_input(self.PLAYER1, key)\n\n if mouse_buttons[0]:\n self.try_shoot(self.PLAYER1, pygame.mouse.get_pos())\n\n\n def start_socket(self):\n \"\"\" Vlákno pro komunikaci přes socket, které bude odebírat data z fronty \"\"\"\n while self.running:\n try:\n while not self.socket_queue.empty(): # Zpracuj všechny dostupné zprávy\n name, data = self.socket_queue.get_nowait()\n # print(\"emitted\", name )\n self.sio.emit(name, data)\n\n except queue.Empty:\n pass # Pokud fronta je prázdná, čekáme\n\n time.sleep(0.01)\n\n def start_info(self):\n \"\"\"Info heartbeat thread\"\"\"\n while self.running:\n time.sleep(INFO_TIMER) # Pauza 1 sekunda\n if self.sio.connected:\n print(\"heartbeat!\")\n now = datetime.now()\n print(now.strftime(\"%H:%M:%S.%f\")[:-3])\n self.sio.emit(\"info\")\n\n def start_client(self):\n \"\"\"SIO connection thread\"\"\"\n headers = {\"role\": \"host\", \"uid\": self.PLAYER1, \"name\": self.PLAYER1_NAME }\n self.sio.connect(GATEWAY_ADDRESS, headers=headers, transports=[\"websocket\"])\n self.sio.wait()\n\n def send_all_positions(self):\n \"\"\"Position synchronizing method\"\"\"\n\n player_data = [player.get_transport_data() for player in self.players.values()]\n\n npc_data = [] # todo :)\n\n self.socket_queue.put((\"game_state\", {\"players\": player_data, \"npcs\": npc_data}))\n\n\n def message(self, msg):\n \"\"\"Message handler\"\"\"\n print(\"Got new message!\", msg)\n\n def info(self, msg):\n \"\"\"Info handler\"\"\"\n now = datetime.now()\n print(now.strftime(\"%H:%M:%S.%f\")[:-3])\n print(\"Got info!\", msg)\n\n for client in msg.get(\"clientList\", []): # add missing players\n uid = client[\"uid\"]\n if uid not in self.players:\n\n print(\"New player\", client.get(\"name\"))\n new_player = Player(uid)\n new_player.set_coords(20, 20)\n new_player.set_name(client.get(\"name\"))\n self.players[uid] = new_player\n\n def move(self, data):\n \"\"\"Incoming move handler\"\"\"\n # print(data, \"Got move!\", data)\n self.input_manager.add_inputs(data[\"uid\"], data[\"inputs\"])\n\n\nclass MultiGameClient(AbstractGame):\n \"\"\"Multi-player game, role client\"\"\"\n\n def __init__(self, **kwargs):\n super().__init__(**kwargs)\n\n self.player1_id = self.short_uid()\n self.player1_name = \"Some player\" # todo config?\n pl1 = Player(self.player1_id)\n pl1.set_coords(20,20)\n pl1.set_name(self.player1_name)\n self.players[self.player1_id] = pl1\n\n self.input_manager.add_keymap(self.player1_id, PLAYER_KEYMAPS[\"wasd\"])\n\n self.npcs.append(NPC(400, 400))\n\n self.client_thread = None\n self.socket_thread = None\n self.socket_queue = queue.Queue()\n\n self.sio = socketio.Client(\n reconnection=True,\n reconnection_attempts=20,\n reconnection_delay=0.1,\n reconnection_delay_max=5\n )\n\n # sio handlers\n self.sio.on('message', self.message)\n self.sio.on('info', self.info)\n self.sio.on('game_state', self.game_state)\n\n # sio info block\n self.sio.on(\"connect\", lambda: print(\"connected\"))\n self.sio.on(\"disconnect\", lambda x: print(\"disconnected\", x))\n self.sio.on(\"reconnect\", lambda x: print(\"reconnected\", x))\n self.sio.on(\"reconnect_error\", lambda x: print(\"reconnect-error\", x))\n\n\n def run(self):\n \"\"\"Running loop\"\"\"\n\n # sio běží v neblokujícím vlákně\n self.client_thread = threading.Thread(target=self.start_client, daemon=True)\n self.client_thread.start()\n\n self.socket_thread = threading.Thread(target=self.start_socket, daemon=True)\n self.socket_thread.start()\n\n cnt = 0\n\n while self.running:\n\n if not self.sio.connected:\n self.screen.fill((0, 0, 0))\n pygame.display.flip()\n print(\"Connecting to server\") # todo some nice screen, add some waiting for host\n else:\n self.handle_events()\n self.handle_key_events()\n self.update_bullets()\n self.send_key_events()\n\n # no drawing, only from game_state event!\n\n cnt += 1\n if cnt > (200 * CLIENT_REFRESH_COEF):\n cpu = psutil.cpu_percent()\n print(f\"CPU: {cpu}\")\n print(\"heartbeat!\")\n now = datetime.now()\n print(now.strftime(\"%H:%M:%S.%f\")[:-3])\n self.sio.emit(\"info\")\n cnt = 0\n\n # todo: rychlejsi tick, protoze se updatuje eventem\n # ale pozor na zahlceni socketu\n self.clock.tick(self.tick * CLIENT_REFRESH_COEF)\n\n print(\"Closing game!\")\n self.sio.disconnect()\n self.client_thread.join()\n\n def start_socket(self):\n \"\"\" Vlákno pro komunikaci přes socket, které bude odebírat data z fronty \"\"\"\n while self.running:\n try:\n while not self.socket_queue.empty(): # Zpracuj všechny dostupné zprávy\n name, data = self.socket_queue.get_nowait()\n # print(\"emitted\", name )\n if self.sio.connected:\n self.sio.emit(name, data)\n\n except queue.Empty:\n pass # Pokud fronta je prázdná, čekáme\n\n time.sleep(0.02)\n\n def handle_key_events(self):\n \"\"\"Handles key presses\"\"\"\n\n keys = pygame.key.get_pressed()\n mouse_buttons = pygame.mouse.get_pressed()\n\n for key in PLAYER_KEYMAPS[\"wasd\"].keys():\n if keys[key]:\n self.input_manager.add_input(self.player1_id, key)\n\n if mouse_buttons[0]:\n self.try_shoot(self.PLAYER1, pygame.mouse.get_pos())\n\n def send_key_events(self):\n \"\"\"Send key presses to socket\"\"\"\n\n inputs = self.input_manager.get_inputs(self.player1_id)\n self.input_manager.clear_inputs(self.player1_id)\n if len(inputs) > 0:\n# self.sio.emit(\"move\", {\"uid\": self.PLAYER1, \"inputs\": inputs})\n self.socket_queue.put((\"move\", {\"uid\": self.player1_id, \"inputs\": inputs}))\n\n def start_client(self):\n \"\"\"Sio connect thread\"\"\"\n\n # todo posílat color v hexa\n\n headers = {\"role\": \"client\", \"uid\": self.player1_id, \"name\": self.player1_name}\n self.sio.connect(GATEWAY_ADDRESS, wait_timeout=5, headers=headers, transports=[\"websocket\"])\n\n self.sio.wait()\n\n def message(self, msg):\n \"\"\"Message handler\"\"\"\n print(\"Got new message!\", msg)\n\n def info(self, msg):\n \"\"\"Info handler\"\"\"\n now = datetime.now()\n print(now.strftime(\"%H:%M:%S.%f\")[:-3])\n print(\"Got info!\", msg)\n\n for client in msg.get(\"clientList\", []): # add missing players\n uid = client[\"uid\"]\n if uid not in self.players:\n\n print(\"New player\", client.get(\"name\"))\n new_player = Player(uid)\n new_player.set_coords(20, 20)\n new_player.set_name(client.get(\"name\"))\n self.players[uid] = new_player\n\n def game_state(self, data):\n \"\"\"Incoming game state handler\"\"\"\n # print(data, \"Got new game state!\", data)\n\n for player_data in data[\"players\"]:\n if player_data[\"uid\"] in self.players:\n pl = self.players.get(player_data[\"uid\"])\n pl.update_data(player_data)\n # self.update_npcs()\n\n self.render_all()\n\n pygame.display.flip()\n -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/game.py b/game.py ---- a/game.py (revision 66a2f5de9ffb6ec19bf108b79b51e22c5f47eeb4) -+++ b/game.py (date 1749753078762) -@@ -41,6 +41,25 @@ - self.npc_last_shot_times = {} - self.npc_shoot_cooldown = 150 - -+ def check_bullet_collisions(self): -+ for bullet in self.npc_bullets[:]: -+ for player in self.players.values(): -+ if bullet.rect.colliderect(player.rect): -+ player.health -= 10 -+ if player.health < 0: -+ player.health = 0 -+ self.npc_bullets.remove(bullet) -+ break -+ -+ for bullet in self.player_bullets[:]: -+ for npc in self.npcs[:]: -+ if bullet.rect.colliderect(npc.rect): -+ npc.health -= 10 -+ if npc.health < 0: -+ npc.health = 0 -+ self.player_bullets.remove(bullet) -+ break -+ - def try_shoot(self, uid, target_pos): - now = pygame.time.get_ticks() - last_shot = self.last_shot_times.get(uid, 0) -@@ -130,6 +149,7 @@ - self.update_players() - self.update_npcs() - self.update_bullets() -+ self.check_bullet_collisions() - - self.render_all() - pygame.display.flip() -@@ -222,6 +242,7 @@ - self.socket_thread = threading.Thread(target=self.start_socket, daemon=True) - self.socket_thread.start() - -+ - while self.running: - - if not self.sio.connected: -@@ -233,6 +254,7 @@ - self.update_players() - self.update_npcs() - self.update_bullets() -+ self.check_bullet_collisions() - self.send_all_positions() - - self.render_all() -Index: npc.py -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.BaseRevisionTextPatchEP -<+>\"\"\"NPC module\"\"\"\nimport math\n\nimport pygame\n\nfrom config import NPC_SPEED\nfrom bullet import Bullet\n\nclass NPC:\n \"\"\"NPC common class\"\"\"\n def __init__(self, x, y):\n self.rect = pygame.Rect(x, y, 20, 20)\n\n def update(self, **kwargs):\n \"\"\"Updates self position according to players\"\"\"\n nearest_player = self.find_closest_player(self, kwargs[\"players\"].values())\n\n\n if self.rect.x < nearest_player.rect.x:\n self.rect.x += NPC_SPEED\n elif self.rect.x > nearest_player.rect.x:\n self.rect.x -= NPC_SPEED\n if self.rect.y < nearest_player.rect.y:\n self.rect.y += NPC_SPEED\n elif self.rect.y > nearest_player.rect.y:\n self.rect.y -= NPC_SPEED\n\n def get_shot_target(self, players):\n return self.find_closest_player(self, players)\n\n # def shoot(self, target):\n # bullet = Bullet(self.rect.centerx, self.rect.centery,\n # target.rect.centerx, target.rect.centery,\n # color=(255, 0, 0))\n # self.bullets.append(bullet)\n\n def draw(self, screen):\n \"\"\"Draws itself\"\"\"\n pygame.draw.rect(screen, (255, 0, 0), self.rect)\n\n\n def distance(self, rect1, rect2):\n \"\"\"Helper function for distance computing\"\"\"\n return math.hypot(rect1.x - rect2.x, rect1.y - rect2.y)\n\n def find_closest_player(self, npc, players):\n \"\"\"Function for NPC to find which player to chase, not optimized\"\"\"\n closest = None\n min_dist = float('inf')\n\n for player in players:\n dist = self.distance(npc.rect, player.rect)\n if dist < min_dist:\n min_dist = dist\n closest = player\n\n return closest\n -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/npc.py b/npc.py ---- a/npc.py (revision 66a2f5de9ffb6ec19bf108b79b51e22c5f47eeb4) -+++ b/npc.py (date 1749751148158) -@@ -11,6 +11,9 @@ - def __init__(self, x, y): - self.rect = pygame.Rect(x, y, 20, 20) - -+ self.health = 100 -+ self.max_health = 100 -+ - def update(self, **kwargs): - """Updates self position according to players""" - nearest_player = self.find_closest_player(self, kwargs["players"].values()) -@@ -34,9 +37,18 @@ - # color=(255, 0, 0)) - # self.bullets.append(bullet) - -+ def draw_lifebar(self,screen): -+ bar_width = self.rect.width -+ bar_heigth = 5 -+ fill = (self.health/self.max_health) * bar_width -+ -+ pygame.draw.rect(screen,(255,0,0),(self.rect.x,self.rect.y - 10,bar_width, bar_heigth)) -+ pygame.draw.rect(screen, (0, 255, 0), (self.rect.x, self.rect.y - 10, fill, bar_heigth)) -+ - def draw(self, screen): - """Draws itself""" - pygame.draw.rect(screen, (255, 0, 0), self.rect) -+ self.draw_lifebar(screen) - - - def distance(self, rect1, rect2): -Index: player.py -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.BaseRevisionTextPatchEP -<+>\"\"\"Player module\"\"\"\n\nimport pygame\nfrom config import PLAYER_WIDTH, PLAYER_HEIGHT, PLAYER_SPEED, SCREEN_WIDTH, SCREEN_HEIGHT\nfrom enums import KeyType\nfrom bullet import Bullet\nimport random\n\nclass Player:\n \"\"\"Player class init\"\"\"\n def __init__(self, uid):\n\n self.uid = uid\n self.rect = pygame.Rect(0, 0, PLAYER_WIDTH, PLAYER_HEIGHT)\n self.color = (0, 255, 0)\n self.name = \"Player1\"\n self.speed = PLAYER_SPEED\n\n\n def set_coords(self, x, y):\n \"\"\"Coords setter\"\"\"\n self.rect = pygame.Rect(x, y, PLAYER_WIDTH, PLAYER_HEIGHT)\n\n def set_name(self, name):\n \"\"\"Name setter\"\"\"\n self.name = name\n\n\n def get_transport_data(self):\n \"\"\"Prepare data for data event\"\"\"\n return {\"uid\": self.uid, \"x\": self.rect.x, \"y\": self.rect.y}\n\n def update_data(self, transport_data):\n \"\"\"Update self position from data event\"\"\"\n self.rect.x = transport_data[\"x\"]\n self.rect.y = transport_data[\"y\"]\n\n def update(self, **kwargs):\n \"\"\"Update self position from move intention\"\"\"\n\n inp = kwargs[\"inputs\"]\n mouse_pos = kwargs.get(\"mouse_pos\",(self.rect.centerx,self.rect.centery))\n\n if KeyType.LEFT.name in inp:\n self.rect.x -= self.speed\n if KeyType.RIGHT.name in inp:\n self.rect.x += self.speed\n if KeyType.UP.name in inp:\n self.rect.y -= self.speed\n if KeyType.DOWN.name in inp:\n self.rect.y += self.speed\n\n # overflow corrections\n self.rect.x = max(self.rect.x, 0)\n self.rect.y = max(self.rect.y, 0)\n self.rect.x = min(self.rect.x, SCREEN_WIDTH - PLAYER_WIDTH)\n self.rect.y = min(self.rect.y, SCREEN_HEIGHT - PLAYER_HEIGHT)\n\n\n\n def shoot(self, target_pos=None, color=None):\n if target_pos is None:\n target_pos = (400, 300)\n\n if color is None:\n color = self.color\n\n bullet = Bullet(self.rect.centerx, self.rect.centery, *target_pos, color=color)\n return bullet\n\n def draw(self, screen):\n \"\"\"Draws itself\"\"\"\n pygame.draw.rect(screen, self.color, self.rect)\n -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/player.py b/player.py ---- a/player.py (revision 66a2f5de9ffb6ec19bf108b79b51e22c5f47eeb4) -+++ b/player.py (date 1749751094222) -@@ -16,6 +16,9 @@ - self.name = "Player1" - self.speed = PLAYER_SPEED - -+ self.health = 100 -+ self.max_health = 100 -+ - - def set_coords(self, x, y): - """Coords setter""" -@@ -68,6 +71,15 @@ - bullet = Bullet(self.rect.centerx, self.rect.centery, *target_pos, color=color) - return bullet - -+ def draw_lifebar(self,screen): -+ bar_width = self.rect.width -+ bar_heigth = 5 -+ fill = (self.health/self.max_health) * bar_width -+ -+ pygame.draw.rect(screen,(255,0,0),(self.rect.x,self.rect.y - 10,bar_width, bar_heigth)) -+ pygame.draw.rect(screen,(0, 255, 0),(self.rect.x, self.rect.y - 10, fill, bar_heigth)) -+ - def draw(self, screen): - """Draws itself""" - pygame.draw.rect(screen, self.color, self.rect) -+ self.draw_lifebar(screen) diff --git a/.idea/shelf/Uncommitted_changes_before_Checkout_at_12_06_2025,_20_36_[Changes]1/shelved.patch b/.idea/shelf/Uncommitted_changes_before_Checkout_at_12_06_2025,_20_36_[Changes]1/shelved.patch deleted file mode 100644 index e69de29..0000000 diff --git a/.idea/shelf/Uncommitted_changes_before_Checkout_at_12_06_2025__20_36__Changes_.xml b/.idea/shelf/Uncommitted_changes_before_Checkout_at_12_06_2025__20_36__Changes_.xml deleted file mode 100644 index d52213d..0000000 --- a/.idea/shelf/Uncommitted_changes_before_Checkout_at_12_06_2025__20_36__Changes_.xml +++ /dev/null @@ -1,4 +0,0 @@ - - \ No newline at end of file diff --git a/.idea/workspace.xml b/.idea/workspace.xml deleted file mode 100644 index 703f46f..0000000 --- a/.idea/workspace.xml +++ /dev/null @@ -1,267 +0,0 @@ - - - - - - - - - - - - - - - - - - - { - "lastFilter": { - "state": "OPEN", - "assignee": "Ren6436" - } -} - { - "selectedUrlAndAccountId": { - "url": "https://github.com/dracek/uuPyGame.git", - "accountId": "bbc4e1cb-aa98-4684-86cc-3d43099966ad" - } -} - { - "associatedIndex": 4 -} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 1745221181550 - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file From f146b6f87563b74d6bbdbd1fe9c7827f0ea89a23 Mon Sep 17 00:00:00 2001 From: Ren Date: Sun, 15 Jun 2025 10:31:14 +0200 Subject: [PATCH 3/3] upd --- bullet.py | 30 ++--- game.py | 368 +++++++++++++++++++++++++++--------------------------- npc.py | 102 +++++++-------- player.py | 146 +++++++++++----------- 4 files changed, 323 insertions(+), 323 deletions(-) diff --git a/bullet.py b/bullet.py index 4b621bf..12b7bea 100644 --- a/bullet.py +++ b/bullet.py @@ -6,25 +6,25 @@ class Bullet: - def __init__(self, x, y, target_x, target_y,color=(0, 0, 0),shooter=None): - self.rect = pygame.Rect(x, y, BULLET_SIZE, BULLET_SIZE) - angle = math.atan2(target_y - y, target_x - x) - self.dx = math.cos(angle) * BULLET_SPEED - self.dy = math.sin(angle) * BULLET_SPEED - self.color = color - self.shooter = shooter + def __init__(self, x, y, target_x, target_y,color=(0, 0, 0),shooter=None): + self.rect = pygame.Rect(x, y, BULLET_SIZE, BULLET_SIZE) + angle = math.atan2(target_y - y, target_x - x) + self.dx = math.cos(angle) * BULLET_SPEED + self.dy = math.sin(angle) * BULLET_SPEED + self.color = color + self.shooter = shooter - def update(self): - self.rect.x += self.dx - self.rect.y += self.dy + def update(self): + self.rect.x += self.dx + self.rect.y += self.dy - def draw(self, screen): - pygame.draw.rect(screen,self.color, self.rect) + def draw(self, screen): + pygame.draw.rect(screen,self.color, self.rect) - def is_off_screen(self): - return (self.rect.x < 0 or self.rect.x > SCREEN_WIDTH or - self.rect.y < 0 or self.rect.y > SCREEN_HEIGHT) + def is_off_screen(self): + return (self.rect.x < 0 or self.rect.x > SCREEN_WIDTH or + self.rect.y < 0 or self.rect.y > SCREEN_HEIGHT) diff --git a/game.py b/game.py index 1dd7126..32d95a5 100644 --- a/game.py +++ b/game.py @@ -18,308 +18,308 @@ class AbstractGame: - """Abstract game ancestor""" + """Abstract game ancestor""" - def __init__(self, **kwargs): - self.input_manager = InputManager() + def __init__(self, **kwargs): + self.input_manager = InputManager() - self.screen = kwargs["screen"] - self.clock = pygame.time.Clock() - self.running = True - self.tick = 1 * GAME_FPS - self.players = {} - self.npcs = [] # todo DICT by id???? + self.screen = kwargs["screen"] + self.clock = pygame.time.Clock() + self.running = True + self.tick = 1 * GAME_FPS + self.players = {} + self.npcs = [] # todo DICT by id???? - self.bullets = [] - self.player_bullets = [] - self.npc_bullets = [] + self.bullets = [] + self.player_bullets = [] + self.npc_bullets = [] - self.npc_last_shot_times = {} - self.npc_shoot_cooldown = 500 - self.score = 0 - self.last_spawn_time = pygame.time.get_ticks() - self.spawn_interval_range = (3000, 5000) + self.npc_last_shot_times = {} + self.npc_shoot_cooldown = 500 + self.score = 0 + self.last_spawn_time = pygame.time.get_ticks() + self.spawn_interval_range = (3000, 5000) - def spawn_random_npc(self): - npc_type = random.choices( - ["easy", "medium", "hard"], - weights=[70, 25, 5], - k=1 - )[0] - x = random.randint(50, self.screen.get_width() - 50) - y = random.randint(50, self.screen.get_height() - 50) - self.npcs.append(NPC(x, y, npc_type=npc_type)) + def spawn_random_npc(self): + npc_type = random.choices( + ["easy", "medium", "hard"], + weights=[70, 25, 5], + k=1 + )[0] + x = random.randint(50, self.screen.get_width() - 50) + y = random.randint(50, self.screen.get_height() - 50) + self.npcs.append(NPC(x, y, npc_type=npc_type)) - def try_spawn_npc(self): - now = pygame.time.get_ticks() - if now - self.last_spawn_time > self.next_spawn_interval: - self.spawn_random_npc() - self.last_spawn_time = now - # Nastav nový random interval pro další spawn - self.next_spawn_interval = random.randint(*self.spawn_interval_range) + def try_spawn_npc(self): + now = pygame.time.get_ticks() + if now - self.last_spawn_time > self.next_spawn_interval: + self.spawn_random_npc() + self.last_spawn_time = now + # Nastav nový random interval pro další spawn + self.next_spawn_interval = random.randint(*self.spawn_interval_range) - def try_npc_shoot(self, npc): - now = pygame.time.get_ticks() - npc_id = id(npc) - last_shot = self.npc_last_shot_times.get(npc_id, 0) - if now - last_shot >= self.npc_shoot_cooldown: - target = npc.get_shot_target(self.players.values()) - if target: - bullet = Bullet( - npc.rect.centerx, npc.rect.centery, - target.rect.centerx, target.rect.centery, - color=(255, 0, 0), - shooter=npc # tady přidáš referenci na střílejícího NPC - ) - self.npc_bullets.append(bullet) - self.npc_last_shot_times[npc_id] = now + def try_npc_shoot(self, npc): + now = pygame.time.get_ticks() + npc_id = id(npc) + last_shot = self.npc_last_shot_times.get(npc_id, 0) + if now - last_shot >= self.npc_shoot_cooldown: + target = npc.get_shot_target(self.players.values()) + if target: + bullet = Bullet( + npc.rect.centerx, npc.rect.centery, + target.rect.centerx, target.rect.centery, + color=(255, 0, 0), + shooter=npc # tady přidáš referenci na střílejícího NPC + ) + self.npc_bullets.append(bullet) + self.npc_last_shot_times[npc_id] = now - def check_bullet_collisions(self): - # NPC střely na hráče - for bullet in self.npc_bullets[:]: - for npc in self.npcs: - if bullet.shooter == npc: - for player in self.players.values(): - if bullet.rect.colliderect(player.rect): - player.health -= npc.damage - self.npc_bullets.remove(bullet) - break - break + def check_bullet_collisions(self): + # NPC střely na hráče + for bullet in self.npc_bullets[:]: + for npc in self.npcs: + if bullet.shooter == npc: + for player in self.players.values(): + if bullet.rect.colliderect(player.rect): + player.health -= npc.damage + self.npc_bullets.remove(bullet) + break + break - for bullet in self.player_bullets[:]: - for npc in self.npcs[:]: - if bullet.rect.colliderect(npc.rect): - npc.health -= 10 - if npc.health <= 0: - self.npcs.remove(npc) - self.score += npc.score - self.player_bullets.remove(bullet) - break + for bullet in self.player_bullets[:]: + for npc in self.npcs[:]: + if bullet.rect.colliderect(npc.rect): + npc.health -= 10 + if npc.health <= 0: + self.npcs.remove(npc) + self.score += npc.score + self.player_bullets.remove(bullet) + break - def handle_events(self): - """Handles events and interruptions""" - for event in pygame.event.get(): - if event.type == pygame.QUIT: - print("Pressed quitting!") - self.running = False + def handle_events(self): + """Handles events and interruptions""" + for event in pygame.event.get(): + if event.type == pygame.QUIT: + print("Pressed quitting!") + self.running = False - def handle_key_events(self): - """Handles key presses""" - raise NotImplementedError + def handle_key_events(self): + """Handles key presses""" + raise NotImplementedError - def update_players(self, **kwargs): - """Handles players updates""" + def update_players(self, **kwargs): + """Handles players updates""" - for player in self.players.values(): + for player in self.players.values(): - inputs = self.input_manager.get_inputs(player.uid) + inputs = self.input_manager.get_inputs(player.uid) - player.update(inputs = inputs, **kwargs) + player.update(inputs = inputs, **kwargs) - if KeyType.SHOOT.name in inputs: - bullet = player.shoot(self.npcs) - if bullet is not None: - self.player_bullets.append(bullet) + if KeyType.SHOOT.name in inputs: + bullet = player.shoot(self.npcs) + if bullet is not None: + self.player_bullets.append(bullet) - self.input_manager.clear_inputs(player.uid) + self.input_manager.clear_inputs(player.uid) - def update_npcs(self, **kwargs): - """Handles npcs updates""" + def update_npcs(self, **kwargs): + """Handles npcs updates""" - for npc in self.npcs: - npc.update(players = self.players, **kwargs) - self.try_npc_shoot(npc) + for npc in self.npcs: + npc.update(players = self.players, **kwargs) + self.try_npc_shoot(npc) - def render_all(self): - """Draws itself""" - self.screen.fill((0, 0, 0)) + def render_all(self): + """Draws itself""" + self.screen.fill((0, 0, 0)) - for player in self.players.values(): - player.draw(self.screen) + for player in self.players.values(): + player.draw(self.screen) - for npc in self.npcs: - npc.draw(self.screen) + for npc in self.npcs: + npc.draw(self.screen) - for bullet in self.player_bullets: - bullet.draw(self.screen) + for bullet in self.player_bullets: + bullet.draw(self.screen) - for bullet in self.npc_bullets: - bullet.draw(self.screen) + for bullet in self.npc_bullets: + bullet.draw(self.screen) - font = pygame.font.SysFont(None, 30) - score_surface = font.render(f"Score: {self.score}", True, (255, 255, 255)) - self.screen.blit(score_surface, (10, 10)) + font = pygame.font.SysFont(None, 30) + score_surface = font.render(f"Score: {self.score}", True, (255, 255, 255)) + self.screen.blit(score_surface, (10, 10)) - def update_bullets(self): - """Update bullets position""" - for bullet_list in [self.player_bullets, self.npc_bullets]: - for bullet in bullet_list[:]: - bullet.update() - if bullet.is_off_screen(): - bullet_list.remove(bullet) + def update_bullets(self): + """Update bullets position""" + for bullet_list in [self.player_bullets, self.npc_bullets]: + for bullet in bullet_list[:]: + bullet.update() + if bullet.is_off_screen(): + bullet_list.remove(bullet) - def check_game_end(self): - alive_players = [p for p in self.players.values() if p.health > 0] - if not alive_players: - self.running = False - if self.score >= 200: - self.display_end_message("You win!") - else: - self.display_end_message("You lost.") + def check_game_end(self): + alive_players = [p for p in self.players.values() if p.health > 0] + if not alive_players: + self.running = False + if self.score >= 200: + self.display_end_message("You win!") + else: + self.display_end_message("You lost.") - def display_end_message(self, message): - font = pygame.font.SysFont(None, 60) - text = font.render(message, True, (255, 255, 255)) - rect = text.get_rect(center=(self.screen.get_width() // 2, self.screen.get_height() // 2)) - self.screen.fill((0, 0, 0)) - self.screen.blit(text, rect) - pygame.display.flip() - pygame.time.wait(3000) + def display_end_message(self, message): + font = pygame.font.SysFont(None, 60) + text = font.render(message, True, (255, 255, 255)) + rect = text.get_rect(center=(self.screen.get_width() // 2, self.screen.get_height() // 2)) + self.screen.fill((0, 0, 0)) + self.screen.blit(text, rect) + pygame.display.flip() + pygame.time.wait(3000) - def run(self): - """Running loop""" - self.next_spawn_interval = random.randint(*self.spawn_interval_range) - while self.running: - self.handle_events() - self.handle_key_events() + def run(self): + """Running loop""" + self.next_spawn_interval = random.randint(*self.spawn_interval_range) + while self.running: + self.handle_events() + self.handle_key_events() - self.try_spawn_npc() + self.try_spawn_npc() - self.update_players() - self.update_npcs() - self.update_bullets() + self.update_players() + self.update_npcs() + self.update_bullets() - self.render_all() - pygame.display.flip() - self.clock.tick(self.tick) - self.check_bullet_collisions() - self.check_game_end() + self.render_all() + pygame.display.flip() + self.clock.tick(self.tick) + self.check_bullet_collisions() + self.check_game_end() - print("Closing game ....") + print("Closing game ....") class SingleGame(AbstractGame): - """Single player game""" + """Single player game""" - PLAYER1 = "Player1" + PLAYER1 = "Player1" - def __init__(self, **kwargs): - super().__init__(**kwargs) + def __init__(self, **kwargs): + super().__init__(**kwargs) - pl1 = Player(self.PLAYER1) # todo some better init - pl1.set_coords(20,20) - self.players[self.PLAYER1] = pl1 + pl1 = Player(self.PLAYER1) # todo some better init + pl1.set_coords(20,20) + self.players[self.PLAYER1] = pl1 - self.input_manager.add_keymap(self.PLAYER1, PLAYER_KEYMAPS["wasd"]) + self.input_manager.add_keymap(self.PLAYER1, PLAYER_KEYMAPS["wasd"]) - self.spawn_random_npc() + self.spawn_random_npc() - def handle_key_events(self): - """Handles key presses""" + def handle_key_events(self): + """Handles key presses""" - keys = pygame.key.get_pressed() + keys = pygame.key.get_pressed() - for key in PLAYER_KEYMAPS["wasd"].keys(): - if keys[key]: - self.input_manager.add_input(self.PLAYER1, key) + for key in PLAYER_KEYMAPS["wasd"].keys(): + if keys[key]: + self.input_manager.add_input(self.PLAYER1, key) class CoopGame(AbstractGame): - """Single player game""" + """Single player game""" - PLAYER1 = "Player1" - PLAYER2 = "Player2" + PLAYER1 = "Player1" + PLAYER2 = "Player2" - def __init__(self, **kwargs): - super().__init__(**kwargs) + def __init__(self, **kwargs): + super().__init__(**kwargs) - pl1 = Player(self.PLAYER1) - pl1.set_coords(20,20) - self.players[self.PLAYER1] = pl1 + pl1 = Player(self.PLAYER1) + pl1.set_coords(20,20) + self.players[self.PLAYER1] = pl1 - self.input_manager.add_keymap(self.PLAYER1, PLAYER_KEYMAPS["wasd"]) + self.input_manager.add_keymap(self.PLAYER1, PLAYER_KEYMAPS["wasd"]) - pl2 = Player(self.PLAYER2) # experimental player 2 - pl2.set_coords(100, 20) - pl2.color = (0,0,255) - self.players[self.PLAYER2] = pl2 + pl2 = Player(self.PLAYER2) # experimental player 2 + pl2.set_coords(100, 20) + pl2.color = (0,0,255) + self.players[self.PLAYER2] = pl2 - self.input_manager.add_keymap(self.PLAYER2, PLAYER_KEYMAPS["arrows"]) + self.input_manager.add_keymap(self.PLAYER2, PLAYER_KEYMAPS["arrows"]) - self.npcs.append(NPC(400, 400)) + self.npcs.append(NPC(400, 400)) - def handle_key_events(self): - """Handles key presses""" + def handle_key_events(self): + """Handles key presses""" - keys = pygame.key.get_pressed() + keys = pygame.key.get_pressed() - for key in PLAYER_KEYMAPS["wasd"].keys(): - if keys[key]: - self.input_manager.add_input(self.PLAYER1, key) + for key in PLAYER_KEYMAPS["wasd"].keys(): + if keys[key]: + self.input_manager.add_input(self.PLAYER1, key) - for key in PLAYER_KEYMAPS["arrows"].keys(): - if keys[key]: - self.input_manager.add_input(self.PLAYER2, key) + for key in PLAYER_KEYMAPS["arrows"].keys(): + if keys[key]: + self.input_manager.add_input(self.PLAYER2, key) diff --git a/npc.py b/npc.py index 9e84516..ffe508b 100644 --- a/npc.py +++ b/npc.py @@ -7,89 +7,89 @@ class NPC: - """NPC common class""" + """NPC common class""" - TYPES = { - "easy": {"health": 50, "damage": 5, "color": (100, 255, 100), "score": 10}, - "medium": {"health": 100, "damage": 10, "color": (255, 255, 0), "score": 25}, - "hard": {"health": 150, "damage": 20, "color": (255, 100, 100), "score": 50}, - } + TYPES = { + "easy": {"health": 50, "damage": 5, "color": (100, 255, 100), "score": 10}, + "medium": {"health": 100, "damage": 10, "color": (255, 255, 0), "score": 25}, + "hard": {"health": 150, "damage": 20, "color": (255, 100, 100), "score": 50}, + } - def __init__(self, x, y,npc_type="medium"): - self.rect = pygame.Rect(x, y, 20, 20) + def __init__(self, x, y,npc_type="medium"): + self.rect = pygame.Rect(x, y, 20, 20) - self.type = npc_type - npc_config = self.TYPES[self.type] - self.score = npc_config["score"] - self.health = npc_config["health"] - self.max_health = npc_config["health"] - self.damage = npc_config["damage"] - self.color = npc_config["color"] + self.type = npc_type + npc_config = self.TYPES[self.type] + self.score = npc_config["score"] + self.health = npc_config["health"] + self.max_health = npc_config["health"] + self.damage = npc_config["damage"] + self.color = npc_config["color"] - def update(self, **kwargs): - """Updates self position according to players""" - nearest_player = self.find_closest_player(self, kwargs["players"].values()) + def update(self, **kwargs): + """Updates self position according to players""" + nearest_player = self.find_closest_player(self, kwargs["players"].values()) - if self.rect.x < nearest_player.rect.x: - self.rect.x += NPC_SPEED - elif self.rect.x > nearest_player.rect.x: - self.rect.x -= NPC_SPEED - if self.rect.y < nearest_player.rect.y: - self.rect.y += NPC_SPEED - elif self.rect.y > nearest_player.rect.y: - self.rect.y -= NPC_SPEED + if self.rect.x < nearest_player.rect.x: + self.rect.x += NPC_SPEED + elif self.rect.x > nearest_player.rect.x: + self.rect.x -= NPC_SPEED + if self.rect.y < nearest_player.rect.y: + self.rect.y += NPC_SPEED + elif self.rect.y > nearest_player.rect.y: + self.rect.y -= NPC_SPEED - def get_shot_target(self, players): - return self.find_closest_player(self, players) + def get_shot_target(self, players): + return self.find_closest_player(self, players) - def draw_lifebar(self, screen): - bar_width = self.rect.width - bar_heigth = 5 - fill = (self.health / self.max_health) * bar_width + def draw_lifebar(self, screen): + bar_width = self.rect.width + bar_heigth = 5 + fill = (self.health / self.max_health) * bar_width - pygame.draw.rect(screen, (255, 0, 0), (self.rect.x, self.rect.y - 10, bar_width, bar_heigth)) - pygame.draw.rect(screen, (0, 255, 0), (self.rect.x, self.rect.y - 10, fill, bar_heigth)) + pygame.draw.rect(screen, (255, 0, 0), (self.rect.x, self.rect.y - 10, bar_width, bar_heigth)) + pygame.draw.rect(screen, (0, 255, 0), (self.rect.x, self.rect.y - 10, fill, bar_heigth)) - def draw(self, screen): - """Draws itself""" - pygame.draw.rect(screen, (255, 0, 0), self.rect) - self.draw_lifebar(screen) + def draw(self, screen): + """Draws itself""" + pygame.draw.rect(screen, (255, 0, 0), self.rect) + self.draw_lifebar(screen) - def distance(self, rect1, rect2): - """Helper function for distance computing""" - return math.hypot(rect1.x - rect2.x, rect1.y - rect2.y) + def distance(self, rect1, rect2): + """Helper function for distance computing""" + return math.hypot(rect1.x - rect2.x, rect1.y - rect2.y) - def find_closest_player(self, npc, players): - """Function for NPC to find which player to chase, not optimized""" - closest = None - min_dist = float('inf') + def find_closest_player(self, npc, players): + """Function for NPC to find which player to chase, not optimized""" + closest = None + min_dist = float('inf') - for player in players: - dist = self.distance(npc.rect, player.rect) - if dist < min_dist: - min_dist = dist - closest = player + for player in players: + dist = self.distance(npc.rect, player.rect) + if dist < min_dist: + min_dist = dist + closest = player - return closest + return closest diff --git a/player.py b/player.py index 301e712..302ffb1 100644 --- a/player.py +++ b/player.py @@ -12,123 +12,123 @@ class Player: - """Player class init""" - def __init__(self, uid): + """Player class init""" + def __init__(self, uid): - self.uid = uid - self.rect = pygame.Rect(0, 0, PLAYER_WIDTH, PLAYER_HEIGHT) - self.color = (0, 255, 0) - self.name = "Player1" - self.speed = PLAYER_SPEED - self.is_moving = False - self.facing = Facing.RIGHT - self.health = 100 - self.max_health = 100 + self.uid = uid + self.rect = pygame.Rect(0, 0, PLAYER_WIDTH, PLAYER_HEIGHT) + self.color = (0, 255, 0) + self.name = "Player1" + self.speed = PLAYER_SPEED + self.is_moving = False + self.facing = Facing.RIGHT + self.health = 100 + self.max_health = 100 - self.shoot_cooldown = 250 - self.last_shot_time = pygame.time.get_ticks() + self.shoot_cooldown = 250 + self.last_shot_time = pygame.time.get_ticks() - def set_coords(self, x, y): - """Coords setter""" - self.rect = pygame.Rect(x, y, PLAYER_WIDTH, PLAYER_HEIGHT) + def set_coords(self, x, y): + """Coords setter""" + self.rect = pygame.Rect(x, y, PLAYER_WIDTH, PLAYER_HEIGHT) - def set_name(self, name): - """Name setter""" - self.name = name + def set_name(self, name): + """Name setter""" + self.name = name - def update(self, **kwargs): - """Update self position from move intention""" + def update(self, **kwargs): + """Update self position from move intention""" - inp = kwargs["inputs"] + inp = kwargs["inputs"] - self.is_moving = any(key in inp for key in movement_keys) + self.is_moving = any(key in inp for key in movement_keys) - if KeyType.UP.name in inp: - self.rect.y -= self.speed - self.facing = Facing.UP - elif KeyType.DOWN.name in inp: - self.rect.y += self.speed - self.facing = Facing.DOWN + if KeyType.UP.name in inp: + self.rect.y -= self.speed + self.facing = Facing.UP + elif KeyType.DOWN.name in inp: + self.rect.y += self.speed + self.facing = Facing.DOWN - if KeyType.LEFT.name in inp: - self.rect.x -= self.speed - self.facing = Facing.LEFT - elif KeyType.RIGHT.name in inp: - self.rect.x += self.speed - self.facing = Facing.RIGHT + if KeyType.LEFT.name in inp: + self.rect.x -= self.speed + self.facing = Facing.LEFT + elif KeyType.RIGHT.name in inp: + self.rect.x += self.speed + self.facing = Facing.RIGHT - # overflow corrections - todo možná udělat přes kolize s okrajem! - self.rect.x = max(self.rect.x, 0) - self.rect.y = max(self.rect.y, 0) - self.rect.x = min(self.rect.x, SCREEN_WIDTH - PLAYER_WIDTH) - self.rect.y = min(self.rect.y, SCREEN_HEIGHT - PLAYER_HEIGHT) + # overflow corrections - todo možná udělat přes kolize s okrajem! + self.rect.x = max(self.rect.x, 0) + self.rect.y = max(self.rect.y, 0) + self.rect.x = min(self.rect.x, SCREEN_WIDTH - PLAYER_WIDTH) + self.rect.y = min(self.rect.y, SCREEN_HEIGHT - PLAYER_HEIGHT) - def distance(self, rect1, rect2): - """Helper function for distance computing""" - return math.hypot(rect1.x - rect2.x, rect1.y - rect2.y) + def distance(self, rect1, rect2): + """Helper function for distance computing""" + return math.hypot(rect1.x - rect2.x, rect1.y - rect2.y) - def find_closest_npc(self, npcs): - closest = None - min_dist = float('inf') + def find_closest_npc(self, npcs): + closest = None + min_dist = float('inf') - for npc in npcs: - dist = self.distance(self.rect, npc.rect) - if dist < min_dist: - min_dist = dist - closest = npc + for npc in npcs: + dist = self.distance(self.rect, npc.rect) + if dist < min_dist: + min_dist = dist + closest = npc - return closest + return closest - def shoot(self, npcs): - """Try to shoot, does not fire in cooldown""" + def shoot(self, npcs): + """Try to shoot, does not fire in cooldown""" - now = pygame.time.get_ticks() - if now - self.last_shot_time >= self.shoot_cooldown: - target = self.find_closest_npc(npcs) - if target is None: - return None + now = pygame.time.get_ticks() + if now - self.last_shot_time >= self.shoot_cooldown: + target = self.find_closest_npc(npcs) + if target is None: + return None - target_pos = (target.rect.centerx, target.rect.centery) - bullet = Bullet(self.rect.centerx, self.rect.centery, *target_pos, color=self.color) - self.last_shot_time = now - return bullet + target_pos = (target.rect.centerx, target.rect.centery) + bullet = Bullet(self.rect.centerx, self.rect.centery, *target_pos, color=self.color) + self.last_shot_time = now + return bullet - def draw_lifebar(self, screen): - bar_width = self.rect.width - bar_heigth = 5 - fill = (self.health / self.max_health) * bar_width + def draw_lifebar(self, screen): + bar_width = self.rect.width + bar_heigth = 5 + fill = (self.health / self.max_health) * bar_width - pygame.draw.rect(screen, (255, 0, 0), (self.rect.x, self.rect.y - 10, bar_width, bar_heigth)) - pygame.draw.rect(screen, (0, 255, 0), (self.rect.x, self.rect.y - 10, fill, bar_heigth)) + pygame.draw.rect(screen, (255, 0, 0), (self.rect.x, self.rect.y - 10, bar_width, bar_heigth)) + pygame.draw.rect(screen, (0, 255, 0), (self.rect.x, self.rect.y - 10, fill, bar_heigth)) - def draw(self, screen): - """Draws itself""" - pygame.draw.rect(screen, self.color, self.rect) - self.draw_lifebar(screen) + def draw(self, screen): + """Draws itself""" + pygame.draw.rect(screen, self.color, self.rect) + self.draw_lifebar(screen)