summaryrefslogtreecommitdiff
path: root/shooter_game.py
diff options
context:
space:
mode:
authordavidovski <david@sendula.com>2023-01-05 11:35:28 +0000
committerdavidovski <david@sendula.com>2023-01-05 11:35:28 +0000
commitb99dbb396c313f4b3130b566c0df42c10eec6084 (patch)
treeffd2bbebe47f66fd1c21000b371ebc897ef84e34 /shooter_game.py
Initial CommitHEADmain
Diffstat (limited to 'shooter_game.py')
-rw-r--r--shooter_game.py363
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()