Return to repo list

heart-of-gold

Tactical RPG written in python, using pygame.
Return to HMagellan.com

commit 90d29a2dfa37b4095e10be7259bdea67d0c15645
parent 6ce38ffedf38474f383d09b4982589ba9715e222
Author: Erik Letson <hmagellan@hmagellan.com>
Date:   Sun, 20 Sep 2020 23:38:37 -0500

Animated movement system, big additions

Diffstat:
Mdata/json/anims.json | 54++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mdata/json/ents/testmap1.json | 1+
Mmain.py | 1+
Msrc/game.py | 1+
Msrc/images.py | 6++++--
Msrc/vgo.py | 111++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------
6 files changed, 149 insertions(+), 25 deletions(-)

diff --git a/data/json/anims.json b/data/json/anims.json @@ -9,6 +9,60 @@ { "sprite" : [1, 1], "timer" : 4 }, { "sprite" : [2, 1], "timer" : 4 }, { "sprite" : [3, 1], "timer" : 4 } + ], + "stand_R" : [ + { "sprite" : [4, 0], "timer" : 4 }, + { "sprite" : [5, 0], "timer" : 4 }, + { "sprite" : [6, 0], "timer" : 4 }, + { "sprite" : [7, 0], "timer" : 4 }, + { "sprite" : [4, 1], "timer" : 4 }, + { "sprite" : [5, 1], "timer" : 4 }, + { "sprite" : [6, 1], "timer" : 4 }, + { "sprite" : [7, 1], "timer" : 4 } + ], + "stand_U" : [ + { "sprite" : [8, 0], "timer" : 4 }, + { "sprite" : [9, 0], "timer" : 4 }, + { "sprite" : [10, 0], "timer" : 4 }, + { "sprite" : [11, 0], "timer" : 4 }, + { "sprite" : [8, 1], "timer" : 4 }, + { "sprite" : [9, 1], "timer" : 4 }, + { "sprite" : [10, 1], "timer" : 4 }, + { "sprite" : [11, 1], "timer" : 4 } + ], + "stand_D" : [ + { "sprite" : [0, 0], "timer" : 4 }, + { "sprite" : [1, 0], "timer" : 4 }, + { "sprite" : [2, 0], "timer" : 4 }, + { "sprite" : [3, 0], "timer" : 4 }, + { "sprite" : [0, 1], "timer" : 4 }, + { "sprite" : [1, 1], "timer" : 4 }, + { "sprite" : [2, 1], "timer" : 4 }, + { "sprite" : [3, 1], "timer" : 4 } + ], + "walk_L" : [ + { "sprite" : [0, 2], "timer" : 6 }, + { "sprite" : [1, 2], "timer" : 6 }, + { "sprite" : [2, 2], "timer" : 6 }, + { "sprite" : [3, 2], "timer" : 6 } + ], + "walk_R" : [ + { "sprite" : [4, 2], "timer" : 6 }, + { "sprite" : [5, 2], "timer" : 6 }, + { "sprite" : [6, 2], "timer" : 6 }, + { "sprite" : [7, 2], "timer" : 6 } + ], + "walk_U" : [ + { "sprite" : [8, 2], "timer" : 6 }, + { "sprite" : [9, 2], "timer" : 6 }, + { "sprite" : [10, 2], "timer" : 6 }, + { "sprite" : [11, 2], "timer" : 6 } + ], + "walk_D" : [ + { "sprite" : [0, 2], "timer" : 6 }, + { "sprite" : [1, 2], "timer" : 6 }, + { "sprite" : [2, 2], "timer" : 6 }, + { "sprite" : [3, 2], "timer" : 6 } ] }, "cursor1" : { diff --git a/data/json/ents/testmap1.json b/data/json/ents/testmap1.json @@ -1,6 +1,7 @@ { "testent1" : { "name" : "Jisella", + "type" : "Piece", "sheet" : "jisella_1", "visible" : true, "animation" : "stand_L", diff --git a/main.py b/main.py @@ -11,6 +11,7 @@ from src import game # the game would be in a loaded state. This seems the ideal solution, and it can be put off a bit. # 3. Implement the "draw()" functionality of DirtySprite for all VGOs, which will be done in anything that manages a LayeredDirty group, e.g. in the # EntityManager object. +# 4. Entities should probably be passed their manager. VGOs probably do not need this. pygame.init() diff --git a/src/game.py b/src/game.py @@ -176,6 +176,7 @@ class GameInterface(GameSubsystem): elif self.control_mode == "Turn_Select_Move": if self.game.entity_manager.set_entity_move_to_tile_pos(self.game.entity_manager.selected_entity, self.game.board_manager.get_overlay_move_vgo_at_pos(pygame.mouse.get_pos())): self.control_mode = "Turn_Normal" + # TODO: Should this really be done here??? self.game.board_manager.load_overlay() def handle_mouse_release(self, event): diff --git a/src/images.py b/src/images.py @@ -50,7 +50,7 @@ class SheetManager(manager.Manager): """ self.sheets_def = json.load(open(os.path.join(JSON_PATH, sheet_json))) for j in self.sheets_def: - self.loaded_sheets[j] = Sheet(self.sheets_def[j]["filename"], tuple(self.sheets_def[j]["dimensions"]), self.sheets_def[j]["total_sprites"]) + self.loaded_sheets[j] = Sheet(self, self.sheets_def[j]["filename"], j, tuple(self.sheets_def[j]["dimensions"]), self.sheets_def[j]["total_sprites"]) def load_animations_from_json(self, anim_json): """ @@ -91,10 +91,12 @@ class Sheet(object): (that is, all sprites are the same dimensions). """ - def __init__(self, filename, sprite_size, total_sprites): + def __init__(self, manager, filename, name, sprite_size, total_sprites): # Important values + self.manager = manager self.filename = filename + self.name = name self.sprite_dimensions = sprite_size self.total_sprites = total_sprites self.sprites = {} diff --git a/src/vgo.py b/src/vgo.py @@ -52,8 +52,8 @@ class VisibleGameObject(pygame.sprite.DirtySprite): self.set_animation(animation, animated) # Motion values - self.motion = [] - self.motion_index = -1 + self.motion = {} + self.motion_timer = 0 def set_sprite(self, sprite_coord): """ @@ -81,17 +81,12 @@ class VisibleGameObject(pygame.sprite.DirtySprite): Assign a new motion to this VGO and start playing it. Unlike animations, motions must be played if assigned. """ - # TODO: THIS OBVIOUSLY DOES NOT WORK!!!! - # FIXME!!!! - mx = target_pos[0] - self.rect.topleft[0] - my = target_pos[1] - self.rect.topleft[1] - direc = math.sqrt((mx**2) + (my**2)) - uv = (mx / direc) + (my / direc) - self.motion = [self.rect.topleft] - for p in range(2, 10):# TODO: 10 steps is totally arbitrary - self.motion.append((self.rect.topleft[0] + (uv * p * speed), self.rect.topleft[1] + (uv * p * speed))) - self.motion.append(target_pos) - self.motion_index = 0 + self.motion = { + "target" : target_pos, + "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): """ @@ -119,12 +114,18 @@ class VisibleGameObject(pygame.sprite.DirtySprite): """ Perform current motion, if it is set. """ - if self.motion != [] and self.motion_index != -1 and self.motion_index < len(self.motion) - 1: - self.rect.topleft = self.motion[self.motion_index] - self.motion_index += 1 + 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: - self.motion = [] - self.motion_index = -1 + if self.motion != {}: + self.rect.topleft = self.motion["target"] # Make sure we end up in exactly the right spot + self.motion = {} def act(self): """ @@ -213,6 +214,63 @@ class TileCursor(Entity): # DirtySprite settings self.layer = 1 +class Piece(Entity): + """ + Object that represents a playable piece on the board. Mostly + only differs from entity in that it expects the standard + animation format to allow for facing, moving, etc. Has some + sligthly modified move_motion and set_motion methods. + """ + + def __init__(self, name, ent_id, sheet, animation = None, animated = False, passable = False, unit = None): + + # Parent initialization + super().__init__(name, ent_id, sheet, animation, animated, passable, unit) + + # Face settings + self.facing = "D" # TODO: This should ABSOLUTELY be an enum + + # Others + self.back_to_stand = False #TODO: This may not be the best way + + def set_motion(self, target_pos, speed): + """ + Overwrite of the basic VGO version to support facing and + animating automatically. + """ + super().set_motion(target_pos, speed) + + # Decide facing + mx = target_pos[0] - self.rect.topleft[0] + my = target_pos[1] - self.rect.topleft[1] + a = math.atan2(mx, my) * 180 / math.pi + if a >= -67.5 and a < 67.5: + self.facing = "D" + elif a >= 67.5 and a < 112.5: + self.facing = "R" + elif a > 112.5 or a < -112.5: + self.facing = "U" + else: + self.facing = "L" + + # Set a movement animation + # TODO: It may be neccessary in the future to indicate whether or not this + # 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], True) + self.back_to_stand = True + + def act(self): + """ + Overwriting basic act mode. + """ + # TODO: Something else should be done so that this doesn't overwrite other + # legit non-walking, non-standing anims. THIS MAY NOT BE THE BEST. + if self.motion == {} and self.back_to_stand: + self.set_animation(self.sheet.manager.animations[self.sheet.name]["stand_" + self.facing], True) + self.back_to_stand = False + ############################################## # Section 4 - Managers for VGOs & Subclasses # ############################################## @@ -249,10 +307,17 @@ class EntityManager(manager.Manager): j = json.load(open(os.path.join(ENTITY_JSON_PATH, entsjson))) for e in j: - ne = Entity(j[e]["name"], self.total_entities, - self.game.sheet_manager.loaded_sheets[j[e]["sheet"]], - self.game.sheet_manager.animations[j[e]["sheet"]][j[e]["animation"]], - j[e]["animated"], j[e]["passable"], unit.Unit(self, self.game.unit_manager.get_stats(j[e]["name"]))) + # TODO: type should be enum + if j[e]["type"] == "Entity": + ne = Entity(j[e]["name"], self.total_entities, + self.game.sheet_manager.loaded_sheets[j[e]["sheet"]], + self.game.sheet_manager.animations[j[e]["sheet"]][j[e]["animation"]], + j[e]["animated"], j[e]["passable"], unit.Unit(self, self.game.unit_manager.get_stats(j[e]["name"]))) + elif j[e]["type"] == "Piece": + ne = Piece(j[e]["name"], self.total_entities, + self.game.sheet_manager.loaded_sheets[j[e]["sheet"]], + self.game.sheet_manager.animations[j[e]["sheet"]][j[e]["animation"]], + j[e]["animated"], j[e]["passable"], unit.Unit(self, self.game.unit_manager.get_stats(j[e]["name"]))) ne.assign_tile(self.game.board_manager.get_tile_at_tile_pos(tuple(j[e]["tile"]))) ne.snap_to_tile() self.add_entity(ne) @@ -329,7 +394,7 @@ class EntityManager(manager.Manager): """ # TODO: Set speed of motion somehow, maybe calculated from MOVE of entity if entity != None and tile_pos != None: - entity.set_motion((tile_pos[0] * self.game.board_manager.current_board.tmx_data.tilewidth, tile_pos[1] * self.game.board_manager.current_board.tmx_data.tileheight), 10) + entity.set_motion((tile_pos[0] * self.game.board_manager.current_board.tmx_data.tilewidth, tile_pos[1] * self.game.board_manager.current_board.tmx_data.tileheight), 6) entity.tile_pos = tile_pos return True else: