Return to repo list

tzed

Simple story-driven open world 2D CRPG.
Return to HMagellan.com

commit 7295f3e9545ab9790e36db202a9a8178b2759e85
parent 204a783a5555c20d6488c7e0c28c32551adb856a
Author: Erik Letson <hmagellan@hmagellan.com>
Date:   Thu,  9 Sep 2021 21:19:59 -0500

add dungeons checkpoint

Diffstat:
Adata/dungeons/testdun1/dungeon.json | 54++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adata/etc/dungeons.json | 5+++++
Mdata/etc/sheets.json | 5++++-
Adata/images/dungeon1_fc.png | 0
Adata/images/dungeon1_h.png | 0
Adata/images/dungeon1_w.png | 0
Asrc/dungeon.py | 111+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/game.py | 21+++++++++++++++++++--
Msrc/gamelib.py | 6+++++-
Msrc/interface.py | 59++++++++++++++++++++++++++++++++++++-----------------------
10 files changed, 234 insertions(+), 27 deletions(-)

diff --git a/data/dungeons/testdun1/dungeon.json b/data/dungeons/testdun1/dungeon.json @@ -0,0 +1,54 @@ +{ + "display_name" : "Test Dungeon", + "width" : 4, + "height" : 4, + "sheet" : "dungeon1", + "entrance" : [0, 0], + "layout" : [ + [ + [ + { + "North" : { + "ceiling" : [0, 0], + "floor" : [0, 1], + "l_wall" : [0, 0], + "r_wall" : [2, 0], + "horizon" : [0, 0], + "pass" : true, + "data" : {} + }, + "South" : { + "ceiling" : [0, 0], + "floor" : [0, 1], + "l_wall" : [0, 0], + "r_wall" : [2, 0], + "horizon" : [3, 0], + "pass" : false, + "data" : {} + }, + "East" : { + "ceiling" : [0, 2], + "floor" : [0, 2], + "l_wall" : [4, 0], + "r_wall" : [4, 0], + "horizon" : [2, 4], + "pass" : false, + "data" : {} + }, + "West" : { + "ceiling" : [0, 2], + "floor" : [0, 2], + "l_wall" : [4, 0], + "r_wall" : [4, 0], + "horizon" : [2, 4], + "pass" : false, + "data" : {} + } + }, + null, null, null], + [null, null, null, null], + [null, null, null, null], + [null, null, null, null] + ] + ] +} diff --git a/data/etc/dungeons.json b/data/etc/dungeons.json @@ -0,0 +1,5 @@ +{ + "dungeons" : [ + "testdungeon1" + ] +} diff --git a/data/etc/sheets.json b/data/etc/sheets.json @@ -11,5 +11,8 @@ "ovrly16" : [16, 16, false], "ovrly32" : [32, 32, false], "ovrly64" : [64, 64, false], - "ui_tray" : [1024, 768, false] + "ui_tray" : [1024, 768, false], + "dungeon1_fc" : [700, 165, false], + "dungeon1_w" : [220, 512, false], + "dungeon1_h" : [242, 182, false] } diff --git a/data/images/dungeon1_fc.png b/data/images/dungeon1_fc.png Binary files differ. diff --git a/data/images/dungeon1_h.png b/data/images/dungeon1_h.png Binary files differ. diff --git a/data/images/dungeon1_w.png b/data/images/dungeon1_w.png Binary files differ. diff --git a/src/dungeon.py b/src/dungeon.py @@ -0,0 +1,111 @@ +import pygame +from .gamelib import * + +############## +# dungeon.py # +############## + +# This file contains: +# 1. The 'Dungeon' class, which represents the over-the-shoulder maze/battle dungeon locations. +# 2. The 'DungeonSprite' class, which represents the visual elements of a dungeon such as architecture. + +################################# +# Section 1 - The Dungeon class # +################################# + +class Dungeon(object): + """ + The Dungeon object represents the logic and graphics + for the dungeon-crawling sections of the game. + """ + + def __init__(self, game, name, disp_str, width, height, sheetname, entrance, layout): + + # Saved vals + self.game = game + self.name = name + self.display_name = disp_str + self.maze_width = width + self.maze_height = height + self.sheetname = sheetname + self.entrance = entrance + self.layout_definition = layout + + # Other vals + self.direction = 0 + self.current_cell = (0, 0) + self.current_floor = 0 + + # Graphics + self.dungeon_view = pygame.sprite.Group() + self.construct_dungeon_view() + + def move_forward(self): + """ + Move to the forward cell if possible. + """ + self.construct_dungeon_view() + + def rotate_direction(self, mod = 1): + """ + Rotate in a left or right motion. + """ + self.direction += mod + if self.direction < 0: + self.direction = 3 + elif self.direction > 3: + self.direction = 0 + + self.construct_dungeon_view() + + def construct_dungeon_view(self): + """ + Construct a visual display of the current + dungeon cell using the dungeon components + defined in the layout_definition as well + as direction data. + """ + # TODO: Lotsa hardcoded numbers here + # Setup + self.dungeon_view.empty() + workcell = self.layout_definition[self.current_floor][self.current_cell[1]][self.current_cell[0]][DIRECTIONS[self.direction]] + + # Add the individual components + self.dungeon_view.add(DungeonSprite(self.game.sheets[self.sheetname + "_w"].sprites[tuple(workcell["l_wall"])], self.game.viewport_rect.topleft)) + self.dungeon_view.add(DungeonSprite(self.game.sheets[self.sheetname + "_w"].sprites[tuple(workcell["r_wall"])], (self.game.viewport_rect.topleft[0] + 462, self.game.viewport_rect.topleft[1]))) + self.dungeon_view.add(DungeonSprite(self.game.sheets[self.sheetname + "_fc"].sprites[tuple(workcell["ceiling"])], self.game.viewport_rect.topleft)) + self.dungeon_view.add(DungeonSprite(self.game.sheets[self.sheetname + "_fc"].sprites[tuple(workcell["floor"])], (self.game.viewport_rect.topleft[0], self.game.viewport_rect.topleft[1] + 347))) + self.dungeon_view.add(DungeonSprite(self.game.sheets[self.sheetname + "_h"].sprites[tuple(workcell["horizon"])], (self.game.viewport_rect.center[0] - 121, self.game.viewport_rect.center[1] - 91))) + + def update(self, surface = None): + """ + Update the dungeon logic and visuals. + """ + self.dungeon_view.update(surface) + +####################################### +# Section 2 - The DungeonSprite class # +####################################### + +class DungeonSprite(pygame.sprite.Sprite): + """ + Simple sprite extension for drawing dungeon + components. + """ + + def __init__(self, image, pos): + + # Parent initialization + pygame.sprite.Sprite.__init__(self) + + # Saved values + self.imgage = image + self.rect = self.image.get_rect() + self.rect.topleft = pos + + def update(self, surface = None): + """ + Update this DungeonSprite. + """ + if surface != None: + surface.blit(self.image, self.rect) diff --git a/src/game.py b/src/game.py @@ -1,5 +1,5 @@ import pygame, os, json -from . import interface, images, board, entity, ui, message +from . import interface, images, board, entity, ui, message, dungeon from .gamelib import * ########### @@ -67,7 +67,9 @@ class Game(object): self.active_scenario = "Tzed 1" # TMP! self.sheets = {} self.loaded_boards = {} + self.loaded_dungeons = {} self.current_board = None + self.current_dungeon = None self.gamedata = {} self.gametime = 0 self.old_gametime = -1 @@ -105,7 +107,7 @@ class Game(object): def load_boards(self, boardfile): """ - Load a board from a file. + Load boards from a file. """ # Load board from json with open(os.path.join(ETC_PATH, boardfile)) as bf: @@ -121,6 +123,21 @@ class Game(object): nb = board.Board(self, b, boarddef["display_name"], pal, (boarddef["board_width"], boarddef["board_height"]), (boarddef["cell_width"], boarddef["cell_height"]), boarddef["cells"], boarddef["scale"], entdef) self.loaded_boards[b] = nb + def load_dungeons(self, dungeonfile): + """ + Load dungeons from file. + """ + # Load dungeons from json + with open(os.path.join(ETC_PATH, dungeonfile)) as df: + dungeonlist = json.load(df) + + # Create dungeons and add to loaded dungeons + for d in dungeonlist: + with open(os.path.join(DUNGEON_PATH, d, "dungeon.json")) as dj: + dungeondef = json.load(dj) + nd = dungeon.Dungeon(self, d, dungeondef["display_name"], dungeondef["width"], dungeondef["height"], dungeondef["sheetname"], tuple(dungeondef["entrance"]), dungeondef["layout"]) + self.loaded_dungeons[d] = nd + def build_ui(self): """ Create the out-of-board game UI. diff --git a/src/gamelib.py b/src/gamelib.py @@ -27,6 +27,7 @@ IMAGE_PATH = os.path.join(DATA_PATH, "images") SOUND_PATH = os.path.join(DATA_PATH, "sounds") FONT_PATH = os.path.join(DATA_PATH, "fonts") BOARD_PATH = os.path.join(DATA_PATH, "boards") +DUNGEON_PATH = os.path.join(DATA_PATH, "dungeons") OVERWORLD_PATH = os.path.join(BOARD_PATH, "overworlds") LOCATION_PATH = os.path.join(BOARD_PATH, "locations") SAVE_PATH = os.path.join(USERLOCAL_PATH, "saves") @@ -53,7 +54,7 @@ COLORKEY = (255, 0, 255) BASE_COLOR = (255, 255, 255) # Modes -STATE_MODES = enum.Enum("STATE_MODES", "Main_Menu_Mode Location_Mode Overworld_Mode Dungeon_Mode") +STATE_MODES = enum.Enum("STATE_MODES", "Main_Menu_Mode Location_Mode Overworld_Mode Dungeon_Mode Battle_Mode") # Mode collections OVERHEAD_MODES = [STATE_MODES.Location_Mode, STATE_MODES.Overworld_Mode] @@ -61,6 +62,9 @@ OVERHEAD_MODES = [STATE_MODES.Location_Mode, STATE_MODES.Overworld_Mode] # Other enums BOARD_TYPES = enum.Enum("BOARD_TYPES", "Location Overworld") +# Other series +DIRECTIONS = ["North", "East", "South", "West"] + ####################### # Section 2 - Classes # ####################### diff --git a/src/interface.py b/src/interface.py @@ -75,35 +75,48 @@ class Interface(object): of keys on a mode-by-mode basis. Called during update. """ - if self.game.state_mode in OVERHEAD_MODES: - for k in self.key_bools: - if self.key_bools[k]: - if k in CONTROLS["up"]: + for k in self.key_bools: + if self.key_bools[k]: + if k in CONTROLS["up"]: + if self.game.state_mode in OVERHEAD_MODES: self.game.move_player_on_board((0, -1)) - for j in CONTROLS["up"]: - self.key_bools[j] = False - return - elif k in CONTROLS["down"]: + elif self.game.stat_mode == STATE_MODES.Dungeon_Mode: + if self.game.current_dungeon != None: + self.game.current_dungeon.move_forward() + for j in CONTROLS["up"]: + self.key_bools[j] = False + return + elif k in CONTROLS["down"]: + if self.game.state_mode in OVERHEAD_MODES: self.game.move_player_on_board((0, 1)) - for j in CONTROLS["down"]: - self.key_bools[j] = False - return - elif k in CONTROLS["left"]: + for j in CONTROLS["down"]: + self.key_bools[j] = False + return + elif k in CONTROLS["left"]: + if self.game.state_mode in OVERHEAD_MODES: self.game.move_player_on_board((-1, 0)) - for j in CONTROLS["left"]: - self.key_bools[j] = False - return - elif k in CONTROLS["right"]: + elif self.game.stat_mode == STATE_MODES.Dungeon_Mode: + if self.game.current_dungeon != None: + self.game.current_dungeon.rotate_direction(-1) + for j in CONTROLS["left"]: + self.key_bools[j] = False + return + elif k in CONTROLS["right"]: + if self.game.state_mode in OVERHEAD_MODES: self.game.move_player_on_board((1, 0)) - for j in CONTROLS["right"]: - self.key_bools[j] = False - return - elif k in CONTROLS["wait"]: + elif self.game.stat_mode == STATE_MODES.Dungeon_Mode: + if self.game.current_dungeon != None: + self.game.current_dungeon.rotate_direction() + for j in CONTROLS["right"]: + self.key_bools[j] = False + return + elif k in CONTROLS["wait"]: + if self.game.state_mode in OVERHEAD_MODES: self.game.post_message("$PLAYERNAME waits.") self.game.pass_time() - for j in CONTROLS["wait"]: - self.key_bools[j] = False - return + for j in CONTROLS["wait"]: + self.key_bools[j] = False + return def handle_mouse_click(self, event): """