Browse Source

mine implementation

master
Matteo Benedetto 4 months ago
parent
commit
c35fc9f1f7
  1. BIN
      assets/Rat/mine.png
  2. 0
      colorize_assets.py
  3. 1
      conf/keybinding_game.json
  4. 77
      convert_audio.py
  5. 3
      engine/controls.py
  6. 4
      engine/graphics.py
  7. 9
      engine/unit_manager.py
  8. 6
      rats.py
  9. 123
      resize_assets.py
  10. BIN
      sound/mine.wav
  11. BIN
      sound/mine_original.wav
  12. 1
      units/__init__.py
  13. 59
      units/mine.py

BIN
assets/Rat/mine.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 273 B

0
colorize_assets.py

1
conf/keybinding_game.json

@ -9,5 +9,6 @@
"scroll_left": ["Left", 10], "scroll_left": ["Left", 10],
"scroll_right": ["Right", 11], "scroll_right": ["Right", 11],
"spawn_bomb": ["Space", 1], "spawn_bomb": ["Space", 1],
"spawn_mine": ["Left Ctrl", "Control_R", 17],
"pause": ["P", 16] "pause": ["P", 16]
} }

77
convert_audio.py

@ -0,0 +1,77 @@
#!/usr/bin/env python3
"""
Audio conversion script to convert mine.wav to u8 22100 Hz format
"""
from pydub import AudioSegment
import os
def convert_mine_wav():
"""Convert mine.wav to u8 format at 22100 Hz"""
# Input and output paths
input_path = "sound/mine.wav"
output_path = "sound/mine_converted.wav"
if not os.path.exists(input_path):
print(f"Error: {input_path} not found!")
return
try:
# Load the audio file
print(f"Loading {input_path}...")
audio = AudioSegment.from_wav(input_path)
# Print current format info
print(f"Original format:")
print(f" Sample rate: {audio.frame_rate} Hz")
print(f" Channels: {audio.channels}")
print(f" Sample width: {audio.sample_width} bytes ({audio.sample_width * 8} bits)")
print(f" Duration: {len(audio)} ms")
# Convert to mono if stereo
if audio.channels > 1:
print("Converting to mono...")
audio = audio.set_channels(1)
# Convert to 22100 Hz sample rate
print("Converting sample rate to 22100 Hz...")
audio = audio.set_frame_rate(22100)
# Convert to 8-bit unsigned (u8)
print("Converting to 8-bit unsigned format...")
audio = audio.set_sample_width(1) # 1 byte = 8 bits
# Export the converted audio
print(f"Saving to {output_path}...")
audio.export(output_path, format="wav")
# Print new format info
converted_audio = AudioSegment.from_wav(output_path)
print(f"\nConverted format:")
print(f" Sample rate: {converted_audio.frame_rate} Hz")
print(f" Channels: {converted_audio.channels}")
print(f" Sample width: {converted_audio.sample_width} bytes ({converted_audio.sample_width * 8} bits)")
print(f" Duration: {len(converted_audio)} ms")
print(f"\nConversion complete! Output saved as: {output_path}")
# Optionally replace the original file
replace = input("\nReplace original mine.wav with converted version? (y/n): ").lower()
if replace == 'y':
import shutil
# Backup original
backup_path = "sound/mine_original.wav"
shutil.copy2(input_path, backup_path)
print(f"Original file backed up as: {backup_path}")
# Replace original
shutil.copy2(output_path, input_path)
os.remove(output_path)
print(f"Original file replaced with converted version.")
except Exception as e:
print(f"Error during conversion: {e}")
if __name__ == "__main__":
convert_mine_wav()

3
engine/controls.py

@ -27,8 +27,9 @@ class KeyBindings:
elif key in keybindings.get("scroll_right", []): elif key in keybindings.get("scroll_right", []):
self.start_scrolling("Right") self.start_scrolling("Right")
elif key in keybindings.get("spawn_bomb", []): elif key in keybindings.get("spawn_bomb", []):
self.render_engine.play_sound("PUTDOWN.WAV")
self.spawn_bomb(self.pointer) self.spawn_bomb(self.pointer)
elif key in keybindings.get("spawn_mine", []):
self.spawn_mine(self.pointer)
elif key in keybindings.get("pause", []): elif key in keybindings.get("pause", []):
self.game_status = "paused" if self.game_status == "game" else "game" self.game_status = "paused" if self.game_status == "game" else "game"
elif key in keybindings.get("start_game", []): elif key in keybindings.get("start_game", []):

4
engine/graphics.py

