Browse Source

Aggiungi la classe Point e implementa la gestione delle esplosioni nel gioco

master
Matteo Benedetto 1 year ago
parent
commit
acbd943b27
  1. 0
      assets/Rat/BMP_BOMB0.png
  2. 3
      engine/maze.py
  3. 35
      engine/sdl2.py
  4. 2
      maze.json
  5. 17
      maze.py
  6. 59
      rats.py
  7. BIN
      units/__pycache__/rat.cpython-313.pyc
  8. 105
      units/bomb.py
  9. 48
      units/points.py
  10. 7
      units/rat.py

0
assets/Rat/BMP_BOMB.png → assets/Rat/BMP_BOMB0.png

Before

Width:  |  Height:  |  Size: 380 B

After

Width:  |  Height:  |  Size: 380 B

3
engine/maze.py

@ -7,3 +7,6 @@ class Map:
self.matrix = json.load(file) self.matrix = json.load(file)
self.height = len(self.matrix) self.height = len(self.matrix)
self.width = len(self.matrix[0]) self.width = len(self.matrix[0])
def is_wall(self, x, y):
return self.matrix[y][x]

35
engine/sdl2.py

@ -12,8 +12,10 @@ class GameWindow:
self.width = width * cell_size self.width = width * cell_size
self.height = height * cell_size self.height = height * cell_size
self.target_size = (640, 480) self.target_size = (640, 480)
self.w_offset = (self.target_size[0] - self.width) // 2 self.w_start_offset = (self.target_size[0] - self.width) // 2
self.h_offset = (self.target_size[1] - self.height) // 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}") print(f"Screen size: {self.width}x{self.height}")
self.delay = 30 self.delay = 30
sdl2.ext.init() sdl2.ext.init()
@ -65,6 +67,12 @@ class GameWindow:
x, y = x + self.w_offset, y + self.h_offset 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)) 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): def delete_tag(self, tag):
pass pass
@ -78,6 +86,9 @@ class GameWindow:
def new_cycle(self, delay, callback): def new_cycle(self, delay, callback):
pass pass
def full_screen(self,flag):
sdl2.SDL_SetWindowFullscreen(self.window.window, flag)
def mainloop(self, **kwargs): def mainloop(self, **kwargs):
while self.running: while self.running:
self.renderer.clear() self.renderer.clear()
@ -91,6 +102,26 @@ class GameWindow:
elif event.type == sdl2.SDL_KEYDOWN and self.key_callback: elif event.type == sdl2.SDL_KEYDOWN and self.key_callback:
key = sdl2.SDL_GetKeyName(event.key.keysym.sym).decode('utf-8') key = sdl2.SDL_GetKeyName(event.key.keysym.sym).decode('utf-8')
self.key_callback(key) 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 # Disegna qui gli sprite
self.renderer.present() self.renderer.present()
sdl2.SDL_Delay(self.delay) 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

2
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]] [[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]]

17
maze.py

@ -69,15 +69,19 @@ class MazeGenerator:
if self.stack: # Continue updating only if there are cells left to visit if self.stack: # Continue updating only if there are cells left to visit
self.window.after(10, self.update_maze) self.window.after(10, self.update_maze)
else: else:
# After the maze is generated, remove some walls randomly self.add_random_passages()
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.draw_maze() self.draw_maze()
with open('maze.json', 'w') as json_file: 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): def draw_maze(self):
self.canvas.delete("all") self.canvas.delete("all")
for i in range(self.height): for i in range(self.height):
@ -93,11 +97,12 @@ class MazeGenerator:
color = "blue" # Color the current position blue color = "blue" # Color the current position blue
self.canvas.create_rectangle(j*10, i*10, (j+1)*10, (i+1)*10, fill=color) self.canvas.create_rectangle(j*10, i*10, (j+1)*10, (i+1)*10, fill=color)
def run(self): def run(self):
self.update_maze() self.update_maze()
self.window.mainloop() self.window.mainloop()
# Crea e avvia il generatore di labirinti # Crea e avvia il generatore di labirinti
generator = MazeGenerator(7, 5) generator = MazeGenerator(15, 8)
generator.run() generator.run()

59
rats.py

