From 17e5abc7df7d0abcbb4badca8784772980672872 Mon Sep 17 00:00:00 2001 From: Matteo Benedetto Date: Wed, 20 Aug 2025 16:43:16 +0200 Subject: [PATCH] Final 2.0 --- conf/keybindings.json | 1 + conf/keybindings_rg40xx.yaml | 3 +- engine/sdl2.py | 6 +-- engine/unit_manager.py | 18 ++++++- rats.py | 15 ++++-- units/__pycache__/rat.cpython-313.pyc | Bin 9429 -> 10049 bytes units/gas.py | 69 ++++++++++++++++++++++++++ units/rat.py | 11 +++- 8 files changed, 112 insertions(+), 11 deletions(-) create mode 100644 units/gas.py diff --git a/conf/keybindings.json b/conf/keybindings.json index 22f3add..b985d2b 100644 --- a/conf/keybindings.json +++ b/conf/keybindings.json @@ -15,6 +15,7 @@ "keydown_Space": "spawn_new_bomb", "keydown_N": "spawn_new_nuclear_bomb", "keydown_Left_Ctrl": "spawn_new_mine", + "keydown_G": "spawn_gas", "keydown_P": "toggle_pause" }, "keybinding_start_menu": { diff --git a/conf/keybindings_rg40xx.yaml b/conf/keybindings_rg40xx.yaml index a9b3d47..717d063 100644 --- a/conf/keybindings_rg40xx.yaml +++ b/conf/keybindings_rg40xx.yaml @@ -6,9 +6,10 @@ keybinding_game: joyhatmotion_0_2: start_scrolling|Right joyhatmotion_0_0: stop_scrolling joybuttondown_3: spawn_new_bomb - joybuttondown_5: spawn_new_nuclear_bomb + joybuttondown_11: spawn_new_nuclear_bomb joybuttondown_4: spawn_new_mine joybuttondown_10: toggle_pause + joybuttondown_5: spawn_gas keybinding_start_menu: joybuttondown_9: reset_game diff --git a/engine/sdl2.py b/engine/sdl2.py index d6738cd..5a2caf3 100644 --- a/engine/sdl2.py +++ b/engine/sdl2.py @@ -266,7 +266,7 @@ class GameWindow: def update_ammo(self, ammo, assets): """Update and display the ammo count""" - ammo_text = f"{ammo['bomb']['count']}/{ammo['bomb']['max']} {ammo['mine']['count']}/{ammo['mine']['max']} {ammo['nuclear']['count']}/{ammo['nuclear']['max']} " + ammo_text = f"{ammo['bomb']['count']}/{ammo['bomb']['max']} {ammo['mine']['count']}/{ammo['mine']['max']} {ammo['gas']['count']}/{ammo['gas']['max']} " if self.ammo_text != ammo_text: self.ammo_text = ammo_text font = self.fonts[20] @@ -279,8 +279,8 @@ class GameWindow: self.renderer.copy(self.ammo_background, dstrect=sdl2.SDL_Rect(position[0] - 5, position[1] - 2, text_width + 10, text_height + 4)) self.renderer.copy(self.ammo_sprite, dstrect=sdl2.SDL_Rect(position[0], position[1], text_width, text_height)) self.renderer.copy(assets["BMP_BOMB0"], dstrect=sdl2.SDL_Rect(position[0]+25, position[1], 20, 20)) - self.renderer.copy(assets["BMP_POISON"], dstrect=sdl2.SDL_Rect(position[0]+80, position[1], 20, 20)) - self.renderer.copy(assets["BMP_NUCLEAR"], dstrect=sdl2.SDL_Rect(position[0]+130, position[1], 20, 20)) + self.renderer.copy(assets["BMP_POISON"], dstrect=sdl2.SDL_Rect(position[0]+85, position[1], 20, 20)) + self.renderer.copy(assets["BMP_GAS"], dstrect=sdl2.SDL_Rect(position[0]+140, position[1], 20, 20)) # ====================== # VIEW & NAVIGATION # ====================== diff --git a/engine/unit_manager.py b/engine/unit_manager.py index db6f18b..01df046 100644 --- a/engine/unit_manager.py +++ b/engine/unit_manager.py @@ -1,6 +1,8 @@ import random import uuid -from units import rat, bomb, mine, points +from units import gas, rat, bomb, mine + + class UnitManager: def count_rats(self): @@ -9,7 +11,16 @@ class UnitManager: if isinstance(unit, rat.Rat): count += 1 return count - + + def spawn_gas(self, parent_id=None): + if self.map.is_wall(self.pointer[0], self.pointer[1]): + return + if self.ammo["gas"]["count"] <= 0: + return + self.ammo["gas"]["count"] -= 1 + self.render_engine.play_sound("GAS.WAV") + self.spawn_unit(gas.Gas, self.pointer, parent_id=parent_id) + def spawn_rat(self, position=None): if position is None: position = self.choose_start() @@ -57,3 +68,6 @@ class UnitManager: if self.map.matrix[y][x] ] return random.choice(self._valid_positions) + + def get_unit_by_id(self, id): + return self.units.get(id) or None diff --git a/rats.py b/rats.py index fadb405..c6b8a31 100644 --- a/rats.py +++ b/rats.py @@ -54,15 +54,19 @@ class MiceMaze( self.ammo = { "bomb": { "count": 2, - "max": 5 + "max": 8 }, "nuclear": { "count": 1, "max": 1 }, "mine": { - "count": 5, - "max": 5 + "count": 2, + "max": 4 + }, + "gas": { + "count": 2, + "max": 4 } } self.blood_stains = {} @@ -84,11 +88,14 @@ class MiceMaze( def refill_ammo(self): for ammo_type, data in self.ammo.items(): if ammo_type == "bomb": - if random.random() < 0.01: + if random.random() < 0.02: data["count"] = min(data["count"] + 1, data["max"]) elif ammo_type == "mine": if random.random() < 0.05: data["count"] = min(data["count"] + 1, data["max"]) + elif ammo_type == "gas": + if random.random() < 0.01: + data["count"] = min(data["count"] + 1, data["max"]) def update_maze(self): if self.game_over(): diff --git a/units/__pycache__/rat.cpython-313.pyc b/units/__pycache__/rat.cpython-313.pyc index e6190f9426606c3d1650aae1fa2e062d606c63d4..ea9145d5f853b8f8eff6798c814dc4039cf98d50 100644 GIT binary patch delta 2626 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 KA4)g~4}SwL?*3B% diff --git a/units/gas.py b/units/gas.py new file mode 100644 index 0000000..a02f51a --- /dev/null +++ b/units/gas.py @@ -0,0 +1,69 @@ +from .unit import Unit +from .rat import Rat +import random + +# Costanti +AGE_THRESHOLD = 200 + +class Gas(Unit): + def __init__(self, game, position=(0,0), id=None, parent_id=None): + super().__init__(game, position, id) + self.parent_id = parent_id + # Specific attributes for gas + self.speed = 50 + self.fight = False + self.age = 0 + if parent_id: + self.age = round(random.uniform(0, AGE_THRESHOLD)) + self.spreading_cells = [] + self.last_spreading_cells = [] + + + def move(self): + if self.age > AGE_THRESHOLD: + self.die() + return + self.age += 1 + #victims = self.game.unit_positions.get(self.position, []) + victims = [rat for rat in self.game.unit_positions.get(self.position, []) if rat.partial_move>0.5] + for rat in self.game.unit_positions_before.get(self.position, []): + if rat.partial_move<0.5 and rat is Rat: + victims.append(rat) + for victim in victims: + victim.gassed += 1 + if self.age % self.speed: + return + parent = self.game.get_unit_by_id(self.parent_id) + if (parent) or self.parent_id is None: + print(f"Gas at {self.position} is spreading") + # Spread gas to adjacent cells + + for dx, dy in [(-1, 0), (1, 0), (0, -1), (0, 1)]: + new_x = self.position[0] + dx + new_y = self.position[1] + dy + if not self.game.map.is_wall(new_x, new_y): + if not any(isinstance(unit, Gas) for unit in self.game.units.values() if unit.position == (new_x, new_y)): + print(f"Spreading gas from {self.position} to ({new_x}, {new_y})") + self.game.spawn_unit(Gas, (new_x, new_y), parent_id=self.parent_id if self.parent_id else self.id) + def collisions(self): + pass + + def die(self, unit=None): + if not unit: + unit = self + self.game.units.pop(unit.id) + + + def draw(self): + image = self.game.assets["BMP_GAS"] + image_size = self.game.render_engine.get_image_size(image) + self.rat_image = image + partial_x, partial_y = 0, 0 + + x_pos = self.position_before[0] * self.game.cell_size + (self.game.cell_size - image_size[0]) // 2 + partial_x + y_pos = self.position_before[1] * self.game.cell_size + (self.game.cell_size - image_size[1]) // 2 + partial_y + self.game.render_engine.draw_image(x_pos, y_pos, image, anchor="nw", tag="unit") + for cell in self.spreading_cells: + x_pos = cell[0] * self.game.cell_size + (self.game.cell_size - image_size[0]) // 2 + partial_x + y_pos = cell[1] * self.game.cell_size + (self.game.cell_size - image_size[1]) // 2 + partial_y + self.game.render_engine.draw_image(x_pos, y_pos, image, anchor="nw", tag="unit") diff --git a/units/rat.py b/units/rat.py index 960a523..ad3b9b9 100644 --- a/units/rat.py +++ b/units/rat.py @@ -17,6 +17,7 @@ class Rat(Unit): # Specific attributes for rats self.speed = 0.10 # Rats are slower self.fight = False + self.gassed = 0 # Initialize position using pathfinding self.position = self.find_next_position() @@ -46,8 +47,15 @@ class Rat(Unit): self.position_before = self.position self.position_before = self.position return neighbors[random.randint(0, len(neighbors) - 1)] - + + def choked(self): + self.game.render_engine.play_sound("CHOKE.WAV") + self.die(score=10) + def move(self): + if self.gassed > 35: + self.choked() + return self.age += 1 if self.age == AGE_THRESHOLD: self.speed *= SPEED_REDUCTION @@ -69,6 +77,7 @@ class Rat(Unit): return units = [] units.extend(self.game.unit_positions.get(self.position_before, [])) + units.extend(self.game.unit_positions_before.get(self.position, [])) for unit in units: if unit.id == self.id or unit.age < AGE_THRESHOLD or self.position != unit.position_before: