mirror of
https://git.davidovski.xyz/tkinter-space-game.git
synced 2024-08-15 00:43:41 +00:00
364 lines
10 KiB
Python
364 lines
10 KiB
Python
|
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()
|