@ -2,17 +2,21 @@
import cProfile import cProfile
import pstats import pstats
import random import random
from units import rat from units import rat, bomb
import uuid import uuid
import subprocess import subprocess
from engine import maze, sdl2 as engine from engine import maze, sdl2 as engine
import os
class MiceMaze: class MiceMaze:
def __init__(self, maze_file): def __init__(self, maze_file):
self.map = maze.Map(maze_file) self.map = maze.Map(maze_file)
self.pointer = (2,2)
self.audio = True self.audio = True
self.cell_size = 40 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.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.graphics_load()
self.units = {} self.units = {}
self.unit_positions = {} self.unit_positions = {}
@ -27,6 +31,14 @@ class MiceMaze:
rat_class = rat.Male if random.random() < 0.5 else rat.Female rat_class = rat.Male if random.random() < 0.5 else rat.Female
self.units[id] = rat_class(self, position, id) 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): def choose_start(self):
if not hasattr(self, '_valid_positions'): if not hasattr(self, '_valid_positions'):
self._valid_positions = [ self._valid_positions = [
@ -45,6 +57,8 @@ class MiceMaze:
def update_maze(self): def update_maze(self):
self.engine.delete_tag("unit") 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.clear()
self.unit_positions_before.clear() self.unit_positions_before.clear()
for unit in self.units.values(): for unit in self.units.values():
@ -54,7 +68,7 @@ class MiceMaze:
unit.move() unit.move()
unit.collisions() unit.collisions()
unit.draw() 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) self.engine.new_cycle(50, self.update_maze)
@ -65,7 +79,7 @@ class MiceMaze:
def key_pressed(self, key): def key_pressed(self, key):
print(key) print(key)
if key == "Q": if key == "Q":
self.engine.window.close() self.engine.close()
elif key == "Return": elif key == "Return":
self.new_rat() self.new_rat()
elif key == "D": elif key == "D":
@ -73,10 +87,30 @@ class MiceMaze:
self.units[random.choice(list(self.units.keys()))].die() self.units[random.choice(list(self.units.keys()))].die()
elif key == "M": elif key == "M":
self.audio = not self.audio self.audio = not self.audio
elif key == "S": elif key == "F":
profiler.disable() self.full_screen = not self.full_screen
stats = pstats.Stats(profiler).sort_stats('cumtime') self.engine.full_screen(self.full_screen)
stats.print_stats() 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): def play_sound(self, sound_file):
if self.audio: if self.audio:
@ -86,10 +120,21 @@ class MiceMaze:
self.tunnel = self.engine.load_image("Rat/BMP_TUNNEL.png") 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.grasses = [self.engine.load_image(f"Rat/BMP_1_GRASS_{i+1}.png") for i in range(4)]
self.rat_assets = {} self.rat_assets = {}
self.bomb_assets = {}
for sex in ["MALE", "FEMALE", "BABY"]: for sex in ["MALE", "FEMALE", "BABY"]:
self.rat_assets[sex] = {} self.rat_assets[sex] = {}
for direction in ["UP", "DOWN", "LEFT", "RIGHT"]: 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)) 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__": if __name__ == "__main__":
profiler = cProfile.Profile() profiler = cProfile.Profile()
profiler.enable() profiler.enable()

BIN
units/__pycache__/rat.cpython-313.pyc

Binary file not shown.

105
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")

48
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")

7
units/rat.py

@ -1,4 +1,5 @@
from .unit import Unit from .unit import Unit
import random import random
import uuid import uuid
@ -21,7 +22,7 @@ class Rat(Unit):
self.speed = .10 self.speed = .10
self.partial_move = 0 self.partial_move = 0
self.position_before = position self.position_before = position
self.fight = True self.fight = False
def calculate_rat_direction(self): def calculate_rat_direction(self):
x, y = self.position x, y = self.position
@ -97,10 +98,8 @@ class Rat(Unit):
def die(self, unit=None): def die(self, unit=None):
if not unit: if not unit:
unit = self unit = self
self.game.play_sound("POISON.WAV")
self.game.units.pop(unit.id) self.game.units.pop(unit.id)
def draw(self): def draw(self):
direction = self.calculate_rat_direction() direction = self.calculate_rat_direction()
sex = self.sex if self.age > AGE_THRESHOLD else "BABY" 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.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.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): class Male(Rat):
def __init__(self, map, position=(0,0), id=None): def __init__(self, map, position=(0,0), id=None):

Loading…
Cancel
Save