Return to repo list

tzed

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

commit 8c25b9759cf05f1d77cab1a3ddec66a9460d49ee
parent 68de58157df92d177f66cfdde80f4acac6ca7570
Author: Erik Letson <hmagellan@hmagellan.com>
Date:   Mon, 14 Jun 2021 15:19:10 -0500

camera and overlays

Diffstat:
Rdata/boards/test_town/test_ow.tmx -> data/boards/test_ow/test_ow.tmx | 0
Adata/boards/test_town/overlay.json | 7+++++++
Adata/img/overlay.png | 0
Mdata/json/sheets.json | 12++++++++++++
Msrc/board.py | 20--------------------
Msrc/bus.py | 29+++++++++++++----------------
Msrc/camera.py | 18+++++++++++++++---
Msrc/entity.py | 38++++++++++++++++++++++++++++++++++++++
Msrc/envvars.py | 6++++++
Msrc/game.py | 3++-
Msrc/interface.py | 2+-
Msrc/manager.py | 4----
Msrc/subsystem.py | 2+-
13 files changed, 95 insertions(+), 46 deletions(-)

diff --git a/data/boards/test_town/test_ow.tmx b/data/boards/test_ow/test_ow.tmx diff --git a/data/boards/test_town/overlay.json b/data/boards/test_town/overlay.json @@ -0,0 +1,7 @@ +{ + "exits": { + "test_ow" : { + "north" : [ [3, 0], [4, 0], [5, 0] ] + } + } +} diff --git a/data/img/overlay.png b/data/img/overlay.png Binary files differ. diff --git a/data/json/sheets.json b/data/json/sheets.json @@ -16,5 +16,17 @@ "dimensions" : [16, 16], "anim_class" : null, "alpha" : false + }, + "locations1": { + "filename" : "locations1.png", + "dimensions" : [16, 16], + "anim_class" : null, + "alpha" : false + }, + "overlay": { + "filename" : "overlay.png", + "dimensions" : [16, 16], + "anim_class" : null, + "alpha" : false } } diff --git a/src/board.py b/src/board.py @@ -26,32 +26,14 @@ class BoardManager(manager.Manager): # Board values self.current_board = None - self.board_overlay = pygame.sprite.LayeredDirty() def load_board(self, boardname): """ Load a given board. """ - # TODO: Bring this inline with loading in managers rather than in managed objects self.current_board = Board(self, boardname) - #self.load_overlay() self.update(None) - def load_overlay(self): - """ - Derive an overlay from available loaded board. Calling - this has the effect of refreshing the overlay. - """ - self.board_overlay = pygame.sprite.LayeredDirty() - for layer in self.current_board.tmx_data.visible_layers: - if isinstance(layer, pytmx.TiledTileLayer): - for x, y, gid in layer: - if self.current_board.tmx_data.get_tile_properties_by_gid(gid)["Passable"] == 1: - e = entity.Entity(self.system_bus.fetch("sheet_system", "sheets")["board_overlays_1"]) - e.set_position((x * self.current_board.tmx_data.tilewidth, y * self.current_board.tmx_data.tileheight)) - e.custom_flags = "OverlayGrid" - self.board_overlay.add(e) - def get_tile_pos_at_position(self, pos): """ Return (x, y) tile_pos if there is a tile at 'pos', and @@ -137,7 +119,6 @@ class BoardManager(manager.Manager): "board_dimensions" : self.current_board.board_dimensions, "board_pixel_dimensions" : self.current_board.pixel_dimensions, "board_tile_layer" : { (x, y) : gid for layer in self.current_board.tmx_data.visible_layers for x, y, gid in layer if isinstance(layer, pytmx.TiledTileLayer) }, - "board_overlay" : self.board_overlay } self.bus.record(self.name, data) @@ -147,7 +128,6 @@ class BoardManager(manager.Manager): """ if surface != None: self.current_board.draw_board(surface) - #self.board_overlay.update(surface) ############################ # Section 2 - Board Object # diff --git a/src/bus.py b/src/bus.py @@ -24,18 +24,20 @@ class Bus(subsystem.GameSubsystem): # Collection of elements self.elements = {} + self.collection = {} - def enter_element(self, element_name): + def enter_element(self, element): """ Make an entry in the records for the given element. This entry is checked for existence in many cases before doing other things. This method should be called when an element is created MOST OF THE TIME. """ - if element_name not in self.elements.keys(): - self.elements[element_name] = {} # Initial value is empty dict + if element.name not in self.elements.keys(): + self.elements[element.name] = {} # Initial value is empty dict + self.collection[element.name] = element # Value is the element itself else: - raise GameBusError(self.name + " BUS SAYS: " + element_name + " already in records!") + raise GameBusError(self.name + " BUS SAYS: " + element.name + " already in records!") def record(self, element_name, data): """ @@ -73,9 +75,6 @@ class SystemBus(Bus): # Parent init super().__init__(game, name, bus) - # Collection of systems - self.elements = {} - def record(self, element_name, data): """ Record an entire subsystem into this bus. @@ -107,6 +106,8 @@ class SystemBus(Bus): self.elements[s].update_fetch_data() # Performs: All callable non-return behaviors of subsystems. + def perform_camera_follow_entity(self, entity, force): + self.collection["camera"].follow_entity(entity, force) #################################### # Section 3 - The ManagerBus class # @@ -131,11 +132,6 @@ class ManagerBus(Bus): # Parent init super().__init__(game, name, bus) - # Collection of managers - self.elements = {} - # TODO: Redundant? The wrong way to do this?? - self.managers = {} - # Recording if self.bus != None: self.bus.record(self.name, self) @@ -177,11 +173,12 @@ class ManagerBus(Bus): # check_for_* = return something else # Performs: All callable non-return behaviors of managers. + # TODO: returning methods below need to be changed to checks def perform_board_manager_get_tile_at_position(self, mousepos): - return self.managers["board_manager"].get_tile_at_position(mousepos) + return self.collection["board_manager"].get_tile_at_position(mousepos) def perform_board_manager_get_tile_at_tile_pos(self, tilepos): - return self.managers["board_manager"].get_tile_at_tile_pos(tilepos) + return self.collection["board_manager"].get_tile_at_tile_pos(tilepos) def perform_board_manager_get_board_tile_properties_by_tile_gid(self, gid): - return self.managers["board_manager"].get_board_tile_properties_by_tile_gid(gid) + return self.collection["board_manager"].get_board_tile_properties_by_tile_gid(gid) def perform_entity_manager_move_player(self, tile_offset): - self.managers["entity_manager"].move_player_by_tile_offset(tile_offset) + self.collection["entity_manager"].move_player_by_tile_offset(tile_offset) diff --git a/src/camera.py b/src/camera.py @@ -31,21 +31,23 @@ class GameCamera(subsystem.GameSubsystem): self.camera_surface_rect = self.camera_surface.get_rect() self.camera_surface_offset = (0, 0) # The rect topleft is never changed; rather, this value is used by game to draw the camera surface at an offset self.camera_surface_base_dimensions = (0, 0) + self.follow = False # Recording if self.bus != None: self.bus.record(self.name, self) - def load_camera_surface(self, dimensions, init_offset = (0, 0)): + def load_camera_surface(self, dimensions, follow = False, init_offset = (0, 0)): """ Load up the camera surface as a new PyGame surface object of the necessary size. Also resets the camera offset. """ - self.camera_surface = pygame.Surface(dimensions).convert() + self.camera_surface_base_dimensions = dimensions + self.camera_surface = pygame.Surface(self.camera_surface_base_dimensions).convert() self.camera_surface_rect = self.camera_surface.get_rect() self.camera_surface_offset = init_offset - self.camera_surface_base_dimensions = dimensions + self.follow = follow def move_offset(self, rel_offset, speed_multiple = 1): """ @@ -75,6 +77,16 @@ class GameCamera(subsystem.GameSubsystem): """ self.camera_surface_offset = ((SCREEN_WIDTH / 2) - position[0], (SCREEN_HEIGHT / 2) - position[1]) + def follow_entity(self, entity, force = False): + """ + Snap the camera to the location of a given + entity. The 'force' bool indicates whether + this snap should happen even if self.follow + is false. + """ + if force or self.follow: + self.snap_to_position((entity.rect.center[0] * TILE_SCALE_FACTOR, entity.rect.center[1] * TILE_SCALE_FACTOR)) + def update_camera(self, surface = None): """ Update and draw the camera contents. diff --git a/src/entity.py b/src/entity.py @@ -33,6 +33,7 @@ class EntityManager(manager.Manager): # Other values self.player = None + self.board_overlay = None def load_board_entities_from_file(self, current_board_name): """ @@ -53,8 +54,29 @@ class EntityManager(manager.Manager): self.player = PlayerEntity(sh["chars1"], (0, 0)) self.player.assign_tile(tuple(ent_def[e]["location"]), self.bus.perform_board_manager_get_tile_at_tile_pos(tuple(ent_def[e]["location"]))[2]) self.player.snap_to_tile() + self.system_bus.perform_camera_follow_entity(self.player, False) self.draw_group.add(self.player) + def load_board_overlay_from_file(self, current_board_name): + """ + Load an overlay from file. + """ + # Load the overlay file + with open(os.path.join(BOARD_PATH, current_board_name, "overlay.json")) as f: overlay_data = json.load(f) + + # Populate the overlay + self.board_overlay = pygame.sprite.LayeredDirty() + sh = self.system_bus.fetch("sheet_system", "sheets") + for oc in overlay_data: + if oc == "exits": + for t in overlay_data[oc]: + for o in overlay_data[oc][t]: + for oo in overlay_data[oc][t][o]: + noe = OverlayEntity(sh["overlay"], (0, 0), None, False, "exit_" + t) + noe.assign_tile(tuple(oo), self.bus.perform_board_manager_get_tile_at_tile_pos(tuple(oo))) + noe.snap_to_tile() + self.board_overlay.add(noe) + def move_player_by_tile_offset(self, tile_offset): """ Move a player entity by relative tile offset. @@ -64,6 +86,7 @@ class EntityManager(manager.Manager): if t != None and self.bus.perform_board_manager_get_board_tile_properties_by_tile_gid(t[2])["Passable"]: self.player.assign_tile((t[0], t[1]), t[2]) self.player.snap_to_tile() + self.system_bus.perform_camera_follow_entity(self.player, False) def update_managed(self, surface = None): """ @@ -71,6 +94,7 @@ class EntityManager(manager.Manager): """ if surface != None: self.draw_group.update(surface) + self.board_overlay.update(surface) ############################ # Section 2 - Entity class # @@ -276,3 +300,17 @@ class PlayerEntity(Entity): # Parent initialization super().__init__(sheet, sprite, animation, animated) + +class OverlayEntity(Entity): + """ + Class representing the transparent overlay entities + that are drawn on the board to represent map exits + and other features. + """ + + def __init__(self, sheet, sprite = (0, 0), animation = None, animated = False, ov_type = None): + + # Parent initialization + super().__init__(sheet, sprite, animation, animated) + + self.overlay_type = ov_type diff --git a/src/envvars.py b/src/envvars.py @@ -47,6 +47,12 @@ CTRL_MODES = enum.Enum('CTRL_MODES', 'No_Control Main_Menu_Normal') GAME_EFFECTS = enum.Enum('GAME_EFFECTS', 'ef_game_dummy ef_game_quit ef_game_switch_mode ef_game_switch_control') TEAMS = enum.Enum('TEAMS', 'Player Ally Neutral Enemy Other') +# Enum collections +OVERHEAD_MODES = [ + STATE_MODES.Overworld_Mode, + STATE_MODES.Location_Mode +] + # Initial state STARTING_STATE_MODE = STATE_MODES.Location_Mode STARTING_BOARD = "test_town" diff --git a/src/game.py b/src/game.py @@ -70,8 +70,9 @@ class Game(object): # State-specific logic if new_mode == STATE_MODES.Location_Mode: self.board_manager.load_board(data["boardname"]) + self.camera.load_camera_surface(VIEWPORT_SIZE, True) self.entity_manager.load_board_entities_from_file(data["boardname"]) - self.camera.load_camera_surface(VIEWPORT_SIZE) + self.entity_manager.load_board_overlay_from_file(data["boardname"]) def shift_frames(self, framerate = FRAMERATE): """ diff --git a/src/interface.py b/src/interface.py @@ -81,7 +81,7 @@ class GameInterface(subsystem.GameSubsystem): of keys on a mode-by-mode basis. Called during update. """ - if self.game.state_mode == STATE_MODES.Location_Mode: + if self.game.state_mode in OVERHEAD_MODES: if self.key_bools[pygame.K_w]: self.manager_bus.perform_entity_manager_move_player((0, -1)) self.key_bools[pygame.K_w] = False diff --git a/src/manager.py b/src/manager.py @@ -54,10 +54,6 @@ class Manager(subsystem.GameSubsystem): self._activated = True self._effectual = False - # Managers should record themselves in the manager bus for performs - # TODO: Redundant? The wrong way to do this?? - self.bus.managers[self.name] = self - def expose(self): """ Expose info about this object to the ManagerBus diff --git a/src/subsystem.py b/src/subsystem.py @@ -49,7 +49,7 @@ class GameSubsystem(object): purposes. """ if bus != None: - bus.enter_element(self.name) + bus.enter_element(self) def update_fetch_data(self): """