From b6ca384b310757a0e1fe39d4e04d520efd080506 Mon Sep 17 00:00:00 2001 From: dyceron Date: Sat, 11 Jan 2025 18:19:09 -0500 Subject: [PATCH 1/7] Add classes to BMSMSD --- .../formats/bmsmsd.py | 118 +++++++++++++++++- tests/formats/test_bmsmsd.py | 29 ++++- 2 files changed, 143 insertions(+), 4 deletions(-) diff --git a/src/mercury_engine_data_structures/formats/bmsmsd.py b/src/mercury_engine_data_structures/formats/bmsmsd.py index f3b9da4c..e5513483 100644 --- a/src/mercury_engine_data_structures/formats/bmsmsd.py +++ b/src/mercury_engine_data_structures/formats/bmsmsd.py @@ -13,6 +13,7 @@ Float32l, Int32sl, Int32ul, + ListContainer, Struct, ) @@ -21,11 +22,15 @@ CVector2D, CVector3D, StrId, + Vec2, + Vec3, VersionAdapter, make_vector, ) if TYPE_CHECKING: + from enum import IntEnum + from mercury_engine_data_structures.game_check import Game TileBorders = FlagsEnum( @@ -65,7 +70,7 @@ "_magic" / Const(b"MMSD"), "version" / VersionAdapter("1.7.0"), "scenario" / StrId, - "tile_size" / construct.Array(2, Float32l), + "tile_size" / CVector2D, "x_tiles" / Int32sl, "y_tiles" / Int32sl, "map_dimensions" / Struct( @@ -96,11 +101,118 @@ ) # fmt: skip +class IconProperties: + def __init__(self, raw: Container) -> None: + self._raw = raw + + @property + def actor_name(self) -> str: + return self._raw.actor_name + + @actor_name.setter + def actor_name(self, value: str) -> None: + self._raw.actor_name = value + + @property + def clear_condition(self) -> str: + return self._raw.clear_condition + + @clear_condition.setter + def clear_condition(self, value: str) -> None: + self._raw.clear_condition = value + + @property + def icon(self) -> str: + return self._raw.icon + + @icon.setter + def icon(self, value: str) -> None: + self._raw.icon = value + + @property + def icon_priority(self) -> str: + return self._raw.icon_priority + + @icon_priority.setter + def icon_priority(self, value: str) -> None: + self._raw.icon_priority = value + + @property + def coordinates(self) -> Vec3: + return self._raw.coordinates + + @coordinates.setter + def coordinates(self, value: Vec3) -> None: + self._raw.coordinates = value + + + def _get_icon_properties(self) -> Container: + icon = Container({ + "actor_name": self.actor_name, + "clear_condition": self.clear_condition, + "icon": self.icon, + "icon_priority": self.icon_priority, + "coordinates": self.coordinates, + }) + return icon + + + +class TileProperties: + def __init__(self, raw: Container) -> None: + self._raw = raw + + @property + def tile_coordinates(self) -> list[int]: + return self._raw.tile_coordinates + + @tile_coordinates.setter + def tile_coordinates(self, value: list[int]) -> None: + self._raw.tile_coordinates = value + + @property + def dimension(self) -> dict[Vec2, Vec2]: + return self._raw.tile_dimension + + @property + def tile_type(self) -> IntEnum: + return self._raw.tile_type + + @tile_type.setter + def tile_type(self, value: IntEnum): + self._raw.tile_type = value + + @property + def icons(self) -> ListContainer: + return self._raw.icons + + def get_icon(self, icon_idx: int = 0) -> IconProperties: + return IconProperties._get_icon_properties(self.icons[icon_idx]) + + def add_icon( + self, + actor_name: str, + clear_condition: str, + icon: str, + icon_priority: str, + coordinates: Vec3, + ) -> Container: + new_icon = Container({ + "actor_name": actor_name, + "clear_condition": clear_condition, + "icon": icon, + "icon_priority": icon_priority, + "coordinates": coordinates + }) + + self.icons.append(new_icon) + + class Bmsmsd(BaseResource): @classmethod @functools.lru_cache def construct_class(cls, target_game: Game) -> Construct: return BMSMSD - def get_tile(self, tile_idx: int) -> Container: - return self.raw.tiles[tile_idx] + def get_tile(self, tile_idx: int) -> TileProperties: + return TileProperties(self.raw.tiles[tile_idx]) diff --git a/tests/formats/test_bmsmsd.py b/tests/formats/test_bmsmsd.py index e6257c3e..e953450c 100644 --- a/tests/formats/test_bmsmsd.py +++ b/tests/formats/test_bmsmsd.py @@ -1,10 +1,12 @@ from __future__ import annotations import pytest +from construct import Container from tests.test_lib import parse_build_compare_editor from mercury_engine_data_structures import samus_returns_data -from mercury_engine_data_structures.formats.bmsmsd import Bmsmsd, TileType +from mercury_engine_data_structures.common_types import Vec3 +from mercury_engine_data_structures.formats.bmsmsd import Bmsmsd, IconPriority, TileType @pytest.mark.parametrize("bmsmsd_path", samus_returns_data.all_files_ending_with(".bmsmsd")) @@ -26,3 +28,28 @@ def test_get_tile(surface_bmsmsd: Bmsmsd): tile = surface_bmsmsd.get_tile(25) assert tile.tile_type == TileType.NORMAL + + +def test_get_icon(surface_bmsmsd: Bmsmsd): + icon = surface_bmsmsd.get_tile(4).get_icon() + assert icon.actor_name == "LE_Item_001" + assert icon.clear_condition == "" + assert icon.icon == "item_missiletank" + assert icon.icon_priority == IconPriority.ACTOR + assert icon.coordinates == Vec3(-5500.0, -9700.0, 0.0) + + +def test_add_icon(surface_bmsmsd: Bmsmsd): + tile = surface_bmsmsd.get_tile(10) + assert tile is not None + + tile.add_icon("LE_Test_Icon", "CollectItem", "itemsphere", IconPriority.ACTOR, Vec3(100.0, 100.0, 0.0)) + + new_icon = Container({ + "actor_name": "LE_Test_Icon", + "clear_condition": "CollectItem", + "icon": "itemsphere", + "icon_priority": IconPriority.ACTOR, + "coordinates": Vec3(100.0, 100.0, 0.0) + }) + assert tile.get_icon(1) == new_icon From affea65d9b917ac09db86a343857d8c7a38a228d Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 11 Jan 2025 23:20:41 +0000 Subject: [PATCH 2/7] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- .../formats/bmsmsd.py | 35 ++++++++++--------- tests/formats/test_bmsmsd.py | 16 +++++---- 2 files changed, 27 insertions(+), 24 deletions(-) diff --git a/src/mercury_engine_data_structures/formats/bmsmsd.py b/src/mercury_engine_data_structures/formats/bmsmsd.py index e5513483..9a8cde26 100644 --- a/src/mercury_engine_data_structures/formats/bmsmsd.py +++ b/src/mercury_engine_data_structures/formats/bmsmsd.py @@ -10,7 +10,6 @@ Container, Enum, FlagsEnum, - Float32l, Int32sl, Int32ul, ListContainer, @@ -145,19 +144,19 @@ def coordinates(self) -> Vec3: def coordinates(self, value: Vec3) -> None: self._raw.coordinates = value - def _get_icon_properties(self) -> Container: - icon = Container({ - "actor_name": self.actor_name, - "clear_condition": self.clear_condition, - "icon": self.icon, - "icon_priority": self.icon_priority, - "coordinates": self.coordinates, - }) + icon = Container( + { + "actor_name": self.actor_name, + "clear_condition": self.clear_condition, + "icon": self.icon, + "icon_priority": self.icon_priority, + "coordinates": self.coordinates, + } + ) return icon - class TileProperties: def __init__(self, raw: Container) -> None: self._raw = raw @@ -197,13 +196,15 @@ def add_icon( icon_priority: str, coordinates: Vec3, ) -> Container: - new_icon = Container({ - "actor_name": actor_name, - "clear_condition": clear_condition, - "icon": icon, - "icon_priority": icon_priority, - "coordinates": coordinates - }) + new_icon = Container( + { + "actor_name": actor_name, + "clear_condition": clear_condition, + "icon": icon, + "icon_priority": icon_priority, + "coordinates": coordinates, + } + ) self.icons.append(new_icon) diff --git a/tests/formats/test_bmsmsd.py b/tests/formats/test_bmsmsd.py index e953450c..d62d2b01 100644 --- a/tests/formats/test_bmsmsd.py +++ b/tests/formats/test_bmsmsd.py @@ -45,11 +45,13 @@ def test_add_icon(surface_bmsmsd: Bmsmsd): tile.add_icon("LE_Test_Icon", "CollectItem", "itemsphere", IconPriority.ACTOR, Vec3(100.0, 100.0, 0.0)) - new_icon = Container({ - "actor_name": "LE_Test_Icon", - "clear_condition": "CollectItem", - "icon": "itemsphere", - "icon_priority": IconPriority.ACTOR, - "coordinates": Vec3(100.0, 100.0, 0.0) - }) + new_icon = Container( + { + "actor_name": "LE_Test_Icon", + "clear_condition": "CollectItem", + "icon": "itemsphere", + "icon_priority": IconPriority.ACTOR, + "coordinates": Vec3(100.0, 100.0, 0.0), + } + ) assert tile.get_icon(1) == new_icon From 890830cfe8271b8b66cf4b0a7da95f46da379dac Mon Sep 17 00:00:00 2001 From: dyceron Date: Sat, 11 Jan 2025 20:00:50 -0500 Subject: [PATCH 3/7] Add missing properties --- .../formats/bmsmsd.py | 20 +++++-- tests/formats/test_bmsmsd.py | 58 ++++++++++++++----- 2 files changed, 60 insertions(+), 18 deletions(-) diff --git a/src/mercury_engine_data_structures/formats/bmsmsd.py b/src/mercury_engine_data_structures/formats/bmsmsd.py index 9a8cde26..1e87dea7 100644 --- a/src/mercury_engine_data_structures/formats/bmsmsd.py +++ b/src/mercury_engine_data_structures/formats/bmsmsd.py @@ -12,7 +12,6 @@ FlagsEnum, Int32sl, Int32ul, - ListContainer, Struct, ) @@ -79,7 +78,7 @@ "tiles" / make_vector( Struct( "tile_coordinates" / construct.Array(2, Int32sl), - "tile_dimension" / Struct( + "tile_dimensions" / Struct( "bottom_left" / CVector2D, "top_right" / CVector2D, ), @@ -170,8 +169,16 @@ def tile_coordinates(self, value: list[int]) -> None: self._raw.tile_coordinates = value @property - def dimension(self) -> dict[Vec2, Vec2]: - return self._raw.tile_dimension + def tile_dimensions(self) -> dict[Vec2, Vec2]: + return self._raw.tile_dimensions + + @tile_dimensions.setter + def tile_dimensions(self, value: dict[Vec2, Vec2]) -> None: + self._raw.tile_dimensions = value + + @property + def tile_borders(self) -> dict[FlagsEnum]: + return self._raw.tile_borders @property def tile_type(self) -> IntEnum: @@ -182,9 +189,12 @@ def tile_type(self, value: IntEnum): self._raw.tile_type = value @property - def icons(self) -> ListContainer: + def icons(self) -> list: return self._raw.icons + def update_tile_borders(self, border_type: FlagsEnum, value: bool) -> None: + self._raw.tile_borders[border_type] = value + def get_icon(self, icon_idx: int = 0) -> IconProperties: return IconProperties._get_icon_properties(self.icons[icon_idx]) diff --git a/tests/formats/test_bmsmsd.py b/tests/formats/test_bmsmsd.py index d62d2b01..89e2a454 100644 --- a/tests/formats/test_bmsmsd.py +++ b/tests/formats/test_bmsmsd.py @@ -1,12 +1,13 @@ from __future__ import annotations +import copy import pytest from construct import Container from tests.test_lib import parse_build_compare_editor from mercury_engine_data_structures import samus_returns_data -from mercury_engine_data_structures.common_types import Vec3 -from mercury_engine_data_structures.formats.bmsmsd import Bmsmsd, IconPriority, TileType +from mercury_engine_data_structures.common_types import Vec2, Vec3 +from mercury_engine_data_structures.formats.bmsmsd import Bmsmsd, IconPriority, TileBorders, TileType @pytest.mark.parametrize("bmsmsd_path", samus_returns_data.all_files_ending_with(".bmsmsd")) @@ -23,12 +24,45 @@ def test_get_tile(surface_bmsmsd: Bmsmsd): tile = surface_bmsmsd.get_tile(4) assert tile.tile_coordinates == [48, 5] - tile = surface_bmsmsd.get_tile(12) - assert len(tile.icons) == 2 + tile = surface_bmsmsd.get_tile(0) + assert tile.tile_dimensions == { + "bottom_left": Vec2(6400.0, -10600.0), + "top_right": Vec2(7000.0, -9800.0) + } + + tile = surface_bmsmsd.get_tile(8) + assert tile.tile_borders == { + TileBorders.TOP: True, + TileBorders.BOTTOM: True, + TileBorders.LEFT: False, + TileBorders.RIGHT: True, + TileBorders.OPEN_TOP: False, + TileBorders.OPEN_BOTTOM: False, + TileBorders.OPEN_LEFT: False, + TileBorders.OPEN_RIGHT: False, + } tile = surface_bmsmsd.get_tile(25) assert tile.tile_type == TileType.NORMAL + tile = surface_bmsmsd.get_tile(12) + assert len(tile.icons) == 2 + + +def test_set_tile_properties(surface_bmsmsd: Bmsmsd): + tile = surface_bmsmsd.get_tile(0) + original_tile = copy.deepcopy(tile) + + tile.tile_coordinates = [30, 20] + tile.tile_dimensions = { + "bottom_left": Vec2(10000.0, -1000.0), + "top_right": Vec2(50000.0, -29000.0) + } + tile.update_tile_borders(TileBorders.OPEN_TOP, True) + tile.tile_type = TileType.ACID_FALL + + assert original_tile != tile + def test_get_icon(surface_bmsmsd: Bmsmsd): icon = surface_bmsmsd.get_tile(4).get_icon() @@ -45,13 +79,11 @@ def test_add_icon(surface_bmsmsd: Bmsmsd): tile.add_icon("LE_Test_Icon", "CollectItem", "itemsphere", IconPriority.ACTOR, Vec3(100.0, 100.0, 0.0)) - new_icon = Container( - { - "actor_name": "LE_Test_Icon", - "clear_condition": "CollectItem", - "icon": "itemsphere", - "icon_priority": IconPriority.ACTOR, - "coordinates": Vec3(100.0, 100.0, 0.0), - } - ) + new_icon = { + "actor_name": "LE_Test_Icon", + "clear_condition": "CollectItem", + "icon": "itemsphere", + "icon_priority": IconPriority.ACTOR, + "coordinates": Vec3(100.0, 100.0, 0.0), + } assert tile.get_icon(1) == new_icon From 9dbbc3f440da948d0eb9deb473422b67bd5be791 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 12 Jan 2025 01:00:57 +0000 Subject: [PATCH 4/7] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- tests/formats/test_bmsmsd.py | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/tests/formats/test_bmsmsd.py b/tests/formats/test_bmsmsd.py index 89e2a454..c1e9eb68 100644 --- a/tests/formats/test_bmsmsd.py +++ b/tests/formats/test_bmsmsd.py @@ -1,8 +1,8 @@ from __future__ import annotations + import copy import pytest -from construct import Container from tests.test_lib import parse_build_compare_editor from mercury_engine_data_structures import samus_returns_data @@ -25,10 +25,7 @@ def test_get_tile(surface_bmsmsd: Bmsmsd): assert tile.tile_coordinates == [48, 5] tile = surface_bmsmsd.get_tile(0) - assert tile.tile_dimensions == { - "bottom_left": Vec2(6400.0, -10600.0), - "top_right": Vec2(7000.0, -9800.0) - } + assert tile.tile_dimensions == {"bottom_left": Vec2(6400.0, -10600.0), "top_right": Vec2(7000.0, -9800.0)} tile = surface_bmsmsd.get_tile(8) assert tile.tile_borders == { @@ -54,10 +51,7 @@ def test_set_tile_properties(surface_bmsmsd: Bmsmsd): original_tile = copy.deepcopy(tile) tile.tile_coordinates = [30, 20] - tile.tile_dimensions = { - "bottom_left": Vec2(10000.0, -1000.0), - "top_right": Vec2(50000.0, -29000.0) - } + tile.tile_dimensions = {"bottom_left": Vec2(10000.0, -1000.0), "top_right": Vec2(50000.0, -29000.0)} tile.update_tile_borders(TileBorders.OPEN_TOP, True) tile.tile_type = TileType.ACID_FALL From d86ec81dd77e90e24c17b631f0a9c3b77e0674b8 Mon Sep 17 00:00:00 2001 From: dyceron Date: Thu, 16 Jan 2025 18:37:56 -0500 Subject: [PATCH 5/7] Address more review and add new adaptor and common_type --- .../adapters/flagsenum_adapter.py | 19 +++ .../common_types.py | 1 + .../formats/bmsmsd.py | 125 ++++++++---------- tests/formats/test_bmsmsd.py | 55 ++++---- 4 files changed, 102 insertions(+), 98 deletions(-) create mode 100644 src/mercury_engine_data_structures/adapters/flagsenum_adapter.py diff --git a/src/mercury_engine_data_structures/adapters/flagsenum_adapter.py b/src/mercury_engine_data_structures/adapters/flagsenum_adapter.py new file mode 100644 index 00000000..cedd52fd --- /dev/null +++ b/src/mercury_engine_data_structures/adapters/flagsenum_adapter.py @@ -0,0 +1,19 @@ +from __future__ import annotations + +from construct import Adapter, FlagsEnum, Int32ub + + +class FlagsEnumAdapter(Adapter): + def __init__(self, enum_class, subcon=Int32ub): + super().__init__(FlagsEnum(subcon, enum_class)) + self._enum_class = enum_class + + def _decode(self, obj, context, path): + return { + self._enum_class[k]: v + for k, v in obj.items() + if k != "_flagsenum" and v is True + } + + def _encode(self, obj, context, path): + return obj diff --git a/src/mercury_engine_data_structures/common_types.py b/src/mercury_engine_data_structures/common_types.py index ac2ab9e4..09d6b4e3 100644 --- a/src/mercury_engine_data_structures/common_types.py +++ b/src/mercury_engine_data_structures/common_types.py @@ -159,6 +159,7 @@ def _emitbuild(self, code: construct.CodeGen) -> str: CVector3D = CVectorConstruct(3) CVector4D = CVectorConstruct(4) Transform3D = construct.Struct("position" / CVector3D, "rotation" / CVector3D, "scale" / CVector3D) +BoundingBox2D = construct.Struct("min" / CVector2D, "max" / CVector2D) class VersionAdapter(Adapter): diff --git a/src/mercury_engine_data_structures/formats/bmsmsd.py b/src/mercury_engine_data_structures/formats/bmsmsd.py index 1e87dea7..10cb2dcd 100644 --- a/src/mercury_engine_data_structures/formats/bmsmsd.py +++ b/src/mercury_engine_data_structures/formats/bmsmsd.py @@ -1,6 +1,7 @@ from __future__ import annotations import functools +from enum import Enum from typing import TYPE_CHECKING import construct @@ -8,15 +9,16 @@ Const, Construct, Container, - Enum, - FlagsEnum, Int32sl, Int32ul, Struct, ) +from mercury_engine_data_structures.adapters.enum_adapter import EnumAdapter +from mercury_engine_data_structures.adapters.flagsenum_adapter import FlagsEnumAdapter from mercury_engine_data_structures.base_resource import BaseResource from mercury_engine_data_structures.common_types import ( + BoundingBox2D, CVector2D, CVector3D, StrId, @@ -27,41 +29,40 @@ ) if TYPE_CHECKING: - from enum import IntEnum from mercury_engine_data_structures.game_check import Game -TileBorders = FlagsEnum( - Int32sl, - TOP=1, - BOTTOM=2, - LEFT=4, - RIGHT=8, - OPEN_TOP=16, - OPEN_BOTTOM=32, - OPEN_LEFT=64, - OPEN_RIGHT=128, -) - -TileType = Enum( - Int32ul, - NORMAL=1, - HEAT=2, - ACID=4, - ACID_RISE=8, - ACID_FALL=12, -) - -IconPriority = Enum( - Int32sl, - METROID=-1, - ACTOR=0, - SHIP=1, - ENERGY_CLOUD=2, - DOOR=3, - CHOZO_SEAL=4, - HIDDEN_ITEM=5, -) +class TileBorder(int, Enum): + TOP = 1 + BOTTOM = 2 + LEFT = 4 + RIGHT = 8 + OPEN_TOP = 16 + OPEN_BOTTOM = 32 + OPEN_LEFT = 64 + OPEN_RIGHT = 128 + +TileBorderConstruct = FlagsEnumAdapter(TileBorder, Int32sl) + +class TileType(int, Enum): + NORMAL = 1 + HEAT = 2 + ACID = 4 + ACID_RISE = 8 + ACID_FALL = 12 + +TileTypeConstruct = EnumAdapter(TileType, Int32ul) + +class IconPriority(int, Enum): + METROID = -1 + ACTOR = 0 + SHIP = 1 + ENERGY_CLOUD = 2 + DOOR = 3 + CHOZO_SEAL = 4 + HIDDEN_ITEM = 5 + +IconPriorityConstruct = EnumAdapter(IconPriority, Int32sl) # BMSMSD BMSMSD = Struct( @@ -71,25 +72,19 @@ "tile_size" / CVector2D, "x_tiles" / Int32sl, "y_tiles" / Int32sl, - "map_dimensions" / Struct( - "bottom_left" / CVector2D, - "top_right" / CVector2D, - ), + "map_dimensions" / BoundingBox2D, "tiles" / make_vector( Struct( "tile_coordinates" / construct.Array(2, Int32sl), - "tile_dimensions" / Struct( - "bottom_left" / CVector2D, - "top_right" / CVector2D, - ), - "tile_borders" / TileBorders, - "tile_type" / TileType, + "tile_dimensions" / BoundingBox2D, + "tile_borders" / TileBorderConstruct, + "tile_type" / TileTypeConstruct, "icons" / make_vector( Struct( "actor_name" / StrId, "clear_condition" / StrId, "icon" / StrId, - "icon_priority" / IconPriority, + "icon_priority" / IconPriorityConstruct, "coordinates" / CVector3D, ) ), @@ -128,11 +123,11 @@ def icon(self, value: str) -> None: self._raw.icon = value @property - def icon_priority(self) -> str: + def icon_priority(self) -> IconPriority: return self._raw.icon_priority @icon_priority.setter - def icon_priority(self, value: str) -> None: + def icon_priority(self, value: IconPriority) -> None: self._raw.icon_priority = value @property @@ -143,18 +138,6 @@ def coordinates(self) -> Vec3: def coordinates(self, value: Vec3) -> None: self._raw.coordinates = value - def _get_icon_properties(self) -> Container: - icon = Container( - { - "actor_name": self.actor_name, - "clear_condition": self.clear_condition, - "icon": self.icon, - "icon_priority": self.icon_priority, - "coordinates": self.coordinates, - } - ) - return icon - class TileProperties: def __init__(self, raw: Container) -> None: @@ -177,26 +160,23 @@ def tile_dimensions(self, value: dict[Vec2, Vec2]) -> None: self._raw.tile_dimensions = value @property - def tile_borders(self) -> dict[FlagsEnum]: + def tile_borders(self) -> dict[TileBorder, bool]: return self._raw.tile_borders + @tile_borders.setter + def tile_borders(self, border_type: dict[TileBorder, bool], value: bool) -> None: + self._raw.tile_borders[border_type] = value + @property - def tile_type(self) -> IntEnum: + def tile_type(self) -> TileType: return self._raw.tile_type @tile_type.setter - def tile_type(self, value: IntEnum): + def tile_type(self, value: TileType): self._raw.tile_type = value - @property - def icons(self) -> list: - return self._raw.icons - - def update_tile_borders(self, border_type: FlagsEnum, value: bool) -> None: - self._raw.tile_borders[border_type] = value - def get_icon(self, icon_idx: int = 0) -> IconProperties: - return IconProperties._get_icon_properties(self.icons[icon_idx]) + return IconProperties(self._raw.icons[icon_idx]) def add_icon( self, @@ -216,7 +196,10 @@ def add_icon( } ) - self.icons.append(new_icon) + self._raw.icons.append(new_icon) + + def remove_icon(self, icon_idx: int = 0) -> None: + self._raw.icons.pop(icon_idx) class Bmsmsd(BaseResource): diff --git a/tests/formats/test_bmsmsd.py b/tests/formats/test_bmsmsd.py index c1e9eb68..c96f0ff3 100644 --- a/tests/formats/test_bmsmsd.py +++ b/tests/formats/test_bmsmsd.py @@ -1,13 +1,11 @@ from __future__ import annotations -import copy - import pytest from tests.test_lib import parse_build_compare_editor from mercury_engine_data_structures import samus_returns_data from mercury_engine_data_structures.common_types import Vec2, Vec3 -from mercury_engine_data_structures.formats.bmsmsd import Bmsmsd, IconPriority, TileBorders, TileType +from mercury_engine_data_structures.formats.bmsmsd import Bmsmsd, IconPriority, TileBorder, TileType @pytest.mark.parametrize("bmsmsd_path", samus_returns_data.all_files_ending_with(".bmsmsd")) @@ -25,37 +23,37 @@ def test_get_tile(surface_bmsmsd: Bmsmsd): assert tile.tile_coordinates == [48, 5] tile = surface_bmsmsd.get_tile(0) - assert tile.tile_dimensions == {"bottom_left": Vec2(6400.0, -10600.0), "top_right": Vec2(7000.0, -9800.0)} + assert tile.tile_dimensions == {"min": Vec2(6400.0, -10600.0), "max": Vec2(7000.0, -9800.0)} tile = surface_bmsmsd.get_tile(8) assert tile.tile_borders == { - TileBorders.TOP: True, - TileBorders.BOTTOM: True, - TileBorders.LEFT: False, - TileBorders.RIGHT: True, - TileBorders.OPEN_TOP: False, - TileBorders.OPEN_BOTTOM: False, - TileBorders.OPEN_LEFT: False, - TileBorders.OPEN_RIGHT: False, + TileBorder.TOP: True, + TileBorder.BOTTOM: True, + TileBorder.RIGHT: True, } tile = surface_bmsmsd.get_tile(25) assert tile.tile_type == TileType.NORMAL tile = surface_bmsmsd.get_tile(12) - assert len(tile.icons) == 2 + assert tile.get_icon(0) is not None + assert tile.get_icon(1) is not None def test_set_tile_properties(surface_bmsmsd: Bmsmsd): tile = surface_bmsmsd.get_tile(0) - original_tile = copy.deepcopy(tile) tile.tile_coordinates = [30, 20] - tile.tile_dimensions = {"bottom_left": Vec2(10000.0, -1000.0), "top_right": Vec2(50000.0, -29000.0)} - tile.update_tile_borders(TileBorders.OPEN_TOP, True) - tile.tile_type = TileType.ACID_FALL + assert tile.tile_coordinates == [30, 20] + + tile.tile_dimensions = {"min": Vec2(10000.0, -1000.0), "max": Vec2(50000.0, -29000.0)} + assert tile.tile_dimensions == {"min": Vec2(10000.0, -1000.0), "max": Vec2(50000.0, -29000.0)} + + tile.tile_borders[TileBorder.OPEN_TOP] = True + assert tile.tile_borders[TileBorder.OPEN_TOP] is True - assert original_tile != tile + tile.tile_type = TileType.ACID_FALL + assert tile.tile_type is TileType.ACID_FALL def test_get_icon(surface_bmsmsd: Bmsmsd): @@ -63,7 +61,7 @@ def test_get_icon(surface_bmsmsd: Bmsmsd): assert icon.actor_name == "LE_Item_001" assert icon.clear_condition == "" assert icon.icon == "item_missiletank" - assert icon.icon_priority == IconPriority.ACTOR + assert icon.icon_priority is IconPriority.ACTOR assert icon.coordinates == Vec3(-5500.0, -9700.0, 0.0) @@ -73,11 +71,14 @@ def test_add_icon(surface_bmsmsd: Bmsmsd): tile.add_icon("LE_Test_Icon", "CollectItem", "itemsphere", IconPriority.ACTOR, Vec3(100.0, 100.0, 0.0)) - new_icon = { - "actor_name": "LE_Test_Icon", - "clear_condition": "CollectItem", - "icon": "itemsphere", - "icon_priority": IconPriority.ACTOR, - "coordinates": Vec3(100.0, 100.0, 0.0), - } - assert tile.get_icon(1) == new_icon + icon = tile.get_icon(1) + assert icon.actor_name == "LE_Test_Icon" + assert icon.clear_condition == "CollectItem" + assert icon.icon == "itemsphere" + assert icon.icon_priority is IconPriority.ACTOR + assert icon.coordinates == Vec3(100.0, 100.0, 0.0) + + +def test_remove_icon(surface_bmsmsd: Bmsmsd): + tile = surface_bmsmsd.get_tile(1) + tile.remove_icon(0) From 6ab3c346683ac6f1b44aa7fe2bf124b3296d4312 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 16 Jan 2025 23:38:06 +0000 Subject: [PATCH 6/7] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- .../adapters/flagsenum_adapter.py | 6 +----- src/mercury_engine_data_structures/formats/bmsmsd.py | 7 ++++++- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/mercury_engine_data_structures/adapters/flagsenum_adapter.py b/src/mercury_engine_data_structures/adapters/flagsenum_adapter.py index cedd52fd..732cd54c 100644 --- a/src/mercury_engine_data_structures/adapters/flagsenum_adapter.py +++ b/src/mercury_engine_data_structures/adapters/flagsenum_adapter.py @@ -9,11 +9,7 @@ def __init__(self, enum_class, subcon=Int32ub): self._enum_class = enum_class def _decode(self, obj, context, path): - return { - self._enum_class[k]: v - for k, v in obj.items() - if k != "_flagsenum" and v is True - } + return {self._enum_class[k]: v for k, v in obj.items() if k != "_flagsenum" and v is True} def _encode(self, obj, context, path): return obj diff --git a/src/mercury_engine_data_structures/formats/bmsmsd.py b/src/mercury_engine_data_structures/formats/bmsmsd.py index 10cb2dcd..c4687d4b 100644 --- a/src/mercury_engine_data_structures/formats/bmsmsd.py +++ b/src/mercury_engine_data_structures/formats/bmsmsd.py @@ -29,9 +29,9 @@ ) if TYPE_CHECKING: - from mercury_engine_data_structures.game_check import Game + class TileBorder(int, Enum): TOP = 1 BOTTOM = 2 @@ -42,8 +42,10 @@ class TileBorder(int, Enum): OPEN_LEFT = 64 OPEN_RIGHT = 128 + TileBorderConstruct = FlagsEnumAdapter(TileBorder, Int32sl) + class TileType(int, Enum): NORMAL = 1 HEAT = 2 @@ -51,8 +53,10 @@ class TileType(int, Enum): ACID_RISE = 8 ACID_FALL = 12 + TileTypeConstruct = EnumAdapter(TileType, Int32ul) + class IconPriority(int, Enum): METROID = -1 ACTOR = 0 @@ -62,6 +66,7 @@ class IconPriority(int, Enum): CHOZO_SEAL = 4 HIDDEN_ITEM = 5 + IconPriorityConstruct = EnumAdapter(IconPriority, Int32sl) # BMSMSD From 612ef65ea5deec72ff375071c5bb9e9bf3e9603a Mon Sep 17 00:00:00 2001 From: dyceron Date: Thu, 16 Jan 2025 18:59:42 -0500 Subject: [PATCH 7/7] Fix tests --- .../adapters/flagsenum_adapter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mercury_engine_data_structures/adapters/flagsenum_adapter.py b/src/mercury_engine_data_structures/adapters/flagsenum_adapter.py index 732cd54c..b14d37fc 100644 --- a/src/mercury_engine_data_structures/adapters/flagsenum_adapter.py +++ b/src/mercury_engine_data_structures/adapters/flagsenum_adapter.py @@ -12,4 +12,4 @@ def _decode(self, obj, context, path): return {self._enum_class[k]: v for k, v in obj.items() if k != "_flagsenum" and v is True} def _encode(self, obj, context, path): - return obj + return {k.name: v for k, v in obj.items()}