mirror of
https://github.com/Faraphel/Atlas-Install.git
synced 2025-07-02 02:38:30 +02:00
some of the Track class now remember the mod_config object for easier call. default_track_attributes have been added to set default value of the track attribute object to avoid unreadable safe eval of getattr
This commit is contained in:
parent
622f43c66e
commit
fb8d20c08f
13 changed files with 146 additions and 105 deletions
|
@ -1,17 +1,17 @@
|
|||
{
|
||||
"GAME_REGION": "str(extracted_game.original_game.wit_path.region)",
|
||||
|
||||
"TRACK_TEXT_SCORE": "get_color(name=['yellow', 'orange', 'dark red', 'azure'][getattr(track, 'warning', 0)]).color_text(f'\\\\x{65296+track.score:x} ') if hasattr(track, 'score') else ''",
|
||||
"TRACK_TEXT_PREFIX": "f'{prefix} ' if (prefix := get_tag_template('prefix', '')) else ''",
|
||||
"TRACK_TEXT_SUFFIX": "f' ({suffix})' if (suffix := get_tag_template('suffix', '')) else ''",
|
||||
"TRACK_TEXT_NAME": "getattr(track, 'name', '')",
|
||||
"TRACK_TEXT_AUTHORS": "'\\\\n'.join(author) if isinstance(author := getattr(track, 'author', '/'), list) else author",
|
||||
"TRACK_TEXT_WARNING_IF_DISABLED": "get_color(name='red').color_text('/') if getattr(track, 'warning', 0) != 0 else ''",
|
||||
"TRACK_TEXT_SCORE": "get_color(name=['yellow', 'orange', 'dark red', 'azure'][track.warning]).color_text(f'\\\\x{65296+track.score:x} ') if track.score else ''",
|
||||
"TRACK_TEXT_PREFIX": "f'{prefix} ' if (prefix := track.get_tag_template('prefix', '')) else ''",
|
||||
"TRACK_TEXT_SUFFIX": "f' ({suffix})' if (suffix := track.get_tag_template('suffix', '')) else ''",
|
||||
"TRACK_TEXT_NAME": "track.name",
|
||||
"TRACK_TEXT_AUTHORS": "'\\\\n'.join(track.author) if isinstance(track.author, list) else track.author",
|
||||
"TRACK_TEXT_WARNING_IF_DISABLED": "get_color(name='red').color_text('/') if track.warning != 0 else ''",
|
||||
"TRACK_TEXT_HIGHLIGHT_START": "get_color(name='azure').raw if eval(highlight_if if (highlight_if := mod_config.specific_settings['highlight_if'].value) is not None else 'False', env={'track': track, 'mod_config': mod_config}) is True else ''",
|
||||
"TRACK_TEXT_HIGHLIGHT_END": "get_color(name='off').raw",
|
||||
|
||||
"SETTINGS_MODE": "mod_config.specific_settings['mode'].value",
|
||||
"SETTINGS_BALANCING": "mod_config.specific_settings['balancing'].value",
|
||||
|
||||
"IF_NO_WARNING": "if getattr(track, 'warning', 0) == 0 else ''"
|
||||
"IF_NO_WARNING": "if track.warning == 0 else ''"
|
||||
}
|
|
@ -18,7 +18,7 @@
|
|||
}
|
||||
},
|
||||
"replace_random_new": {
|
||||
"default": "'Retro' not in getattr(track, 'tags', []) and getattr(track, 'warning', 0) == 0"
|
||||
"default": "'Retro' not in track.tags and track.warning == 0"
|
||||
}
|
||||
},
|
||||
"specific_settings": {
|
||||
|
@ -39,7 +39,7 @@
|
|||
"fr": "Surligner si"
|
||||
},
|
||||
"type": "string",
|
||||
"default": "'v' + getattr(track, 'since_version', '') == mod_config.version",
|
||||
"default": "'v' + track.since_version == mod_config.version",
|
||||
"preview": "track_selecting"
|
||||
},
|
||||
"balancing": {
|
||||
|
@ -124,9 +124,18 @@
|
|||
"GP"
|
||||
],
|
||||
|
||||
"track_file_template": "{{ getattr(track, 'sha1', '_') }}",
|
||||
"multiplayer_disable_if": "getattr(track, 'warning', 0) >= 1",
|
||||
"track_file_template": "{{ track.sha1 }}",
|
||||
"multiplayer_disable_if": "track.warning >= 1",
|
||||
|
||||
"default_track_attributes": {
|
||||
"since_version": "",
|
||||
"sha1": "_",
|
||||
"name": "",
|
||||
"author": "/",
|
||||
"warning": 0,
|
||||
"score": 0,
|
||||
"tags": []
|
||||
},
|
||||
"tracks": [
|
||||
{
|
||||
"name":"Mario Kart Stadium",
|
||||
|
|
|
@ -52,9 +52,7 @@ class Window(AbstractPreviewWindow):
|
|||
# insert all the tracks representation
|
||||
for track in self.mod_config.get_all_arenas_tracks(ignore_filter=True):
|
||||
try:
|
||||
track_repr = track.repr_format(
|
||||
self.mod_config, self.entry_template_input.get()
|
||||
)
|
||||
track_repr = track.repr_format(template=self.entry_template_input.get())
|
||||
|
||||
offset: int = 0 # the color tag is removed at every sub, so keep track of the offset
|
||||
tags: list[tuple[int | None, str | None]] = [] # list of all the position of the tags, with the offset
|
||||
|
|
|
@ -80,7 +80,7 @@ class Window(AbstractPreviewWindow):
|
|||
self.text_track_select.configure(state=tkinter.NORMAL)
|
||||
self.text_track_select.delete(1.0, tkinter.END)
|
||||
|
||||
template_func = self.mod_config.safe_eval(self.entry_template_input.get(), args=["track"])
|
||||
template_func = self.mod_config.safe_eval(template=self.entry_template_input.get(), args=["track"])
|
||||
|
||||
for track in self.mod_config.get_all_tracks(ignore_filter=True):
|
||||
value: bool = template_func(track) is True
|
||||
|
|
|
@ -7,12 +7,13 @@ from PIL import Image
|
|||
|
||||
from source import threaded
|
||||
from source.mkw import Tag
|
||||
from source.mkw.Cup import Cup
|
||||
from source.mkw.Track.Cup import Cup
|
||||
from source.mkw.collection import MKWColor, Slot
|
||||
from source.mkw.ModSettings import AbstractModSettings
|
||||
from source.mkw.Track import CustomTrack, DefaultTrack, Arena
|
||||
from source.progress import Progress
|
||||
from source.safe_eval import safe_eval, multiple_safe_eval
|
||||
from source.safe_eval.safe_eval import safe_eval
|
||||
from source.safe_eval.multiple_safe_eval import multiple_safe_eval
|
||||
from source.wt.szs import SZSPath
|
||||
from source.translation import translate as _
|
||||
|
||||
|
@ -78,8 +79,9 @@ class ModConfig:
|
|||
version: str = "v1.0.0"
|
||||
variant: str = "01"
|
||||
|
||||
_tracks: list["Track | TrackGroup"] = field(default_factory=list)
|
||||
_arenas: list["Arena"] = field(default_factory=list)
|
||||
_tracks: list["Track | TrackGroup | dict"] = field(default_factory=list)
|
||||
default_track_attributes: dict[str, any] = field(default_factory=dict)
|
||||
_arenas: list["Arena | dict"] = field(default_factory=list)
|
||||
track_file_template: "TemplateMultipleSafeEval" = "{{ getattr(track, 'sha1', '_') }}"
|
||||
multiplayer_disable_if: "TemplateSafeEval" = "False"
|
||||
|
||||
|
@ -94,8 +96,8 @@ class ModConfig:
|
|||
macros: dict[str, "TemplateSafeEval"] = field(default_factory=dict)
|
||||
messages: dict[str, dict[str, "TemplateMultipleSafeEval"]] = field(default_factory=dict)
|
||||
|
||||
global_settings: dict[str, "AbstractModSettings"] = field(default_factory=dict)
|
||||
specific_settings: dict[str, "AbstractModSettings"] = field(default_factory=dict)
|
||||
global_settings: dict[str, "AbstractModSettings | dict"] = field(default_factory=dict)
|
||||
specific_settings: dict[str, "AbstractModSettings | dict"] = field(default_factory=dict)
|
||||
|
||||
lpar_template: "TemplateMultipleSafeEval" = "normal.lpar"
|
||||
|
||||
|
@ -103,6 +105,15 @@ class ModConfig:
|
|||
self.path = Path(self.path)
|
||||
if self.nickname is None: self.nickname = self.name
|
||||
|
||||
self._tracks = [CustomTrack.from_dict(self, track) for track in self._tracks if isinstance(track, dict)]
|
||||
self._arenas = [Arena.from_dict(self, arena) for arena in self._arenas if isinstance(arena, dict)]
|
||||
|
||||
self.global_settings = {name: AbstractModSettings.get(data) for name, data in merge_dict(
|
||||
default_global_settings, self.global_settings, dict_keys=default_global_settings.keys()
|
||||
# Avoid modder to add their own settings to globals one
|
||||
).items()}
|
||||
self.specific_settings = {name: AbstractModSettings.get(data) for name, data in self.specific_settings.items()}
|
||||
|
||||
def __hash__(self) -> int:
|
||||
return hash(self.name)
|
||||
|
||||
|
@ -125,21 +136,11 @@ class ModConfig:
|
|||
kwargs = config_dict | {
|
||||
"path": path,
|
||||
|
||||
"_tracks": [CustomTrack.from_dict(track) for track in config_dict.pop("tracks", [])],
|
||||
"_arenas": [Arena.from_dict(arena) for arena in config_dict.pop("arenas", [])],
|
||||
"_tracks": config_dict.pop("tracks", []),
|
||||
"_arenas": config_dict.pop("arenas", []),
|
||||
|
||||
"macros": macros,
|
||||
"messages": messages,
|
||||
|
||||
"global_settings": {name: AbstractModSettings.get(data) for name, data in merge_dict(
|
||||
default_global_settings,
|
||||
config_dict.get("global_settings", {}),
|
||||
dict_keys=default_global_settings.keys() # Avoid modder to add their own settings to globals one
|
||||
).items()},
|
||||
|
||||
"specific_settings": {name: AbstractModSettings.get(data) for name, data in config_dict.get(
|
||||
"specific_settings", {}
|
||||
).items()},
|
||||
}
|
||||
|
||||
return cls(**kwargs)
|
||||
|
@ -233,7 +234,7 @@ class ModConfig:
|
|||
# filter_template_func is the function checking if the track should be included. If no parameter is set,
|
||||
# then always return True
|
||||
filter_template_func: Callable = self.safe_eval(
|
||||
filter_template if filter_template is not None else "True", args=["track"]
|
||||
template=filter_template if filter_template is not None else "True", args=["track"]
|
||||
)
|
||||
|
||||
# if a sorting function is set, use it. If no function is set, but sorting is not disabled, use settings.
|
||||
|
@ -247,7 +248,7 @@ class ModConfig:
|
|||
# wrap the iterator inside a sort function
|
||||
iterator = sorted(iterator, key=sorting_template_func)
|
||||
|
||||
# Go though all the tracks and filter them if enabled
|
||||
# Go through all the tracks and filter them if enabled
|
||||
for track in iterator:
|
||||
yield track
|
||||
|
||||
|
@ -267,13 +268,13 @@ class ModConfig:
|
|||
|
||||
if len(track_buffer) > 4:
|
||||
current_tag_count += 1
|
||||
yield Cup(tracks=track_buffer, cup_name=f"{current_tag_name}/{current_tag_count}")
|
||||
yield Cup(mod_config=self, tracks=track_buffer, cup_name=f"{current_tag_name}/{current_tag_count}")
|
||||
track_buffer = []
|
||||
|
||||
# if there is still tracks in the buffer, create a cup with them and fill with default>
|
||||
if len(track_buffer) > 0:
|
||||
track_buffer.extend([DefaultTrack()] * (4 - len(track_buffer)))
|
||||
yield Cup(tracks=track_buffer, cup_name=f"{current_tag_name}/{current_tag_count + 1}")
|
||||
track_buffer.extend([DefaultTrack(mod_config=self)] * (4 - len(track_buffer)))
|
||||
yield Cup(mod_config=self, tracks=track_buffer, cup_name=f"{current_tag_name}/{current_tag_count + 1}")
|
||||
|
||||
def get_unordered_cups(self) -> Generator["Cup", None, None]:
|
||||
"""
|
||||
|
@ -289,13 +290,13 @@ class ModConfig:
|
|||
track_buffer.append(track)
|
||||
|
||||
if len(track_buffer) > 4:
|
||||
yield Cup(tracks=track_buffer)
|
||||
yield Cup(mod_config=self, tracks=track_buffer)
|
||||
track_buffer = []
|
||||
|
||||
# if there is still tracks in the buffer, create a cup with them and fill with default
|
||||
if len(track_buffer) > 0:
|
||||
track_buffer.extend([DefaultTrack()] * (4 - len(track_buffer)))
|
||||
yield Cup(tracks=track_buffer)
|
||||
track_buffer.extend([DefaultTrack(mod_config=self)] * (4 - len(track_buffer)))
|
||||
yield Cup(mod_config=self, tracks=track_buffer)
|
||||
|
||||
def get_cups(self) -> Generator["Cup", None, None]:
|
||||
"""
|
||||
|
@ -328,11 +329,11 @@ class ModConfig:
|
|||
|
||||
for cup in self.get_cups():
|
||||
# get all the cup ctfile, use "-" for the template since the track's name are not used here
|
||||
ctfile += cup.get_ctfile(mod_config=self, template=template)
|
||||
ctfile += cup.get_ctfile(template=template)
|
||||
|
||||
ctfile_override_property = "[SETUP-ARENA]\n"
|
||||
for arena in self.get_arenas():
|
||||
arena_definition, arena_override_property = arena.get_ctfile(mod_config=self, template=template)
|
||||
arena_definition, arena_override_property = arena.get_ctfile(template=template)
|
||||
ctfile += arena_definition
|
||||
ctfile_override_property += arena_override_property
|
||||
|
||||
|
@ -433,7 +434,7 @@ class ModConfig:
|
|||
|
||||
for track in self.get_all_arenas_tracks():
|
||||
track_file: Path = next(
|
||||
track_directory.rglob(f"{track.repr_format(self, self.track_file_template)}.*")
|
||||
track_directory.rglob(f"{track.repr_format(template=self.track_file_template)}.*")
|
||||
)
|
||||
|
||||
@threaded
|
||||
|
|
|
@ -49,11 +49,11 @@ class FormatOriginalTrackLayer(AbstractLayer):
|
|||
else: tag = "Wii"
|
||||
|
||||
patched_name = CustomTrack(
|
||||
mod_config=patch.mod_config,
|
||||
name=name,
|
||||
tags=[tag]
|
||||
).repr_format(
|
||||
patch.mod_config,
|
||||
self.template
|
||||
template=self.template
|
||||
)
|
||||
|
||||
return f" {id}\t= {patched_name}"
|
||||
|
|
|
@ -15,13 +15,17 @@ class TrackForbiddenCustomAttribute(Exception):
|
|||
|
||||
|
||||
class AbstractTrack(ABC):
|
||||
|
||||
mod_config: "ModConfig"
|
||||
music: Slot.Slot
|
||||
special: Slot.Slot
|
||||
tags: list[Tag]
|
||||
weight: int
|
||||
|
||||
def __init__(self, music: Slot.Slot = "T11", special: Slot.Slot = "T11",
|
||||
def __init__(self, mod_config: "ModConfig", music: Slot.Slot = "T11", special: Slot.Slot = "T11",
|
||||
tags: list[Tag] = None, weight: int = 1, **kwargs):
|
||||
|
||||
self.mod_config = mod_config
|
||||
self.music = Slot.find(music)
|
||||
self.special = Slot.find(special)
|
||||
self.tags = tags if tags is not None else []
|
||||
|
@ -30,7 +34,8 @@ class AbstractTrack(ABC):
|
|||
# others not mandatory attributes
|
||||
for key, value in kwargs.items():
|
||||
# if the attribute start with __, this is a magic attribute, and it should not be modified
|
||||
if "__" in key or hasattr(self, key): raise TrackForbiddenCustomAttribute(key)
|
||||
if "__" in key: raise TrackForbiddenCustomAttribute(key)
|
||||
# TODO: check potential security issue with setattr and already implemented method and attribute
|
||||
setattr(self, key, value)
|
||||
|
||||
def __repr__(self):
|
||||
|
@ -45,34 +50,33 @@ class AbstractTrack(ABC):
|
|||
yield self
|
||||
|
||||
@abstractmethod
|
||||
def repr_format(self, mod_config: "ModConfig", template: "TemplateMultipleSafeEval") -> str:
|
||||
def repr_format(self, template: "TemplateMultipleSafeEval") -> str:
|
||||
"""
|
||||
return the representation of the track from the format
|
||||
:param template: template for the way the text will be represented
|
||||
:param mod_config: configuration of the mod
|
||||
:return: formatted representation of the track
|
||||
"""
|
||||
...
|
||||
|
||||
@property
|
||||
@abstractmethod
|
||||
def get_filename(self, mod_config: "ModConfig") -> str:
|
||||
def filename(self) -> str:
|
||||
"""
|
||||
Return the filename of the track
|
||||
:param mod_config: the mod_config object
|
||||
:return: the filename of the track
|
||||
"""
|
||||
...
|
||||
|
||||
@property
|
||||
@abstractmethod
|
||||
def is_new(self, mod_config: "ModConfig") -> bool:
|
||||
def is_new(self) -> bool:
|
||||
"""
|
||||
Return if the track should be considered as new for random selection
|
||||
:param mod_config: mod configuration
|
||||
:return: is the track new
|
||||
"""
|
||||
...
|
||||
|
||||
def get_ctfile(self, mod_config: "ModConfig", template: "TemplateMultipleSafeEval", hidden: bool = False) -> str:
|
||||
def get_ctfile(self, template: "TemplateMultipleSafeEval", hidden: bool = False) -> str:
|
||||
"""
|
||||
return the ctfile of the track
|
||||
:hidden: if the track is in a group
|
||||
|
@ -80,11 +84,11 @@ class AbstractTrack(ABC):
|
|||
:return: ctfile
|
||||
"""
|
||||
category: str = "H" if hidden else "T"
|
||||
name: str = self.repr_format(mod_config=mod_config, template=template)
|
||||
filename: str = self.get_filename(mod_config=mod_config)
|
||||
name: str = self.repr_format(template=template)
|
||||
filename: str = self.filename
|
||||
flags: int = (
|
||||
(0x04 if hidden else 0) |
|
||||
(0x01 if self.is_new(mod_config) else 0)
|
||||
(0x01 if self.is_new else 0)
|
||||
)
|
||||
|
||||
return (
|
||||
|
|
|
@ -16,13 +16,20 @@ class ArenaForbiddenCustomAttribute(Exception):
|
|||
|
||||
|
||||
class Arena(RealArenaTrack):
|
||||
"""
|
||||
Represent an arena object
|
||||
"""
|
||||
|
||||
mod_config: "ModConfig"
|
||||
slot: Slot.Slot
|
||||
music: Slot.Slot
|
||||
special: Slot.Slot
|
||||
tags: list[Tag]
|
||||
|
||||
def __init__(self, slot: Slot.Slot, music: Slot.Slot = None, special: Slot.Slot = None,
|
||||
def __init__(self, mod_config: "ModConfig", slot: Slot.Slot, music: Slot.Slot = None, special: Slot.Slot = None,
|
||||
tags: list[Tag] = None, **kwargs):
|
||||
|
||||
self.mod_config = mod_config
|
||||
self.slot = Slot.find(slot)
|
||||
self.music = Slot.find(music if music is not None else slot)
|
||||
self.special = Slot.find(special if special is not None else slot)
|
||||
|
@ -31,26 +38,25 @@ class Arena(RealArenaTrack):
|
|||
# others not mandatory attributes
|
||||
for key, value in kwargs.items():
|
||||
# if the attribute start with __, this is a magic attribute, and it should not be modified
|
||||
if "__" in key or hasattr(self, key): raise ArenaForbiddenCustomAttribute(key)
|
||||
if "__" in key: raise ArenaForbiddenCustomAttribute(key)
|
||||
setattr(self, key, value)
|
||||
|
||||
def __repr__(self):
|
||||
return f"<{self.__class__.__name__} name={getattr(self, 'name', '/')} tags={getattr(self, 'tags', '/')}>"
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls, arena_dict: dict[str, any]) -> "Arena":
|
||||
return cls(**arena_dict)
|
||||
def from_dict(cls, mod_config: "ModConfig", arena_dict: dict[str, any]) -> "Arena":
|
||||
return cls(mod_config, **arena_dict)
|
||||
|
||||
def get_ctfile(self, mod_config: "ModConfig", template: "TemplateMultipleSafeEval") -> (str, str):
|
||||
def get_ctfile(self, template: "TemplateMultipleSafeEval") -> (str, str):
|
||||
"""
|
||||
Return the ctfile for the arena and the redefinition of the slot property
|
||||
:param mod_config: the mod_config object
|
||||
:param template: the template of the track name
|
||||
:return: the ctfile for the arena and the redefinition of the slot property
|
||||
"""
|
||||
|
||||
name: str = self.repr_format(mod_config=mod_config, template=template)
|
||||
filename: str = self.get_filename(mod_config=mod_config)
|
||||
name: str = self.repr_format(template=template)
|
||||
filename: str = self.filename
|
||||
|
||||
return (
|
||||
(
|
||||
|
|
|
@ -12,10 +12,12 @@ class Cup:
|
|||
class that represent a mario kart wii track cup
|
||||
"""
|
||||
|
||||
__slots__ = ["_tracks", "cup_name", "cup_id"]
|
||||
__slots__ = ["_tracks", "cup_name", "cup_id", "mod_config"]
|
||||
_last_cup_id = 0
|
||||
mod_config: "ModConfig"
|
||||
|
||||
def __init__(self, tracks: list["Track | TrackGroup"], cup_name: str | None = None):
|
||||
def __init__(self, mod_config: "ModConfig", tracks: list["Track | TrackGroup"], cup_name: str | None = None):
|
||||
self.mod_config = mod_config
|
||||
self._tracks = tracks[:4]
|
||||
|
||||
self.cup_id = self.__class__._last_cup_id
|
||||
|
@ -25,7 +27,7 @@ class Cup:
|
|||
def __repr__(self):
|
||||
return f"<Cup name={self.cup_name} id={self.cup_id} tracks={self._tracks}>"
|
||||
|
||||
def get_default_cticon(self, mod_config: "ModConfig") -> Image.Image:
|
||||
def get_default_cticon(self) -> Image.Image:
|
||||
"""
|
||||
Get the default cticon for this cup
|
||||
:return: the default cticon
|
||||
|
@ -33,7 +35,7 @@ class Cup:
|
|||
from source.mkw.ModConfig import CT_ICON_SIZE
|
||||
|
||||
ct_icon = Image.new("RGBA", (CT_ICON_SIZE, CT_ICON_SIZE))
|
||||
default_font_path = str(mod_config.get_default_font().resolve())
|
||||
default_font_path = str(self.mod_config.get_default_font().resolve())
|
||||
draw = ImageDraw.Draw(ct_icon)
|
||||
|
||||
draw.text(
|
||||
|
@ -63,15 +65,15 @@ class Cup:
|
|||
path = mod_config.get_mod_directory() / f"_CUPS/{self.cup_name}.png"
|
||||
if path.exists(): return Image.open(path)
|
||||
# if the icon doesn't exist, use the default automatically generated one
|
||||
return self.get_default_cticon(mod_config=mod_config)
|
||||
return self.get_default_cticon()
|
||||
|
||||
def get_ctfile(self, mod_config: "ModConfig", template: "TemplateMultipleSafeEval") -> str:
|
||||
def get_ctfile(self, template: "TemplateMultipleSafeEval") -> str:
|
||||
"""
|
||||
Get the ctfile for this cup
|
||||
:return: the ctfile
|
||||
"""
|
||||
ctfile = f'C "{self.cup_name}"\n'
|
||||
for track in self._tracks: ctfile += track.get_ctfile(mod_config, template)
|
||||
for track in self._tracks: ctfile += track.get_ctfile(template=template)
|
||||
ctfile += "\n"
|
||||
|
||||
return ctfile
|
|
@ -15,19 +15,21 @@ class CustomTrack(RealArenaTrack, AbstractTrack):
|
|||
return f"<{self.__class__.__name__} name={getattr(self, 'name', '/')} tags={getattr(self, 'tags', '/')}>"
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls, track_dict: dict) -> "Track | TrackGroup":
|
||||
def from_dict(cls, mod_config: "ModConfig", track_dict: dict) -> "Track | TrackGroup":
|
||||
"""
|
||||
create a track from a dict, or create a track group is it is a group
|
||||
:param mod_config: the mod configuration
|
||||
:param track_dict: dict containing the track information
|
||||
:return: Track
|
||||
"""
|
||||
if "group" in track_dict:
|
||||
from source.mkw.Track.TrackGroup import TrackGroup
|
||||
return TrackGroup.from_dict(track_dict)
|
||||
return cls(**track_dict)
|
||||
return TrackGroup.from_dict(mod_config, track_dict)
|
||||
return cls(mod_config, **track_dict)
|
||||
|
||||
def is_new(self, mod_config: "ModConfig") -> bool:
|
||||
return mod_config.safe_eval(
|
||||
mod_config.global_settings["replace_random_new"].value,
|
||||
@property
|
||||
def is_new(self) -> bool:
|
||||
return self.mod_config.safe_eval(
|
||||
self.mod_config.global_settings["replace_random_new"].value,
|
||||
args=["track"]
|
||||
)(track=self) is True
|
||||
|
|
|
@ -7,11 +7,13 @@ if TYPE_CHECKING:
|
|||
|
||||
|
||||
class DefaultTrack(AbstractTrack):
|
||||
def repr_format(self, mod_config: "ModConfig", template: str) -> str:
|
||||
def repr_format(self, template: str) -> str:
|
||||
return " " # the name is always a blank space. Using nothing result in the filename being used instead
|
||||
|
||||
def get_filename(self, mod_config: "ModConfig") -> str:
|
||||
@property
|
||||
def filename(self) -> str:
|
||||
return "beginner_course" # by default, use the T11 track, beginner_course
|
||||
|
||||
def is_new(self, mod_config: "ModConfig") -> bool:
|
||||
@property
|
||||
def is_new(self) -> bool:
|
||||
return False # default track are never selected for random cup
|
||||
|
|
|
@ -12,32 +12,29 @@ class RealArenaTrack:
|
|||
(For example, DefaultTrack is not considered a real track class)
|
||||
"""
|
||||
|
||||
mod_config: "ModConfig"
|
||||
tags: list["Tag"]
|
||||
|
||||
def get_tag_template(self, mod_config: "ModConfig", template_name: str, default: any = None) -> any:
|
||||
def get_tag_template(self, template_name: str, default: any = None) -> any:
|
||||
"""
|
||||
Return the tag template found in templates. If not found, return default
|
||||
:param mod_config: mod configuration
|
||||
:param template_name: name of the template of the tags
|
||||
:param default: default value if no tag template is found
|
||||
:return: formatted representation of the tag
|
||||
"""
|
||||
for tag in filter(lambda tag: tag in mod_config.tags_templates[template_name], self.tags):
|
||||
return mod_config.multiple_safe_eval(
|
||||
mod_config.tags_templates[template_name][tag],
|
||||
for tag in filter(lambda tag: tag in self.mod_config.tags_templates[template_name], self.tags):
|
||||
return self.mod_config.multiple_safe_eval(
|
||||
template=self.mod_config.tags_templates[template_name][tag],
|
||||
args=["tag"],
|
||||
)(tag=tag)
|
||||
return default
|
||||
|
||||
def repr_format(self, mod_config: "ModConfig", template: "TemplateMultipleSafeEval") -> str:
|
||||
return mod_config.multiple_safe_eval(
|
||||
template,
|
||||
args=["track", "get_tag_template"]
|
||||
)(
|
||||
track=self,
|
||||
get_tag_template=lambda *args, **kwargs: self.get_tag_template(mod_config, *args, **kwargs)
|
||||
# get_tag_template can't be in env because it is dependent of the track self
|
||||
)
|
||||
def repr_format(self, template: "TemplateMultipleSafeEval") -> str:
|
||||
return self.mod_config.multiple_safe_eval(template=template, args=["track"])(track=self)
|
||||
|
||||
def get_filename(self, mod_config: "ModConfig") -> str:
|
||||
return self.repr_format(mod_config=mod_config, template=mod_config.track_file_template)
|
||||
@property
|
||||
def filename(self) -> str:
|
||||
return self.repr_format(template=self.mod_config.track_file_template)
|
||||
|
||||
def __getattr__(self, item):
|
||||
return self.mod_config.default_track_attributes.get(item, None)
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
from typing import Generator, TYPE_CHECKING
|
||||
|
||||
from source.mkw import Tag
|
||||
from source.translation import translate as _
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from source import TemplateMultipleSafeEval
|
||||
|
@ -8,11 +9,29 @@ if TYPE_CHECKING:
|
|||
from source.mkw.ModConfig import ModConfig
|
||||
|
||||
|
||||
class TrackGroupForbiddenCustomAttribute(Exception):
|
||||
def __init__(self, attribute_name: str):
|
||||
super().__init__(_("FORBIDDEN_TRACKGROUP_ATTRIBUTE", " : ", repr(attribute_name)))
|
||||
|
||||
|
||||
class TrackGroup:
|
||||
def __init__(self, tracks: list["CustomTrack"] = None, tags: list[Tag] = None):
|
||||
|
||||
mod_config: "ModConfig"
|
||||
tracks: list["CustomTrack"]
|
||||
tags: list["Tag"]
|
||||
|
||||
def __init__(self, mod_config: "ModConfig", tracks: list["CustomTrack"] = None,
|
||||
tags: list["Tag"] = None, **kwargs):
|
||||
self.mod_config = mod_config
|
||||
self.tracks = tracks if tracks is not None else []
|
||||
self.tags = tags if tags is not None else []
|
||||
|
||||
# others not mandatory attributes
|
||||
for key, value in kwargs.items():
|
||||
# if the attribute start with __, this is a magic attribute, and it should not be modified
|
||||
if "__" in key: raise TrackGroupForbiddenCustomAttribute(key)
|
||||
setattr(self, key, value)
|
||||
|
||||
def get_tracks(self) -> Generator["CustomTrack", None, None]:
|
||||
"""
|
||||
Get all the track elements
|
||||
|
@ -22,27 +41,28 @@ class TrackGroup:
|
|||
yield from track.get_tracks()
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls, group_dict: dict) -> "TrackGroup | Track":
|
||||
def from_dict(cls, mod_config: "ModConfig", group_dict: dict) -> "TrackGroup | Track":
|
||||
"""
|
||||
create a track group from a dict, or create a track from the dict if not a group
|
||||
:param mod_config: the mod configuration
|
||||
:param group_dict: dict containing the track information
|
||||
:return: TrackGroup or Track
|
||||
"""
|
||||
from source.mkw.Track.CustomTrack import CustomTrack
|
||||
|
||||
if "group" not in group_dict: return CustomTrack.from_dict(group_dict)
|
||||
if "group" not in group_dict: return CustomTrack.from_dict(mod_config, group_dict)
|
||||
return cls(
|
||||
tracks=[CustomTrack.from_dict(track) for track in group_dict["group"]],
|
||||
tags=group_dict.get("tags"),
|
||||
tracks=[CustomTrack.from_dict(mod_config, track) for track in group_dict["group"]],
|
||||
**group_dict,
|
||||
)
|
||||
|
||||
def get_ctfile(self, mod_config: "ModConfig", template: "TemplateMultipleSafeEval") -> str:
|
||||
def get_ctfile(self, template: "TemplateMultipleSafeEval") -> str:
|
||||
"""
|
||||
return the ctfile of the track group
|
||||
:return: ctfile
|
||||
"""
|
||||
ctfile = f'T T11; T11; 0x02; "-"; "info"; "-"\n'
|
||||
for track in self.get_tracks():
|
||||
ctfile += track.get_ctfile(template=template, mod_config=mod_config, hidden=True)
|
||||
ctfile += track.get_ctfile(template=template, hidden=True)
|
||||
|
||||
return ctfile
|
||||
|
|
Loading…
Reference in a new issue