You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
164 lines
5.5 KiB
164 lines
5.5 KiB
1 month ago
|
"""
|
||
|
ICE WALKER GAME RUNNER
|
||
|
The main file that runs the game itself
|
||
|
"""
|
||
|
|
||
|
import objects
|
||
|
import json
|
||
|
import pyxel
|
||
|
import pathlib
|
||
|
|
||
|
CELL_SIZE = 8
|
||
|
GRID_X = 11
|
||
|
GRID_Y = 6
|
||
|
|
||
|
WIN_X = CELL_SIZE*GRID_X
|
||
|
WIN_Y = CELL_SIZE*GRID_Y
|
||
|
|
||
|
|
||
|
def load_file_json(lvl: int) -> list:
|
||
|
"""
|
||
|
:param lvl: (int) The id of the level to load
|
||
|
:return: (list) The loaded level from the file
|
||
|
Reads the corresponding level file and returns its loaded content
|
||
|
"""
|
||
|
file = open(f"lvl/{lvl}.json", "r", encoding="utf-8")
|
||
|
decoder = json.JSONDecoder()
|
||
|
return decoder.decode(file.read())
|
||
|
|
||
|
|
||
|
def load_file(lvl: int) -> list:
|
||
|
"""
|
||
|
:param lvl: (int) The id of the level to load
|
||
|
:return: (list) The loaded level from the file
|
||
|
Reads the corresponding level file and returns its loaded content
|
||
|
"""
|
||
|
file = open(f"lvl_bin/{lvl}.dat", "rb")
|
||
|
content = file.read()
|
||
|
res = []
|
||
|
mem = []
|
||
|
for v in content:
|
||
|
if int(v) == 255:
|
||
|
res.append(mem)
|
||
|
mem = []
|
||
|
else:
|
||
|
mem.append(int(v))
|
||
|
return res
|
||
|
|
||
|
|
||
|
def can_load(lvl: int) -> bool:
|
||
|
"""
|
||
|
:param lvl: (int) The id of the file to check for
|
||
|
:return: (bool) Returns if the level file exists and can be loaded
|
||
|
Tests if a level can be loaded, returns the result as a boolean
|
||
|
"""
|
||
|
path = pathlib.Path(f"lvl_bin/{lvl}.dat")
|
||
|
return path.is_file()
|
||
|
|
||
|
|
||
|
class LevelManager:
|
||
|
"""
|
||
|
This class is responsible for handling game data
|
||
|
"""
|
||
|
def __init__(self):
|
||
|
self.current_level = 0
|
||
|
self.cache = []
|
||
|
|
||
|
def load_level(self, lvl: int):
|
||
|
"""
|
||
|
:param lvl: (int) The id of the level to load
|
||
|
Loads the specified level and stores it into the cache
|
||
|
"""
|
||
|
if can_load(lvl):
|
||
|
self.cache = load_file(lvl)
|
||
|
self.current_level = lvl
|
||
|
|
||
|
def load_collection_from_cache(self, collection: objects.Collection) -> objects.Player | None:
|
||
|
"""
|
||
|
:param collection: (Collection) The collection to load the objects into
|
||
|
:return: The player object created by this method
|
||
|
Loads the level elements using the loaded cache, returns the new player instance
|
||
|
"""
|
||
|
new_player = None
|
||
|
for y in range(len(self.cache)):
|
||
|
for x in range(len(self.cache[y])):
|
||
|
element_id = self.cache[y][x]
|
||
|
elt = objects.get_type_from_id(element_id)
|
||
|
if elt:
|
||
|
new = elt()
|
||
|
new.position = objects.V2(x, y)
|
||
|
collection.add(new)
|
||
|
if element_id == 3:
|
||
|
new_player = new
|
||
|
return new_player
|
||
|
|
||
|
|
||
|
class Game:
|
||
|
"""
|
||
|
This class manages the game runtime
|
||
|
"""
|
||
|
def __init__(self):
|
||
|
self.level = None # An instance of the LevelManager class
|
||
|
self.main_collection = None # An instance of Collection, usually the main one
|
||
|
self.player = None # The current instance of the Player object
|
||
|
|
||
|
def _upd(self):
|
||
|
if self.player:
|
||
|
if pyxel.btnp(pyxel.KEY_R): # Reset the current level
|
||
|
self.main_collection.clear_all()
|
||
|
self.player = self.level.load_collection_from_cache(self.main_collection)
|
||
|
|
||
|
direction = objects.V2()
|
||
|
if pyxel.btnp(pyxel.KEY_Z): # Determine move direction
|
||
|
direction.Y = -1
|
||
|
elif pyxel.btnp(pyxel.KEY_S):
|
||
|
direction.Y = 1
|
||
|
elif pyxel.btnp(pyxel.KEY_Q):
|
||
|
direction.X = -1
|
||
|
elif pyxel.btnp(pyxel.KEY_D):
|
||
|
direction.X = 1
|
||
|
|
||
|
if not direction.is_zero(): # Handles the actual movement and collisions
|
||
|
new_pos, colliders = self.main_collection.ray_cast(self.player.position, direction, self.player)
|
||
|
self.player.position = new_pos
|
||
|
for v in colliders:
|
||
|
v.touched(self.player)
|
||
|
|
||
|
if self.main_collection.exits == 0: # Handles when the level was cleared
|
||
|
self.level.load_level(self.level.current_level + 1)
|
||
|
self.main_collection.clear_all()
|
||
|
new_player = self.level.load_collection_from_cache(self.main_collection)
|
||
|
self.player = new_player
|
||
|
|
||
|
def _display(self):
|
||
|
pyxel.cls(0)
|
||
|
for v in self.main_collection.get_objects(): # Render all active objects
|
||
|
if isinstance(v, objects.MapObject):
|
||
|
v.draw()
|
||
|
elif isinstance(v, objects.Collection) and v.visible: # Render embedded collections, if visible
|
||
|
for embed_v in v.get_objects():
|
||
|
embed_v.draw()
|
||
|
|
||
|
def run(self, lvl_m: LevelManager, main_c: objects.Collection):
|
||
|
"""
|
||
|
:param lvl_m: (LevelManager) The instance of LevelManager to assign with the game instance
|
||
|
:param main_c: (Collection) The primary collection containing the entirety of the game
|
||
|
Initializes and runs the game
|
||
|
"""
|
||
|
self.level = lvl_m
|
||
|
self.main_collection = main_c
|
||
|
pyxel.init(WIN_X, WIN_Y, title="ice_walker")
|
||
|
pyxel.run(self._upd, self._display)
|
||
|
|
||
|
|
||
|
if __name__ == "__main__":
|
||
|
main_collection = objects.Collection()
|
||
|
|
||
|
level = LevelManager() # Load the first level of the game
|
||
|
level.load_level(1)
|
||
|
player = level.load_collection_from_cache(main_collection)
|
||
|
|
||
|
game = Game() # Initializes the game engine
|
||
|
game.player = player
|
||
|
game.run(level, main_collection)
|