diff --git a/conf/keybinding_game.json b/conf/keybinding_game.json new file mode 100644 index 0000000..e9a8d30 --- /dev/null +++ b/conf/keybinding_game.json @@ -0,0 +1,13 @@ +{ + "quit": ["Q", 12], + "new_rat": ["Return", 13], + "kill_rat": ["D"], + "toggle_audio": ["M"], + "toggle_full_screen": ["F"], + "scroll_up": ["Up", 8], + "scroll_down": ["Down", 9], + "scroll_left": ["Left", 10], + "scroll_right": ["Right", 11], + "spawn_bomb": ["Space", 1], + "pause": ["P", 16] +} \ No newline at end of file diff --git a/cover.jpg b/cover.jpg new file mode 100644 index 0000000..6bfe859 Binary files /dev/null and b/cover.jpg differ diff --git a/engine/controls.py b/engine/controls.py index 103532a..a28666d 100644 --- a/engine/controls.py +++ b/engine/controls.py @@ -5,37 +5,34 @@ import random class KeyBindings: def key_pressed(self, key, coords=None): - - #print(key) - if key == "Q" or key == 12: + if key in self.keybindings["quit"]: self.engine.close() - elif key == "Return" or key == 13: + elif key in self.keybindings["new_rat"]: self.new_rat() - elif key == "D": + elif key in self.keybindings["kill_rat"]: if self.units: self.units[random.choice(list(self.units.keys()))].die(score=5) - elif key == "M": + elif key in self.keybindings["toggle_audio"]: self.audio = not self.audio - elif key == "F": + elif key in self.keybindings["toggle_full_screen"]: self.full_screen = not self.full_screen self.engine.full_screen(self.full_screen) - elif key == "Up" or key == 8: + elif key in self.keybindings["scroll_up"]: self.start_scrolling("Up") - elif key == "Down" or key == 9: + elif key in self.keybindings["scroll_down"]: self.start_scrolling("Down") - elif key == "Left" or key == 10: + elif key in self.keybindings["scroll_left"]: self.start_scrolling("Left") - elif key == "Right" or key == 11: + elif key in self.keybindings["scroll_right"]: self.start_scrolling("Right") - elif key == "Space" or key == 1: - self.play_sound("PUTDOWN.WAV") + elif key in self.keybindings["spawn_bomb"]: + self.play_sound("PUTDOWN.WAV", tag="effects") self.spawn_bomb(self.pointer) - elif key == "P" or key == 16: + elif key in self.keybindings["pause"]: self.pause = not self.pause - # elif key == "mouse": - # adjusted_coords = (coords[0]//self.cell_size*2, coords[1]//self.cell_size*2) - # self.pointer = adjusted_coords - # self.scroll_cursor() + + def quit_game(self): + self.engine.close() def key_released(self, key): if key in ["Up", "Down", "Left", "Right", 8, 9, 10, 11]: self.stop_scrolling() diff --git a/engine/sdl2.py b/engine/sdl2.py index 656b434..a37677c 100644 --- a/engine/sdl2.py +++ b/engine/sdl2.py @@ -7,7 +7,7 @@ from ctypes import * from PIL import Image from sdl2 import SDL_AudioSpec -import random + class GameWindow: def __init__(self, width, height, cell_size, title="Default", key_callback=None): @@ -23,6 +23,7 @@ class GameWindow: self.h_offset = self.h_start_offset self.max_w_offset = self.target_size[0] - self.width self.max_h_offset = self.target_size[1] - self.height + self.scale = self.target_size[1] // self.cell_size print(f"Screen size: {self.width}x{self.height}") sdl2.ext.init(joystick=True) sdl2.SDL_Init(sdl2.SDL_INIT_AUDIO) @@ -37,11 +38,12 @@ class GameWindow: self.running = True self.key_down, self.key_up, self.axis_scroll = key_callback self.performance = 0 - self.audio_devs = [] - #for i in range(5): - # self.audio_devs.append((True, sdl2.SDL_OpenAudioDevice(None, 0, SDL_AudioSpec(freq=22050, aformat=sdl2.AUDIO_U8, channels=1, samples=2048), None, 0))) - - self.audio_dev = sdl2.SDL_OpenAudioDevice(None, 0, SDL_AudioSpec(freq=22050, aformat=sdl2.AUDIO_U8, channels=1, samples=2048), None, 0) + self.audio_devs = {} + self.button_cursor = [0, 0] + self.buttons = {} + self.audio_devs["base"] = sdl2.SDL_OpenAudioDevice(None, 0, SDL_AudioSpec(freq=22050, aformat=sdl2.AUDIO_U8, channels=1, samples=2048), None, 0) + self.audio_devs["effects"] = sdl2.SDL_OpenAudioDevice(None, 0, SDL_AudioSpec(freq=22050, aformat=sdl2.AUDIO_U8, channels=1, samples=2048), None, 0) + self.audio_devs["music"] = sdl2.SDL_OpenAudioDevice(None, 0, SDL_AudioSpec(freq=22050, aformat=sdl2.AUDIO_U8, channels=1, samples=2048), None, 0) def create_texture(self, tiles: list): bg_surface = sdl2.SDL_CreateRGBSurface(0, self.width, self.height, 32, 0, 0, 0, 0) for tile in tiles: @@ -97,8 +99,8 @@ class GameWindow: sprite.position = (x+self.w_offset, y+self.h_offset) self.renderer.copy(sprite, dstrect=sprite.position) + def draw_rectangle(self, x, y, width, height, tag, outline="red", filling=None): - x, y = x + self.w_offset, y + self.h_offset if filling: self.renderer.fill((x, y, width, height), sdl2.ext.Color(*filling)) else: @@ -113,8 +115,8 @@ class GameWindow: def delete_tag(self, tag): pass - def win_screen(self, text, **kwargs): - self.draw_rectangle(50 - self.w_offset, 50 - self.h_offset, + def dialog(self, text, **kwargs): + self.draw_rectangle(50, 50, self.target_size[0] - 100, self.target_size[1] - 100, "win", filling=(255, 255, 255)) self.draw_text(text, self.fonts[self.target_size[1]//20], "center", sdl2.ext.Color(0, 0, 0)) if image := kwargs.get("image"): @@ -133,11 +135,7 @@ class GameWindow: sprite_score.position = (self.target_size[0] // 2 - 50-sprite_score.size[0] // 4, self.target_size[1] // 2 + 60 + 20 * (i + 1)) self.renderer.copy(sprite_score, dstrect=sprite_score.position) - def pause_screen(self, text): - self.draw_rectangle(50 - self.w_offset, 50 - self.h_offset, - self.target_size[0] - 100, self.target_size[1] - 100, "pause", filling=(255, 255, 255)) - self.draw_text(text, self.fonts[self.target_size[1]//20], "center", sdl2.ext.Color(0, 0, 0)) - + def get_image_size(self, image): return image.size @@ -159,6 +157,9 @@ class GameWindow: def is_in_visible_area(self, x, y): return -self.w_offset -self.cell_size <= x <= self.width - self.w_offset and -self.h_offset -self.cell_size <= y <= self.height - self.h_offset + def get_perf_counter(self): + return sdl2.SDL_GetPerformanceCounter() + def mainloop(self, **kwargs): while self.running: performance_start = sdl2.SDL_GetPerformanceCounter() @@ -228,7 +229,7 @@ class GameWindow: self.w_offset = x self.h_offset = y - def play_sound(self, sound_file): + def play_sound(self, sound_file, tag="base"): print(sound_file) sound_file = os.path.join("sound", sound_file) rw = sdl2.SDL_RWFromFile(byteify(sound_file, "utf-8"), b"rb") @@ -237,12 +238,12 @@ class GameWindow: _buf = POINTER(sdl2.Uint8)() _length = sdl2.Uint32() - spec = SDL_AudioSpec(freq=22050, aformat=sdl2.AUDIO_U8, channels=1, samples=2048) + spec = SDL_AudioSpec(freq=22050, aformat=sdl2.AUDIO_U8, channels=1, samples=2048) if sdl2.SDL_LoadWAV_RW(rw, 1, byref(spec), byref(_buf), byref(_length)) == None: raise RuntimeError("Failed to load WAV") - devid = self.audio_dev - sdl2.SDL_ClearQueuedAudio(devid) + devid = self.audio_devs[tag] + # Clear any queued audio sdl2.SDL_ClearQueuedAudio(devid) # Start playing audio @@ -255,4 +256,16 @@ class GameWindow: sdl2.SDL_PauseAudioDevice(dev[1], 1) sdl2.SDL_ClearQueuedAudio(dev[1]) - + def start_dialog(self, **kwargs): + self.dialog("Welcome to the Mice!", **kwargs) + center = self.get_view_center() + self.draw_button(center[0], center[1] + 10 * self.scale, "Start", 120, 50, (0, 0)) + + def draw_button(self, x, y, text, width, height, coords): + if self.button_cursor[0] == coords[0] and self.button_cursor[1] == coords[1]: + color = (0, 0, 255) + self.draw_rectangle(x, y, width, height, "button", outline8u=color) + self.draw_text(text, self.fonts[20], (x + 10, y + 10), (0,0,0)) + + def get_view_center(self): + return self.w_offset + self.width // 2, self.h_offset + self.height // 2 \ No newline at end of file diff --git a/engine/statuses.py b/engine/statuses.py new file mode 100644 index 0000000..e69de29 diff --git a/rats.py b/rats.py index e1e0fdb..5115383 100644 --- a/rats.py +++ b/rats.py @@ -7,6 +7,7 @@ from engine import maze, sdl2 as engine, controls import os import datetime import subprocess +import json class MiceMaze(controls.KeyBindings): def __init__(self, maze_file): @@ -27,13 +28,19 @@ class MiceMaze(controls.KeyBindings): self.scrolling_direction = None self.pause = False self.game_end = (False, None) + self.dialog = "start_menu" self.scrolling = False self.sounds = {} - for _ in range(5): - self.new_rat() + self.start_game() self.background_texture = None + keybindings_file, = "keybinding_game.json", + with open(os.path.join("conf", keybindings_file)) as f: + self.keybindings = json.load(f) - + def start_game(self): + for _ in range(5): + self.new_rat() + def count_rats(self): count = 0 for unit in self.units.values(): @@ -78,9 +85,9 @@ class MiceMaze(controls.KeyBindings): def game_over(self): if self.game_end[0]: if not self.game_end[1]: - self.engine.win_screen("Game Over: Mice are too many!", image=self.assets["BMP_WEWIN"]) + self.engine.dialog("Game Over: Mice are too many!", image=self.assets["BMP_WEWIN"]) else: - self.engine.win_screen(f"You Win! Points: {self.points}", image=self.assets["BMP_WEWIN"], scores=self.read_score()) + self.engine.dialog(f"You Win! Points: {self.points}", image=self.assets["BMP_WEWIN"], scores=self.read_score()) return True @@ -106,7 +113,9 @@ class MiceMaze(controls.KeyBindings): def update_maze(self): if self.pause: - self.engine.pause_screen("Paused") + if self.dialog != "start_menu": + self.engine.dialog("Pause") + self.engine.start_dialog(image=self.assets["BMP_WEWIN"]) return if self.game_over(): return @@ -128,7 +137,6 @@ class MiceMaze(controls.KeyBindings): def run(self): - self.draw_maze() self.engine.mainloop(update=self.update_maze, bg_update=self.draw_maze) def scroll_cursor(self, x=0, y=0): diff --git a/sound/BOMB.WAV b/sound/BOMB.WAV old mode 100755 new mode 100644 index 0d79db2..d132896 Binary files a/sound/BOMB.WAV and b/sound/BOMB.WAV differ diff --git a/sound/PUTDOWN.WAV b/sound/PUTDOWN.WAV old mode 100755 new mode 100644 index 8120098..a0bf608 Binary files a/sound/PUTDOWN.WAV and b/sound/PUTDOWN.WAV differ diff --git a/sound/converted/Death.wav b/sound/converted/Death.wav new file mode 100644 index 0000000..68ba53d Binary files /dev/null and b/sound/converted/Death.wav differ diff --git a/sound/converted_BOMB.wav b/sound/converted_BOMB.wav new file mode 100644 index 0000000..56b6b56 Binary files /dev/null and b/sound/converted_BOMB.wav differ diff --git a/units/rat.py b/units/rat.py index a797f38..baa8b29 100644 --- a/units/rat.py +++ b/units/rat.py @@ -103,7 +103,9 @@ class Rat(Unit): self.game.spawn_unit(Point, unit.position_before, value=score) def draw(self): + start_perf = self.game.engine.get_perf_counter() direction = self.calculate_rat_direction() + sex = self.sex if self.age > AGE_THRESHOLD else "BABY" image = self.game.rat_assets[sex][direction] image_size = self.game.engine.get_image_size(image) @@ -117,7 +119,6 @@ class Rat(Unit): 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") 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") diff --git a/wgdzh b/wgdzh index 58d978d..e54d622 100644 --- a/wgdzh +++ b/wgdzh @@ -1,15 +1,12 @@ - ./dRally.sh - Death Rally - Death Rally is a strategic top-down racing game where players start with a modest budget and car, competing across 19 tracks in races against increasingly challenging opponents to earn money for upgrades. With choices of six car models and enhancements in engi> - -In addition to race earnings, players can score bonuses for achievements like wiping out all competition, completing races unscathed, or fulfilling sponsor missions. Bonuses vary based on the chosen car, with the ultimate goal being to top the championship table and defeat the Adve> - ./drally/cover.png - 19960101T000000 - Remedy Entertainment Ltd. - Apogee Software - Simulation-Race 3rd Pers. view-Race, Driving - 1-4 + /roms/tools/mice/rats + Mice! + Mice! is a strategic game where players must kill rats with bombs before they reproduce and become too numerous. The game is a clone of the classic game Rats! for Windows 95. ./drally/cover.png + 20241225T000000 + Matteo Benedetto + Check Mate Corp. + Casual + 1 1 20240915T212155 \ No newline at end of file