mirror of
https://github.com/Faraphel/Atlas-Install.git
synced 2025-07-02 10:48:29 +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)",
|
"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_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 := get_tag_template('prefix', '')) else ''",
|
"TRACK_TEXT_PREFIX": "f'{prefix} ' if (prefix := track.get_tag_template('prefix', '')) else ''",
|
||||||
"TRACK_TEXT_SUFFIX": "f' ({suffix})' if (suffix := get_tag_template('suffix', '')) else ''",
|
"TRACK_TEXT_SUFFIX": "f' ({suffix})' if (suffix := track.get_tag_template('suffix', '')) else ''",
|
||||||
"TRACK_TEXT_NAME": "getattr(track, 'name', '')",
|
"TRACK_TEXT_NAME": "track.name",
|
||||||
"TRACK_TEXT_AUTHORS": "'\\\\n'.join(author) if isinstance(author := getattr(track, 'author', '/'), list) else author",
|
"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 getattr(track, 'warning', 0) != 0 else ''",
|
"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_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",
|
"TRACK_TEXT_HIGHLIGHT_END": "get_color(name='off').raw",
|
||||||
|
|
||||||
"SETTINGS_MODE": "mod_config.specific_settings['mode'].value",
|
"SETTINGS_MODE": "mod_config.specific_settings['mode'].value",
|
||||||
"SETTINGS_BALANCING": "mod_config.specific_settings['balancing'].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": {
|
"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": {
|
"specific_settings": {
|
||||||
|
@ -39,7 +39,7 @@
|
||||||
"fr": "Surligner si"
|
"fr": "Surligner si"
|
||||||
},
|
},
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"default": "'v' + getattr(track, 'since_version', '') == mod_config.version",
|
"default": "'v' + track.since_version == mod_config.version",
|
||||||
"preview": "track_selecting"
|
"preview": "track_selecting"
|
||||||
},
|
},
|
||||||
"balancing": {
|
"balancing": {
|
||||||
|
@ -124,9 +124,18 @@
|
||||||
"GP"
|
"GP"
|
||||||
],
|
],
|
||||||
|
|
||||||
"track_file_template": "{{ getattr(track, 'sha1', '_') }}",
|
"track_file_template": "{{ track.sha1 }}",
|
||||||
"multiplayer_disable_if": "getattr(track, 'warning', 0) >= 1",
|
"multiplayer_disable_if": "track.warning >= 1",
|
||||||
|
|
||||||
|
"default_track_attributes": {
|
||||||
|
"since_version": "",
|
||||||
|
"sha1": "_",
|
||||||
|
"name": "",
|
||||||
|
"author": "/",
|
||||||
|
"warning": 0,
|
||||||
|
"score": 0,
|
||||||
|
"tags": []
|
||||||
|
},
|
||||||
"tracks": [
|
"tracks": [
|
||||||
{
|
{
|
||||||
"name":"Mario Kart Stadium",
|
"name":"Mario Kart Stadium",
|
||||||
|
|
|
@ -52,9 +52,7 @@ class Window(AbstractPreviewWindow):
|
||||||
# insert all the tracks representation
|
# insert all the tracks representation
|
||||||
for track in self.mod_config.get_all_arenas_tracks(ignore_filter=True):
|
for track in self.mod_config.get_all_arenas_tracks(ignore_filter=True):
|
||||||
try:
|
try:
|
||||||
track_repr = track.repr_format(
|
track_repr = track.repr_format(template=self.entry_template_input.get())
|
||||||
self.mod_config, self.entry_template_input.get()
|
|
||||||
)
|
|
||||||
|
|
||||||
offset: int = 0 # the color tag is removed at every sub, so keep track of the offset
|
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
|
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.configure(state=tkinter.NORMAL)
|
||||||
self.text_track_select.delete(1.0, tkinter.END)
|
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):
|
for track in self.mod_config.get_all_tracks(ignore_filter=True):
|
||||||
value: bool = template_func(track) is True
|
value: bool = template_func(track) is True
|
||||||
|
|
|
@ -7,12 +7,13 @@ from PIL import Image
|
||||||
|
|
||||||
from source import threaded
|
from source import threaded
|
||||||
from source.mkw import Tag
|
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.collection import MKWColor, Slot
|
||||||
from source.mkw.ModSettings import AbstractModSettings
|
from source.mkw.ModSettings import AbstractModSettings
|
||||||
from source.mkw.Track import CustomTrack, DefaultTrack, Arena
|
from source.mkw.Track import CustomTrack, DefaultTrack, Arena
|
||||||
from source.progress import Progress
|
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.wt.szs import SZSPath
|
||||||
from source.translation import translate as _
|
from source.translation import translate as _
|
||||||
|
|
||||||
|
@ -78,8 +79,9 @@ class ModConfig:
|
||||||
version: str = "v1.0.0"
|
version: str = "v1.0.0"
|
||||||
variant: str = "01"
|
variant: str = "01"
|
||||||
|
|
||||||
_tracks: list["Track | TrackGroup"] = field(default_factory=list)
|
_tracks: list["Track | TrackGroup | dict"] = field(default_factory=list)
|
||||||
_arenas: list["Arena"] = 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', '_') }}"
|
track_file_template: "TemplateMultipleSafeEval" = "{{ getattr(track, 'sha1', '_') }}"
|
||||||
multiplayer_disable_if: "TemplateSafeEval" = "False"
|
multiplayer_disable_if: "TemplateSafeEval" = "False"
|
||||||
|
|
||||||
|
@ -94,8 +96,8 @@ class ModConfig:
|
||||||
macros: dict[str, "TemplateSafeEval"] = field(default_factory=dict)
|
macros: dict[str, "TemplateSafeEval"] = field(default_factory=dict)
|
||||||
messages: dict[str, dict[str, "TemplateMultipleSafeEval"]] = field(default_factory=dict)
|
messages: dict[str, dict[str, "TemplateMultipleSafeEval"]] = field(default_factory=dict)
|
||||||
|
|
||||||
global_settings: dict[str, "AbstractModSettings"] = field(default_factory=dict)
|
global_settings: dict[str, "AbstractModSettings | dict"] = field(default_factory=dict)
|
||||||
specific_settings: dict[str, "AbstractModSettings"] = field(default_factory=dict)
|
specific_settings: dict[str, "AbstractModSettings | dict"] = field(default_factory=dict)
|
||||||
|
|
||||||
lpar_template: "TemplateMultipleSafeEval" = "normal.lpar"
|
lpar_template: "TemplateMultipleSafeEval" = "normal.lpar"
|
||||||
|
|
||||||
|
@ -103,6 +105,15 @@ class ModConfig:
|
||||||
self.path = Path(self.path)
|
self.path = Path(self.path)
|
||||||
if self.nickname is None: self.nickname = self.name
|
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:
|
def __hash__(self) -> int:
|
||||||
return hash(self.name)
|
return hash(self.name)
|
||||||
|
|
||||||
|
@ -125,21 +136,11 @@ class ModConfig:
|
||||||
kwargs = config_dict | {
|
kwargs = config_dict | {
|
||||||
"path": path,
|
"path": path,
|
||||||
|
|
||||||
"_tracks": [CustomTrack.from_dict(track) for track in config_dict.pop("tracks", [])],
|
"_tracks": config_dict.pop("tracks", []),
|
||||||
"_arenas": [Arena.from_dict(arena) for arena in config_dict.pop("arenas", [])],
|
"_arenas": config_dict.pop("arenas", []),
|
||||||
|
|
||||||
"macros": macros,
|
"macros": macros,
|
||||||
"messages": messages,
|
"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)
|
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,
|
# filter_template_func is the function checking if the track should be included. If no parameter is set,
|
||||||
# then always return True
|
# then always return True
|
||||||
filter_template_func: Callable = self.safe_eval(
|
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.
|
# 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
|
# wrap the iterator inside a sort function
|
||||||
iterator = sorted(iterator, key=sorting_template_func)
|
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:
|
for track in iterator:
|
||||||
yield track
|
yield track
|
||||||
|
|
||||||
|
@ -267,13 +268,13 @@ class ModConfig:
|
||||||
|
|
||||||
if len(track_buffer) > 4:
|
if len(track_buffer) > 4:
|
||||||
current_tag_count += 1
|
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 = []
|
track_buffer = []
|
||||||
|
|
||||||
# if there is still tracks in the buffer, create a cup with them and fill with default>
|
# if there is still tracks in the buffer, create a cup with them and fill with default>
|
||||||
if len(track_buffer) > 0:
|
if len(track_buffer) > 0:
|
||||||
track_buffer.extend([DefaultTrack()] * (4 - len(track_buffer)))
|
track_buffer.extend([DefaultTrack(mod_config=self)] * (4 - len(track_buffer)))
|
||||||
yield Cup(tracks=track_buffer, cup_name=f"{current_tag_name}/{current_tag_count + 1}")
|
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]:
|
def get_unordered_cups(self) -> Generator["Cup", None, None]:
|
||||||
"""
|
"""
|
||||||
|
@ -289,13 +290,13 @@ class ModConfig:
|
||||||
track_buffer.append(track)
|
track_buffer.append(track)
|
||||||
|
|
||||||
if len(track_buffer) > 4:
|
if len(track_buffer) > 4:
|
||||||
yield Cup(tracks=track_buffer)
|
yield Cup(mod_config=self, tracks=track_buffer)
|
||||||
track_buffer = []
|
track_buffer = []
|
||||||
|
|
||||||
# if there is still tracks in the buffer, create a cup with them and fill with default
|
# if there is still tracks in the buffer, create a cup with them and fill with default
|
||||||
if len(track_buffer) > 0:
|
if len(track_buffer) > 0:
|
||||||
track_buffer.extend([DefaultTrack()] * (4 - len(track_buffer)))
|
track_buffer.extend([DefaultTrack(mod_config=self)] * (4 - len(track_buffer)))
|
||||||
yield Cup(tracks=track_buffer)
|
yield Cup(mod_config=self, tracks=track_buffer)
|
||||||
|
|
||||||
def get_cups(self) -> Generator["Cup", None, None]:
|
def get_cups(self) -> Generator["Cup", None, None]:
|
||||||
"""
|
"""
|
||||||
|
@ -328,11 +329,11 @@ class ModConfig:
|
||||||
|
|
||||||
for cup in self.get_cups():
|
for cup in self.get_cups():
|
||||||
# get all the cup ctfile, use "-" for the template since the track's name are not used here
|
# 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"
|
ctfile_override_property = "[SETUP-ARENA]\n"
|
||||||
for arena in self.get_arenas():
|
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 += arena_definition
|
||||||
ctfile_override_property += arena_override_property
|
ctfile_override_property += arena_override_property
|
||||||
|
|
||||||
|
@ -433,7 +434,7 @@ class ModConfig:
|
||||||
|
|
||||||
for track in self.get_all_arenas_tracks():
|
for track in self.get_all_arenas_tracks():
|
||||||
track_file: Path = next(
|
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
|
@threaded
|
||||||
|
|
|
@ -49,11 +49,11 @@ class FormatOriginalTrackLayer(AbstractLayer):
|
||||||
else: tag = "Wii"
|
else: tag = "Wii"
|
||||||
|
|
||||||
patched_name = CustomTrack(
|
patched_name = CustomTrack(
|
||||||
|
mod_config=patch.mod_config,
|
||||||
name=name,
|
name=name,
|
||||||
tags=[tag]
|
tags=[tag]
|
||||||
).repr_format(
|
).repr_format(
|
||||||
patch.mod_config,
|
template=self.template
|
||||||
self.template
|
|
||||||
)
|
)
|
||||||
|
|
||||||
return f" {id}\t= {patched_name}"
|
return f" {id}\t= {patched_name}"
|
||||||
|
|
|
@ -15,13 +15,17 @@ class TrackForbiddenCustomAttribute(Exception):
|
||||||
|
|
||||||
|
|
||||||
class AbstractTrack(ABC):
|
class AbstractTrack(ABC):
|
||||||
|
|
||||||
|
mod_config: "ModConfig"
|
||||||
music: Slot.Slot
|
music: Slot.Slot
|
||||||
special: Slot.Slot
|
special: Slot.Slot
|
||||||
tags: list[Tag]
|
tags: list[Tag]
|
||||||
weight: int
|
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):
|
tags: list[Tag] = None, weight: int = 1, **kwargs):
|
||||||
|
|
||||||
|
self.mod_config = mod_config
|
||||||
self.music = Slot.find(music)
|
self.music = Slot.find(music)
|
||||||
self.special = Slot.find(special)
|
self.special = Slot.find(special)
|
||||||
self.tags = tags if tags is not None else []
|
self.tags = tags if tags is not None else []
|
||||||
|
@ -30,7 +34,8 @@ class AbstractTrack(ABC):
|
||||||
# others not mandatory attributes
|
# others not mandatory attributes
|
||||||
for key, value in kwargs.items():
|
for key, value in kwargs.items():
|
||||||
# if the attribute start with __, this is a magic attribute, and it should not be modified
|
# 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)
|
setattr(self, key, value)
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
|
@ -45,34 +50,33 @@ class AbstractTrack(ABC):
|
||||||
yield self
|
yield self
|
||||||
|
|
||||||
@abstractmethod
|
@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
|
return the representation of the track from the format
|
||||||
:param template: template for the way the text will be represented
|
:param template: template for the way the text will be represented
|
||||||
:param mod_config: configuration of the mod
|
|
||||||
:return: formatted representation of the track
|
:return: formatted representation of the track
|
||||||
"""
|
"""
|
||||||
...
|
...
|
||||||
|
|
||||||
|
@property
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def get_filename(self, mod_config: "ModConfig") -> str:
|
def filename(self) -> str:
|
||||||
"""
|
"""
|
||||||
Return the filename of the track
|
Return the filename of the track
|
||||||
:param mod_config: the mod_config object
|
|
||||||
:return: the filename of the track
|
:return: the filename of the track
|
||||||
"""
|
"""
|
||||||
...
|
...
|
||||||
|
|
||||||
|
@property
|
||||||
@abstractmethod
|
@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
|
Return if the track should be considered as new for random selection
|
||||||
:param mod_config: mod configuration
|
|
||||||
:return: is the track new
|
: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
|
return the ctfile of the track
|
||||||
:hidden: if the track is in a group
|
:hidden: if the track is in a group
|
||||||
|
@ -80,11 +84,11 @@ class AbstractTrack(ABC):
|
||||||
:return: ctfile
|
:return: ctfile
|
||||||
"""
|
"""
|
||||||
category: str = "H" if hidden else "T"
|
category: str = "H" if hidden else "T"
|
||||||
name: str = self.repr_format(mod_config=mod_config, template=template)
|
name: str = self.repr_format(template=template)
|
||||||
filename: str = self.get_filename(mod_config=mod_config)
|
filename: str = self.filename
|
||||||
flags: int = (
|
flags: int = (
|
||||||
(0x04 if hidden else 0) |
|
(0x04 if hidden else 0) |
|
||||||
(0x01 if self.is_new(mod_config) else 0)
|
(0x01 if self.is_new else 0)
|
||||||
)
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -16,13 +16,20 @@ class ArenaForbiddenCustomAttribute(Exception):
|
||||||
|
|
||||||
|
|
||||||
class Arena(RealArenaTrack):
|
class Arena(RealArenaTrack):
|
||||||
|
"""
|
||||||
|
Represent an arena object
|
||||||
|
"""
|
||||||
|
|
||||||
|
mod_config: "ModConfig"
|
||||||
slot: Slot.Slot
|
slot: Slot.Slot
|
||||||
music: Slot.Slot
|
music: Slot.Slot
|
||||||
special: Slot.Slot
|
special: Slot.Slot
|
||||||
tags: list[Tag]
|
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):
|
tags: list[Tag] = None, **kwargs):
|
||||||
|
|
||||||
|
self.mod_config = mod_config
|
||||||
self.slot = Slot.find(slot)
|
self.slot = Slot.find(slot)
|
||||||
self.music = Slot.find(music if music is not None else 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)
|
self.special = Slot.find(special if special is not None else slot)
|
||||||
|
@ -31,26 +38,25 @@ class Arena(RealArenaTrack):
|
||||||
# others not mandatory attributes
|
# others not mandatory attributes
|
||||||
for key, value in kwargs.items():
|
for key, value in kwargs.items():
|
||||||
# if the attribute start with __, this is a magic attribute, and it should not be modified
|
# 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)
|
setattr(self, key, value)
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return f"<{self.__class__.__name__} name={getattr(self, 'name', '/')} tags={getattr(self, 'tags', '/')}>"
|
return f"<{self.__class__.__name__} name={getattr(self, 'name', '/')} tags={getattr(self, 'tags', '/')}>"
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_dict(cls, arena_dict: dict[str, any]) -> "Arena":
|
def from_dict(cls, mod_config: "ModConfig", arena_dict: dict[str, any]) -> "Arena":
|
||||||
return cls(**arena_dict)
|
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
|
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
|
:param template: the template of the track name
|
||||||
:return: the ctfile for the arena and the redefinition of the slot property
|
:return: the ctfile for the arena and the redefinition of the slot property
|
||||||
"""
|
"""
|
||||||
|
|
||||||
name: str = self.repr_format(mod_config=mod_config, template=template)
|
name: str = self.repr_format(template=template)
|
||||||
filename: str = self.get_filename(mod_config=mod_config)
|
filename: str = self.filename
|
||||||
|
|
||||||
return (
|
return (
|
||||||
(
|
(
|
||||||
|
|
|
@ -12,10 +12,12 @@ class Cup:
|
||||||
class that represent a mario kart wii track 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
|
_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._tracks = tracks[:4]
|
||||||
|
|
||||||
self.cup_id = self.__class__._last_cup_id
|
self.cup_id = self.__class__._last_cup_id
|
||||||
|
@ -25,7 +27,7 @@ class Cup:
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return f"<Cup name={self.cup_name} id={self.cup_id} tracks={self._tracks}>"
|
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
|
Get the default cticon for this cup
|
||||||
:return: the default cticon
|
:return: the default cticon
|
||||||
|
@ -33,7 +35,7 @@ class Cup:
|
||||||
from source.mkw.ModConfig import CT_ICON_SIZE
|
from source.mkw.ModConfig import CT_ICON_SIZE
|
||||||
|
|
||||||
ct_icon = Image.new("RGBA", (CT_ICON_SIZE, 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 = ImageDraw.Draw(ct_icon)
|
||||||
|
|
||||||
draw.text(
|
draw.text(
|
||||||
|
@ -63,15 +65,15 @@ class Cup:
|
||||||
path = mod_config.get_mod_directory() / f"_CUPS/{self.cup_name}.png"
|
path = mod_config.get_mod_directory() / f"_CUPS/{self.cup_name}.png"
|
||||||
if path.exists(): return Image.open(path)
|
if path.exists(): return Image.open(path)
|
||||||
# if the icon doesn't exist, use the default automatically generated one
|
# 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
|
Get the ctfile for this cup
|
||||||
:return: the ctfile
|
:return: the ctfile
|
||||||
"""
|
"""
|
||||||
ctfile = f'C "{self.cup_name}"\n'
|
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"
|
ctfile += "\n"
|
||||||
|
|
||||||
return ctfile
|
return ctfile
|
|
@ -15,19 +15,21 @@ class CustomTrack(RealArenaTrack, AbstractTrack):
|
||||||
return f"<{self.__class__.__name__} name={getattr(self, 'name', '/')} tags={getattr(self, 'tags', '/')}>"
|
return f"<{self.__class__.__name__} name={getattr(self, 'name', '/')} tags={getattr(self, 'tags', '/')}>"
|
||||||
|
|
||||||
@classmethod
|
@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
|
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
|
:param track_dict: dict containing the track information
|
||||||
:return: Track
|
:return: Track
|
||||||
"""
|
"""
|
||||||
if "group" in track_dict:
|
if "group" in track_dict:
|
||||||
from source.mkw.Track.TrackGroup import TrackGroup
|
from source.mkw.Track.TrackGroup import TrackGroup
|
||||||
return TrackGroup.from_dict(track_dict)
|
return TrackGroup.from_dict(mod_config, track_dict)
|
||||||
return cls(**track_dict)
|
return cls(mod_config, **track_dict)
|
||||||
|
|
||||||
def is_new(self, mod_config: "ModConfig") -> bool:
|
@property
|
||||||
return mod_config.safe_eval(
|
def is_new(self) -> bool:
|
||||||
mod_config.global_settings["replace_random_new"].value,
|
return self.mod_config.safe_eval(
|
||||||
|
self.mod_config.global_settings["replace_random_new"].value,
|
||||||
args=["track"]
|
args=["track"]
|
||||||
)(track=self) is True
|
)(track=self) is True
|
||||||
|
|
|
@ -7,11 +7,13 @@ if TYPE_CHECKING:
|
||||||
|
|
||||||
|
|
||||||
class DefaultTrack(AbstractTrack):
|
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
|
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
|
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
|
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)
|
(For example, DefaultTrack is not considered a real track class)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
mod_config: "ModConfig"
|
||||||
tags: list["Tag"]
|
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
|
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 template_name: name of the template of the tags
|
||||||
:param default: default value if no tag template is found
|
:param default: default value if no tag template is found
|
||||||
:return: formatted representation of the tag
|
:return: formatted representation of the tag
|
||||||
"""
|
"""
|
||||||
for tag in filter(lambda tag: tag in mod_config.tags_templates[template_name], self.tags):
|
for tag in filter(lambda tag: tag in self.mod_config.tags_templates[template_name], self.tags):
|
||||||
return mod_config.multiple_safe_eval(
|
return self.mod_config.multiple_safe_eval(
|
||||||
mod_config.tags_templates[template_name][tag],
|
template=self.mod_config.tags_templates[template_name][tag],
|
||||||
args=["tag"],
|
args=["tag"],
|
||||||
)(tag=tag)
|
)(tag=tag)
|
||||||
return default
|
return default
|
||||||
|
|
||||||
def repr_format(self, mod_config: "ModConfig", template: "TemplateMultipleSafeEval") -> str:
|
def repr_format(self, template: "TemplateMultipleSafeEval") -> str:
|
||||||
return mod_config.multiple_safe_eval(
|
return self.mod_config.multiple_safe_eval(template=template, args=["track"])(track=self)
|
||||||
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 get_filename(self, mod_config: "ModConfig") -> str:
|
@property
|
||||||
return self.repr_format(mod_config=mod_config, template=mod_config.track_file_template)
|
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 typing import Generator, TYPE_CHECKING
|
||||||
|
|
||||||
from source.mkw import Tag
|
from source.mkw import Tag
|
||||||
|
from source.translation import translate as _
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from source import TemplateMultipleSafeEval
|
from source import TemplateMultipleSafeEval
|
||||||
|
@ -8,11 +9,29 @@ if TYPE_CHECKING:
|
||||||
from source.mkw.ModConfig import ModConfig
|
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:
|
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.tracks = tracks if tracks is not None else []
|
||||||
self.tags = tags if tags 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]:
|
def get_tracks(self) -> Generator["CustomTrack", None, None]:
|
||||||
"""
|
"""
|
||||||
Get all the track elements
|
Get all the track elements
|
||||||
|
@ -22,27 +41,28 @@ class TrackGroup:
|
||||||
yield from track.get_tracks()
|
yield from track.get_tracks()
|
||||||
|
|
||||||
@classmethod
|
@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
|
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
|
:param group_dict: dict containing the track information
|
||||||
:return: TrackGroup or Track
|
:return: TrackGroup or Track
|
||||||
"""
|
"""
|
||||||
from source.mkw.Track.CustomTrack import CustomTrack
|
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(
|
return cls(
|
||||||
tracks=[CustomTrack.from_dict(track) for track in group_dict["group"]],
|
tracks=[CustomTrack.from_dict(mod_config, track) for track in group_dict["group"]],
|
||||||
tags=group_dict.get("tags"),
|
**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 the ctfile of the track group
|
||||||
:return: ctfile
|
:return: ctfile
|
||||||
"""
|
"""
|
||||||
ctfile = f'T T11; T11; 0x02; "-"; "info"; "-"\n'
|
ctfile = f'T T11; T11; 0x02; "-"; "info"; "-"\n'
|
||||||
for track in self.get_tracks():
|
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
|
return ctfile
|
||||||
|
|
Loading…
Reference in a new issue