@ -2,6 +2,7 @@ import os
class Graphics(): class Graphics():
def load_assets(self): def load_assets(self):
print("Loading graphics assets...")
self.tunnel = self.render_engine.load_image("Rat/BMP_TUNNEL.png", surface=True) self.tunnel = self.render_engine.load_image("Rat/BMP_TUNNEL.png", surface=True)
self.grasses = [self.render_engine.load_image(f"Rat/BMP_1_GRASS_{i+1}.png", surface=True) for i in range(4)] self.grasses = [self.render_engine.load_image(f"Rat/BMP_1_GRASS_{i+1}.png", surface=True) for i in range(4)]
self.rat_assets = {} self.rat_assets = {}
@ -16,6 +17,7 @@ class Graphics():
for file in os.listdir("assets/Rat"): for file in os.listdir("assets/Rat"):
if file.endswith(".png"): if file.endswith(".png"):
self.assets[file[:-4]] = self.render_engine.load_image(f"Rat/{file}") self.assets[file[:-4]] = self.render_engine.load_image(f"Rat/{file}")
# ==================== RENDERING ==================== # ==================== RENDERING ====================
@ -65,7 +67,7 @@ class Graphics():
self.blood_stains[position] = new_blood_surface self.blood_stains[position] = new_blood_surface
# Regenerate background to include the updated blood stain # Regenerate background to include the updated blood stain
self.regenerate_background() self.background_texture = None
def scroll_cursor(self, x=0, y=0): def scroll_cursor(self, x=0, y=0):
if self.pointer[0] + x > self.map.width or self.pointer[1] + y > self.map.height: if self.pointer[0] + x > self.map.width or self.pointer[1] + y > self.map.height:

9
engine/unit_manager.py

@ -1,6 +1,6 @@
import random import random
import uuid import uuid
from units import rat, bomb from units import rat, bomb, mine
class UnitManager: class UnitManager:
def count_rats(self): def count_rats(self):
@ -17,7 +17,14 @@ class UnitManager:
self.spawn_unit(rat_class, position) self.spawn_unit(rat_class, position)
def spawn_bomb(self, position): def spawn_bomb(self, position):
self.render_engine.play_sound("PUTDOWN.WAV")
self.spawn_unit(bomb.Timer, position) self.spawn_unit(bomb.Timer, position)
def spawn_mine(self, position):
if self.map.is_wall(position[0], position[1]):
return
self.render_engine.play_sound("PUTDOWN.WAV")
self.spawn_unit(mine.Mine, position)
def spawn_unit(self, unit, position, **kwargs): def spawn_unit(self, unit, position, **kwargs):
id = uuid.uuid4() id = uuid.uuid4()

6
rats.py

