diff --git a/assets/Rat/BMP_BOMB.png b/assets/Rat/BMP_BOMB0.png similarity index 100% rename from assets/Rat/BMP_BOMB.png rename to assets/Rat/BMP_BOMB0.png diff --git a/engine/maze.py b/engine/maze.py index f17deb8..10a3e21 100644 --- a/engine/maze.py +++ b/engine/maze.py @@ -6,4 +6,7 @@ class Map: with open(maze_file, 'r') as file: self.matrix = json.load(file) self.height = len(self.matrix) - self.width = len(self.matrix[0]) \ No newline at end of file + self.width = len(self.matrix[0]) + + def is_wall(self, x, y): + return self.matrix[y][x] \ No newline at end of file diff --git a/engine/sdl2.py b/engine/sdl2.py index cea9718..113705f 100644 --- a/engine/sdl2.py +++ b/engine/sdl2.py @@ -12,8 +12,10 @@ class GameWindow: self.width = width * cell_size self.height = height * cell_size self.target_size = (640, 480) - self.w_offset = (self.target_size[0] - self.width) // 2 - self.h_offset = (self.target_size[1] - self.height) // 2 + self.w_start_offset = (self.target_size[0] - self.width) // 2 + self.h_start_offset = (self.target_size[1] - self.height) // 2 + self.w_offset = self.w_start_offset + self.h_offset = self.h_start_offset print(f"Screen size: {self.width}x{self.height}") self.delay = 30 sdl2.ext.init() @@ -64,7 +66,13 @@ class GameWindow: def draw_rectangle(self, x, y, width, height, tag, outline="red"): x, y = x + self.w_offset, y + self.h_offset self.renderer.draw_rect((x, y, width, height), color=sdl2.ext.Color(255, 0, 0)) - + + def draw_pointer(self, x, y): + x=x+self.w_offset + y=y+self.h_offset + for i in range(3): + self.renderer.draw_rect((x + i,y+i, self.cell_size-2*i, self.cell_size-2*i), color=sdl2.ext.Color(255, 0, 0)) + def delete_tag(self, tag): pass @@ -77,6 +85,9 @@ class GameWindow: def new_cycle(self, delay, callback): pass + + def full_screen(self,flag): + sdl2.SDL_SetWindowFullscreen(self.window.window, flag) def mainloop(self, **kwargs): while self.running: @@ -91,6 +102,26 @@ class GameWindow: elif event.type == sdl2.SDL_KEYDOWN and self.key_callback: key = sdl2.SDL_GetKeyName(event.key.keysym.sym).decode('utf-8') self.key_callback(key) + elif event.type == sdl2.SDL_MOUSEMOTION: + self.scroll_view((event.motion.x//self.cell_size, event.motion.y//self.cell_size)) + elif event.type == sdl2.SDL_JOYBUTTONDOWN: + if event.jbutton.button == 0: + self.close() + # Disegna qui gli sprite self.renderer.present() sdl2.SDL_Delay(self.delay) + + def close(self): + self.running = False + sdl2.ext.quit() + + def scroll_view(self, pointer): + x, y = pointer + x = -x * self.cell_size + y = -y * self.cell_size + + self.w_offset = x //2 + + if -y < self.height-(self.height-self.target_size[1])-self.cell_size: + self.h_offset = y //2 \ No newline at end of file diff --git a/maze.json b/maze.json index cc266ea..45545fc 100644 --- a/maze.json +++ b/maze.json @@ -1 +1 @@ -[[true, true, true, true, true, true, true, true, true, true, true, true, true, true, true], [true, false, true, false, false, false, false, false, false, false, false, false, false, false, true], [true, false, true, true, true, false, true, true, true, true, true, true, true, false, true], [true, false, false, false, true, false, true, false, false, false, false, false, true, false, true], [true, true, true, false, true, false, true, false, true, true, true, false, true, false, true], [true, false, false, false, true, false, false, false, true, false, false, false, true, false, true], [true, false, true, true, true, true, true, true, true, false, true, true, true, false, true], [true, false, false, false, true, false, false, false, true, false, false, false, true, false, true], [true, true, true, false, true, false, true, false, true, true, true, false, true, true, true], [true, false, false, false, false, false, true, false, false, false, false, false, false, false, true], [true, true, true, true, true, true, true, true, true, true, true, true, true, true, true]] \ No newline at end of file +[[true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true], [true, false, false, false, true, false, false, false, false, false, false, false, false, false, false, false, true, false, false, false, false, false, true, false, false, false, false, false, false, false, true], [true, true, true, false, true, true, true, false, true, true, true, true, true, true, true, false, true, true, true, false, true, false, true, true, true, true, true, false, true, true, true], [true, false, false, false, true, false, false, false, true, false, true, false, false, false, true, false, false, false, false, false, true, false, false, false, false, false, true, false, false, false, true], [true, false, true, true, true, false, true, true, true, false, true, false, true, false, true, true, true, true, true, true, true, true, true, true, true, false, true, true, true, false, true], [true, false, false, false, true, false, true, false, false, false, false, false, true, false, false, false, false, false, false, false, false, false, true, false, true, false, false, false, true, false, true], [true, true, true, false, true, false, true, false, true, true, true, true, true, false, true, true, true, true, true, true, true, false, true, false, true, true, true, false, true, false, true], [true, false, false, false, true, false, true, false, true, false, false, false, true, false, true, false, true, false, false, false, true, false, false, false, false, false, true, false, false, false, true], [true, false, true, true, true, false, true, true, true, false, true, false, true, false, true, false, true, false, true, false, true, true, true, true, true, false, true, true, true, false, true], [true, false, false, false, true, false, false, false, true, false, true, false, false, false, true, false, false, false, true, false, false, false, true, false, false, false, true, false, true, false, true], [true, true, true, false, true, false, true, false, true, false, true, true, true, true, true, true, true, true, true, true, true, false, true, false, true, true, true, false, true, false, true], [true, false, true, false, true, false, true, false, true, false, true, false, false, false, false, false, false, false, false, false, true, false, false, false, false, false, false, false, true, false, true], [true, false, true, false, true, true, true, false, true, false, true, false, true, true, true, true, true, true, true, false, true, false, true, true, true, true, true, true, true, false, true], [true, false, true, false, false, false, true, false, true, false, true, false, true, false, false, false, true, false, true, false, true, false, false, false, true, false, false, false, false, false, true], [true, false, true, true, true, false, true, false, true, false, true, false, true, false, true, false, true, false, true, false, true, true, true, true, true, false, true, true, true, true, true], [true, false, false, false, false, false, false, false, true, false, false, false, false, false, true, false, false, false, true, false, false, false, false, false, false, false, false, false, false, false, true], [true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true]] \ No newline at end of file diff --git a/maze.py b/maze.py index f8be063..1ae5637 100644 --- a/maze.py +++ b/maze.py @@ -69,14 +69,18 @@ class MazeGenerator: if self.stack: # Continue updating only if there are cells left to visit self.window.after(10, self.update_maze) else: - # After the maze is generated, remove some walls randomly - for _ in range(int((self.width - 1) * (self.height - 1) * 0.1 // 4)): - x = random.randrange(1, self.width - 1, 2) - y = random.randrange(1, self.height - 1, 2) - self.maze[y][x] = False + self.add_random_passages() self.draw_maze() with open('maze.json', 'w') as json_file: - json.dump(self.maze, json_file) + json.dump(self.maze, json_file) + + def add_random_passages(self): + # Aggiungi passaggi casuali tra i corridoi + for _ in range(30): + x = random.randrange(1, self.width - 1, 2) + y = random.randrange(1, self.height - 1, 2) + print(x, y) + self.maze[y][x] = False def draw_maze(self): self.canvas.delete("all") @@ -92,6 +96,7 @@ class MazeGenerator: elif self.stack and (j, i) == self.stack[-1]: color = "blue" # Color the current position blue self.canvas.create_rectangle(j*10, i*10, (j+1)*10, (i+1)*10, fill=color) + def run(self): self.update_maze() @@ -99,5 +104,5 @@ class MazeGenerator: self.window.mainloop() # Crea e avvia il generatore di labirinti -generator = MazeGenerator(7, 5) +generator = MazeGenerator(15, 8) generator.run() diff --git a/rats.py b/rats.py index 9c813bc..1805e8c 100644 --- a/rats.py +++ b/rats.py @@ -2,17 +2,21 @@ import cProfile import pstats import random -from units import rat +from units import rat, bomb import uuid import subprocess from engine import maze, sdl2 as engine +import os class MiceMaze: def __init__(self, maze_file): self.map = maze.Map(maze_file) + self.pointer = (2,2) self.audio = True self.cell_size = 40 + self.full_screen = False self.engine = engine.GameWindow(self.map.width, self.map.height, self.cell_size, "Mice!", key_callback=self.key_pressed) + self.points = 0 self.graphics_load() self.units = {} self.unit_positions = {} @@ -26,7 +30,15 @@ class MiceMaze: id = uuid.uuid4() rat_class = rat.Male if random.random() < 0.5 else rat.Female self.units[id] = rat_class(self, position, id) - + + def spawn_bomb(self, position): + id = uuid.uuid4() + self.units[id] = bomb.Timer(self, position, id) + + def spawn_unit(self, unit, position, **kwargs): + id = uuid.uuid4() + self.units[id] = unit(self, position, id, **kwargs) + def choose_start(self): if not hasattr(self, '_valid_positions'): self._valid_positions = [ @@ -45,6 +57,8 @@ class MiceMaze: def update_maze(self): self.engine.delete_tag("unit") + self.engine.delete_tag("effect") + self.engine.draw_pointer(self.pointer[0] * self.cell_size, self.pointer[1] * self.cell_size) self.unit_positions.clear() self.unit_positions_before.clear() for unit in self.units.values(): @@ -54,7 +68,7 @@ class MiceMaze: unit.move() unit.collisions() unit.draw() - self.engine.update_status(f"Mice: {len(self.units)}") + self.engine.update_status(f"Mice: {len(self.units)} - Points: {self.points}") self.engine.new_cycle(50, self.update_maze) @@ -65,7 +79,7 @@ class MiceMaze: def key_pressed(self, key): print(key) if key == "Q": - self.engine.window.close() + self.engine.close() elif key == "Return": self.new_rat() elif key == "D": @@ -73,10 +87,30 @@ class MiceMaze: self.units[random.choice(list(self.units.keys()))].die() elif key == "M": self.audio = not self.audio - elif key == "S": - profiler.disable() - stats = pstats.Stats(profiler).sort_stats('cumtime') - stats.print_stats() + elif key == "F": + self.full_screen = not self.full_screen + self.engine.full_screen(self.full_screen) + elif key == "Up": + self.scroll_cursor(y=-1) + elif key == "Down": + self.scroll_cursor(y=1) + elif key == "Left": + self.scroll_cursor(x=-1) + elif key == "Right": + self.scroll_cursor(x=1) + elif key == "Space": + self.play_sound("BOMB.wav") + self.spawn_bomb(self.pointer) + + def scroll_cursor(self, x=0, y=0): + if self.pointer[0] + x > self.map.width or self.pointer[1] + y > self.map.height: + return + + self.pointer = ( + max(1, min(self.map.width-2, self.pointer[0] + x)), + max(1, min(self.map.height-2, self.pointer[1] + y)) + ) + self.engine.scroll_view(self.pointer) def play_sound(self, sound_file): if self.audio: @@ -86,10 +120,21 @@ class MiceMaze: self.tunnel = self.engine.load_image("Rat/BMP_TUNNEL.png") self.grasses = [self.engine.load_image(f"Rat/BMP_1_GRASS_{i+1}.png") for i in range(4)] self.rat_assets = {} + self.bomb_assets = {} for sex in ["MALE", "FEMALE", "BABY"]: self.rat_assets[sex] = {} for direction in ["UP", "DOWN", "LEFT", "RIGHT"]: self.rat_assets[sex][direction] = self.engine.load_image(f"Rat/BMP_{sex}_{direction}.png", transparent_color=(128, 128, 128)) + for n in range(5): + self.bomb_assets[n] = self.engine.load_image(f"Rat/BMP_BOMB{n}.png", transparent_color=(128, 128, 128)) + self.assets = {} + for file in os.listdir("assets/Rat"): + if file.endswith(".png"): + self.assets[file[:-4]] = self.engine.load_image(f"Rat/{file}") + + def add_point(self, value): + self.points += value + if __name__ == "__main__": profiler = cProfile.Profile() profiler.enable() diff --git a/units/__pycache__/rat.cpython-313.pyc b/units/__pycache__/rat.cpython-313.pyc index d749aa5..a721d1d 100644 Binary files a/units/__pycache__/rat.cpython-313.pyc and b/units/__pycache__/rat.cpython-313.pyc differ diff --git a/units/bomb.py b/units/bomb.py new file mode 100644 index 0000000..fea34c0 --- /dev/null +++ b/units/bomb.py @@ -0,0 +1,105 @@ +from .unit import Unit +from .points import Point +import uuid + +# Costanti +AGE_THRESHOLD = 200 + + +class Bomb(Unit): + def __init__(self, game, position=(0,0), id=None): + super().__init__(position) + self.id = id if id else uuid.uuid4() + self.game = game + self.position = position + self.bbox = (0, 0, 0, 0) + self.stop = 0 + self.age = 0 + self.speed = 4 + self.partial_move = 0 + self.position_before = position + self.fight = False + + def move(self): + pass + + def collisions(self): + pass + + def die(self, unit=None): + if not unit: + unit = self + self.game.units.pop(unit.id) + + + def draw(self): + n = self.age // 40 + n= 3 -n +1 + if n < 0: + n = 0 + print(self.age) + image = self.game.bomb_assets[n] + image_size = self.game.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.engine.draw_image(x_pos, y_pos, image, anchor="nw", tag="unit") + +class Timer(Bomb): + def move(self): + self.age += self.speed + if self.age == AGE_THRESHOLD: + self.die() + + def die(self, unit=None): + print("BOOM") + if not unit: + unit = self + self.game.play_sound("BOMB.WAV") + self.game.units.pop(unit.id) + self.game.spawn_unit(Explosion, unit.position) + for direction in ["N", "S", "E", "W"]: + x, y = unit.position + while True: + if not self.game.map.is_wall(x, y): + self.game.spawn_unit(Explosion, (x, y)) + for victim in self.game.unit_positions.get((x, y), []): + if victim.id in self.game.units: + if victim.partial_move >= 0.5: + victim.die() + self.game.spawn_unit(Point, (x, y), value=10) + for victim in self.game.unit_positions_before.get((x, y), []): + if victim.id in self.game.units: + if victim.partial_move < 0.5: + victim.die() + self.game.spawn_unit(Point, (x, y), value=10) + else: + break + if direction == "N": + y -= 1 + elif direction == "S": + y += 1 + elif direction == "E": + x += 1 + elif direction == "W": + x -= 1 + + +class Explosion(Bomb): + def move(self): + self.age += self.speed*5 + if self.age == AGE_THRESHOLD: + self.die() + + def draw(self): + image = self.game.assets["BMP_EXPLOSION"] + image_size = self.game.engine.get_image_size(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.engine.draw_image(x_pos, y_pos, image, anchor="nw", tag="unit") \ No newline at end of file diff --git a/units/points.py b/units/points.py new file mode 100644 index 0000000..072bb4c --- /dev/null +++ b/units/points.py @@ -0,0 +1,48 @@ +from .unit import Unit +import random +import uuid + +# Costanti +AGE_THRESHOLD = 200 + + +class Point(Unit): + def __init__(self, game, position=(0,0), id=None, value=5): + super().__init__(position) + self.id = id if id else uuid.uuid4() + self.game = game + self.position = position + self.bbox = (0, 0, 0, 0) + self.stop = 0 + self.age = 0 + self.speed = 4 + self.partial_move = 0 + self.position_before = position + self.fight = False + self.value = value + self.game.add_point(self.value) + + def move(self): + self.age += self.speed + if self.age == AGE_THRESHOLD: + self.die() + + 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[f"BMP_BONUS_{self.value}"] + image_size = self.game.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.engine.draw_image(x_pos, y_pos, image, anchor="nw", tag="unit") diff --git a/units/rat.py b/units/rat.py index 8eeff9e..4a8e190 100644 --- a/units/rat.py +++ b/units/rat.py @@ -1,4 +1,5 @@ from .unit import Unit + import random import uuid @@ -21,7 +22,7 @@ class Rat(Unit): self.speed = .10 self.partial_move = 0 self.position_before = position - self.fight = True + self.fight = False def calculate_rat_direction(self): x, y = self.position @@ -97,10 +98,8 @@ class Rat(Unit): def die(self, unit=None): if not unit: unit = self - self.game.play_sound("POISON.WAV") self.game.units.pop(unit.id) - def draw(self): direction = self.calculate_rat_direction() sex = self.sex if self.age > AGE_THRESHOLD else "BABY" @@ -119,7 +118,7 @@ class Rat(Unit): self.game.engine.draw_image(x_pos, y_pos, image, anchor="nw", tag="unit") self.bbox = (x_pos, y_pos, x_pos + image_size[0], y_pos + image_size[1]) - self.game.engine.draw_rectangle(self.bbox[0], self.bbox[1], self.bbox[2] - self.bbox[0], self.bbox[3] - self.bbox[1], "unit") + #self.game.engine.draw_rectangle(self.bbox[0], self.bbox[1], self.bbox[2] - self.bbox[0], self.bbox[3] - self.bbox[1], "unit") class Male(Rat): def __init__(self, map, position=(0,0), id=None):