|
|
|
"""
|
|
|
|
ICE WALKER GAME RUNNER
|
|
|
|
The main file that runs the game itself
|
|
|
|
"""
|
|
|
|
|
|
|
|
import objects
|
|
|
|
import json
|
|
|
|
import pyxel
|
|
|
|
from os.path import exists
|
|
|
|
|
|
|
|
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_json/{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/{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
|
|
|
|
"""
|
|
|
|
return exists(f"lvl/{lvl}.dat")
|
|
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
"""
|
|
|
|
print(f"Loading level {lvl}")
|
|
|
|
if can_load(lvl):
|
|
|
|
self.cache = load_file(lvl)
|
|
|
|
self.current_level = lvl
|
|
|
|
else:
|
|
|
|
print("level doesn't exist")
|
|
|
|
|
|
|
|
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:
|
|
|
|
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)
|