@ -96,14 +96,14 @@ class MiceMaze(
return True return True
count_rats = self.count_rats()
if self.count_rats() > 200: if count_rats > 200:
self.render_engine.stop_sound() self.render_engine.stop_sound()
self.render_engine.play_sound("WEWIN.WAV") self.render_engine.play_sound("WEWIN.WAV")
self.game_end = (True, False) self.game_end = (True, False)
self.game_status = "paused" self.game_status = "paused"
return True return True
if not len(self.units): if not count_rats:
self.render_engine.stop_sound() self.render_engine.stop_sound()
self.render_engine.play_sound("VICTORY.WAV") self.render_engine.play_sound("VICTORY.WAV")
self.render_engine.play_sound("WELLDONE.WAV", tag="effects") self.render_engine.play_sound("WELLDONE.WAV", tag="effects")

123
resize_assets.py

@ -0,0 +1,123 @@
#!/usr/bin/env python3
"""
Script to resize PNG asset files to 18x18 pixels and center them on a 20x20 canvas.
Saves the result back to the same file.
"""
import os
import glob
from PIL import Image, ImageOps
import argparse
def resize_and_center_image(image_path, target_size=(18, 18), canvas_size=(20, 20)):
"""
Resize an image to target_size and center it on a canvas of canvas_size.
Args:
image_path (str): Path to the image file
target_size (tuple): Size to resize the image to (width, height)
canvas_size (tuple): Size of the final canvas (width, height)
"""
try:
# Open the image
with Image.open(image_path) as img:
# Convert to RGBA to handle transparency
img = img.convert("RGBA")
# Resize the image to target size using high-quality resampling
resized_img = img.resize(target_size, Image.Resampling.LANCZOS)
# Create a new transparent canvas
canvas = Image.new("RGBA", canvas_size, (0, 0, 0, 0))
# Calculate position to center the resized image
x_offset = (canvas_size[0] - target_size[0]) // 2
y_offset = (canvas_size[1] - target_size[1]) // 2
# Paste the resized image onto the canvas
canvas.paste(resized_img, (x_offset, y_offset), resized_img)
# Save back to the same file
canvas.save(image_path, "PNG", optimize=True)
print(f"✓ Processed: {os.path.basename(image_path)}")
except Exception as e:
print(f"✗ Error processing {image_path}: {str(e)}")
def process_directory(directory_path, file_pattern="*.png"):
"""
Process all PNG files in a directory.
Args:
directory_path (str): Path to the directory containing PNG files
file_pattern (str): Pattern to match files (default: "*.png")
"""
if not os.path.exists(directory_path):
print(f"Error: Directory '{directory_path}' does not exist.")
return
# Find all PNG files matching the pattern
search_pattern = os.path.join(directory_path, file_pattern)
png_files = glob.glob(search_pattern)
if not png_files:
print(f"No PNG files found in '{directory_path}' matching pattern '{file_pattern}'")
return
print(f"Found {len(png_files)} PNG files to process...")
# Process each file
for png_file in png_files:
resize_and_center_image(png_file)
print(f"\nCompleted processing {len(png_files)} files.")
def process_single_file(file_path):
"""
Process a single PNG file.
Args:
file_path (str): Path to the PNG file
"""
if not os.path.exists(file_path):
print(f"Error: File '{file_path}' does not exist.")
return
if not file_path.lower().endswith('.png'):
print(f"Error: File '{file_path}' is not a PNG file.")
return
print(f"Processing single file: {os.path.basename(file_path)}")
resize_and_center_image(file_path)
print("Processing complete.")
def main():
parser = argparse.ArgumentParser(description="Resize PNG assets to 18x18px and center on 20x20px canvas")
parser.add_argument("path", help="Path to PNG file or directory containing PNG files")
parser.add_argument("--pattern", default="*.png", help="File pattern to match (default: *.png)")
args = parser.parse_args()
if os.path.isfile(args.path):
process_single_file(args.path)
elif os.path.isdir(args.path):
process_directory(args.path, args.pattern)
else:
print(f"Error: '{args.path}' is not a valid file or directory.")
if __name__ == "__main__":
# If run without arguments, process the assets/Rat directory by default
import sys
if len(sys.argv) == 1:
# Default to processing the assets/Rat directory
script_dir = os.path.dirname(os.path.abspath(__file__))
assets_dir = os.path.join(script_dir, "assets", "Rat")
if os.path.exists(assets_dir):
print("No arguments provided. Processing assets/Rat directory by default...")
process_directory(assets_dir)
else:
print("assets/Rat directory not found. Please provide a path as argument.")
print("Usage: python resize_assets.py <path_to_file_or_directory>")
else:
main()

BIN
sound/mine.wav

Binary file not shown.

BIN
sound/mine_original.wav

Binary file not shown.

1
units/__init__.py

@ -6,3 +6,4 @@ from .unit import Unit
from .rat import Rat, Male, Female from .rat import Rat, Male, Female
from .bomb import Bomb, Timer, Explosion from .bomb import Bomb, Timer, Explosion
from .points import Point from .points import Point
from .mine import Mine

59
units/mine.py

@ -0,0 +1,59 @@
from .unit import Unit
from .bomb import Explosion
class Mine(Unit):
def __init__(self, game, position=(0,0), id=None):
super().__init__(game, position, id)
self.speed = 1.0 # Mine doesn't move but needs speed for consistency
self.armed = True # Mine is active and ready to explode
def move(self):
"""Mines don't move, but we need to check for collision with rats each frame."""
pass
def collisions(self):
"""Check if a rat steps on the mine (has position_before on mine's position)."""
if not self.armed:
return
# Check for rats that have position_before on this mine's position
for rat_unit in self.game.unit_positions_before.get(self.position, []):
if hasattr(rat_unit, 'sex'): # Check if it's a rat (rats have sex attribute)
# Mine explodes and kills the rat
self.explode(rat_unit)
break
def explode(self, victim_rat):
"""Mine explodes, killing the rat and destroying itself."""
if not self.armed:
return
print("MINE EXPLOSION!")
self.game.render_engine.play_sound("BOMB.WAV")
# Kill the rat that stepped on the mine
if victim_rat.id in self.game.units:
victim_rat.die(score=5)
# Remove the mine from the game
self.die()
def draw(self):
"""Draw the mine using the mine asset."""
if not self.armed:
return
# Use mine asset
image = self.game.assets["mine"]
image_size = self.game.render_engine.get_image_size(image)
# Center the mine in the cell
x_pos = self.position[0] * self.game.cell_size + (self.game.cell_size - image_size[0]) // 2
y_pos = self.position[1] * self.game.cell_size + (self.game.cell_size - image_size[1]) // 2
self.game.render_engine.draw_image(x_pos, y_pos, image, anchor="nw", tag="unit")
def die(self, score=None):
"""Remove mine from game and disarm it."""
self.armed = False
Explosion(self.game, self.position).draw()
super().die(score)
Loading…
Cancel
Save