-
Notifications
You must be signed in to change notification settings - Fork 10
Add more methods to BRSA #280
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
1b7c838
e49ee6d
d89443e
311d7d0
d50c4a6
e389c1a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,13 +3,15 @@ | |
import functools | ||
from typing import TYPE_CHECKING | ||
|
||
from construct import Container, ListContainer | ||
|
||
from mercury_engine_data_structures.base_resource import BaseResource | ||
from mercury_engine_data_structures.formats import standard_format | ||
|
||
if TYPE_CHECKING: | ||
from collections.abc import Iterator | ||
|
||
from construct import Construct, Container | ||
from construct import Construct | ||
|
||
from mercury_engine_data_structures.game_check import Game | ||
|
||
|
@@ -24,8 +26,168 @@ | |
def subarea_setups(self) -> Iterator[Container]: | ||
yield from self.raw.Root.pSubareaManager.vSubareaSetups | ||
|
||
def get_subarea_setup(self, id: str) -> Container: | ||
return next(setup for setup in self.subarea_setups if setup.sId == id) | ||
@property | ||
def charclass_groups(self) -> Iterator[Container]: | ||
yield from self.raw.Root.pSubareaManager.vCharclassGroups | ||
|
||
def get_subarea_setup(self, setup_id: str) -> Container: | ||
"""Gets a setup | ||
param setup_id: the name of the setup | ||
returns: the setup""" | ||
return next(setup for setup in self.subarea_setups if setup.sId == setup_id) | ||
|
||
def add_setup(self, setup_id: str) -> Container: | ||
"""Adds a new setup | ||
param setup_id: the name of the new setup | ||
returns: the newly created setup""" | ||
if next((setup for setup in self.subarea_setups if setup.sId == setup_id), None): | ||
raise ValueError(f"Setup {setup_id} is already present") | ||
|
||
new_setup = Container({"sId": setup_id, "vSubareaConfigs": ListContainer()}) | ||
self.raw.Root.pSubareaManager.vSubareaSetups.append(new_setup) | ||
|
||
return new_setup | ||
|
||
def get_subarea_config(self, subarea_id: str, setup_id: str = "Default") -> Container: | ||
"""Gets a config for a subarea | ||
param subarea_id: the name of the subarea the config is for | ||
param setup_id: the name of the setup the config is in | ||
returns: the config for the subarea""" | ||
return next(config for config in self.get_subarea_setup(setup_id).vSubareaConfigs if config.sId == subarea_id) | ||
|
||
def add_subarea_config( | ||
self, | ||
subarea_id: str, | ||
setup_id: str = "Default", | ||
*, | ||
disable_subarea: bool = False, | ||
camera_distance: float = -1.0, | ||
ignore_camera_offsets: bool = False, | ||
charclass_group: str = "No Enemies", | ||
camera_ids: list[str] = [], | ||
cutscene_ids: list[str] = [], | ||
) -> Container: | ||
"""Adds a config for a subarea | ||
param subarea_id: the name of the subarea the config is for | ||
param setup_id: the name of the setup the config will be in | ||
returns: the newly created config""" | ||
if next( | ||
(config for config in self.get_subarea_setup(setup_id).vSubareaConfigs if config.sId == subarea_id), None | ||
): | ||
raise ValueError(f"Config for {subarea_id} is already present in {setup_id}") | ||
|
||
new_config = Container( | ||
{ | ||
"sId": subarea_id, | ||
"sSetupId": setup_id, | ||
"bDisableSubarea": disable_subarea, | ||
"fCameraZDistance": camera_distance, | ||
"bIgnoreMetroidCameraOffsets": ignore_camera_offsets, | ||
"sCharclassGroupId": charclass_group, | ||
"asItemsIds": ListContainer([""] * 9), | ||
"vsCameraCollisionsIds": ListContainer(camera_ids) if camera_ids else ListContainer(), | ||
"vsCutscenesIds": ListContainer(cutscene_ids) if cutscene_ids else ListContainer(), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. what's with these ternaries? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. these are here because of the mutable default arguments |
||
} | ||
) | ||
self.get_subarea_setup(setup_id).vSubareaConfigs.append(new_config) | ||
|
||
return new_config | ||
|
||
def set_scenario_collider(self, subarea_id: str, collider_name: str, setup_id: str = "Default") -> None: | ||
"""Sets the scenario collider for a subarea | ||
param subarea_id: the name of the subarea | ||
param collider_name: the name of the collider | ||
param setup_id: the name of the setup the subarea is in""" | ||
self.get_subarea_config(subarea_id, setup_id).asItemsIds[0] = collider_name | ||
|
||
def set_light_group(self, subarea_id: str, group_name: str, setup_id: str = "Default") -> None: | ||
"""Sets the light group for a subarea | ||
param subarea_id: the name of the subarea | ||
param group_name: the name of the group | ||
param setup_id: the name of the setup the subarea is in""" | ||
self.get_subarea_config(subarea_id, setup_id).asItemsIds[1] = group_name | ||
|
||
def set_sound_group(self, subarea_id: str, group_name: str, setup_id: str = "Default") -> None: | ||
"""Sets the sound group for a subarea | ||
param subarea_id: the name of the subarea | ||
param collider_name: the name of the group | ||
param setup_id: the name of the setup the subarea is in""" | ||
self.get_subarea_config(subarea_id, setup_id).asItemsIds[2] = group_name | ||
|
||
def set_scene_group(self, subarea_id: str, group_name: str, setup_id: str = "Default") -> None: | ||
"""Sets the scene group for a subarea | ||
param subarea_id: the name of the subarea | ||
param group_name: the name of the group | ||
param setup_id: the name of the setup the subarea is in""" | ||
self.get_subarea_config(subarea_id, setup_id).asItemsIds[3] = group_name | ||
|
||
def set_entity_group(self, subarea_id: str, group_name: str, setup_id: str = "Default") -> None: | ||
"""Sets the entity group for a subarea | ||
param subarea_id: the name of the subarea | ||
param group_name: the name of the group | ||
param setup_id: the name of the setup the subarea is in""" | ||
self.get_subarea_config(subarea_id, setup_id).asItemsIds[4] = group_name | ||
|
||
def set_tilegroup_group(self, subarea_id: str, group_name: str, setup_id: str = "Default") -> None: | ||
"""Sets the tilegroup group for a subarea | ||
param subarea_id: the name of the subarea | ||
param group_name: the name of the group | ||
param setup_id: the name of the setup the subarea is in""" | ||
self.get_subarea_config(subarea_id, setup_id).asItemsIds[5] = group_name | ||
|
||
def set_visual_preset(self, subarea_id: str, preset_name: str, setup_id: str = "Default") -> None: | ||
"""Sets the visual preset for a subarea | ||
param subarea_id: the name of the subarea | ||
param preset_name: the name of the preset | ||
param setup_id: the name of the setup the subarea is in""" | ||
self.get_subarea_config(subarea_id, setup_id).asItemsIds[6] = preset_name | ||
|
||
def set_sound_preset(self, subarea_id: str, preset_name: str, setup_id: str = "Default") -> None: | ||
"""Sets the sound preset for a subarea | ||
param subarea_id: the name of the subarea | ||
param preset_name: the name of the preset | ||
param setup_id: the name of the setup the subarea is in""" | ||
self.get_subarea_config(subarea_id, setup_id).asItemsIds[7] = preset_name | ||
|
||
def set_music_preset(self, subarea_id: str, preset_name: str, setup_id: str = "Default") -> None: | ||
"""Sets the music preset for a subarea | ||
param subarea_id: the name of the subarea | ||
param preset_name: the name of the preset | ||
param setup_id: the name of the setup the subarea is in""" | ||
self.get_subarea_config(subarea_id, setup_id).asItemsIds[8] = preset_name | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ok I appreciate your concern about it being out of scope of this PR to abolish the use of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. do you mean just There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. it would be easiest to add a wrapper that gets returned by something like the BMSLD pr: https://github.com/randovania/mercury-engine-data-structures/pull/258/files#diff-bf70997618c433f3cb8e9e854527f73c9a3a8327c932b8bd6101897ec7c55857R145 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yeah, definitely seems easiest. |
||
|
||
def get_charclass_group(self, group_id: str) -> Container: | ||
"""Gets a charclass group | ||
param group_id: the name of the group | ||
returns: the charclass group""" | ||
return next(group for group in self.charclass_groups if group.sId == group_id) | ||
|
||
def add_charclass_group(self, group_id: str, charclasses: list[str] = []) -> Container: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. mutable default argument |
||
"""Adds a new charclass group | ||
param group_id: the name of the new group | ||
param charclasses: the charclasses in this group | ||
returns: the newly created charclass group""" | ||
if next((group for group in self.charclass_groups if group.sId == group_id), None): | ||
raise ValueError(f"Charclass {group_id} is already present") | ||
|
||
new_group = Container( | ||
{"sId": group_id, "vsCharClassesIds": ListContainer(charclasses) if charclasses else ListContainer()} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. another weird ternary |
||
) | ||
self.raw.Root.pSubareaManager.vCharclassGroups.append(new_group) | ||
|
||
def get_subarea_config(self, id: str, setup_id: str) -> Container: | ||
return next(config for config in self.get_subarea_setup(setup_id).vSubareaConfigs if config.sId == id) | ||
return new_group |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
as a rule, you should never use a mutable value as a default argument
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wanted to show that if it's not provided, it defaults to empty, hence the ternaries below
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
in that case do
cutscene_ids: list[str] | None = None
andif cutscene_ids is None: cutscene_ids = []
and drop the ternariesThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
facepalming at myself for not doing that to begin with. thanks
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
actually, are the ternaries actually that bad? I feel like it only needs to default to None and then the ternary is just more compact.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is more compact and readable imo than
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
to be fair, you only need
if cutscene_ids
there. I also personally like inlining it, but maybe that's the JavaScript background talking, haha. if the former way is more pythonic, I'll just go with thatThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes, please do. though if you're making a new wrapper class, you should also convert this function into a classmethod to create the wrapper and then have this function accept an instance of the wrapper class