diff options
Diffstat (limited to 'shooter_game.py')
-rw-r--r-- | shooter_game.py | 363 |
1 files changed, 363 insertions, 0 deletions
diff --git a/shooter_game.py b/shooter_game.py new file mode 100644 index 0000000..d50a66c --- /dev/null +++ b/shooter_game.py @@ -0,0 +1,363 @@ +from enum import Enum, auto +from os import path, remove +from random import random + +from boss_key import BossKey +from cheat_engine import Cheat, CheatEngine, DevModeCheat, InvincibilityCheat +from config import Config +from formation_spawner import FormationSpawner +from game import Game +from hud import GameHud +from leaderboard import Leaderboard +from menu import KeybindsMenu, Menu +from shooter import Shooter, ShooterAttributes +from textures import Textures + + +class GameState(Enum): + """Enum of possible game states""" + + MAIN_MENU = auto() + GAME = auto() + PAUSED = auto() + END_LEADERBOARD = auto() + LEADERBOARD = auto() + SETTINGS = auto() + + +class GameSave: + """Static class for saving and loading game""" + + @staticmethod + def save_game(game): + """Save game state to a file + + :param game: Game to save + """ + + phase = int.to_bytes(game.formation_spawner.phase, 2, "big") + hp = int.to_bytes(game.player.hp, 1, "big") + score = int.to_bytes(game.score, 8, "big") + + with open(Config.SAVE_FILE, "wb") as file: + file.write(phase) + file.write(hp) + file.write(score) + + if not game.menu.has_item("Continue"): + game.menu.add_item("Continue", game.restore_game, index=0) + + @staticmethod + def load_game(game): + """load game state from file + + :param game: Game to load + """ + with open(Config.SAVE_FILE, "rb") as file: + game.formation_spawner.phase = int.from_bytes(file.read(2), "big") + game.player.hp = int.from_bytes(file.read(1), "big") + game.score = int.from_bytes(file.read(8), "big") + + @staticmethod + def remove_save(game): + """Remove the game save file + + :param game: + """ + if path.exists(Config.SAVE_FILE): + remove(Config.SAVE_FILE) + if game.menu.has_item("Continue"): + game.menu.del_item("Continue") + + +class Player(Shooter): + """Controllable player object""" + + def __init__(self, game: Game): + """Initialise the player + + :param game: The game which this belongs to + :type game: Game + """ + attributes = ShooterAttributes( + cooldown=12, + velocity=-2, + hp=10 + ) + + super().__init__(game, "ship", attributes) + self.set_pos( + ((self.game.w - self.w) // 2, (self.game.h - self.h))) + + def tick(self): + """Update this object""" + super().tick() + if self.game.inputs.k_left: + self.move(-1, 0) + if self.game.inputs.k_right: + self.move(1, 0) + if self.game.inputs.k_up: + self.move(0, -1) + if self.game.inputs.k_down: + self.move(0, 1) + + # clamp the player to the screen + if self.x < 0: + self.set_pos((0, self.y)) + if self.y < 0: + self.set_pos((self.x, 0)) + if self.x + self.w > self.game.w: + self.set_pos((self.game.w - self.w, self.y)) + if self.y + self.h > self.game.h: + self.set_pos((self.x, self.game.h - self.h)) + + if self.game.inputs.k_action: + self.shoot() + + +class ShooterGame(Game): + """Game with menus and enemies to be shot at """ + + def __init__(self): + """Initialise the game""" + super().__init__() + + self.state = GameState.MAIN_MENU + self.death_time = -1 + self.paused_frame = 0 + + # load textures + Textures.load_textures(self.texture_factory) + self.effect_player.load_textures() + self.effect_player.create_stars() + + self.state = GameState.MAIN_MENU + + self.formation_spawner = FormationSpawner(self) + + self.player = Player(self) + + # make the game hud last to make sure its ontop + self.game_hud = GameHud(self) + + # create the leaderboard sprites + self.leaderboard = Leaderboard(self) + self.leaderboard.callback = self.show_menu + + # make the settings menu + self.settings_menu = KeybindsMenu(self, "Keybinds") + for name, value in vars(self.inputs.settings).items(): + label = self.settings_menu.get_label(name, value) + self.settings_menu.add_item( + label, + self.settings_menu.get_set_keybind(name) + ) + self.settings_menu.add_item("Return", self.show_menu) + + # make the main menu + self.menu = Menu(self, "Main Menu") + if path.exists(Config.SAVE_FILE): + self.menu.add_item("Continue", self.restore_game) + self.menu.add_item("New Game", self.start_game) + self.menu.add_item("Leaderboard", self.show_leaderboard) + self.menu.add_item("Settings", self.show_settings) + self.menu.show() + + # make the pause menu + self.pause_menu = Menu(self, "Game Paused") + self.pause_menu.add_item("Resume", self.resume_game) + + self.pause_menu.add_item("Save", lambda: ( + self.save_game(), + self.effect_player.splash_text("Game saved"), + self.resume_game()) + ) + + self.pause_menu.add_item("Exit", self.show_menu) + + # initialise cheats + self.cheat_engine = CheatEngine(self) + self.cheat_engine.add_cheat( + Cheat(self, list("test"), + (lambda: self.effect_player.splash_text("test ok")) + )) + + self.cheat_engine.add_cheat( + Cheat(self, [ + "Up", + "Up", + "Down", + "Down", + "Left", + "Right", + "Left", + "Right", + "b", + "a", + ], + (lambda: [self.formation_spawner.spawn_rectangle() + for _ in range(20)]) + )) + + self.cheat_engine.add_cheat(DevModeCheat(self, [ + "Left", + "Right", + "Left", + "Right", + "Escape", + "d", + "Up", + "t", + "b", + "b", + "a", + "b", + "s" + ])) + + self.cheat_engine.add_cheat(InvincibilityCheat(self, list("xyzzy"))) + + self.boss_key = BossKey(self, self.pause_game) + + def tick(self): + """Update the game state""" + if self.state != GameState.PAUSED: + super().tick() + + if random() > 0.9: + self.effect_player.create_star() + + if self.state == GameState.MAIN_MENU: + self.menu.tick() + elif self.state == GameState.SETTINGS: + self.settings_menu.tick() + elif self.state == GameState.GAME: + self.tick_game() + elif self.state == GameState.PAUSED: + self.alpha = self.paused_frame + self.pause_menu.tick() + elif self.state == GameState.END_LEADERBOARD: + self.leaderboard.tick() + elif self.state == GameState.LEADERBOARD: + self.leaderboard.tick() + + def tick_game(self): + """Update the game during game play""" + self.game_hud.tick() + self.formation_spawner.tick() + self.player.tick() + + if self.player.destroyed: + if self.death_time == -1: + self.death_time = self.alpha + self.effect_player.splash_text("GAME OVER", 100) + elif self.alpha - self.death_time > 100: + self.show_score() + + if self.inputs.k_pause: + self.pause_game() + + def pause_game(self): + """Set the game to paused state""" + if self.state == GameState.GAME: + self.state = GameState.PAUSED + + self.paused_frame = self.alpha + self.pause_menu.show() + + def resume_game(self): + """Resume the game from paused state""" + self.state = GameState.GAME + + self.pause_menu.hide() + + def start_game(self): + """Start a new game""" + self.state = GameState.GAME + + GameSave.remove_save(self) + + self.menu.hide() + self.pause_menu.hide() + self.formation_spawner.phase = -1 + self.clear_all() + + self.score = 0 + self.player = Player(self) + + self.formation_spawner.next_phase() + + self.player.show() + self.game_hud.show() + + self.death_time = -1 + + def show_leaderboard(self): + """Show the game's leaderboard""" + self.state = GameState.LEADERBOARD + + self.menu.hide() + self.pause_menu.hide() + self.leaderboard.editing = False + self.leaderboard.populate_entries() + self.leaderboard.start_animation() + + self.leaderboard.show() + + def show_score(self): + """Allow the user to enter their name into the leaderboard""" + self.state = GameState.END_LEADERBOARD + + self.clear_all() + self.game_hud.hide() + self.leaderboard.editing = True + self.leaderboard.populate_entries() + self.leaderboard.start_animation() + self.leaderboard.show() + GameSave.remove_save(self) + + def show_menu(self): + """Show the main menu""" + self.state = GameState.MAIN_MENU + + self.clear_all() + self.leaderboard.hide() + self.game_hud.hide() + self.player.hide() + self.pause_menu.hide() + self.settings_menu.hide() + + self.menu.show() + + def clear_all(self): + """Remove all the associated game objects""" + self.formation_spawner.clear_all() + self.player.destroy() + + def restore_game(self): + """Restore the game's state from file""" + self.state = GameState.GAME + + self.menu.hide() + self.pause_menu.hide() + self.clear_all() + + self.player = Player(self) + self.death_time = -1 + + GameSave.load_game(self) + + self.formation_spawner.start_phase() + self.game_hud.show() + self.player.show() + + def save_game(self): + """Save the game's state to a file""" + GameSave.save_game(self) + + def show_settings(self): + """Show the keybind setting menu""" + self.state = GameState.SETTINGS + + self.menu.hide() + self.settings_menu.show() |