diff --git a/bbp_ng/OP_ADDS_component.py b/bbp_ng/OP_ADDS_component.py index ed51dbd..ada7e18 100644 --- a/bbp_ng/OP_ADDS_component.py +++ b/bbp_ng/OP_ADDS_component.py @@ -1,22 +1,100 @@ import bpy -from . import UTIL_functions, UTIL_icons_manager -from . import PROP_preferences, PROP_ballance_element, PROP_virtools_group +from . import UTIL_functions, UTIL_icons_manager, UTIL_naming_convension +from . import PROP_ballance_element, PROP_virtools_group -_g_UniqueElements = { - "PS_FourFlames": 'PS_FourFlames_01', - "PE_Balloon": 'PE_Balloon_01' -} +#region Help Classes & Functions -def _get_component_name(comp_name: str, comp_sector: int) -> str: - return '{}_{:0>2d}_'.format(comp_name, comp_sector) +def _get_component_info(comp_type: PROP_ballance_element.BallanceElementType, comp_sector: int) -> UTIL_naming_convension.BallanceObjectInfo: + match(comp_type): + # process special for 2 unique components + case PROP_ballance_element.BallanceElementType.PS_FourFlames: + return UTIL_naming_convension.BallanceObjectInfo.create_from_others(UTIL_naming_convension.BallanceObjectType.LEVEL_START) + case PROP_ballance_element.BallanceElementType.PE_Balloon: + return UTIL_naming_convension.BallanceObjectInfo.create_from_others(UTIL_naming_convension.BallanceObjectType.LEVEL_END) + # process naming convention required special components + case PROP_ballance_element.BallanceElementType.PC_TwoFlames: + return UTIL_naming_convension.BallanceObjectInfo.create_from_checkpoint(comp_sector) + case PROP_ballance_element.BallanceElementType.PR_Resetpoint: + return UTIL_naming_convension.BallanceObjectInfo.create_from_resetpoint(comp_sector) + # process for other components + case _: + return UTIL_naming_convension.BallanceObjectInfo.create_from_component(comp_type.name, comp_sector) + +def _set_component_by_info(obj: bpy.types.Object, info: UTIL_naming_convension.BallanceObjectInfo) -> None: + # set component name and grouping it into virtools group at the same time + # set name first + if not UTIL_naming_convension.YYCToolchainConvention.set_to_object(obj, info, None): + raise UTIL_functions.BBPException('impossible fail to set component name.') + + # set vt group next + if not UTIL_naming_convension.VirtoolsGroupConvention.set_to_object(obj, info, None): + raise UTIL_functions.BBPException('impossible fail to set component virtools groups.') + +def _check_component_existance(comp_type: PROP_ballance_element.BallanceElementType, comp_sector: int) -> str | None: + """ + Check the existance of 4 special components name, PS, PE, PC, PR + These 4 components will have special name. + + @return Return name if selected component is one of PS, PE, PC, PR and there already is a name conflict, otherwise None. + """ + # check component type requirements + match(comp_type): + case PROP_ballance_element.BallanceElementType.PS_FourFlames | PROP_ballance_element.BallanceElementType.PE_Balloon | PROP_ballance_element.BallanceElementType.PC_TwoFlames | PROP_ballance_element.BallanceElementType.PR_Resetpoint: + pass # exit match and start check + case _: + return None # return, do not check + + # get info + comp_info: UTIL_naming_convension.BallanceObjectInfo = _get_component_info(comp_type, comp_sector) + + # get expected name + expect_name: str | None = UTIL_naming_convension.YYCToolchainConvention.set_to_name(comp_info, None) + if expect_name is None: + raise UTIL_functions.BBPException('impossible fail to get component name.') + + # check expected name + if expect_name in bpy.data.objects: return expect_name + else: return None + + +class EnumPropHelper(): + """ + Generate component types for this module's operator + """ + @staticmethod + def generate_items() -> tuple[tuple, ...]: + # token, display name, descriptions, icon, index + return tuple( + ( + str(item.value), + item.name, + "", + UTIL_icons_manager.get_element_icon(item.name), + item.value + ) for item in PROP_ballance_element.BallanceElementType + ) + + @staticmethod + def get_selection(prop: str) -> PROP_ballance_element.BallanceElementType: + # prop will return identifier which is defined as the string type of int value. + # so we parse it to int and then parse it to enum type. + return PROP_ballance_element.BallanceElementType(int(prop)) + + @staticmethod + def to_selection(val: PROP_ballance_element.BallanceElementType) -> str: + # like get_selection, we need get it int value, then convert it to string as the indetifier of enum props + # them enum property will accept it. + return str(val.value) + +#endregion class BBP_OT_add_component(bpy.types.Operator): - """Add Element""" + """Add Component""" bl_idname = "bbp.add_component" - bl_label = "Add Element" + bl_label = "Add Component" bl_options = {'UNDO'} - element_sector: bpy.props.IntProperty( + component_sector: bpy.props.IntProperty( name = "Sector", description = "Define which sector the object will be grouped in", min = 1, max = 999, @@ -24,15 +102,10 @@ class BBP_OT_add_component(bpy.types.Operator): default = 1, ) - element_type: bpy.props.EnumProperty( + component_type: bpy.props.EnumProperty( name = "Type", - description = "This element type", - #items=tuple(map(lambda x: (x, x, ""), UTILS_constants.bmfile_componentList)), - items = tuple( - # token, display name, descriptions, icon, index - (str(item.value), item.name, "", UTIL_icons_manager.get_element_icon(item.name), item.value) - for item in PROP_ballance_element.BallanceElementType - ), + description = "This component type", + items = EnumPropHelper.generate_items(), ) def invoke(self, context, event): @@ -41,22 +114,30 @@ class BBP_OT_add_component(bpy.types.Operator): def draw(self, context): layout = self.layout - layout.prop(self, "element_type") - layout.prop(self, "element_sector") + # show type + layout.prop(self, "component_type") - # check for unique name and show warning - elename: str | None = _g_UniqueElements.get(PROP_ballance_element.BallanceElementType(int(self.element_type)).name, None) - if elename is not None and elename in bpy.data.objects: - layout.label(f'Warning: {elename} already exist.') + # only show sector for non-PE/PS component + eletype: PROP_ballance_element.BallanceElementType = EnumPropHelper.get_selection(self.component_type) + if eletype != PROP_ballance_element.BallanceElementType.PS_FourFlames and eletype != PROP_ballance_element.BallanceElementType.PE_Balloon: + layout.prop(self, "component_sector") + + # check for some special components and show warning + elename: str | None = _check_component_existance(EnumPropHelper.get_selection(self.component_type), self.component_sector) + if elename is not None: + layout.label(text = f'Warning: {elename} already exist.') def execute(self, context): - # create by ballance elements - eletype: PROP_ballance_element.BallanceElementType = PROP_ballance_element.BallanceElementType(int(self.element_type)) + # create by ballance components + eletype: PROP_ballance_element.BallanceElementType = EnumPropHelper.get_selection(self.component_type) + eleinfo: UTIL_naming_convension.BallanceObjectInfo = _get_component_info(eletype, self.component_sector) + with PROP_ballance_element.BallanceElementsHelper(bpy.context.scene) as creator: - obj = bpy.data.objects.new( - _get_component_name(eletype.name, self.element_sector), - creator.get_element(eletype.value) - ) + # create with empty name first + obj = bpy.data.objects.new('', creator.get_element(eletype.value)) + # assign its props, including name + _set_component_by_info(obj, eleinfo) + # scene cursor UTIL_functions.add_into_scene_and_move_to_cursor(obj) return {'FINISHED'} @@ -67,7 +148,7 @@ class BBP_OT_add_component(bpy.types.Operator): cop = layout.operator( self.bl_idname, text = item.name, icon_value = UTIL_icons_manager.get_element_icon(item.name)) - cop.element_type = str(item.value) + cop.component_type = EnumPropHelper.to_selection(item) def register(): # register all classes diff --git a/bbp_ng/PROP_preferences.py b/bbp_ng/PROP_preferences.py index 23e9ac6..3605af3 100644 --- a/bbp_ng/PROP_preferences.py +++ b/bbp_ng/PROP_preferences.py @@ -5,16 +5,13 @@ from . import UTIL_naming_convension class RawPreferences(): cBallanceTextureFolder: typing.ClassVar[str] = "" cNoComponentCollection: typing.ClassVar[str] = "" - cDefaultNamingConvention: typing.ClassVar[UTIL_naming_convension.NamingConvention] = UTIL_naming_convension._EnumPropHelper.get_default_naming_identifier() mBallanceTextureFolder: str mNoComponentCollection: str - mDefaultNamingConvention: UTIL_naming_convension.NamingConvention def __init__(self, **kwargs): self.mBallanceTextureFolder = kwargs.get("mBallanceTextureFolder", "") self.mNoComponentCollection = kwargs.get("mNoComponentCollection", "") - self.mDefaultNamingConvention = kwargs.get('mDefaultNamingConvention', UTIL_naming_convension._EnumPropHelper.get_default_naming_identifier()) def has_valid_blc_tex_folder(self) -> bool: return os.path.isdir(self.mBallanceTextureFolder) @@ -34,13 +31,6 @@ class BBPPreferences(bpy.types.AddonPreferences): description = "(Import) The object which stored in this collectiion will not be saved as component. (Export) All forced no component objects will be stored in this collection", default = RawPreferences.cNoComponentCollection, ) - - default_naming_convention: bpy.props.EnumProperty( - name = "Default Naming Convention", - description = "The default naming convention when creating objects, import and export BM files.", - items = UTIL_naming_convension._EnumPropHelper.generate_items(), - default = UTIL_naming_convension._EnumPropHelper.to_selection(RawPreferences.cDefaultNamingConvention), - ) def draw(self, context): layout = self.layout @@ -51,8 +41,6 @@ class BBPPreferences(bpy.types.AddonPreferences): col.prop(self, "ballance_texture_folder", text = "") col.label(text = "No Component Collection") col.prop(self, "no_component_collection", text = "") - col.label(text = "Default Naming Convention") - col.prop(self, "default_naming_convention", text = "") def get_preferences() -> BBPPreferences: return bpy.context.preferences.addons[__package__].preferences @@ -63,7 +51,6 @@ def get_raw_preferences() -> RawPreferences: rawdata.mBallanceTextureFolder = pref.ballance_texture_folder rawdata.mNoComponentCollection = pref.no_component_collection - rawdata.mDefaultNamingConvention = UTIL_naming_convension._EnumPropHelper.get_selection(pref.default_naming_convention) return rawdata diff --git a/bbp_ng/PROP_virtools_material.py b/bbp_ng/PROP_virtools_material.py index 302cf5c..2a881bb 100644 --- a/bbp_ng/PROP_virtools_material.py +++ b/bbp_ng/PROP_virtools_material.py @@ -651,9 +651,13 @@ class BBP_OT_direct_set_virtools_texture(bpy.types.Operator, UTIL_file_browser.I if try_filepath is None: # load as other texture tex = UTIL_ballance_texture.load_other_texture(texture_filepath) + # set texture props + PROP_virtools_texture.set_raw_virtools_texture(tex, PROP_virtools_texture.get_nonballance_texture_preset()) else: # load as ballance texture tex = UTIL_ballance_texture.load_ballance_texture(try_filepath) + # set texture props + PROP_virtools_texture.set_raw_virtools_texture(tex, PROP_virtools_texture.get_ballance_texture_preset(try_filepath)) # assign texture rawmtl.mTexture = tex diff --git a/bbp_ng/PROP_virtools_texture.py b/bbp_ng/PROP_virtools_texture.py index e040036..bbcfd10 100644 --- a/bbp_ng/PROP_virtools_texture.py +++ b/bbp_ng/PROP_virtools_texture.py @@ -79,6 +79,121 @@ def draw_virtools_texture(img: bpy.types.Image, layout: bpy.types.UILayout): #endregion +#region Ballance Texture Preset + +_g_OpaqueBallanceTexturePreset: RawVirtoolsTexture = RawVirtoolsTexture( + mSaveOptions = UTIL_virtools_types.CK_TEXTURE_SAVEOPTIONS.CKTEXTURE_EXTERNAL, + mVideoFormat = UTIL_virtools_types.VX_PIXELFORMAT._16_ARGB1555, +) +_g_TransparentBallanceTexturePreset: RawVirtoolsTexture = RawVirtoolsTexture( + mSaveOptions = UTIL_virtools_types.CK_TEXTURE_SAVEOPTIONS.CKTEXTURE_EXTERNAL, + mVideoFormat = UTIL_virtools_types.VX_PIXELFORMAT._32_ARGB8888, +) +_g_NonBallanceTexturePreset: RawVirtoolsTexture = RawVirtoolsTexture( + mSaveOptions = UTIL_virtools_types.CK_TEXTURE_SAVEOPTIONS.CKTEXTURE_RAWDATA, + mVideoFormat = UTIL_virtools_types.VX_PIXELFORMAT._32_ARGB8888, +) + +## The preset collection of all Ballance texture. +# Key is texture name and can be used as file name checking. +# Value is its preset which can be assigned. +_g_BallanceTexturePresets: dict[str, RawVirtoolsTexture] = { + # "atari.avi": _g_TransparentBallanceTexturePreset, + "atari.bmp": _g_OpaqueBallanceTexturePreset, + "Ball_LightningSphere1.bmp": _g_OpaqueBallanceTexturePreset, + "Ball_LightningSphere2.bmp": _g_OpaqueBallanceTexturePreset, + "Ball_LightningSphere3.bmp": _g_OpaqueBallanceTexturePreset, + "Ball_Paper.bmp": _g_OpaqueBallanceTexturePreset, + "Ball_Stone.bmp": _g_OpaqueBallanceTexturePreset, + "Ball_Wood.bmp": _g_OpaqueBallanceTexturePreset, + "Brick.bmp": _g_OpaqueBallanceTexturePreset, + "Button01_deselect.tga": _g_TransparentBallanceTexturePreset, + "Button01_select.tga": _g_TransparentBallanceTexturePreset, + "Button01_special.tga": _g_TransparentBallanceTexturePreset, + "Column_beige.bmp": _g_OpaqueBallanceTexturePreset, + "Column_beige_fade.tga": _g_TransparentBallanceTexturePreset, + "Column_blue.bmp": _g_OpaqueBallanceTexturePreset, + "Cursor.tga": _g_TransparentBallanceTexturePreset, + "Dome.bmp": _g_OpaqueBallanceTexturePreset, + "DomeEnvironment.bmp": _g_OpaqueBallanceTexturePreset, + "DomeShadow.tga": _g_TransparentBallanceTexturePreset, + "ExtraBall.bmp": _g_OpaqueBallanceTexturePreset, + "ExtraParticle.bmp": _g_OpaqueBallanceTexturePreset, + "E_Holzbeschlag.bmp": _g_OpaqueBallanceTexturePreset, + "FloorGlow.bmp": _g_OpaqueBallanceTexturePreset, + "Floor_Side.bmp": _g_OpaqueBallanceTexturePreset, + "Floor_Top_Border.bmp": _g_OpaqueBallanceTexturePreset, + "Floor_Top_Borderless.bmp": _g_OpaqueBallanceTexturePreset, + "Floor_Top_Checkpoint.bmp": _g_OpaqueBallanceTexturePreset, + "Floor_Top_Flat.bmp": _g_OpaqueBallanceTexturePreset, + "Floor_Top_Profil.bmp": _g_OpaqueBallanceTexturePreset, + "Floor_Top_ProfilFlat.bmp": _g_OpaqueBallanceTexturePreset, + "Font_1.tga": _g_TransparentBallanceTexturePreset, + "Gravitylogo_intro.bmp": _g_OpaqueBallanceTexturePreset, + "HardShadow.bmp": _g_OpaqueBallanceTexturePreset, + "Laterne_Glas.bmp": _g_OpaqueBallanceTexturePreset, + "Laterne_Schatten.tga": _g_TransparentBallanceTexturePreset, + "Laterne_Verlauf.tga": _g_TransparentBallanceTexturePreset, + "Logo.bmp": _g_OpaqueBallanceTexturePreset, + "Metal_stained.bmp": _g_OpaqueBallanceTexturePreset, + "Misc_Ufo.bmp": _g_OpaqueBallanceTexturePreset, + "Misc_UFO_Flash.bmp": _g_OpaqueBallanceTexturePreset, + "Modul03_Floor.bmp": _g_OpaqueBallanceTexturePreset, + "Modul03_Wall.bmp": _g_OpaqueBallanceTexturePreset, + "Modul11_13_Wood.bmp": _g_OpaqueBallanceTexturePreset, + "Modul11_Wood.bmp": _g_OpaqueBallanceTexturePreset, + "Modul15.bmp": _g_OpaqueBallanceTexturePreset, + "Modul16.bmp": _g_OpaqueBallanceTexturePreset, + "Modul18.bmp": _g_OpaqueBallanceTexturePreset, + "Modul18_Gitter.tga": _g_TransparentBallanceTexturePreset, + "Modul30_d_Seiten.bmp": _g_OpaqueBallanceTexturePreset, + "Particle_Flames.bmp": _g_OpaqueBallanceTexturePreset, + "Particle_Smoke.bmp": _g_OpaqueBallanceTexturePreset, + "PE_Bal_balloons.bmp": _g_OpaqueBallanceTexturePreset, + "PE_Bal_platform.bmp": _g_OpaqueBallanceTexturePreset, + "PE_Ufo_env.bmp": _g_OpaqueBallanceTexturePreset, + "Pfeil.tga": _g_TransparentBallanceTexturePreset, + "P_Extra_Life_Oil.bmp": _g_OpaqueBallanceTexturePreset, + "P_Extra_Life_Particle.bmp": _g_OpaqueBallanceTexturePreset, + "P_Extra_Life_Shadow.bmp": _g_OpaqueBallanceTexturePreset, + "Rail_Environment.bmp": _g_OpaqueBallanceTexturePreset, + "sandsack.bmp": _g_OpaqueBallanceTexturePreset, + "SkyLayer.bmp": _g_OpaqueBallanceTexturePreset, + "Sky_Vortex.bmp": _g_OpaqueBallanceTexturePreset, + "Stick_Bottom.tga": _g_TransparentBallanceTexturePreset, + "Stick_Stripes.bmp": _g_OpaqueBallanceTexturePreset, + "Target.bmp": _g_OpaqueBallanceTexturePreset, + "Tower_Roof.bmp": _g_OpaqueBallanceTexturePreset, + "Trafo_Environment.bmp": _g_OpaqueBallanceTexturePreset, + "Trafo_FlashField.bmp": _g_OpaqueBallanceTexturePreset, + "Trafo_Shadow_Big.tga": _g_TransparentBallanceTexturePreset, + "Tut_Pfeil01.tga": _g_TransparentBallanceTexturePreset, + "Tut_Pfeil_Hoch.tga": _g_TransparentBallanceTexturePreset, + "Wolken_intro.tga": _g_TransparentBallanceTexturePreset, + "Wood_Metal.bmp": _g_OpaqueBallanceTexturePreset, + "Wood_MetalStripes.bmp": _g_OpaqueBallanceTexturePreset, + "Wood_Misc.bmp": _g_OpaqueBallanceTexturePreset, + "Wood_Nailed.bmp": _g_OpaqueBallanceTexturePreset, + "Wood_Old.bmp": _g_OpaqueBallanceTexturePreset, + "Wood_Panel.bmp": _g_OpaqueBallanceTexturePreset, + "Wood_Plain.bmp": _g_OpaqueBallanceTexturePreset, + "Wood_Plain2.bmp": _g_OpaqueBallanceTexturePreset, + "Wood_Raft.bmp": _g_OpaqueBallanceTexturePreset, +} + +def get_ballance_texture_preset(texname: str) -> RawVirtoolsTexture: + try_preset: RawVirtoolsTexture | None = _g_BallanceTexturePresets.get(texname, None) + if try_preset is None: + # fallback to non-ballance one + try_preset = _g_NonBallanceTexturePreset + + return try_preset + +def get_nonballance_texture_preset() -> RawVirtoolsTexture: + return _g_NonBallanceTexturePreset + +#endregion + def register(): bpy.utils.register_class(BBP_PG_virtools_texture) diff --git a/bbp_ng/UTIL_ballance_texture.py b/bbp_ng/UTIL_ballance_texture.py index 8fabde0..9f6f3c1 100644 --- a/bbp_ng/UTIL_ballance_texture.py +++ b/bbp_ng/UTIL_ballance_texture.py @@ -1,7 +1,7 @@ import bpy, bpy_extras import typing, os -from . import PROP_preferences, PROP_virtools_texture -from . import UTIL_virtools_types, UTIL_functions +from . import PROP_preferences +from . import UTIL_functions ## Ballance Texture Usage # The aim of this module is to make sure every Ballance texture only have 1 instance in Blender as much as we can @@ -56,6 +56,90 @@ from . import UTIL_virtools_types, UTIL_functions #region Ballance Texture Assist Functions +_g_BallanceTextureFileNames: set[str] = set(( + # "atari.avi", + "atari.bmp", + "Ball_LightningSphere1.bmp", + "Ball_LightningSphere2.bmp", + "Ball_LightningSphere3.bmp", + "Ball_Paper.bmp", + "Ball_Stone.bmp", + "Ball_Wood.bmp", + "Brick.bmp", + "Button01_deselect.tga", + "Button01_select.tga", + "Button01_special.tga", + "Column_beige.bmp", + "Column_beige_fade.tga", + "Column_blue.bmp", + "Cursor.tga", + "Dome.bmp", + "DomeEnvironment.bmp", + "DomeShadow.tga", + "ExtraBall.bmp", + "ExtraParticle.bmp", + "E_Holzbeschlag.bmp", + "FloorGlow.bmp", + "Floor_Side.bmp", + "Floor_Top_Border.bmp", + "Floor_Top_Borderless.bmp", + "Floor_Top_Checkpoint.bmp", + "Floor_Top_Flat.bmp", + "Floor_Top_Profil.bmp", + "Floor_Top_ProfilFlat.bmp", + "Font_1.tga", + "Gravitylogo_intro.bmp", + "HardShadow.bmp", + "Laterne_Glas.bmp", + "Laterne_Schatten.tga", + "Laterne_Verlauf.tga", + "Logo.bmp", + "Metal_stained.bmp", + "Misc_Ufo.bmp", + "Misc_UFO_Flash.bmp", + "Modul03_Floor.bmp", + "Modul03_Wall.bmp", + "Modul11_13_Wood.bmp", + "Modul11_Wood.bmp", + "Modul15.bmp", + "Modul16.bmp", + "Modul18.bmp", + "Modul18_Gitter.tga", + "Modul30_d_Seiten.bmp", + "Particle_Flames.bmp", + "Particle_Smoke.bmp", + "PE_Bal_balloons.bmp", + "PE_Bal_platform.bmp", + "PE_Ufo_env.bmp", + "Pfeil.tga", + "P_Extra_Life_Oil.bmp", + "P_Extra_Life_Particle.bmp", + "P_Extra_Life_Shadow.bmp", + "Rail_Environment.bmp", + "sandsack.bmp", + "SkyLayer.bmp", + "Sky_Vortex.bmp", + "Stick_Bottom.tga", + "Stick_Stripes.bmp", + "Target.bmp", + "Tower_Roof.bmp", + "Trafo_Environment.bmp", + "Trafo_FlashField.bmp", + "Trafo_Shadow_Big.tga", + "Tut_Pfeil01.tga", + "Tut_Pfeil_Hoch.tga", + "Wolken_intro.tga", + "Wood_Metal.bmp", + "Wood_MetalStripes.bmp", + "Wood_Misc.bmp", + "Wood_Nailed.bmp", + "Wood_Old.bmp", + "Wood_Panel.bmp", + "Wood_Plain.bmp", + "Wood_Plain2.bmp", + "Wood_Raft.bmp", +)) + def _get_ballance_texture_folder() -> str: """! Get Ballance texture folder from preferences. @@ -88,106 +172,6 @@ def _is_path_equal(path1: str, path2: str) -> bool: #region Ballance Texture Detect Functions -g_OpaqueBallanceTexturePreset: PROP_virtools_texture.RawVirtoolsTexture = PROP_virtools_texture.RawVirtoolsTexture( - mSaveOptions = UTIL_virtools_types.CK_TEXTURE_SAVEOPTIONS.CKTEXTURE_EXTERNAL, - mVideoFormat = UTIL_virtools_types.VX_PIXELFORMAT._16_ARGB1555, -) -g_TransparentBallanceTexturePreset: PROP_virtools_texture.RawVirtoolsTexture = PROP_virtools_texture.RawVirtoolsTexture( - mSaveOptions = UTIL_virtools_types.CK_TEXTURE_SAVEOPTIONS.CKTEXTURE_EXTERNAL, - mVideoFormat = UTIL_virtools_types.VX_PIXELFORMAT._32_ARGB8888, -) -g_NonBallanceTexturePreset: PROP_virtools_texture.RawVirtoolsTexture = PROP_virtools_texture.RawVirtoolsTexture( - mSaveOptions = UTIL_virtools_types.CK_TEXTURE_SAVEOPTIONS.CKTEXTURE_RAWDATA, - mVideoFormat = UTIL_virtools_types.VX_PIXELFORMAT._32_ARGB8888, -) - -## The preset collection of all Ballance texture. -# Key is texture name and can be used as file name checking. -# Value is its preset which can be assigned. -g_BallanceTexturePresets: dict[str, PROP_virtools_texture.RawVirtoolsTexture] = { - # "atari.avi": g_TransparentBallanceTexturePreset, - "atari.bmp": g_OpaqueBallanceTexturePreset, - "Ball_LightningSphere1.bmp": g_OpaqueBallanceTexturePreset, - "Ball_LightningSphere2.bmp": g_OpaqueBallanceTexturePreset, - "Ball_LightningSphere3.bmp": g_OpaqueBallanceTexturePreset, - "Ball_Paper.bmp": g_OpaqueBallanceTexturePreset, - "Ball_Stone.bmp": g_OpaqueBallanceTexturePreset, - "Ball_Wood.bmp": g_OpaqueBallanceTexturePreset, - "Brick.bmp": g_OpaqueBallanceTexturePreset, - "Button01_deselect.tga": g_TransparentBallanceTexturePreset, - "Button01_select.tga": g_TransparentBallanceTexturePreset, - "Button01_special.tga": g_TransparentBallanceTexturePreset, - "Column_beige.bmp": g_OpaqueBallanceTexturePreset, - "Column_beige_fade.tga": g_TransparentBallanceTexturePreset, - "Column_blue.bmp": g_OpaqueBallanceTexturePreset, - "Cursor.tga": g_TransparentBallanceTexturePreset, - "Dome.bmp": g_OpaqueBallanceTexturePreset, - "DomeEnvironment.bmp": g_OpaqueBallanceTexturePreset, - "DomeShadow.tga": g_TransparentBallanceTexturePreset, - "ExtraBall.bmp": g_OpaqueBallanceTexturePreset, - "ExtraParticle.bmp": g_OpaqueBallanceTexturePreset, - "E_Holzbeschlag.bmp": g_OpaqueBallanceTexturePreset, - "FloorGlow.bmp": g_OpaqueBallanceTexturePreset, - "Floor_Side.bmp": g_OpaqueBallanceTexturePreset, - "Floor_Top_Border.bmp": g_OpaqueBallanceTexturePreset, - "Floor_Top_Borderless.bmp": g_OpaqueBallanceTexturePreset, - "Floor_Top_Checkpoint.bmp": g_OpaqueBallanceTexturePreset, - "Floor_Top_Flat.bmp": g_OpaqueBallanceTexturePreset, - "Floor_Top_Profil.bmp": g_OpaqueBallanceTexturePreset, - "Floor_Top_ProfilFlat.bmp": g_OpaqueBallanceTexturePreset, - "Font_1.tga": g_TransparentBallanceTexturePreset, - "Gravitylogo_intro.bmp": g_OpaqueBallanceTexturePreset, - "HardShadow.bmp": g_OpaqueBallanceTexturePreset, - "Laterne_Glas.bmp": g_OpaqueBallanceTexturePreset, - "Laterne_Schatten.tga": g_TransparentBallanceTexturePreset, - "Laterne_Verlauf.tga": g_TransparentBallanceTexturePreset, - "Logo.bmp": g_OpaqueBallanceTexturePreset, - "Metal_stained.bmp": g_OpaqueBallanceTexturePreset, - "Misc_Ufo.bmp": g_OpaqueBallanceTexturePreset, - "Misc_UFO_Flash.bmp": g_OpaqueBallanceTexturePreset, - "Modul03_Floor.bmp": g_OpaqueBallanceTexturePreset, - "Modul03_Wall.bmp": g_OpaqueBallanceTexturePreset, - "Modul11_13_Wood.bmp": g_OpaqueBallanceTexturePreset, - "Modul11_Wood.bmp": g_OpaqueBallanceTexturePreset, - "Modul15.bmp": g_OpaqueBallanceTexturePreset, - "Modul16.bmp": g_OpaqueBallanceTexturePreset, - "Modul18.bmp": g_OpaqueBallanceTexturePreset, - "Modul18_Gitter.tga": g_TransparentBallanceTexturePreset, - "Modul30_d_Seiten.bmp": g_OpaqueBallanceTexturePreset, - "Particle_Flames.bmp": g_OpaqueBallanceTexturePreset, - "Particle_Smoke.bmp": g_OpaqueBallanceTexturePreset, - "PE_Bal_balloons.bmp": g_OpaqueBallanceTexturePreset, - "PE_Bal_platform.bmp": g_OpaqueBallanceTexturePreset, - "PE_Ufo_env.bmp": g_OpaqueBallanceTexturePreset, - "Pfeil.tga": g_TransparentBallanceTexturePreset, - "P_Extra_Life_Oil.bmp": g_OpaqueBallanceTexturePreset, - "P_Extra_Life_Particle.bmp": g_OpaqueBallanceTexturePreset, - "P_Extra_Life_Shadow.bmp": g_OpaqueBallanceTexturePreset, - "Rail_Environment.bmp": g_OpaqueBallanceTexturePreset, - "sandsack.bmp": g_OpaqueBallanceTexturePreset, - "SkyLayer.bmp": g_OpaqueBallanceTexturePreset, - "Sky_Vortex.bmp": g_OpaqueBallanceTexturePreset, - "Stick_Bottom.tga": g_TransparentBallanceTexturePreset, - "Stick_Stripes.bmp": g_OpaqueBallanceTexturePreset, - "Target.bmp": g_OpaqueBallanceTexturePreset, - "Tower_Roof.bmp": g_OpaqueBallanceTexturePreset, - "Trafo_Environment.bmp": g_OpaqueBallanceTexturePreset, - "Trafo_FlashField.bmp": g_OpaqueBallanceTexturePreset, - "Trafo_Shadow_Big.tga": g_TransparentBallanceTexturePreset, - "Tut_Pfeil01.tga": g_TransparentBallanceTexturePreset, - "Tut_Pfeil_Hoch.tga": g_TransparentBallanceTexturePreset, - "Wolken_intro.tga": g_TransparentBallanceTexturePreset, - "Wood_Metal.bmp": g_OpaqueBallanceTexturePreset, - "Wood_MetalStripes.bmp": g_OpaqueBallanceTexturePreset, - "Wood_Misc.bmp": g_OpaqueBallanceTexturePreset, - "Wood_Nailed.bmp": g_OpaqueBallanceTexturePreset, - "Wood_Old.bmp": g_OpaqueBallanceTexturePreset, - "Wood_Panel.bmp": g_OpaqueBallanceTexturePreset, - "Wood_Plain.bmp": g_OpaqueBallanceTexturePreset, - "Wood_Plain2.bmp": g_OpaqueBallanceTexturePreset, - "Wood_Raft.bmp": g_OpaqueBallanceTexturePreset, -} - def get_ballance_texture_filename(texpath: str) -> str | None: """! Return the filename part for valid Ballance texture path. @@ -203,7 +187,7 @@ def get_ballance_texture_filename(texpath: str) -> str | None: # check file name first filename: str = os.path.basename(texpath) - if filename not in g_BallanceTexturePresets: return None + if filename not in _g_BallanceTextureFileNames: return None # if file name matched, check whether it located in ballance texture folder probe: str = os.path.join(_get_ballance_texture_folder(), filename) @@ -284,9 +268,8 @@ def load_ballance_texture(texname: str) -> bpy.types.Image: @return The loaded image. """ - # try getting preset (also check texture name) - tex_preset: PROP_virtools_texture.RawVirtoolsTexture | None = g_BallanceTexturePresets.get(texname, None) - if tex_preset is None: + # check texture name + if texname not in _g_BallanceTextureFileNames: raise UTIL_functions.BBPException("Invalid Ballance texture file name.") # load image @@ -294,8 +277,6 @@ def load_ballance_texture(texname: str) -> bpy.types.Image: filepath: str = os.path.join(_get_ballance_texture_folder(), texname) ret: bpy.types.Image = bpy.data.images.load(filepath, check_existing = True) - # apply preset and return - PROP_virtools_texture.set_raw_virtools_texture(ret, tex_preset) return ret def load_other_texture(texname: str) -> bpy.types.Image: @@ -321,8 +302,6 @@ def load_other_texture(texname: str) -> bpy.types.Image: # then immediately pack it into file. ret.pack() - # apply general non-ballance texture preset and return image - PROP_virtools_texture.set_raw_virtools_texture(ret, g_NonBallanceTexturePreset) return ret def generate_other_texture_save_path(tex: bpy.types.Image, file_folder: str) -> str: diff --git a/bbp_ng/UTIL_naming_convension.py b/bbp_ng/UTIL_naming_convension.py index 4b2bb17..ec85f43 100644 --- a/bbp_ng/UTIL_naming_convension.py +++ b/bbp_ng/UTIL_naming_convension.py @@ -1,6 +1,6 @@ import bpy import typing, enum, re -from . import UTIL_functions, UTIL_icons_manager +from . import UTIL_functions from . import PROP_virtools_group #region Rename Error Reporter @@ -18,28 +18,85 @@ class _RenameErrorItem(): self.mErrType = err_t self.mDescription = description -class _RenameErrorReporter(): +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 begin_object(self, obj: bpy.types.Object) -> None: + 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 end_object(self, obj:bpy.types.Object) -> None: + 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 @@ -50,10 +107,11 @@ class _RenameErrorReporter(): # output error list with indent for item in self.mErrList: - print('\t' + _RenameErrorReporter.__erritem_to_string(item)) + 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: @@ -64,7 +122,7 @@ class _RenameErrorReporter(): 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}' + return f'[{RenameErrorReporter.__errtype_to_string(item.mErrType)}]\t{item.mDescription}' #endregion @@ -124,21 +182,6 @@ class BallanceObjectInfo(): def create_from_others(cls, basic_type: BallanceObjectType): return cls(basic_type) -class _NamingConventionProfile(): - _TParseFct = typing.Callable[[bpy.types.Object, _RenameErrorReporter | None], BallanceObjectInfo | None] - _TSetFct = typing.Callable[[bpy.types.Object,BallanceObjectInfo, _RenameErrorReporter | None], bool] - - mName: str - mDesc: str - mParseFct: _TParseFct - mSetFct: _TSetFct - - def __init__(self, name: str, desc: str, parse_fct: _TParseFct, set_fct: _TSetFct): - self.mName = name - self.mDesc = desc - self.mParseFct = parse_fct - self.mSetFct = set_fct - #endregion #region Naming Convention Declaration @@ -183,20 +226,20 @@ _g_BlcWood: set[str] = set(( PROP_virtools_group.VirtoolsGroupsPreset.Sound_RollID_02.value, )) -class _VirtoolsGroupConvention(): +class VirtoolsGroupConvention(): cRegexGroupSector: typing.ClassVar[re.Pattern] = re.compile('^Sector_(0[1-8]|[1-9][0-9]{1,2}|9)$') 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) + 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) + regex_result = VirtoolsGroupConvention.cRegexPR.match(name) if regex_result is not None: return BallanceObjectInfo.create_from_resetpoint( int(regex_result.group(1)) @@ -212,7 +255,7 @@ class _VirtoolsGroupConvention(): counter: int = 0 last_matched_sector: int = 0 for i in gps: - regex_result = _VirtoolsGroupConvention.cRegexGroupSector.match(i) + regex_result = VirtoolsGroupConvention.cRegexGroupSector.match(i) if regex_result is not None: last_matched_sector = int(regex_result.group(1)) counter += 1 @@ -220,9 +263,8 @@ class _VirtoolsGroupConvention(): if counter != 1: return None else: return last_matched_sector - @staticmethod - def parse_from_object(obj: bpy.types.Object, reporter: _RenameErrorReporter | None) -> BallanceObjectInfo | None: + 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 @@ -241,7 +283,7 @@ class _VirtoolsGroupConvention(): return BallanceObjectInfo.create_from_others(BallanceObjectType.LEVEL_END) case PROP_virtools_group.VirtoolsGroupsPreset.PC_Checkpoints.value | PROP_virtools_group.VirtoolsGroupsPreset.PR_Resetpoints.value: # these type's data should be gotten from its name - return _VirtoolsGroupConvention.__get_pcpr_from_name(obj.name, reporter) + return VirtoolsGroupConvention.__get_pcpr_from_name(obj.name, reporter) case _: if reporter: reporter.add_error("The match of Unique Component lost.") return None @@ -255,7 +297,7 @@ class _VirtoolsGroupConvention(): # 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()) + 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.") @@ -294,7 +336,7 @@ class _VirtoolsGroupConvention(): return None @staticmethod - def set_to_object(obj: bpy.types.Object, info: BallanceObjectInfo, reporter: _RenameErrorReporter | None) -> bool: + 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 @@ -346,20 +388,11 @@ class _VirtoolsGroupConvention(): return True +class YYCToolchainConvention(): @staticmethod - def register() -> _NamingConventionProfile: - return _NamingConventionProfile( - 'Virtools Group', - 'Virtools Group', - _VirtoolsGroupConvention.parse_from_object, - _VirtoolsGroupConvention.set_to_object - ) - -class _YYCToolchainConvention(): - @staticmethod - def parse_from_object(obj: bpy.types.Object, reporter: _RenameErrorReporter | None) -> BallanceObjectInfo | None: + def parse_from_name(name: str, reporter: RenameErrorReporter | None) -> BallanceObjectInfo | None: # check component first - regex_result = _VirtoolsGroupConvention.cRegexComponent.match(obj.name) + 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), @@ -367,103 +400,105 @@ class _YYCToolchainConvention(): ) # check PC PR elements - regex_result = _VirtoolsGroupConvention.cRegexPC.match(obj.name) + 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(obj.name) + 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 obj.name == "PS_FourFlames_01": + if name == "PS_FourFlames_01": return BallanceObjectInfo.create_from_others(BallanceObjectType.LEVEL_START) - if obj.name == "PE_Balloon_01": + if name == "PE_Balloon_01": return BallanceObjectInfo.create_from_others(BallanceObjectType.LEVEL_END) # process floors - if obj.name.startswith("A_Floor"): + if name.startswith("A_Floor"): return BallanceObjectInfo.create_from_others(BallanceObjectType.FLOOR) - if obj.name.startswith("A_Rail"): + if name.startswith("A_Rail"): return BallanceObjectInfo.create_from_others(BallanceObjectType.RAIL) - if obj.name.startswith("A_Wood"): + if name.startswith("A_Wood"): return BallanceObjectInfo.create_from_others(BallanceObjectType.WOOD) - if obj.name.startswith("A_Stopper"): + if name.startswith("A_Stopper"): return BallanceObjectInfo.create_from_others(BallanceObjectType.STOPPER) # process others - if obj.name.startswith("DepthCubes"): + if name.startswith("DepthCubes"): return BallanceObjectInfo.create_from_others(BallanceObjectType.DEPTH_CUBE) - if obj.name.startswith("D_"): + if name.startswith("D_"): return BallanceObjectInfo.create_from_others(BallanceObjectType.DECORATION) - if obj.name == 'SkyLayer': + 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 set_to_object(obj: bpy.types.Object, info: BallanceObjectInfo, reporter: _RenameErrorReporter | None) -> bool: + 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: - obj.name = 'D_' + return 'D_' case BallanceObjectType.SKYLAYER: - obj.name = 'SkyLayer' + return 'SkyLayer' case BallanceObjectType.LEVEL_START: - obj.name = 'PS_FourFlames_01' + return 'PS_FourFlames_01' case BallanceObjectType.LEVEL_END: - obj.name = 'PE_Balloon_01' + return 'PE_Balloon_01' case BallanceObjectType.CHECKPOINT: - obj.name = f'PR_Resetpoint_{info.mSector:0>2d}' + return f'PR_Resetpoint_{info.mSector:0>2d}' case BallanceObjectType.RESETPOINT: - obj.name = f'PC_TwoFlames_{info.mSector:0>2d}' + return f'PC_TwoFlames_{info.mSector:0>2d}' case BallanceObjectType.DEPTH_CUBE: - obj.name = 'DepthCubes_' + return 'DepthCubes_' case BallanceObjectType.FLOOR: - obj.name = 'A_Floor_' + return 'A_Floor_' case BallanceObjectType.RAIL: - obj.name = 'A_Wood_' + return 'A_Wood_' case BallanceObjectType.WOOD: - obj.name = 'A_Rail_' + return 'A_Rail_' case BallanceObjectType.STOPPER: - obj.name = 'A_Stopper_' + return 'A_Stopper_' case BallanceObjectType.COMPONENT: - obj.name = '{}_{:0>2d}_'.format( + return '{}_{:0>2d}_'.format( info.mComponentType, info.mSector) case _: if reporter is not None: reporter.add_error('No matched info.') - return False + 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 - @staticmethod - def register() -> _NamingConventionProfile: - return _NamingConventionProfile( - 'YYC Toolchain', - 'YYC Toolchain name standard.', - _YYCToolchainConvention.parse_from_object, - _YYCToolchainConvention.set_to_object - ) - -class _ImengyuConvention(): +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_object(obj: bpy.types.Object, reporter: _RenameErrorReporter | None) -> BallanceObjectInfo | None: + def parse_from_name(name: str, reporter: RenameErrorReporter | None) -> BallanceObjectInfo | None: # check component first - regex_result = _ImengyuConvention.cRegexComponent.match(obj.name) + regex_result = ImengyuConvention.cRegexComponent.match(name) if regex_result is not None: return BallanceObjectInfo.create_from_component( regex_result.group(1), @@ -471,39 +506,39 @@ class _ImengyuConvention(): ) # check PC PR elements - regex_result = _ImengyuConvention.cRegexPC.match(obj.name) + 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(obj.name) + 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 obj.name == "PS_LevelStart": + if name == "PS_LevelStart": return BallanceObjectInfo.create_from_others(BallanceObjectType.LEVEL_START) - if obj.name == "PE_LevelEnd": + if name == "PE_LevelEnd": return BallanceObjectInfo.create_from_others(BallanceObjectType.LEVEL_END) # process floors - if obj.name.startswith("S_Floors"): + if name.startswith("S_Floors"): return BallanceObjectInfo.create_from_others(BallanceObjectType.FLOOR) - if obj.name.startswith("S_FloorRails"): + if name.startswith("S_FloorRails"): return BallanceObjectInfo.create_from_others(BallanceObjectType.RAIL) - if obj.name.startswith("S_FloorWoods"): + if name.startswith("S_FloorWoods"): return BallanceObjectInfo.create_from_others(BallanceObjectType.WOOD) - if obj.name.startswith("S_FloorStopper"): + if name.startswith("S_FloorStopper"): return BallanceObjectInfo.create_from_others(BallanceObjectType.STOPPER) # process others - if obj.name.startswith("DepthTestCubes"): + if name.startswith("DepthTestCubes"): return BallanceObjectInfo.create_from_others(BallanceObjectType.DEPTH_CUBE) - if obj.name.startswith("O_"): + if name.startswith("O_"): return BallanceObjectInfo.create_from_others(BallanceObjectType.DECORATION) - if obj.name == 'SkyLayer': + if name == 'SkyLayer': return BallanceObjectInfo.create_from_others(BallanceObjectType.SKYLAYER) if reporter is not None: @@ -511,175 +546,57 @@ class _ImengyuConvention(): return None @staticmethod - def set_to_object(obj: bpy.types.Object, info: BallanceObjectInfo, reporter: _RenameErrorReporter | None) -> bool: + 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: - obj.name = 'O_' + return 'O_' case BallanceObjectType.SKYLAYER: - obj.name = 'SkyLayer' + return 'SkyLayer' case BallanceObjectType.LEVEL_START: - obj.name = 'PS_LevelStart' + return 'PS_LevelStart' case BallanceObjectType.LEVEL_END: - obj.name = 'PE_LevelEnd' + return 'PE_LevelEnd' case BallanceObjectType.CHECKPOINT: - obj.name = f'PR_ResetPoint:{info.mSector:d}' + return f'PR_ResetPoint:{info.mSector:d}' case BallanceObjectType.RESETPOINT: - obj.name = f'PC_CheckPoint:{info.mSector:d}' + return f'PC_CheckPoint:{info.mSector:d}' case BallanceObjectType.DEPTH_CUBE: - obj.name = 'DepthTestCubes' + return 'DepthTestCubes' case BallanceObjectType.FLOOR: - obj.name = 'S_Floors' + return 'S_Floors' case BallanceObjectType.RAIL: - obj.name = 'S_FloorWoods' + return 'S_FloorWoods' case BallanceObjectType.WOOD: - obj.name = 'S_FloorRails' + return 'S_FloorRails' case BallanceObjectType.STOPPER: - obj.name = 'S_FloorStopper' + return 'S_FloorStopper' case BallanceObjectType.COMPONENT: - obj.name = '{}:{}:{:d}'.format( - info.mComponentType, obj.name.replace(':', '_'), info.mSector) + 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 False + 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 - @staticmethod - def register() -> _NamingConventionProfile: - return _NamingConventionProfile( - 'Imengyu Ballance', - 'Auto grouping name standard for Imengyu/Ballance.', - _ImengyuConvention.parse_from_object, - _ImengyuConvention.set_to_object - ) #endregion - -#region Naming Convention Register - -## All available naming conventions -# Each naming convention should have a identifier for visiting them. -# The identifier is its index in this tuple. -_g_NamingConventions: list[_NamingConventionProfile] = [] -def _register_naming_convention_with_index(profile: _NamingConventionProfile) -> int: - global _g_NamingConventions - ret: int = len(_g_NamingConventions) - _g_NamingConventions.append(profile) - return ret - -# register and assign to a enum -class NamingConvention(enum.IntEnum): - VirtoolsGroup = _register_naming_convention_with_index(_VirtoolsGroupConvention.register()) - YYCToolchain = _register_naming_convention_with_index(_YYCToolchainConvention.register()) - Imengyu = _register_naming_convention_with_index(_ImengyuConvention.register()) - -class _EnumPropHelper(): - """ - Operate like UTIL_virtools_types.EnumPropHelper - Return the identifier (index) of naming convention. - """ - - @staticmethod - def generate_items() -> tuple[tuple, ...]: - # create a function to filter Virtools Group profile - # and return index at the same time - def naming_convention_iter() -> typing.Iterator[tuple[int, _NamingConventionProfile]]: - for item in NamingConvention: - if item != NamingConvention.VirtoolsGroup: - yield (item.value, _g_NamingConventions[item.value]) - - # token, display name, descriptions, icon, index - return tuple( - ( - str(idx), - item.mName, - item.mDesc, - "", - idx - ) for idx, item in naming_convention_iter() - ) - - @staticmethod - def get_selection(prop: str) -> NamingConvention: - return NamingConvention(int(prop)) - - @staticmethod - def to_selection(val: NamingConvention) -> str: - return str(val.value) - - @staticmethod - def get_virtools_group_identifier() -> NamingConvention: - # The native naming convention is Virtools Group - # We treat it as naming convention because we want use a universal interface to process naming converting. - # So Virtools Group can no be seen as a naming convention, but we treat it like naming convention in code. - return NamingConvention.VirtoolsGroup - - @staticmethod - def get_default_naming_identifier() -> NamingConvention: - # The default fallback naming convention is YYC toolchain - return NamingConvention.YYCToolchain - -#endregion - -def name_setter_core(ident: NamingConvention, info: BallanceObjectInfo, obj: bpy.types.Object) -> None: - # get profile - profile: _NamingConventionProfile = _g_NamingConventions[ident.value] - # set name. don't care whether success. - profile.mSetFct(obj, info, None) - -def name_converting_core(src_ident: NamingConvention, dst_ident: NamingConvention, objs: typing.Iterable[bpy.types.Object]) -> None: - # no convert needed - if src_ident == dst_ident: return - - # get convert profile - src: _NamingConventionProfile = _g_NamingConventions[src_ident.value] - dst: _NamingConventionProfile = _g_NamingConventions[dst_ident.value] - - # create reporter and success counter - failed_obj_counter: int = 0 - all_obj_counter: int = 0 - err_reporter: _RenameErrorReporter = _RenameErrorReporter() - - # print console report header - print('============') - print('Rename Report') - print('------------') - - # start converting - for obj in objs: - # inc counter all - all_obj_counter += 1 - # begin object processing - err_reporter.begin_object(obj) - # parsing from src and set by dst - # inc failed counter if failed - obj_info: BallanceObjectInfo | None= src.mParseFct(obj, err_reporter) - if obj_info is not None: - ret: bool = dst.mSetFct(obj, obj_info, err_reporter) - if not ret: failed_obj_counter += 1 - else: - failed_obj_counter += 1 - # end object processing and output err list - err_reporter.end_object(obj) - - # print console report tail - print('------------') - print(f'All / Failed - {all_obj_counter} / {failed_obj_counter}') - print('============') - - # popup blender window to notice user - UTIL_functions.message_box( - ( - 'View console to get more detail.', - f'All: {all_obj_counter}', - f'Failed: {failed_obj_counter}', - ), - "Rename Report", - UTIL_icons_manager.BlenderPresetIcons.Info.value - )