yyc12345
3396947115
- add ballance map sector info in scene to indicate the maximum sector count of this map. - this adding will prevent the bug that the exported ballance map do not have successive sector groups. because original implement will not create sector group if no component in corresponding sector and previous remedy still have bug. and if this happended, ballance will show spaceship in wrong sector. this adding is the final solution of this bug. - exlarge ballance map sector info when user adding component. the enlarged value will be calculated by user input sector. - auto enlarge ballance map sector info when importing. this will give user a fluent experience when modifying existing map. - exporting map will also use ballance map sector info to pre-create successive sector group as term 2 stated. - move sector name extractor from virtools file exporting module to naming convention module.
637 lines
24 KiB
Python
637 lines
24 KiB
Python
import bpy
|
|
import typing, enum, re
|
|
from . import UTIL_functions
|
|
from . import PROP_virtools_group
|
|
|
|
#region Rename Error Reporter
|
|
|
|
class _RenameErrorType(enum.IntEnum):
|
|
ERROR = enum.auto()
|
|
WARNING = enum.auto()
|
|
INFO = enum.auto()
|
|
|
|
class _RenameErrorItem():
|
|
mErrType: _RenameErrorType
|
|
mDescription: str
|
|
|
|
def __init__(self, err_t: _RenameErrorType, description: str):
|
|
self.mErrType = err_t
|
|
self.mDescription = description
|
|
|
|
class RenameErrorReporter():
|
|
"""
|
|
A basic 'rename error report' using simple prints in console.
|
|
|
|
This object can be used as a context manager.
|
|
|
|
It supports multiple levels of 'substeps' - you shall always enter at least one substep (because level 0
|
|
has only one single step, representing the whole 'area' of the progress stuff).
|
|
|
|
You should give the object renaming of substeps each time you enter a new one.
|
|
|
|
Leaving a substep automatically steps by one the parent level.
|
|
|
|
```
|
|
with RenameErrorReporter() as reporter:
|
|
progress.enter_object(obj)
|
|
|
|
# process for object with reporter
|
|
reporter.add_error('fork!')
|
|
|
|
progress.leave_object()
|
|
```
|
|
"""
|
|
mAllObjCounter: int
|
|
mFailedObjCounter: int
|
|
|
|
mErrList: list[_RenameErrorItem]
|
|
mOldName: str
|
|
mHasError: bool
|
|
|
|
def __init__(self):
|
|
self.mAllObjCounter = 0
|
|
self.mFailedObjCounter = 0
|
|
|
|
self.mErrList = []
|
|
self.mOldName = ""
|
|
self.mHasError = False
|
|
|
|
def add_error(self, description: str):
|
|
self.mHasError = True
|
|
self.mErrList.append(_RenameErrorItem(_RenameErrorType.ERROR, description))
|
|
def add_warning(self, description: str):
|
|
self.mErrList.append(_RenameErrorItem(_RenameErrorType.WARNING, description))
|
|
def add_info(self, description: str):
|
|
self.mErrList.append(_RenameErrorItem(_RenameErrorType.INFO, description))
|
|
|
|
def get_all_objs_count(self) -> int: return self.mAllObjCounter
|
|
def get_failed_objs_count(self) -> int: return self.mFailedObjCounter
|
|
|
|
def __enter__(self):
|
|
# print console report header
|
|
print('============')
|
|
print('Rename Report')
|
|
print('------------')
|
|
# return self as context
|
|
return self
|
|
|
|
def __exit__(self, exc_type, exc_value, traceback):
|
|
# print console report tail
|
|
print('------------')
|
|
print(f'All / Failed - {self.mAllObjCounter} / {self.mFailedObjCounter}')
|
|
print('============')
|
|
# reset variables
|
|
self.mAllObjCounter = 0
|
|
self.mFailedObjCounter = 0
|
|
|
|
def enter_object(self, obj: bpy.types.Object) -> None:
|
|
# inc all counter
|
|
self.mAllObjCounter += 1
|
|
# assign old name
|
|
self.mOldName = obj.name
|
|
def leave_object(self, obj:bpy.types.Object) -> None:
|
|
# if error list is empty, no need to report
|
|
if len(self.mErrList) == 0: return
|
|
|
|
# inc failed if necessary
|
|
if self.mHasError:
|
|
self.mFailedObjCounter += 1
|
|
|
|
# output header
|
|
# if new name is different with old name, output both of them
|
|
new_name: str = obj.name
|
|
if self.mOldName == new_name:
|
|
print(f'For object "{new_name}"')
|
|
else:
|
|
print(f'For object "{new_name}" (Old name: "{self.mOldName}")')
|
|
|
|
# output error list with indent
|
|
for item in self.mErrList:
|
|
print('\t' + RenameErrorReporter.__erritem_to_string(item))
|
|
|
|
# clear error list for next object
|
|
self.mErrList.clear()
|
|
self.mHasError = False
|
|
|
|
@staticmethod
|
|
def __errtype_to_string(err_v: _RenameErrorType) -> str:
|
|
match(err_v):
|
|
case _RenameErrorType.ERROR: return 'ERROR'
|
|
case _RenameErrorType.WARNING: return 'WARN'
|
|
case _RenameErrorType.INFO: return 'INFO'
|
|
case _: raise UTIL_functions.BBPException("Unknown error type.")
|
|
@staticmethod
|
|
def __erritem_to_string(item: _RenameErrorItem) -> str:
|
|
return f'[{RenameErrorReporter.__errtype_to_string(item.mErrType)}]\t{item.mDescription}'
|
|
|
|
#endregion
|
|
|
|
#region Naming Convention Used Types
|
|
|
|
class BallanceObjectType(enum.IntEnum):
|
|
COMPONENT = enum.auto()
|
|
|
|
FLOOR = enum.auto()
|
|
RAIL = enum.auto()
|
|
WOOD = enum.auto()
|
|
STOPPER = enum.auto()
|
|
|
|
LEVEL_START = enum.auto()
|
|
LEVEL_END = enum.auto()
|
|
CHECKPOINT = enum.auto()
|
|
RESETPOINT = enum.auto()
|
|
|
|
DEPTH_CUBE = enum.auto()
|
|
SKYLAYER = enum.auto()
|
|
|
|
DECORATION = enum.auto()
|
|
|
|
class BallanceObjectInfo():
|
|
mBasicType: BallanceObjectType
|
|
|
|
## Only available for COMPONENT basic type
|
|
mComponentType: str | None
|
|
## Only available for COMPONENT, CHECKPOINT, RESETPOINT basic type
|
|
# For COMPONENT, it indicate which sector this component belong to.
|
|
# For CHECKPOINT, RESETPOINT, it indicate the index of this object.
|
|
# In CHECKPOINT, RESETPOINT mode, the sector actually is the suffix number of these objects' name. So checkpoint starts with 1, not 0.
|
|
mSector: int | None
|
|
|
|
def __init__(self, basic_type: BallanceObjectType):
|
|
self.mBasicType = basic_type
|
|
|
|
@classmethod
|
|
def create_from_component(cls, comp_type: str, sector: int):
|
|
inst = cls(BallanceObjectType.COMPONENT)
|
|
inst.mComponentType = comp_type
|
|
inst.mSector = sector
|
|
return inst
|
|
|
|
@classmethod
|
|
def create_from_checkpoint(cls, sector: int):
|
|
inst = cls(BallanceObjectType.CHECKPOINT)
|
|
inst.mSector = sector
|
|
return inst
|
|
@classmethod
|
|
def create_from_resetpoint(cls, sector: int):
|
|
inst = cls(BallanceObjectType.RESETPOINT)
|
|
inst.mSector = sector
|
|
return inst
|
|
|
|
@classmethod
|
|
def create_from_others(cls, basic_type: BallanceObjectType):
|
|
return cls(basic_type)
|
|
|
|
#endregion
|
|
|
|
#region Sector Extractor
|
|
|
|
_g_RegexBlcSectorGroup: re.Pattern = re.compile('^Sector_(0[1-8]|[1-9][0-9]{1,2}|9)$')
|
|
|
|
def extract_sector_from_name(group_name: str) -> int | None:
|
|
"""
|
|
A convenient function to extract sector index from given group name.
|
|
This function also supports 999 sector plugin.
|
|
|
|
Not only in this module, but also in outside modules, this function is vary used to extract sector index info.
|
|
|
|
Function return the index extracted, or None if given group name is not a valid sector group.
|
|
The valid sector index is range from 1 to 999 (inclusive)
|
|
"""
|
|
regex_result = _g_RegexBlcSectorGroup.match(group_name)
|
|
if regex_result is not None:
|
|
return int(regex_result.group(1))
|
|
else:
|
|
return None
|
|
|
|
def build_name_from_sector_index(sector_index: int) -> str:
|
|
"""
|
|
A convenient function to build Ballance recognizable sector group name.
|
|
This function also supports 999 sector plugin.
|
|
|
|
This function also is used in this module or other modules outside.
|
|
|
|
Function return a sector name string. It basically the reverse operation of `extract_sector_from_name`.
|
|
"""
|
|
if sector_index == 9:
|
|
return 'Sector_9'
|
|
else:
|
|
return f'Sector_{sector_index:0>2d}'
|
|
|
|
#endregion
|
|
|
|
#region Naming Convention Declaration
|
|
|
|
_g_BlcNormalComponents: set[str] = set((
|
|
"P_Extra_Life",
|
|
"P_Extra_Point",
|
|
"P_Trafo_Paper",
|
|
"P_Trafo_Stone",
|
|
"P_Trafo_Wood",
|
|
"P_Ball_Paper",
|
|
"P_Ball_Stone",
|
|
"P_Ball_Wood",
|
|
"P_Box",
|
|
"P_Dome",
|
|
"P_Modul_01",
|
|
"P_Modul_03",
|
|
"P_Modul_08",
|
|
"P_Modul_17",
|
|
"P_Modul_18",
|
|
"P_Modul_19",
|
|
"P_Modul_25",
|
|
"P_Modul_26",
|
|
"P_Modul_29",
|
|
"P_Modul_30",
|
|
"P_Modul_34",
|
|
"P_Modul_37",
|
|
"P_Modul_41"
|
|
))
|
|
_g_BlcUniqueComponents: set[str] = set((
|
|
"PS_Levelstart",
|
|
"PE_Levelende",
|
|
"PC_Checkpoints",
|
|
"PR_Resetpoints"
|
|
))
|
|
_g_BlcFloor: set[str] = set((
|
|
"Sound_HitID_01",
|
|
"Sound_RollID_01"
|
|
))
|
|
_g_BlcWood: set[str] = set((
|
|
"Sound_HitID_02",
|
|
"Sound_RollID_02"
|
|
))
|
|
|
|
class VirtoolsGroupConvention():
|
|
cRegexComponent: typing.ClassVar[re.Pattern] = re.compile('^(' + '|'.join(_g_BlcNormalComponents) + ')_(0[1-9]|[1-9][0-9])_.*$')
|
|
cRegexPC: typing.ClassVar[re.Pattern] = re.compile('^PC_TwoFlames_(0[1-7])$')
|
|
cRegexPR: typing.ClassVar[re.Pattern] = re.compile('^PR_Resetpoint_(0[1-8])$')
|
|
|
|
@staticmethod
|
|
def __get_pcpr_from_name(name: str, reporter: RenameErrorReporter | None) -> BallanceObjectInfo | None:
|
|
regex_result = VirtoolsGroupConvention.cRegexPC.match(name)
|
|
if regex_result is not None:
|
|
return BallanceObjectInfo.create_from_checkpoint(
|
|
int(regex_result.group(1))
|
|
)
|
|
regex_result = VirtoolsGroupConvention.cRegexPR.match(name)
|
|
if regex_result is not None:
|
|
return BallanceObjectInfo.create_from_resetpoint(
|
|
int(regex_result.group(1))
|
|
)
|
|
|
|
if reporter: reporter.add_error("PC_Checkpoints or PR_Resetpoints detected. But couldn't get sector from name.")
|
|
return None
|
|
|
|
@staticmethod
|
|
def __get_sector_from_groups(gps: typing.Iterator[str]) -> int | None:
|
|
# this counter is served for stupid
|
|
# multi-sector-grouping accident.
|
|
counter: int = 0
|
|
last_matched_sector: int = 0
|
|
for i in gps:
|
|
regex_result: int | None = extract_sector_from_name(i)
|
|
if regex_result is not None:
|
|
last_matched_sector = regex_result
|
|
counter += 1
|
|
|
|
if counter != 1: return None
|
|
else: return last_matched_sector
|
|
|
|
@staticmethod
|
|
def parse_from_object(obj: bpy.types.Object, reporter: RenameErrorReporter | None) -> BallanceObjectInfo | None:
|
|
# create visitor
|
|
with PROP_virtools_group.VirtoolsGroupsHelper(obj) as gp:
|
|
# if no group, we should consider it is decoration or skylayer
|
|
if gp.get_count() == 0:
|
|
if obj.name == 'SkyLayer': return BallanceObjectInfo.create_from_others(BallanceObjectType.SKYLAYER)
|
|
else: return BallanceObjectInfo.create_from_others(BallanceObjectType.DECORATION)
|
|
|
|
# try to filter unique elements first
|
|
inter_gps: set[str] = gp.intersect_groups(_g_BlcUniqueComponents)
|
|
if len(inter_gps) == 1:
|
|
# get it
|
|
match((tuple(inter_gps))[0]):
|
|
case 'PS_Levelstart':
|
|
return BallanceObjectInfo.create_from_others(BallanceObjectType.LEVEL_START)
|
|
case 'PE_Levelende':
|
|
return BallanceObjectInfo.create_from_others(BallanceObjectType.LEVEL_END)
|
|
case 'PC_Checkpoints' | 'PR_Resetpoints':
|
|
# these type's data should be gotten from its name
|
|
return VirtoolsGroupConvention.__get_pcpr_from_name(obj.name, reporter)
|
|
case _:
|
|
if reporter: reporter.add_error("The match of Unique Component lost.")
|
|
return None
|
|
elif len(inter_gps) != 0:
|
|
if reporter: reporter.add_error("A Multi-grouping Unique Component.")
|
|
return None
|
|
|
|
# distinguish normal elements
|
|
inter_gps = gp.intersect_groups(_g_BlcNormalComponents)
|
|
if len(inter_gps) == 1:
|
|
# get it
|
|
# now try get its sector
|
|
gotten_elements: str = (tuple(inter_gps))[0]
|
|
gotten_sector: int | None = VirtoolsGroupConvention.__get_sector_from_groups(gp.iterate_groups())
|
|
if gotten_sector is None:
|
|
# fail to get sector
|
|
if reporter: reporter.add_error("Component detected. But couldn't get sector from CKGroup data.")
|
|
return None
|
|
return BallanceObjectInfo.create_from_component(
|
|
gotten_elements,
|
|
gotten_sector
|
|
)
|
|
elif len(inter_gps) != 0:
|
|
# must be a weird grouping, report it
|
|
if reporter: reporter.add_error("A Multi-grouping Component.")
|
|
return None
|
|
|
|
# distinguish road
|
|
if gp.contain_group('Phys_FloorRails'):
|
|
# rail
|
|
return BallanceObjectInfo.create_from_others(BallanceObjectType.RAIL)
|
|
elif gp.contain_group('Phys_Floors'):
|
|
# distinguish it between Floor and Wood
|
|
floor_result = gp.intersect_groups(_g_BlcFloor)
|
|
rail_result = gp.intersect_groups(_g_BlcWood)
|
|
if len(floor_result) > 0 and len(rail_result) == 0:
|
|
return BallanceObjectInfo.create_from_others(BallanceObjectType.FLOOR)
|
|
elif len(floor_result) == 0 and len(rail_result) > 0:
|
|
return BallanceObjectInfo.create_from_others(BallanceObjectType.WOOD)
|
|
else:
|
|
if reporter: reporter.add_warning("Can't distinguish object between Floors and Rails. Suppose it is Floors.")
|
|
return BallanceObjectInfo.create_from_others(BallanceObjectType.FLOOR)
|
|
elif gp.contain_group('Phys_FloorStopper'):
|
|
return BallanceObjectInfo.create_from_others(BallanceObjectType.STOPPER)
|
|
elif gp.contain_group('DepthTestCubes'):
|
|
return BallanceObjectInfo.create_from_others(BallanceObjectType.DEPTH_CUBE)
|
|
|
|
# no matched
|
|
if reporter: reporter.add_error("Group match lost.")
|
|
return None
|
|
|
|
@staticmethod
|
|
def set_to_object(obj: bpy.types.Object, info: BallanceObjectInfo, reporter: RenameErrorReporter | None) -> bool:
|
|
# create visitor
|
|
with PROP_virtools_group.VirtoolsGroupsHelper(obj) as gp:
|
|
# match by basic type
|
|
match(info.mBasicType):
|
|
case BallanceObjectType.DECORATION: pass # decoration do not need group
|
|
case BallanceObjectType.SKYLAYER: pass # sky layer do not need group
|
|
|
|
case BallanceObjectType.LEVEL_START:
|
|
gp.add_group('PS_Levelstart')
|
|
case BallanceObjectType.LEVEL_END:
|
|
gp.add_group('PE_Levelende')
|
|
case BallanceObjectType.CHECKPOINT:
|
|
gp.add_group('PC_Checkpoints')
|
|
case BallanceObjectType.RESETPOINT:
|
|
gp.add_group('PR_Resetpoints')
|
|
|
|
case BallanceObjectType.DEPTH_CUBE:
|
|
gp.add_group('PE_Levelende')
|
|
|
|
case BallanceObjectType.FLOOR:
|
|
gp.add_group('Phys_Floors')
|
|
gp.add_group('Sound_HitID_01')
|
|
gp.add_group('Sound_RollID_01')
|
|
# floor type also need group into shadow group.
|
|
gp.add_group('Shadow')
|
|
case BallanceObjectType.RAIL:
|
|
gp.add_group('Phys_FloorRails')
|
|
gp.add_group('Sound_HitID_03')
|
|
gp.add_group('Sound_RollID_03')
|
|
case BallanceObjectType.WOOD:
|
|
gp.add_group('Phys_Floors')
|
|
gp.add_group('Sound_HitID_02')
|
|
gp.add_group('Sound_RollID_02')
|
|
case BallanceObjectType.STOPPER:
|
|
gp.add_group('Phys_FloorStopper')
|
|
|
|
case BallanceObjectType.COMPONENT:
|
|
# group into component type
|
|
# use typing.cast() to force linter accept it because None is impossible
|
|
gp.add_group(typing.cast(str, info.mComponentType))
|
|
# group to sector
|
|
gp.add_group(build_name_from_sector_index(typing.cast(int, info.mSector)))
|
|
|
|
case _:
|
|
if reporter is not None:
|
|
reporter.add_error('No matched info.')
|
|
return False
|
|
|
|
return True
|
|
|
|
class YYCToolchainConvention():
|
|
@staticmethod
|
|
def parse_from_name(name: str, reporter: RenameErrorReporter | None) -> BallanceObjectInfo | None:
|
|
# check component first
|
|
regex_result = VirtoolsGroupConvention.cRegexComponent.match(name) # use vt one because they are same
|
|
if regex_result is not None:
|
|
return BallanceObjectInfo.create_from_component(
|
|
regex_result.group(1),
|
|
int(regex_result.group(2))
|
|
)
|
|
|
|
# check PC PR elements
|
|
regex_result = VirtoolsGroupConvention.cRegexPC.match(name) # use vt one because they are same
|
|
if regex_result is not None:
|
|
return BallanceObjectInfo.create_from_checkpoint(
|
|
int(regex_result.group(1))
|
|
)
|
|
regex_result = VirtoolsGroupConvention.cRegexPR.match(name) # use vt one because they are same
|
|
if regex_result is not None:
|
|
return BallanceObjectInfo.create_from_resetpoint(
|
|
int(regex_result.group(1))
|
|
)
|
|
|
|
# check other unique elements
|
|
if name == "PS_FourFlames_01":
|
|
return BallanceObjectInfo.create_from_others(BallanceObjectType.LEVEL_START)
|
|
if name == "PE_Balloon_01":
|
|
return BallanceObjectInfo.create_from_others(BallanceObjectType.LEVEL_END)
|
|
|
|
# process floors
|
|
if name.startswith("A_Floor"):
|
|
return BallanceObjectInfo.create_from_others(BallanceObjectType.FLOOR)
|
|
if name.startswith("A_Rail"):
|
|
return BallanceObjectInfo.create_from_others(BallanceObjectType.RAIL)
|
|
if name.startswith("A_Wood"):
|
|
return BallanceObjectInfo.create_from_others(BallanceObjectType.WOOD)
|
|
if name.startswith("A_Stopper"):
|
|
return BallanceObjectInfo.create_from_others(BallanceObjectType.STOPPER)
|
|
|
|
# process others
|
|
if name.startswith("DepthCubes"):
|
|
return BallanceObjectInfo.create_from_others(BallanceObjectType.DEPTH_CUBE)
|
|
if name.startswith("D_"):
|
|
return BallanceObjectInfo.create_from_others(BallanceObjectType.DECORATION)
|
|
if name == 'SkyLayer':
|
|
return BallanceObjectInfo.create_from_others(BallanceObjectType.SKYLAYER)
|
|
|
|
if reporter is not None:
|
|
reporter.add_error("Name match lost.")
|
|
return None
|
|
|
|
|
|
@staticmethod
|
|
def parse_from_object(obj: bpy.types.Object, reporter: RenameErrorReporter | None) -> BallanceObjectInfo | None:
|
|
return YYCToolchainConvention.parse_from_name(obj.name, reporter)
|
|
|
|
@staticmethod
|
|
def set_to_name(info: BallanceObjectInfo, reporter: RenameErrorReporter | None) -> str | None:
|
|
match(info.mBasicType):
|
|
case BallanceObjectType.DECORATION:
|
|
return 'D_'
|
|
case BallanceObjectType.SKYLAYER:
|
|
return 'SkyLayer'
|
|
|
|
case BallanceObjectType.LEVEL_START:
|
|
return 'PS_FourFlames_01'
|
|
case BallanceObjectType.LEVEL_END:
|
|
return 'PE_Balloon_01'
|
|
case BallanceObjectType.CHECKPOINT:
|
|
return f'PC_TwoFlames_{info.mSector:0>2d}'
|
|
case BallanceObjectType.RESETPOINT:
|
|
return f'PR_Resetpoint_{info.mSector:0>2d}'
|
|
|
|
case BallanceObjectType.DEPTH_CUBE:
|
|
return 'DepthCubes_'
|
|
|
|
case BallanceObjectType.FLOOR:
|
|
return 'A_Floor_'
|
|
case BallanceObjectType.RAIL:
|
|
return 'A_Rail_'
|
|
case BallanceObjectType.WOOD:
|
|
return 'A_Wood_'
|
|
case BallanceObjectType.STOPPER:
|
|
return 'A_Stopper_'
|
|
|
|
case BallanceObjectType.COMPONENT:
|
|
return '{}_{:0>2d}_'.format(
|
|
info.mComponentType, info.mSector)
|
|
|
|
case _:
|
|
if reporter is not None:
|
|
reporter.add_error('No matched info.')
|
|
return None
|
|
|
|
@staticmethod
|
|
def set_to_object(obj: bpy.types.Object, info: BallanceObjectInfo, reporter: RenameErrorReporter | None) -> bool:
|
|
expect_name: str | None = YYCToolchainConvention.set_to_name(info, reporter)
|
|
if expect_name is None: return False
|
|
|
|
obj.name = expect_name
|
|
return True
|
|
|
|
class ImengyuConvention():
|
|
cRegexComponent: typing.ClassVar[re.Pattern] = re.compile('^(' + '|'.join(_g_BlcNormalComponents) + '):[^:]*:([1-9]|[1-9][0-9])$')
|
|
cRegexPC: typing.ClassVar[re.Pattern] = re.compile('^PC_CheckPoint:([0-9]+)$')
|
|
cRegexPR: typing.ClassVar[re.Pattern] = re.compile('^PR_ResetPoint:([0-9]+)$')
|
|
|
|
@staticmethod
|
|
def parse_from_name(name: str, reporter: RenameErrorReporter | None) -> BallanceObjectInfo | None:
|
|
# check component first
|
|
regex_result = ImengyuConvention.cRegexComponent.match(name)
|
|
if regex_result is not None:
|
|
return BallanceObjectInfo.create_from_component(
|
|
regex_result.group(1),
|
|
int(regex_result.group(2))
|
|
)
|
|
|
|
# check PC PR elements
|
|
regex_result = ImengyuConvention.cRegexPC.match(name)
|
|
if regex_result is not None:
|
|
return BallanceObjectInfo.create_from_checkpoint(
|
|
int(regex_result.group(1))
|
|
)
|
|
regex_result = ImengyuConvention.cRegexPR.match(name)
|
|
if regex_result is not None:
|
|
return BallanceObjectInfo.create_from_resetpoint(
|
|
int(regex_result.group(1))
|
|
)
|
|
|
|
# check other unique elements
|
|
if name == "PS_LevelStart":
|
|
return BallanceObjectInfo.create_from_others(BallanceObjectType.LEVEL_START)
|
|
if name == "PE_LevelEnd":
|
|
return BallanceObjectInfo.create_from_others(BallanceObjectType.LEVEL_END)
|
|
|
|
# process floors
|
|
if name.startswith("S_Floors"):
|
|
return BallanceObjectInfo.create_from_others(BallanceObjectType.FLOOR)
|
|
if name.startswith("S_FloorRails"):
|
|
return BallanceObjectInfo.create_from_others(BallanceObjectType.RAIL)
|
|
if name.startswith("S_FloorWoods"):
|
|
return BallanceObjectInfo.create_from_others(BallanceObjectType.WOOD)
|
|
if name.startswith("S_FloorStopper"):
|
|
return BallanceObjectInfo.create_from_others(BallanceObjectType.STOPPER)
|
|
|
|
# process others
|
|
if name.startswith("DepthTestCubes"):
|
|
return BallanceObjectInfo.create_from_others(BallanceObjectType.DEPTH_CUBE)
|
|
if name.startswith("O_"):
|
|
return BallanceObjectInfo.create_from_others(BallanceObjectType.DECORATION)
|
|
if name == 'SkyLayer':
|
|
return BallanceObjectInfo.create_from_others(BallanceObjectType.SKYLAYER)
|
|
|
|
if reporter is not None:
|
|
reporter.add_error("Name match lost.")
|
|
return None
|
|
|
|
@staticmethod
|
|
def parse_from_object(obj: bpy.types.Object, reporter: RenameErrorReporter | None) -> BallanceObjectInfo | None:
|
|
return ImengyuConvention.parse_from_name(obj.name, reporter)
|
|
|
|
@staticmethod
|
|
def set_to_name(info: BallanceObjectInfo, oldname: str | None, reporter: RenameErrorReporter | None) -> str | None:
|
|
match(info.mBasicType):
|
|
case BallanceObjectType.DECORATION:
|
|
return 'O_'
|
|
case BallanceObjectType.SKYLAYER:
|
|
return 'SkyLayer'
|
|
|
|
case BallanceObjectType.LEVEL_START:
|
|
return 'PS_LevelStart'
|
|
case BallanceObjectType.LEVEL_END:
|
|
return 'PE_LevelEnd'
|
|
case BallanceObjectType.CHECKPOINT:
|
|
return f'PR_ResetPoint:{info.mSector:d}'
|
|
case BallanceObjectType.RESETPOINT:
|
|
return f'PC_CheckPoint:{info.mSector:d}'
|
|
|
|
case BallanceObjectType.DEPTH_CUBE:
|
|
return 'DepthTestCubes'
|
|
|
|
case BallanceObjectType.FLOOR:
|
|
return 'S_Floors'
|
|
case BallanceObjectType.RAIL:
|
|
return 'S_FloorWoods'
|
|
case BallanceObjectType.WOOD:
|
|
return 'S_FloorRails'
|
|
case BallanceObjectType.STOPPER:
|
|
return 'S_FloorStopper'
|
|
|
|
case BallanceObjectType.COMPONENT:
|
|
return '{}:{}:{:d}'.format(
|
|
info.mComponentType,
|
|
oldname.replace(':', '_') if oldname is not None else '',
|
|
info.mSector
|
|
)
|
|
|
|
case _:
|
|
if reporter is not None:
|
|
reporter.add_error('No matched info.')
|
|
return None
|
|
|
|
@staticmethod
|
|
def set_to_object(obj: bpy.types.Object, info: BallanceObjectInfo, reporter: RenameErrorReporter | None) -> bool:
|
|
expect_name: str | None = ImengyuConvention.set_to_name(info, obj.name, reporter)
|
|
if expect_name is None: return False
|
|
|
|
obj.name = expect_name
|
|
return True
|
|
|
|
|
|
#endregion
|