diff --git a/src/mercury_engine_data_structures/formats/bmsad.py b/src/mercury_engine_data_structures/formats/bmsad.py index e25d9d75..aed93322 100644 --- a/src/mercury_engine_data_structures/formats/bmsad.py +++ b/src/mercury_engine_data_structures/formats/bmsad.py @@ -69,7 +69,7 @@ def find_charclass_for_type(type_name: str): return as_char return find_charclass_for_type( - type_lib.get_parent_for(type_name), + type_lib.get_parent_for(Game.DREAD, type_name), ) @@ -124,7 +124,7 @@ def DreadDependencies(): def component_type(this): for component_type in component_dependencies.keys(): - if type_lib.is_child_of(this.type, component_type): + if type_lib.is_child_of(Game.DREAD, this.type, component_type): return component_type return None @@ -165,7 +165,7 @@ def component_type(this): ) ), extra_fields=construct.If( - lambda this: type_lib.is_child_of(this.type, "CComponent"), + lambda this: type_lib.is_child_of(Game.DREAD, this.type, "CComponent"), ExtraFields, ), functions=Functions, diff --git a/src/mercury_engine_data_structures/formats/bmsld.py b/src/mercury_engine_data_structures/formats/bmsld.py index e5ec17a4..830d5c1d 100644 --- a/src/mercury_engine_data_structures/formats/bmsld.py +++ b/src/mercury_engine_data_structures/formats/bmsld.py @@ -1,3 +1,4 @@ +from typing import Iterator import construct from construct import Array, Const, Construct, Flag, Float32l, Hex, Int32ul, Struct @@ -172,3 +173,13 @@ class Bmsld(BaseResource): @classmethod def construct_class(cls, target_game: Game) -> Construct: return BMSLD + + def all_actor_groups(self) -> Iterator[str]: + for area in self.raw.sub_areas: + yield area.name + + def is_actor_in_group(self, group_name: str, actor_name: str, layer_name: str = "default") -> bool: + generator = (area for area in self.raw.sub_areas if area.name == group_name) + for area in generator: + return actor_name in area.names + return False diff --git a/src/mercury_engine_data_structures/formats/standard_format.py b/src/mercury_engine_data_structures/formats/standard_format.py index 6627130b..aced8362 100644 --- a/src/mercury_engine_data_structures/formats/standard_format.py +++ b/src/mercury_engine_data_structures/formats/standard_format.py @@ -4,6 +4,7 @@ from mercury_engine_data_structures import type_lib from mercury_engine_data_structures.formats.property_enum import PropertyEnum +from mercury_engine_data_structures.game_check import Game def create(name: str, version: int, root_name: Optional[str] = None, explicit_root: bool = False): @@ -14,10 +15,10 @@ def create(name: str, version: int, root_name: Optional[str] = None, explicit_ro root = construct.FocusedSeq( "root", "type" / construct.Rebuild(PropertyEnum, name), - "root" / type_lib.GetTypeConstruct(lambda this: this._.type) + "root" / type_lib.GetTypeConstruct(Game.DREAD, lambda this: this._.type) ) else: - root = type_lib.get_type(root_name).construct + root = type_lib.get_type(Game.DREAD, root_name).construct result = construct.Struct( _class_crc=construct.Const(name, PropertyEnum), diff --git a/src/mercury_engine_data_structures/samus_returns_data.py b/src/mercury_engine_data_structures/samus_returns_data.py index 07aca980..62e9e274 100644 --- a/src/mercury_engine_data_structures/samus_returns_data.py +++ b/src/mercury_engine_data_structures/samus_returns_data.py @@ -11,7 +11,9 @@ @functools.lru_cache def get_raw_types() -> Dict[str, typing.Any]: - raise NotImplementedError() + path = Path(__file__).parent.joinpath("samus_returns_types.json") + with path.open() as f: + return json.load(f) @functools.lru_cache diff --git a/src/mercury_engine_data_structures/samus_returns_types.json b/src/mercury_engine_data_structures/samus_returns_types.json index 761f4e1f..39257118 100644 --- a/src/mercury_engine_data_structures/samus_returns_types.json +++ b/src/mercury_engine_data_structures/samus_returns_types.json @@ -1347,5 +1347,67 @@ "kind":"struct", "parent": "CZetaAttack", "fields": {} + }, + "ProperActor": { + "kind": "struct", + "parent": "CGameObject", + "fields": { + "type": "base::global::StrId", + "x": "float", + "y": "float", + "z": "float", + "unk05": "unsigned", + "unk06": "unsigned", + "unk07": "unsigned", + "components": "base::global::CRntVector" + } + }, + "CGameObject": { + "kind": "struct", + "parent": "base::core::CBaseObject", + "fields": {} + }, + "Component": { + "kind": "struct", + "parent": "CGameObject", + "fields": { + "component_type": "base::global::StrId", + "command": "base::global::StrId", + "arguments": "base::global::CRntVector" + } + }, + "FunctionArgument": { + "kind": "struct", + "parent": "CGameObject", + "fields": { + "type": "base::global::StrId", + "value": "base::global::StrId" + } + }, + "base::global::StrId": { + "kind": "primitive", + "primitive_kind": "string" + }, + "base::core::CBaseObject": { + "kind": "struct", + "parent": null, + "fields": {} + }, + "base::global::CRntVector": { + "kind": "vector", + "value_type": "Component" + }, + + "base::global::CRntVector": { + "kind": "vector", + "value_type": "FunctionArgument" + }, + "float": { + "kind": "primitive", + "primitive_kind": "float" + }, + "unsigned": { + "kind": "primitive", + "primitive_kind": "uint" } } diff --git a/src/mercury_engine_data_structures/sr_resource_names.json b/src/mercury_engine_data_structures/sr_resource_names.json index f7c258a6..19b0df09 100644 --- a/src/mercury_engine_data_structures/sr_resource_names.json +++ b/src/mercury_engine_data_structures/sr_resource_names.json @@ -11613,7 +11613,7 @@ "packs/cutscenes/intrometroidboss_discardables.pkg": 1759505438, "packs/cutscenes/intrometroidlarvasurface.pkg": 366587959, "packs/cutscenes/intrometroidlarvasurface_discardables.pkg": 4046021773, - "packs/cutscenes/introomega.pkg": 723017427, + "packs/cutscenes/introomega.pkg": 3477018848, "packs/cutscenes/introomega_discardables.pkg": 682647711, "packs/cutscenes/introqueen.pkg": 803003252, "packs/cutscenes/introqueen_discardables.pkg": 2787700573, diff --git a/src/mercury_engine_data_structures/type_lib.py b/src/mercury_engine_data_structures/type_lib.py index 3b8f7b9d..560a16d6 100644 --- a/src/mercury_engine_data_structures/type_lib.py +++ b/src/mercury_engine_data_structures/type_lib.py @@ -10,6 +10,8 @@ import construct from mercury_engine_data_structures import dread_data +from mercury_engine_data_structures import samus_returns_data +from mercury_engine_data_structures.game_check import Game, is_dread # from mercury_engine_data_structures.construct_extensions.misc import ErrorWithMessage @@ -203,47 +205,48 @@ def decode_type(name: str, data: dict) -> BaseType: @functools.lru_cache -def all_types() -> Dict[str, BaseType]: +def all_types(game: Game) -> Dict[str, BaseType]: + data = dread_data if is_dread(game) else samus_returns_data return { name: decode_type(name, data) - for name, data in dread_data.get_raw_types().items() + for name, data in data.get_raw_types().items() } @functools.lru_cache -def all_constructs() -> Dict[str, construct.Construct]: +def all_constructs(game: Game) -> Dict[str, construct.Construct]: return { name: type.construct - for name, type in all_types().items() + for name, type in all_types(game).items() } -def get_type(type_name: str, *, follow_typedef: bool = True) -> BaseType: - result = all_types()[type_name] +def get_type(game: Game, type_name: str, *, follow_typedef: bool = True) -> BaseType: + result = all_types(game)[type_name] if follow_typedef and result.kind == TypeKind.TYPEDEF: assert isinstance(result, TypedefType) - return get_type(result.alias, follow_typedef=follow_typedef) + return get_type(game, result.alias, follow_typedef=follow_typedef) return result -def GetTypeConstruct(keyfunc, follow_typedef: bool = True) -> construct.Construct: +def GetTypeConstruct(game: Game, keyfunc, follow_typedef: bool = True) -> construct.Construct: return construct.FocusedSeq( "switch", "key" / construct.Computed(keyfunc), - "type" / construct.Computed(lambda this: get_type(this.key, follow_typedef=follow_typedef).name), + "type" / construct.Computed(lambda this: get_type(game, this.key, follow_typedef=follow_typedef).name), "switch" / construct.Switch( lambda this: this.type, - all_constructs(), + all_constructs(game), construct.Error # ErrorWithMessage(lambda this: f"Unknown type: {this.type}", construct.SwitchError) ) ) -def get_parent_for(type_name: str) -> Optional[str]: - data = get_type(type_name) +def get_parent_for(game: Game, type_name: str) -> Optional[str]: + data = get_type(game, type_name) if data.kind == TypeKind.STRUCT: assert isinstance(data, StructType) @@ -252,7 +255,7 @@ def get_parent_for(type_name: str) -> Optional[str]: return None -def is_child_of(type_name: Optional[str], parent_name: str) -> bool: +def is_child_of(game: Game, type_name: Optional[str], parent_name: str) -> bool: """ Checks if the type_name is a direct or indirect child of the type parent_name """ @@ -262,24 +265,24 @@ def is_child_of(type_name: Optional[str], parent_name: str) -> bool: if type_name is None: return False - return is_child_of(get_parent_for(type_name), parent_name) + return is_child_of(game, get_parent_for(game, type_name), parent_name) @functools.lru_cache -def all_direct_children() -> Dict[str, Set[str]]: +def all_direct_children(game: Game) -> Dict[str, Set[str]]: """ Returns a mapping of type names to all their direct children. """ result = collections.defaultdict(set) - for type_name in all_types().keys(): - if (parent := get_parent_for(type_name)) is not None: + for type_name in all_types(game).keys(): + if (parent := get_parent_for(game, type_name)) is not None: result[parent].add(type_name) return dict(result) -def get_all_children_for(type_name: str) -> Set[str]: +def get_all_children_for(game: Game, type_name: str) -> Set[str]: """ Get all direct and indirect children for a given type. """ @@ -293,6 +296,6 @@ def get_all_children_for(type_name: str) -> Set[str]: continue result.add(next_type) - types_to_check.update(all_direct_children().get(next_type, set())) + types_to_check.update(all_direct_children(game).get(next_type, set())) return result