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 e6190f9..ea9145d 100644 Binary files a/units/__pycache__/rat.cpython-313.pyc and b/units/__pycache__/rat.cpython-313.pyc differ 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: