Skip to content

Commit

Permalink
Start adding info on animations
Browse files Browse the repository at this point in the history
  • Loading branch information
ExcaliburZero committed Oct 7, 2023
1 parent f022f63 commit 95ed16a
Show file tree
Hide file tree
Showing 4 changed files with 166 additions and 0 deletions.
1 change: 1 addition & 0 deletions cbpickaxe/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""
A library for data mining the game Cassette Beasts.
"""
from .animation import Animation
from .hoylake import Hoylake
from .monster_form import Evolution, MonsterForm, TapeUpgrade
from .move import Move
Expand Down
130 changes: 130 additions & 0 deletions cbpickaxe/animation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
"""
Classes related to sprite animations.
"""
from dataclasses import dataclass
from typing import Any, Dict, List


@dataclass
class Box:
"""
A rectangular region of an image.
"""

x: int
y: int
width: int
height: int

@staticmethod
def from_dict(d: Dict[Any, Any]) -> "Box":
"""
Converts the given dict into a Box.
"""
x = d["x"]
y = d["y"]
w = d["w"]
h = d["h"]

assert isinstance(x, int)
assert isinstance(y, int)
assert isinstance(w, int)
assert isinstance(h, int)

return Box(x, y, w, h)


@dataclass
class Frame:
"""
A frame that can be used in animations.
"""

box: Box

@staticmethod
def from_dict(d: Dict[Any, Any]) -> "Frame":
"""
Converts the given dict into a Frame.
"""
box = Box.from_dict(d["frame"])

return Frame(box)


@dataclass
class FrameTag:
"""
A tag descripting the frames of an animation.
"""

name: str
start_frame: int
end_frame: int

@staticmethod
def from_dict(d: Dict[Any, Any]) -> "FrameTag":
"""
Converts the given dict into a FrameTag.
"""
name = d["name"]
start_frame = d["from"]
end_frame = d["to"]

assert isinstance(name, str)
assert isinstance(start_frame, int)
assert isinstance(end_frame, int)

return FrameTag(name, start_frame, end_frame)


@dataclass
class Animation:
"""
An animated sprite consisting of a set of frames with several tags indicating types of
animations (ex. idle, attack, hurt),
"""

frames: List[Frame]
frame_tags: List[FrameTag]
image: str

def get_frame(self, animation_name: str, frame_offset: int) -> Frame:
"""
Returns the frame at the given offset within the animation with the given name.
"""
return self.frames[
self.get_frame_tag(animation_name).start_frame + frame_offset
]

def get_frame_tag(self, name: str) -> FrameTag:
"""
Returns the FrameTag with the given name.
"""
for frame_tag in self.frame_tags:
if frame_tag.name == name:
return frame_tag

raise KeyError(name)

@staticmethod
def from_dict(d: Dict[Any, Any]) -> "Animation":
"""
Converts the given dict into an Animation.
"""
frames = [
Frame.from_dict(data)
for name, data in sorted(
d["frames"].items(), key=lambda e: Animation.__get_frame_id(e[0])
)
]
frame_tags = [FrameTag.from_dict(entry) for entry in d["meta"]["frameTags"]]
image = d["meta"]["image"]

assert isinstance(image, str)

return Animation(frames, frame_tags, image)

@staticmethod
def __get_frame_id(frame_name: str) -> int:
return int(frame_name.split(" ")[-1].split(".")[0])
30 changes: 30 additions & 0 deletions cbpickaxe/hoylake.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@
from typing import Dict, List, Iterable, Set

import collections
import json
import logging
import pathlib

from .animation import Animation
from .monster_form import MonsterForm
from .move import Move
from .translation_table import TranslationTable
Expand All @@ -27,6 +29,7 @@ def __init__(self) -> None:

self.__monster_forms: Dict[RelativeResPath, MonsterForm] = {}
self.__moves: Dict[RelativeResPath, Move] = {}
self.__animations: Dict[RelativeResPath, Animation] = {}

self.__moves_to_ignore = ["res://data/battle_moves/placeholder.tres"]

Expand All @@ -44,6 +47,33 @@ def load_root(self, new_root: pathlib.Path) -> None:
self.__roots.append(new_root)
self.__load_translation_tables(new_root)

def load_animation(self, path: str) -> Animation:
"""
Loads in the animation at the given res:// filepath.
Must have loaded at least one root before running.
If there is no animation file at that location in any of the loaded root directories,
then a ValueError will be raised.
"""
self.__check_if_root_loaded()

relative_path = Hoylake.__parse_res_path(path)

if relative_path in self.__animations:
return self.__animations[relative_path]

for root in self.__roots:
animation_path = root / relative_path
if animation_path.exists():
with open(animation_path, "r", encoding="utf-8") as input_stream:
animation = Animation.from_dict(json.load(input_stream))
self.__animations[relative_path] = animation

return animation

raise ValueError(f"Could not find animation file at path: {path}")

def load_monster_form(self, path: str) -> MonsterForm:
"""
Loads in the monster form at the given res:// filepath.
Expand Down
5 changes: 5 additions & 0 deletions cbpickaxe/monster_form.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ class MonsterForm:
evolutions: List[Evolution]
bestiary_index: int
move_tags: List[str]
battle_sprite_path: str
tape_upgrades: List[Union[TapeUpgrade, str]]
bestiary_bios: List[str]

Expand Down Expand Up @@ -108,6 +109,7 @@ def from_tres(input_stream: IO[str]) -> "MonsterForm":
max_ap = None
evolutions = None
move_tags = None
battle_sprite_path = None
tape_upgrades: Optional[List[Union[TapeUpgrade, str]]] = None
bestiary_bios = None

Expand All @@ -131,6 +133,7 @@ def from_tres(input_stream: IO[str]) -> "MonsterForm":
evasion = section["evasion"]
max_ap = section["max_ap"]
move_tags = section["move_tags"]
battle_sprite_path = section["battle_sprite_path"]
bestiary_bios = section["bestiary_bios"]

tape_upgrades = MonsterForm.__parse_tape_upgrades(scene, section)
Expand All @@ -153,6 +156,7 @@ def from_tres(input_stream: IO[str]) -> "MonsterForm":
assert isinstance(accuracy, int)
assert isinstance(evasion, int)
assert isinstance(max_ap, int)
assert isinstance(battle_sprite_path, str)
assert isinstance(move_tags, list)
assert isinstance(bestiary_bios, list)
assert tape_upgrades is not None
Expand Down Expand Up @@ -186,6 +190,7 @@ def from_tres(input_stream: IO[str]) -> "MonsterForm":
evolutions=evolutions,
bestiary_index=bestiary_index,
move_tags=move_tags,
battle_sprite_path=battle_sprite_path,
tape_upgrades=tape_upgrades,
bestiary_bios=bestiary_bios,
)
Expand Down

0 comments on commit 95ed16a

Please sign in to comment.