commit 5762f91147b69d43d4a95bc149c1c9525c58c950
parent 92e8b7ff43a46d685b01b3f7bb2829256f8b59c0
Author: Erik Letson <hmagellan@hmagellan.com>
Date: Wed, 4 Nov 2020 15:38:50 -0600
Further refactoring work, checkpoint 2
Diffstat:
11 files changed, 295 insertions(+), 358 deletions(-)
diff --git a/src/board.py b/src/board.py
@@ -1,5 +1,5 @@
import pygame, pytmx, os, queue
-from . import manager, vgo
+from . import manager, entity
from .constants import BOARD_PATH
############
@@ -60,10 +60,10 @@ class BoardManager(manager.Manager):
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:
- v = vgo.VisibleGameObject(self.game.sheet_manager.loaded_sheets["board_overlays_1"])
- v.set_position((x * self.current_board.tmx_data.tilewidth, y * self.current_board.tmx_data.tileheight))
- v.custom_flags = "OverlayGrid"
- self.board_overlay.add(v)
+ e = entity.Entity(self.game.sheet_manager.loaded_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 switch_to_board(self, boardname):
"""
@@ -114,14 +114,14 @@ class BoardManager(manager.Manager):
return (x, y, gid)
return None
- def get_overlay_move_vgo_at_pos(self, pos):
+ def get_overlay_move_entity_at_pos(self, pos):
"""
- Return (x, y) if there is a legal overlay move vgo at 'pos',
+ Return (x, y) if there is a legal overlay move entity at 'pos',
and return None otherwise.
"""
- for v in self.board_overlay:
- if "OverlayMove" in v.custom_flags and v.rect.collidepoint(pos):
- return v.custom_flags[1]
+ for e in self.board_overlay:
+ if "OverlayMove" in e.custom_flags and e.rect.collidepoint(pos):
+ return e.custom_flags[1]
return None
def display_as_move_range(self, tile_pos_list):
@@ -135,10 +135,10 @@ class BoardManager(manager.Manager):
# TEMP!!!!
self.create_move_range(self.game.piece_manager.selected_piece)
for t in self.move_targets:
- v = vgo.VisibleGameObject(self.game.sheet_manager.loaded_sheets["board_overlays_1"], (1, 0))
- v.set_position((t[0] * self.current_board.tmx_data.tilewidth, t[1] * self.current_board.tmx_data.tileheight))
- v.custom_flags = ("OverlayMove", t)
- self.board_overlay.add(v)
+ e = entity.Entity(self.game.sheet_manager.loaded_sheets["board_overlays_1"], (1, 0))
+ e.set_position((t[0] * self.current_board.tmx_data.tilewidth, t[1] * self.current_board.tmx_data.tileheight))
+ e.custom_flags = ("OverlayMove", t)
+ self.board_overlay.add(e)
def create_move_range(self, piece):
"""
diff --git a/src/constants.py b/src/constants.py
@@ -36,7 +36,6 @@ FONT_PATH = os.path.join(DATA_PATH, "font")
BOARD_PATH = os.path.join(DATA_PATH, "map")
JSON_PATH = os.path.join(DATA_PATH, "json")
ENTITY_JSON_PATH = os.path.join(JSON_PATH, "ents")
-STATUS_JSON_PATH = os.path.join(JSON_PATH, "stats")
MENU_JSON_PATH = os.path.join(JSON_PATH, "menus")
SCENE_JSON_PATH = os.path.join(JSON_PATH, "scenes")
diff --git a/src/cursor.py b/src/cursor.py
@@ -1,2 +0,0 @@
-import pygame
-
diff --git a/src/entity.py b/src/entity.py
@@ -0,0 +1,225 @@
+import pygame, json, os, math
+from . import manager
+from .constants import *
+
+#############
+# entity.py #
+#############
+
+# TODO:
+# 1. Some serious refactoring needs to happen here. EntityManager needs to
+# be brought in line with how managers in general are imagined to work
+# now, especially if the generic manager is going to be significantly
+# extended and the bus is going to be implemented. The differences
+# between a VGO and an Entity are blurred right now. Hypothetically
+# speaking, an Entity should be an extended VGO that has features that
+# go beyond what a generally-extended pygame Sprite object should have.
+# A VGO is a just a sprite that can be drawn and animated by any other
+# object, but an Entity should be created and managed ONLY by the
+# EntityManager object, because of its extensions. Positioning and
+# animating can be VGO functions, but objects you interact with should
+# be Entities. This means the EntityManager should be active and working
+# in any mode that has on-screen object interaction (e.g. in Menu mode).
+# This is NOT how EntityManager currently works and refactoring of the
+# EntityManager, Entity, and VGO objects will be required to bring the
+# imagined function of this system of objects to realization. One way to
+# accomplish some of this would be to try to encapsulate more of what
+# EntityManager does each frame into its own update method, and make
+# the behavoir therein game_mode-conditional.
+# 2. An alternative to the above: Reimagine EntityManager as PieceManager,
+# Meaning it is only responsible for board Piece entities. The BoardCursor
+# could be handled by a seperate manager of its own, which could manage
+# the cursor across multiple modes. This would mean splitting the EntityManager
+# and Piece classes off into their own source file (a good option would
+# be to combine them with Unit, remove UnitManager, and just make units
+# be something PieceManager handles). All of this would be in line with
+# the natural path that the code has been taking, whereby there is not
+# one single management object for ALL entities, but rather entities and
+# other VGOs are drawn and updated by the managers that logically are
+# already associated with them. Drawing entities is a pan-Manager thing,
+# and one of the important distinctions between managers and subsystems.
+# The codebase should probably reflect this by doing away with EntityManager
+# entirely and refactoring it into PieceManager. EntityManager's functionality
+# involving identifying entities by name/id could be moved to a subsystem
+# object called ObjectOracle. This subsystem could be alerted each time
+# a new object is created, and could maintain a list of references to
+# those objects. It could manage ID by itself, and spread it across any
+# VGO, not just Entities. VGOs would know their own ID, and they could be
+# accessed from the oracly by ID by any object with a basic call to
+# game.oracle. It would be excellent for debug as well. It is possible that
+# even the non-VGO objects like the managers should have IDs and be referred
+# to by the oracle.
+
+# This file contains:
+# 1. The Entity class that is the extension of pygame.Sprite used throughout the game
+# 2. Various kinds of specialized entity classes that don't fit in other source files
+
+# TODO: This should eventually use LayeredDirty sprites for performance reasons
+# TODO: Eventually need to add a UI-element class type (sub of entity? Should entity replace VGO entirely???)
+
+############################
+# Section 1 - Entity class #
+############################
+
+class Entity(pygame.sprite.DirtySprite):
+ """
+ The parent of all visible objects. VisibleGameObject (VGO) is
+ essentially an extended, customised version of the PyGame Sprite
+ object. Entity supports animations, motions (movement over time),
+ and tile operations, but none of that is required.
+ """
+
+ def __init__(self, sheet, sprite = (0, 0), animation = None, animated = False):
+
+ # Parent initialization
+ super().__init__()
+
+ # Saved values
+ self.sheet = sheet
+ self.image = sheet.sprites[sprite]
+ self.rect = self.image.get_rect()
+ self.rect.topleft = (0, 0)
+ self.custom_flags = None # Used to pass special info to VGOs
+
+ # DirtySprite class defaults
+ self.visible = 1
+ self.dirty = 0
+ self.layer = 0
+
+ # Animation values
+ self.animated = animated
+ self.animation = {}
+ self.animation_timer = 0
+ self.animation_frame = 0
+ if animated:
+ self.set_animation(animation, animated)
+
+ # Motion values
+ self.motion = {}
+ self.motion_timer = 0
+
+ # Tile values
+ self.tile_pos = (-1, -1)
+ self.tile_gid = None
+
+ def set_sprite(self, sprite_coord):
+ """
+ Set the VGO's sprite to another one on the board. The
+ argument 'sprite_coord' is a tuple (x, y). This is used
+ to assign an image from the saved sheet to non-animated
+ VGO objects.
+ """
+ # TODO: Error-checking
+ self.image = self.sheet.sprites[sprite_coord]
+
+ def set_animation(self, new_animation, play = False, init_frame = 0):
+ """
+ Assign a new animation to this VGO and configure all
+ necessary values to get it playing.
+ """
+ self.animation = new_animation
+ self.animation_frame = init_frame
+ self.animation_timer = new_animation[init_frame]["timer"]
+ self.image = self.sheet.sprites[new_animation[init_frame]["sprite"]]
+ self.animated = play
+
+ def set_motion(self, target_pos, speed):
+ """
+ Assign a new motion to this VGO and start playing it.
+ Unlike animations, motions must be played if assigned.
+ Directions go by center rather than topleft.
+ """
+ self.motion = {
+ "target" : (target_pos[0] + (TILE_WIDTH / 2), target_pos[1] + (TILE_HEIGHT / 2)),
+ "speed" : speed,
+ "direction" : ((target_pos[0] - self.rect.topleft[0]) / speed, (target_pos[1] - self.rect.topleft[1]) / speed),
+ "timer" : speed
+ }
+
+ def set_position(self, pos):
+ """
+ Assign the rect topleft to a raw position tuple, independent
+ of e.g. a tile.
+ """
+ self.rect.topleft = pos
+
+ def set_center_position(self, pos):
+ """
+ Assign the rect center to a raw position tuple, independent
+ of e.g. a tile.
+ """
+ self.rect.center = pos
+
+ def animate(self):
+ """
+ Play the current animation. This method is called as part of
+ the update() logic.
+ """
+ if self.animation_timer > 0:
+ self.animation_timer -= 1
+ else:
+ if self.animation_frame < len(self.animation) - 1:
+ self.animation_frame += 1
+ else:
+ self.animation_frame = 0
+ self.animation_timer = self.animation[self.animation_frame]["timer"]
+ self.image = self.sheet.sprites[self.animation[self.animation_frame]["sprite"]]
+
+ def motion_move(self):
+ """
+ Perform current motion, if it is set.
+ """
+ if self.motion != {} and not self.rect.collidepoint(self.motion["target"]):
+ if self.motion_timer == 0:
+ mx = self.rect.topleft[0] + self.motion["direction"][0]
+ my = self.rect.topleft[1] + self.motion["direction"][1]
+ self.rect.topleft = (mx, my)
+ self.motion_timer = self.motion["speed"]
+ else:
+ self.motion_timer -= 1
+ else:
+ if self.motion != {}:
+ self.set_center_position(self.motion["target"]) # Make sure we end up in exactly the right spot
+ self.motion = {}
+
+ def snap_to_tile(self):
+ """
+ Snap the Entity to its current tile.
+ """
+ if self.tile_pos != (-1, -1):
+ self.set_position((self.tile_pos[0] * TILE_WIDTH, self.tile_pos[1] * TILE_HEIGHT))
+
+ def assign_tile(self, tile_def):
+ """
+ Assign a tile as this entity object's occupied tile.
+ Assume the values are legitimate.
+ """
+ self.tile_pos = (tile_def[0], tile_def[1])
+ self.tile_gid = tile_def[2]
+
+ def act(self):
+ """
+ This method is called as part of update() and is meant
+ to be overwritten in subclasses with any logic that needs
+ to happen each update.
+ """
+ pass
+
+ def update(self, surface = None):
+ """
+ Draw the VGO to the surface. Also calls act() for update
+ logic for specific VGO children, and animate() to animate
+ the sprite image.
+ """
+ self.act()
+ if surface != None:
+ self.motion_move()
+ if self.animated:
+ self.animate()
+ surface.blit(self.image, self.rect)
+
+#########################################
+# Section 2 - Various Entity subclasses #
+#########################################
+
+# TODO:
diff --git a/src/game.py b/src/game.py
@@ -1,5 +1,5 @@
import pygame
-from . import subsystem, manager, images, sound, board, vgo, piece, unit, menu, scene
+from . import subsystem, manager, images, sound, board, entity, piece, menu, scene
from .constants import *
###########
@@ -51,7 +51,6 @@ class Game(object):
self.board_manager = board.BoardManager(self, self.manager_bus)
#self.entity_manager = vgo.EntityManager(self, self.manager_bus)
self.piece_manager = piece.PieceManager(self, self.manager_bus)
- self.unit_manager = unit.UnitManager(self, self.manager_bus)
self.scene_manager = scene.SceneManager(self, self.manager_bus)
# Setup (This is WIP)
@@ -62,13 +61,15 @@ class Game(object):
# Switch to game control
self.switch_mode(STATE_MODES.Main_Menu_Mode)
- def switch_mode(self, new_mode):
+ def switch_mode(self, new_mode, data = None):
"""
Change the current state_mode, as well as load up
the elements of the new mode. This large method
should take account of every possible mode in the
- game. Such data has no reason to exists e.g. as a
- JSON file.
+ game. Such a structure has no reason to exists e.g.
+ as a JSON file. The 'data' option contains the info
+ needed as part of a mode switch. This method handles
+ a change even if data is None.
"""
if new_mode == None:
return
@@ -84,16 +85,15 @@ class Game(object):
self.control_mode = CTRL_MODES.Turn_Normal
# TODO: Obv this must be made more generic. Currently only
# sources from the one map file.
- self.board_manager.load_board_from_file("testmap1.tmx")
- self.board_manager.switch_to_board("testmap1.tmx")
- self.unit_manager.load_stats_from_json("jisella_1.json")
- self.entity_manager.load_entities_from_json("testmap1.json")
- self.entity_manager.load_tile_cursor("cursor1")
+ #self.board_manager.load_board_from_file("testmap1.tmx")
+ #self.board_manager.switch_to_board("testmap1.tmx")
+ #self.entity_manager.load_entities_from_json("testmap1.json")
+ #self.entity_manager.load_tile_cursor("cursor1")
self.camera.load_camera_surface(self.board_manager.current_board_dimensions)
elif new_mode == STATE_MODES.Still_Scene_Mode:
self.control_mode = CTRL_MODES.Still_Scene_Normal
# TODO: Generic-ify
- self.scene_manager.load_still_scene_from_file("testscene.json")
+ #self.scene_manager.load_still_scene_from_file("testscene.json")
self.camera.load_camera_surface((SCREEN_WIDTH, SCREEN_HEIGHT))
def lose_control(self, time = -1, followup_mode = None):
diff --git a/src/menu.py b/src/menu.py
@@ -1,9 +1,7 @@
import pygame, json, os
-from . import images, vgo, manager
+from . import images, manager, entity
from .constants import *
-# TODO: EntityManager should be used to manage menu entities as well, eventually.
-
###########
# menu.py #
###########
@@ -88,9 +86,6 @@ class Menu(object):
self.entities_group = pygame.sprite.LayeredDirty()
self.definition = json.load(open(os.path.join(MENU_JSON_PATH, menufile)))
- # TODO: This will not be permenant
- self.contained_entities = 0
-
# Load/setup menu
self.load_menu()
@@ -103,14 +98,9 @@ class Menu(object):
if self.definition != None:
# Load background, if any
- # TODO: Background should probably be a VGO subclass of its own rather than
- # a generic entity. It would be better since currently decisions such
- # as e.g. being animated are hardcoded here.
- self.background = vgo.Entity(self.definition["bg_sheet"] + " _ent", self.contained_entities,
- self.manager.game.sheet_manager.loaded_sheets[self.definition["bg_sheet"]],
- tuple(self.definition["bg_sprite"]), None, False, True, None)
- self.contained_entities += 1
-
+ self.background = entity.Entity(self.manager.bus.fetch_sheet(self.definition["bg_sheet"]),
+ tuple(self.definition["bg_sprite"]), None, False)
+
# Load buttons, if any
for b in self.definition["buttons"]:
nmb = MenuButton(b, self.contained_entities,
@@ -134,17 +124,17 @@ class Menu(object):
# Section 3 - Menu Entities #
#############################
-class MenuButton(vgo.Entity):
+class MenuButton(entity.Entity):
"""
Object that represents pushable menu buttons in menu mode. Created
and managed by Menu objects.
"""
# TODO: MenuButton should probably eventually be passed info on its draw layer.
- def __init__(self, name, ent_id, sheet, sprite = (0, 0), animation = None, animated = False, passable = True, unit = None, effects = []):
+ def __init__(self, sheet, sprite = (0, 0), animation = None, animated = False, effects = []):
# Parent initialization
- super().__init__(name, ent_id, sheet, sprite, animation, animated, passable, unit)
+ super().__init__(sheet, sprite, animation, animated)
# Saved values
self.effects = effects # A list of effects. Each effect goes with a method in MenuManager, and each one is triggered on a press.
diff --git a/src/piece.py b/src/piece.py
@@ -1,5 +1,5 @@
import pygame, os, json
-from . import manager, unit
+from . import manager, entity
from .constants import *
############
@@ -20,7 +20,7 @@ class PieceManager(manager.Manager):
PieceManager acts as a manager for all Pieces (Entities
representing playable characters on the game board), as
well as for the TileCursor object that is used to interact
- with units and tiles.
+ with pieces and tiles.
"""
def __init__(self, game, bus):
@@ -212,7 +212,7 @@ class PieceManager(manager.Manager):
# Section 2 - The Piece class #
###############################
-class Piece(vgo.Entity):
+class Piece(entity.Entity):
"""
Object that represents a playable piece on the board. Mostly
only differs from entity in that it expects the standard
@@ -234,12 +234,19 @@ class Piece(vgo.Entity):
self.passable = passable
self.normal_stats = normal_stats
self.active_stats = active_stats
- self.team = team # TODO: team maybe should be defined in unit
+ self.team = team
self.back_to_stand = False # TODO: This may not be the best way
self.current_tile_path = []
self.current_tile_path_index = 0
self.path_moving = False
+ def set_facing(self, direction):
+ """
+ Set the direction this piece should face. 'direction' should
+ be a valid enum value matching FACE_DIR.
+ """
+ self.facing = direction
+
def set_motion(self, target_pos, speed):
"""
Overwrite of the basic Entity version to support facing and
@@ -266,7 +273,7 @@ class Piece(vgo.Entity):
# animation should be assigned according to whether or not a board move is
# actually taking place. Also, this is a very roundabout way to reference
# the sheet's animations. Maybe sheets should be aware of their anims?
- self.set_animation(self.sheet.manager.animations[self.sheet.name]["walk_" + self.facing.name], True)
+ self.set_animation(self.manager.bus.fetch_animation("walk_" + self.facing.name), True)
def set_move_along_tile_path(self, tile_seq):
"""
@@ -276,6 +283,14 @@ class Piece(vgo.Entity):
self.current_tile_path_index = 0
self.path_moving = True
+ # Set facing values
+ #c = None
+ #for t in tile_seq:
+ # if c != None:
+ # if tile_seq[t][0] < tile_seq[c][0]:
+ #
+ # c = t
+
def execute_tile_path_move(self):
"""
Execute a move along a tile path.
@@ -284,6 +299,8 @@ class Piece(vgo.Entity):
if self.motion == {} and self.current_tile_path_index < len(self.current_tile_path):
next_tar = (self.current_tile_path[self.current_tile_path_index][0] * TILE_WIDTH, self.current_tile_path[self.current_tile_path_index][1] * TILE_HEIGHT)
self.set_motion(next_tar, PIECE_MOVE_SPEED)
+ #anim = None
+ #if next_tar[
self.current_tile_path_index += 1
elif self.motion == {}:
self.tile_pos = self.current_tile_path[self.current_tile_path_index - 1]
@@ -306,18 +323,18 @@ class Piece(vgo.Entity):
# Section 3 - TileCursor #
##########################
-class TileCursor(vgo.Entity):
+class TileCursor(entity.Entity):
"""
Object that follows the cursor to indicate selected/highlighted
tiles.
"""
- def __init__(self, name, ent_id, sheet, sprite = (0, 0), animation = None, animated = False, passable = True, unit = None):
+ def __init__(self, sheet, sprite = (0, 0), animation = None, animated = False):
# Parent initialization
- super().__init__(name, ent_id, sheet, sprite, animation, animated, passable, unit)
+ super().__init__(sheet, sprite, animation, animated)
- # VGO settings
+ # Entity settings
self.custom_flags = "TileCursor"
# DirtySprite settings
diff --git a/src/scene.py b/src/scene.py
@@ -1,5 +1,5 @@
import pygame, os, json
-from . import manager, vgo
+from . import manager, entity
from .constants import *
############
@@ -114,19 +114,19 @@ class StillScene(object):
Load a scene from file.
"""
# Load univeral elements
- self.name_box = vgo.VisibleGameObject(self.manager.game.sheet_manager.loaded_sheets["still_scene_name_box_1"])
+ self.name_box = entity.Entity(self.manager.bus.fetch_sheet("still_scene_name_box_1"))
self.name_box.set_position(self.name_box_pos)
- self.text_box = vgo.VisibleGameObject(self.manager.game.sheet_manager.loaded_sheets["still_scene_text_box_1"])
+ self.text_box = entity.Entity(self.manager.bus.fetch_sheet("still_scene_text_box_1"))
self.text_box.set_position(self.text_box_pos)
- self.continue_prompt = vgo.VisibleGameObject(self.manager.game.sheet_manager.loaded_sheets["continue_prompt_1"], (0, 0),
- self.manager.game.sheet_manager.animations["continue_prompt_1"]["shimmer"], True)
+ self.continue_prompt = entity.Entity(self.manager.bus.fetch_sheet("continue_prompt_1"), (0, 0),
+ self.manager.bus.fetch_animation("continue_prompt_1", "shimmer"))
self.continue_prompt.set_position(self.continue_prompt_pos)
# Load from scene definition JSON
scenedef = json.load(open(os.path.join(SCENE_JSON_PATH, scenefile)))
self.name = scenedef["name"]
- self.background = vgo.VisibleGameObject(self.manager.game.sheet_manager.loaded_sheets[scenedef["bg_sheet"]], tuple(scenedef["bg_sprite"]))
+ self.background = entity.Entity(self.manager.bus.fetch_sheet(scenedef["bg_sheet"]), tuple(scenedef["bg_sprite"]))
self.script = scenedef["script"]
for f in scenedef["fonts"]:
self.fonts[f] = pygame.font.Font(os.path.join(FONT_PATH, scenedef["fonts"][f][0]), scenedef["fonts"][f][1])
@@ -157,7 +157,7 @@ class StillScene(object):
self.current_voice = self.script[self.script_index]["voice"]
self.current_text_string = self.script[self.script_index]["line"]
for c in self.script[self.script_index]["characters"]:
- nc = vgo.VisibleGameObject(self.manager.game.sheet_manager.loaded_sheets[c["sheet"]], tuple(c["sprite"]))
+ nc = entity.Entity(self.manager.bus.fetch_sheet(c["sheet"]), tuple(c["sprite"]))
nc.set_position(tuple(c["pos"]))
self.displayed_characters.add(nc)
if self.script[self.script_index]["speaker"] != None:
diff --git a/src/turn.py b/src/turn.py
@@ -1,5 +1,5 @@
import pygame
-from . import manager, vgo
+from . import manager, entity
from .constants import *
###########
@@ -64,7 +64,7 @@ class TurnManager(manager.Manager):
# Section 2 - Turn Entities #
#############################
-class TurnTray(vgo.Entity):
+class TurnTray(entity.Entity):
"""
The TurnTray is the on-screen object where icons
and names representing the next Pieces to take
@@ -72,19 +72,19 @@ class TurnTray(vgo.Entity):
mostly because it must write specific text.
"""
- def __init__(self, name, ent_id, sheet, sprite = (0, 0), animation = None, animated = False):
+ def __init__(self, sheet, sprite = (0, 0), animation = None, animated = False):
# Parent initialization
super().__init__(sheet, sprite, animation, animated)
-class TurnButton(vgo.Entity):
+class TurnButton(entity.Entity):
"""
Class representing the buttons with icons and names
that appear in order inside the TurnTray object.
Clicking one will lock the cursor on that Piece.
"""
- def __init__(self, name, ent_id, sheet, sprite = (0, 0), animation = None, animated = False):
+ def __init__(self, sheet, sprite = (0, 0), animation = None, animated = False):
# Parent initialization
super().__init__(sheet, sprite, animation, animated)
diff --git a/src/unit.py b/src/unit.py
@@ -1,67 +0,0 @@
-import pygame, json, os
-from . import manager
-from .constants import STATUS_JSON_PATH
-
-###########
-# unit.py #
-###########
-
-# TODO:
-# 1. The utility/appropriateness of this entire file is suspect. Especially seems like Unit does not need its own manager.
-
-# This file contains
-# 1. The UnitManager class, which loads Unit JSONs and keeps track of them (like SheetManager)
-# 2. The Unit class, which represents the statistics of a charachter unit
-
-#################################
-# Section 1. UnitManager Object #
-#################################
-
-class UnitManager(manager.Manager):
- """
- Loads and manages Unit status JSONs. This manager is
- called whenever a Unit must be created and assigned to
- an Entity, and it gives back a loaded dict representing
- the stats of that Unit.
- """
-
- def __init__(self, game, bus):
-
- # Parent initialization
- super().__init__(game, bus)
-
- # Saved values
- self.loaded_stats = {}
-
- def load_stats_from_json(self, stats_json):
- """
- Load the stats from a given stats_json file.
- """
- j = json.load(open(os.path.join(STATUS_JSON_PATH, stats_json)))
- self.loaded_stats[j["name"]] = j
-
- def get_stats(self, name):
- """
- Return stats if it exists.
- """
- if name in self.loaded_stats.keys():
- return self.loaded_stats[name]
-
-##########################
-# Section 2. Unit Object #
-##########################
-
-class Unit(object):
- """
- Unit represents a turn-taking Entity's statistics that
- are important for gameplay. An Entity can have a Unit,
- and that Unit can be used to compare against the other
- Unit's owned by other Entities.
- """
-
- def __init__(self, entity, stats_definition):
-
- # Saved values
- self.entity = entity
- self.static_stats = stats_definition
- self.active_stats = stats_definition
diff --git a/src/vgo.py b/src/vgo.py
@@ -1,225 +0,0 @@
-import pygame, json, os, math
-from . import manager, unit
-from .constants import *
-
-##########
-# vgo.py #
-##########
-
-# TODO:
-# 1. Some serious refactoring needs to happen here. EntityManager needs to
-# be brought in line with how managers in general are imagined to work
-# now, especially if the generic manager is going to be significantly
-# extended and the bus is going to be implemented. The differences
-# between a VGO and an Entity are blurred right now. Hypothetically
-# speaking, an Entity should be an extended VGO that has features that
-# go beyond what a generally-extended pygame Sprite object should have.
-# A VGO is a just a sprite that can be drawn and animated by any other
-# object, but an Entity should be created and managed ONLY by the
-# EntityManager object, because of its extensions. Positioning and
-# animating can be VGO functions, but objects you interact with should
-# be Entities. This means the EntityManager should be active and working
-# in any mode that has on-screen object interaction (e.g. in Menu mode).
-# This is NOT how EntityManager currently works and refactoring of the
-# EntityManager, Entity, and VGO objects will be required to bring the
-# imagined function of this system of objects to realization. One way to
-# accomplish some of this would be to try to encapsulate more of what
-# EntityManager does each frame into its own update method, and make
-# the behavoir therein game_mode-conditional.
-# 2. An alternative to the above: Reimagine EntityManager as PieceManager,
-# Meaning it is only responsible for board Piece entities. The BoardCursor
-# could be handled by a seperate manager of its own, which could manage
-# the cursor across multiple modes. This would mean splitting the EntityManager
-# and Piece classes off into their own source file (a good option would
-# be to combine them with Unit, remove UnitManager, and just make units
-# be something PieceManager handles). All of this would be in line with
-# the natural path that the code has been taking, whereby there is not
-# one single management object for ALL entities, but rather entities and
-# other VGOs are drawn and updated by the managers that logically are
-# already associated with them. Drawing entities is a pan-Manager thing,
-# and one of the important distinctions between managers and subsystems.
-# The codebase should probably reflect this by doing away with EntityManager
-# entirely and refactoring it into PieceManager. EntityManager's functionality
-# involving identifying entities by name/id could be moved to a subsystem
-# object called ObjectOracle. This subsystem could be alerted each time
-# a new object is created, and could maintain a list of references to
-# those objects. It could manage ID by itself, and spread it across any
-# VGO, not just Entities. VGOs would know their own ID, and they could be
-# accessed from the oracly by ID by any object with a basic call to
-# game.oracle. It would be excellent for debug as well. It is possible that
-# even the non-VGO objects like the managers should have IDs and be referred
-# to by the oracle.
-
-# This file contains:
-# 1. The Entity class that is the extension of pygame.Sprite used throughout the game
-# 2. Various kinds of specialized entity classes that don't fit in other source files
-
-# TODO: This should eventually use LayeredDirty sprites for performance reasons
-# TODO: Eventually need to add a UI-element class type (sub of entity? Should entity replace VGO entirely???)
-
-############################
-# Section 1 - Entity class #
-############################
-
-class Entity(pygame.sprite.DirtySprite):
- """
- The parent of all visible objects. VisibleGameObject (VGO) is
- essentially an extended, customised version of the PyGame Sprite
- object. Entity supports animations, motions (movement over time),
- and tile operations, but none of that is required.
- """
-
- def __init__(self, sheet, sprite = (0, 0), animation = None, animated = False):
-
- # Parent initialization
- super().__init__()
-
- # Saved values
- self.sheet = sheet
- self.image = sheet.sprites[sprite]
- self.rect = self.image.get_rect()
- self.rect.topleft = (0, 0)
- self.custom_flags = None # Used to pass special info to VGOs
-
- # DirtySprite class defaults
- self.visible = 1
- self.dirty = 0
- self.layer = 0
-
- # Animation values
- self.animated = animated
- self.animation = {}
- self.animation_timer = 0
- self.animation_frame = 0
- if animated:
- self.set_animation(animation, animated)
-
- # Motion values
- self.motion = {}
- self.motion_timer = 0
-
- # Tile values
- self.tile_pos = (-1, -1)
- self.tile_gid = None
-
- def set_sprite(self, sprite_coord):
- """
- Set the VGO's sprite to another one on the board. The
- argument 'sprite_coord' is a tuple (x, y). This is used
- to assign an image from the saved sheet to non-animated
- VGO objects.
- """
- # TODO: Error-checking
- self.image = self.sheet.sprites[sprite_coord]
-
- def set_animation(self, new_animation, play = False, init_frame = 0):
- """
- Assign a new animation to this VGO and configure all
- necessary values to get it playing.
- """
- self.animation = new_animation
- self.animation_frame = init_frame
- self.animation_timer = new_animation[init_frame]["timer"]
- self.image = self.sheet.sprites[new_animation[init_frame]["sprite"]]
- self.animated = play
-
- def set_motion(self, target_pos, speed):
- """
- Assign a new motion to this VGO and start playing it.
- Unlike animations, motions must be played if assigned.
- Directions go by center rather than topleft.
- """
- self.motion = {
- "target" : (target_pos[0] + (TILE_WIDTH / 2), target_pos[1] + (TILE_HEIGHT / 2)),
- "speed" : speed,
- "direction" : ((target_pos[0] - self.rect.topleft[0]) / speed, (target_pos[1] - self.rect.topleft[1]) / speed),
- "timer" : speed
- }
-
- def set_position(self, pos):
- """
- Assign the rect topleft to a raw position tuple, independent
- of e.g. a tile.
- """
- self.rect.topleft = pos
-
- def set_center_position(self, pos):
- """
- Assign the rect center to a raw position tuple, independent
- of e.g. a tile.
- """
- self.rect.center = pos
-
- def animate(self):
- """
- Play the current animation. This method is called as part of
- the update() logic.
- """
- if self.animation_timer > 0:
- self.animation_timer -= 1
- else:
- if self.animation_frame < len(self.animation) - 1:
- self.animation_frame += 1
- else:
- self.animation_frame = 0
- self.animation_timer = self.animation[self.animation_frame]["timer"]
- self.image = self.sheet.sprites[self.animation[self.animation_frame]["sprite"]]
-
- def motion_move(self):
- """
- Perform current motion, if it is set.
- """
- if self.motion != {} and not self.rect.collidepoint(self.motion["target"]):
- if self.motion_timer == 0:
- mx = self.rect.topleft[0] + self.motion["direction"][0]
- my = self.rect.topleft[1] + self.motion["direction"][1]
- self.rect.topleft = (mx, my)
- self.motion_timer = self.motion["speed"]
- else:
- self.motion_timer -= 1
- else:
- if self.motion != {}:
- self.set_center_position(self.motion["target"]) # Make sure we end up in exactly the right spot
- self.motion = {}
-
- def snap_to_tile(self):
- """
- Snap the Entity to its current tile.
- """
- if self.tile_pos != (-1, -1):
- self.set_position((self.tile_pos[0] * TILE_WIDTH, self.tile_pos[1] * TILE_HEIGHT))
-
- def assign_tile(self, tile_def):
- """
- Assign a tile as this entity object's occupied tile.
- Assume the values are legitimate.
- """
- self.tile_pos = (tile_def[0], tile_def[1])
- self.tile_gid = tile_def[2]
-
- def act(self):
- """
- This method is called as part of update() and is meant
- to be overwritten in subclasses with any logic that needs
- to happen each update.
- """
- pass
-
- def update(self, surface = None):
- """
- Draw the VGO to the surface. Also calls act() for update
- logic for specific VGO children, and animate() to animate
- the sprite image.
- """
- self.act()
- if surface != None:
- self.motion_move()
- if self.animated:
- self.animate()
- surface.blit(self.image, self.rect)
-
-#########################################
-# Section 2 - Various Entity subclasses #
-#########################################
-
-# TODO: