diff --git a/triple-dungeon/.gitignore b/triple-dungeon/.gitignore new file mode 100644 index 0000000..d19812c --- /dev/null +++ b/triple-dungeon/.gitignore @@ -0,0 +1 @@ +.idea/** \ No newline at end of file diff --git a/triple-dungeon/config.py b/triple-dungeon/config.py new file mode 100644 index 0000000..933abaf --- /dev/null +++ b/triple-dungeon/config.py @@ -0,0 +1,30 @@ +""" +config.py +Holds all constants used for setting up the game. +May later hold functions for loading/saving configuration files. +""" + + +class Config(object): + """ + A simple class dedicated to loading, storing and organizing constants. + """ + + # Constants + SCREEN_WIDTH = 1000 + SCREEN_HEIGHT = 650 + SCREEN_TITLE = "Triple Dungeon" + + # Constants used to scale our sprites from their original size + CHARACTER_SCALING = 1 + TILE_SCALING = 2 + + # Movement speed of player, in pixels per frame + PLAYER_MOVEMENT_SPEED = 5 + + # How many pixels to keep as a minimum margin between the character + # and the edge of the screen. + LEFT_VIEWPORT_MARGIN = 250 + RIGHT_VIEWPORT_MARGIN = 250 + BOTTOM_VIEWPORT_MARGIN = 50 + TOP_VIEWPORT_MARGIN = 100 diff --git a/triple-dungeon/main.py b/triple-dungeon/main.py index f85d015..0b13d56 100644 --- a/triple-dungeon/main.py +++ b/triple-dungeon/main.py @@ -1,37 +1,22 @@ """ -Platformer Game +main.py +The main class used to load the game. +Holds the main game window, as well as manages basic functions for organizing the game. """ + import arcade -# Constants -SCREEN_WIDTH = 1000 -SCREEN_HEIGHT = 650 -SCREEN_TITLE = "Triple Dungeon!!!" - -# Constants used to scale our sprites from their original size -CHARACTER_SCALING = 1 -TILE_SCALING = 2 - -# Movement speed of player, in pixels per frame -PLAYER_MOVEMENT_SPEED = 5 +from config import Config -# How many pixels to keep as a minimum margin between the character -# and the edge of the screen. -LEFT_VIEWPORT_MARGIN = 250 -RIGHT_VIEWPORT_MARGIN = 250 -BOTTOM_VIEWPORT_MARGIN = 50 -TOP_VIEWPORT_MARGIN = 100 - - -class MyGame(arcade.Window): +class Game(arcade.Window): """ Main application class. """ def __init__(self): # Call the parent class and set up the window - super().__init__(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_TITLE) + super().__init__(Config.SCREEN_WIDTH, Config.SCREEN_HEIGHT, Config.SCREEN_TITLE) # These are 'lists' that keep track of our sprites. Each sprite should # go into a list. @@ -45,7 +30,7 @@ class MyGame(arcade.Window): # Our physics engine self.physics_engine = None - #list to keep track of keypresses + # list to keep track of keypresses self.prev_keypress = [] # Used to keep track of our scrolling @@ -63,17 +48,17 @@ class MyGame(arcade.Window): # Set up the player, specifically placing it at these coordinates. image_source = "images/monsters/skeleton.png" - self.player_sprite = arcade.Sprite(image_source, CHARACTER_SCALING) - self.player_sprite.center_x = SCREEN_WIDTH / 2 - self.player_sprite.center_y = SCREEN_HEIGHT / 2 + self.player_sprite = arcade.Sprite(image_source, Config.CHARACTER_SCALING) + self.player_sprite.center_x = Config.SCREEN_WIDTH / 2 + self.player_sprite.center_y = Config.SCREEN_HEIGHT / 2 self.player_sprite.scale = 4 self.player_list.append(self.player_sprite) # Create the floor # This shows using a loop to place multiple sprites horizontally and vertically - for y in range(0, 1250, 63 * TILE_SCALING): - for x in range(0, 1250, 63 * TILE_SCALING): - floor = arcade.Sprite("images/tiles/floor_tile.png", TILE_SCALING) + for y in range(0, 1250, 63 * Config.TILE_SCALING): + for x in range(0, 1250, 63 * Config.TILE_SCALING): + floor = arcade.Sprite("images/tiles/floor_tile.png", Config.TILE_SCALING) floor.center_x = x floor.center_y = y self.floor_list.append(floor) @@ -95,16 +80,16 @@ class MyGame(arcade.Window): """Called whenever a key is pressed. """ if key == arcade.key.UP or key == arcade.key.W: - self.player_sprite.change_y = PLAYER_MOVEMENT_SPEED + self.player_sprite.change_y = Config.PLAYER_MOVEMENT_SPEED self.prev_keypress.append(key) elif key == arcade.key.DOWN or key == arcade.key.S: - self.player_sprite.change_y = -PLAYER_MOVEMENT_SPEED + self.player_sprite.change_y = -Config.PLAYER_MOVEMENT_SPEED self.prev_keypress.append(key) elif key == arcade.key.LEFT or key == arcade.key.A: - self.player_sprite.change_x = -PLAYER_MOVEMENT_SPEED + self.player_sprite.change_x = -Config.PLAYER_MOVEMENT_SPEED self.prev_keypress.append(key) elif key == arcade.key.RIGHT or key == arcade.key.D: - self.player_sprite.change_x = PLAYER_MOVEMENT_SPEED + self.player_sprite.change_x = Config.PLAYER_MOVEMENT_SPEED self.prev_keypress.append(key) def on_key_release(self, key, modifiers): @@ -124,40 +109,33 @@ class MyGame(arcade.Window): self.prev_keypress.remove(key) if self.prev_keypress: - self.on_key_press(self.prev_keypress.pop(0), '') + self.on_key_press(self.prev_keypress.pop(0), 0) def on_update(self, delta_time): """ Movement and game logic """ # Move the player with the physics engine self.physics_engine.update() + changed = False # Track if we need to change the viewport - # --- Manage Scrolling --- - - # Track if we need to change the viewport - - changed = False - + # Below manages all scrolling mechanics # Scroll left - left_boundary = self.view_left + LEFT_VIEWPORT_MARGIN + left_boundary = self.view_left + Config.LEFT_VIEWPORT_MARGIN if self.player_sprite.left < left_boundary: self.view_left -= left_boundary - self.player_sprite.left changed = True - # Scroll right - right_boundary = self.view_left + SCREEN_WIDTH - RIGHT_VIEWPORT_MARGIN + right_boundary = self.view_left + Config.SCREEN_WIDTH - Config.RIGHT_VIEWPORT_MARGIN if self.player_sprite.right > right_boundary: self.view_left += self.player_sprite.right - right_boundary changed = True - # Scroll up - top_boundary = self.view_bottom + SCREEN_HEIGHT - TOP_VIEWPORT_MARGIN + top_boundary = self.view_bottom + Config.SCREEN_HEIGHT - Config.TOP_VIEWPORT_MARGIN if self.player_sprite.top > top_boundary: self.view_bottom += self.player_sprite.top - top_boundary changed = True - # Scroll down - bottom_boundary = self.view_bottom + BOTTOM_VIEWPORT_MARGIN + bottom_boundary = self.view_bottom + Config.BOTTOM_VIEWPORT_MARGIN if self.player_sprite.bottom < bottom_boundary: self.view_bottom -= bottom_boundary - self.player_sprite.bottom changed = True @@ -170,14 +148,17 @@ class MyGame(arcade.Window): # Do the scrolling arcade.set_viewport(self.view_left, - SCREEN_WIDTH + self.view_left, + Config.SCREEN_WIDTH + self.view_left, self.view_bottom, - SCREEN_HEIGHT + self.view_bottom) + Config.SCREEN_HEIGHT + self.view_bottom) -def main(): - """ Main method """ - window = MyGame() +def main() -> None: + """ + Setups up window classes and runs the game. + """ + + window = Game() window.setup() arcade.run() diff --git a/triple-dungeon/map.py b/triple-dungeon/map.py new file mode 100644 index 0000000..52d593b --- /dev/null +++ b/triple-dungeon/map.py @@ -0,0 +1,37 @@ +from __future__ import annotations + +class Dungeon(object): + """ + Organizes Level objects into an easy to render and path through object. + """ + + def __init__(self, level_count: int = 3, size: int = 3) -> None: + """ + Initializes the Dungeon object. + + :param level_count: The number of Active Levels that should be stored within the Dungeon. + :param size: The diameter of the dungeon. Allows for a total of size^2 slots for levels. + """ + + self.levels, self.size = level_count, size + + +class Level(object): + """ + A 10x10 space holding wall and background sprites, enemies, items and so forth. + Should be loaded from + + """ + + def __init__(self,) -> None: + self.wallGrid = [] + + @staticmethod + def load_file(path: str) -> Level: + """ + Builds a Level from a given file path. + + :param path: Path to the Level file. + :return: The new generated Level file. + """ + pass diff --git a/triple-dungeon/mobs.py b/triple-dungeon/mobs.py new file mode 100644 index 0000000..97b7ff7 --- /dev/null +++ b/triple-dungeon/mobs.py @@ -0,0 +1,64 @@ +""" +mobs.py +Organizes all classes related to Mobs, Entities, Enemies, Players and Items. +""" + +import arcade + +from config import Config + + +class Mob(object): + """ + Represents a Mob. No defined behaviour, it has no intelligence. + """ + def __init__(self, sprite, max_health=100, max_armor=0) -> None: + self.sprite_path = sprite + self.sprite = arcade.Sprite(self.sprite_path, Config.CHARACTER_SCALING) + self.max_health, self.max_armor = max_health, max_armor + self.health, self.armor = max_health, max_armor + + def tick(self) -> None: + """ + A on_update function, the Mob should decide it's next actions here. + """ + pass + + +class Player(Mob): + """ + Represents a Player. + While this is a instance, there should only be one in the world at any given time. + """ + def __init__(self, *args, **kwargs) -> None: + super(Player, self).__init__(*args, **kwargs) + + def tick(self): + """ + While Player objects do not have any AI (they are controlled by the user), + the tick function can keep track of statistics that progress over time, like + regenerating health/armor or status effects like poison. + """ + + +class Enemy(Mob): + """ + Represents an Enemy Mob. + Will take basic offensive actions against Player objects. + """ + def __init__(self, *args, **kwargs) -> None: + super(Enemy, self).__init__(*args, **kwargs) + + def tick(self) -> None: + """ + A on_update function, the Enemy Mob should scan for the player, decide how to path to it, and + decide how to take offensive action. + """ + pass + + def path(self) -> None: + """ + Not yet decided how this function should work. + Basically, most pathfinding decisions should be kept within this function. + """ + pass