feat: add ballance map sector field in scene
- 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.
This commit is contained in:
parent
6cf2ab895d
commit
3396947115
@ -1,7 +1,7 @@
|
||||
import bpy, mathutils
|
||||
import math, typing
|
||||
from . import UTIL_functions, UTIL_icons_manager, UTIL_naming_convension
|
||||
from . import PROP_ballance_element, PROP_virtools_group
|
||||
from . import PROP_ballance_element, PROP_virtools_group, PROP_ballance_map_info
|
||||
|
||||
#region Param Help Classes
|
||||
|
||||
@ -128,6 +128,7 @@ class _GeneralComponentCreator():
|
||||
"""
|
||||
# get element info first
|
||||
ele_info: UTIL_naming_convension.BallanceObjectInfo = _get_component_info(comp_type, comp_sector)
|
||||
|
||||
# create blc element context
|
||||
with PROP_ballance_element.BallanceElementsHelper(bpy.context.scene) as creator:
|
||||
# object creation counter
|
||||
@ -143,6 +144,27 @@ class _GeneralComponentCreator():
|
||||
# put into created object list
|
||||
self.__mObjList.append(obj)
|
||||
|
||||
# enlarge scene sector field for non-PS (start point) PE (end point) component
|
||||
# read from scene and create var for enlarged sector count
|
||||
map_info: PROP_ballance_map_info.RawBallanceMapInfo = PROP_ballance_map_info.get_raw_ballance_map_info(bpy.context.scene)
|
||||
enlarged_sector: int
|
||||
# check component type to get enlarged value
|
||||
match(ele_info.mBasicType):
|
||||
case UTIL_naming_convension.BallanceObjectType.COMPONENT:
|
||||
enlarged_sector = comp_sector
|
||||
case UTIL_naming_convension.BallanceObjectType.CHECKPOINT:
|
||||
# checkpoint 1 means that there is sector 2, so we plus 1 for it.
|
||||
enlarged_sector = comp_sector + 1
|
||||
case UTIL_naming_convension.BallanceObjectType.RESETPOINT:
|
||||
enlarged_sector = comp_sector
|
||||
case _:
|
||||
# this component is not a sector based component
|
||||
# so we do not change it (use original value)
|
||||
enlarged_sector = map_info.mSectorCount
|
||||
# enlarge it
|
||||
map_info.mSectorCount = max(map_info.mSectorCount, enlarged_sector)
|
||||
PROP_ballance_map_info.set_raw_ballance_map_info(bpy.context.scene, map_info)
|
||||
|
||||
def finish_component(self) -> None:
|
||||
"""
|
||||
Finish up component creation.
|
||||
|
@ -1,9 +1,9 @@
|
||||
import bpy
|
||||
from bpy_extras.wm_utils.progress_report import ProgressReport
|
||||
import tempfile, os, typing, re
|
||||
import tempfile, os, typing
|
||||
from . import PROP_preferences, UTIL_ioport_shared
|
||||
from . import UTIL_virtools_types, UTIL_functions, UTIL_file_browser, UTIL_blender_mesh, UTIL_ballance_texture, UTIL_icons_manager
|
||||
from . import PROP_virtools_group, PROP_virtools_material, PROP_virtools_mesh, PROP_virtools_texture
|
||||
from . import UTIL_virtools_types, UTIL_functions, UTIL_file_browser, UTIL_blender_mesh, UTIL_ballance_texture, UTIL_icons_manager, UTIL_naming_convension
|
||||
from . import PROP_virtools_group, PROP_virtools_material, PROP_virtools_mesh, PROP_virtools_texture, PROP_ballance_map_info
|
||||
from .PyBMap import bmap_wrapper as bmap
|
||||
|
||||
# define global tex save opt blender enum prop helper
|
||||
@ -84,6 +84,11 @@ class BBP_OT_export_virtools(bpy.types.Operator, UTIL_file_browser.ExportVirtool
|
||||
if self.use_compress:
|
||||
box.prop(self, 'compress_level')
|
||||
|
||||
# show sector info to notice user
|
||||
layout.separator()
|
||||
map_info: PROP_ballance_map_info.RawBallanceMapInfo = PROP_ballance_map_info.get_raw_ballance_map_info(bpy.context.scene)
|
||||
layout.label(text = f'Map Sectors: {map_info.mSectorCount}')
|
||||
|
||||
_TObj3dPair = tuple[bpy.types.Object, bmap.BM3dObject]
|
||||
_TMeshPair = tuple[bpy.types.Object, bpy.types.Mesh, bmap.BMMesh]
|
||||
_TMaterialPair = tuple[bpy.types.Material, bmap.BMMaterial]
|
||||
@ -170,8 +175,22 @@ def _export_virtools_groups(
|
||||
# start saving
|
||||
progress.enter_substeps(len(obj3d_crets), "Saving Groups")
|
||||
|
||||
# create group exporting helper
|
||||
group_cret_guard: VirtoolsGroupCreationGuard = VirtoolsGroupCreationGuard(writer)
|
||||
# create sector group first
|
||||
# This step is designed for ensure that the created sector group is successive.
|
||||
#
|
||||
# Due to the design of Ballance, Ballance rely on checking the existance of Sector_XX to get how many sectors this map have.
|
||||
# Thus if there are no component in a sector, it still need to create a empty Sector_XX group, otherwise the game will crash
|
||||
# or be ended at a accident sector.
|
||||
#
|
||||
# So we create all needed sector group in here to make sure exported virtools file can be read by Ballancde correctly.
|
||||
map_info: PROP_ballance_map_info.RawBallanceMapInfo = PROP_ballance_map_info.get_raw_ballance_map_info(bpy.context.scene)
|
||||
for i in range(map_info.mSectorCount):
|
||||
gp_name: str = UTIL_naming_convension.build_name_from_sector_index(i + 1)
|
||||
vtgroup: bmap.BMGroup | None = group_cret_map.get(gp_name, None)
|
||||
if vtgroup is None:
|
||||
vtgroup = writer.create_group()
|
||||
vtgroup.set_name(gp_name)
|
||||
group_cret_map[gp_name] = vtgroup
|
||||
|
||||
for obj3d, vtobj3d in obj3d_crets:
|
||||
# open group visitor
|
||||
@ -180,8 +199,8 @@ def _export_virtools_groups(
|
||||
# get group or create new group from guard
|
||||
vtgroup: bmap.BMGroup | None = group_cret_map.get(gp_name, None)
|
||||
if vtgroup is None:
|
||||
# note: no need to set name, guard has set it.
|
||||
vtgroup = group_cret_guard.create_group(gp_name)
|
||||
vtgroup = writer.create_group()
|
||||
vtgroup.set_name(gp_name)
|
||||
group_cret_map[gp_name] = vtgroup
|
||||
|
||||
# group this object
|
||||
@ -471,73 +490,6 @@ def _save_virtools_document(
|
||||
progress.step()
|
||||
progress.leave_substeps()
|
||||
|
||||
class VirtoolsGroupCreationGuard():
|
||||
"""
|
||||
This class is designed for ensure that the created sector group is successive.
|
||||
|
||||
Due to the design of Ballance, Ballance rely on checking the existance of Sector_XX to get how many sectors this map have.
|
||||
Thus if there are no component in a sector, it still need to create a empty Sector_XX group, otherwise the game will crash
|
||||
or be ended at a accident sector.
|
||||
|
||||
This class hook the operation of Virtools group creation and check all Sector group creation.
|
||||
Create essential group to make Sector_XX group successive.
|
||||
Thus all group creation in this module should be passed by this class.
|
||||
"""
|
||||
cRegexGroupSector: typing.ClassVar[re.Pattern] = re.compile('^Sector_(0[1-8]|[1-9][0-9]{1,2}|9)$')
|
||||
|
||||
@staticmethod
|
||||
def __get_group_index(group_name: str) -> int | None:
|
||||
"""
|
||||
Return the sector index of group name if it is. Otherwise None.
|
||||
"""
|
||||
regex_result = VirtoolsGroupCreationGuard.cRegexGroupSector.match(group_name)
|
||||
if regex_result is not None:
|
||||
return int(regex_result.group(1))
|
||||
else:
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def __get_group_name(group_index: int) -> str:
|
||||
"""
|
||||
Output Sector group name by given sector index
|
||||
"""
|
||||
if group_index == 9:
|
||||
return 'Sector_9'
|
||||
else:
|
||||
return f'Sector_{group_index:0>2d}'
|
||||
|
||||
__mWriter: bmap.BMFileWriter
|
||||
__mSectors: list[bmap.BMGroup]
|
||||
|
||||
def __init__(self, assoc_writer: bmap.BMFileWriter):
|
||||
self.__mWriter = assoc_writer
|
||||
self.__mSectors = []
|
||||
|
||||
def create_group(self, group_name: str) -> bmap.BMGroup:
|
||||
"""
|
||||
The hooked group creation function.
|
||||
|
||||
Please note the passed group name argument is just for name checking.
|
||||
This function will set group name for you, not like BMFileWriter.create_group() operated.
|
||||
"""
|
||||
# check whether it is sector group
|
||||
# note: the return sector index is 1 based, not 0
|
||||
sector_idx: int | None = VirtoolsGroupCreationGuard.__get_group_index(group_name)
|
||||
# if it is regular group, return normal creation
|
||||
if sector_idx is None:
|
||||
gp: bmap.BMGroup = self.__mWriter.create_group()
|
||||
gp.set_name(group_name)
|
||||
return gp
|
||||
|
||||
# get from sector cahce list
|
||||
# enlarge sector cache list if it is not fulfilled given sector index
|
||||
while sector_idx > len(self.__mSectors):
|
||||
gp: bmap.BMGroup = self.__mWriter.create_group()
|
||||
self.__mSectors.append(gp)
|
||||
gp.set_name(self.__get_group_name(len(self.__mSectors)))
|
||||
# return ordered sector from sector caches
|
||||
return self.__mSectors[sector_idx - 1]
|
||||
|
||||
def register() -> None:
|
||||
bpy.utils.register_class(BBP_OT_export_virtools)
|
||||
|
||||
|
@ -2,8 +2,8 @@ import bpy
|
||||
from bpy_extras.wm_utils.progress_report import ProgressReport
|
||||
import tempfile, os, typing
|
||||
from . import PROP_preferences, UTIL_ioport_shared
|
||||
from . import UTIL_virtools_types, UTIL_functions, UTIL_file_browser, UTIL_blender_mesh, UTIL_ballance_texture
|
||||
from . import PROP_virtools_group, PROP_virtools_material, PROP_virtools_mesh, PROP_virtools_texture
|
||||
from . import UTIL_virtools_types, UTIL_functions, UTIL_file_browser, UTIL_blender_mesh, UTIL_ballance_texture, UTIL_naming_convension
|
||||
from . import PROP_virtools_group, PROP_virtools_material, PROP_virtools_mesh, PROP_virtools_texture, PROP_ballance_map_info
|
||||
from .PyBMap import bmap_wrapper as bmap
|
||||
|
||||
class BBP_OT_import_virtools(bpy.types.Operator, UTIL_file_browser.ImportVirtoolsFile, UTIL_ioport_shared.ImportParams, UTIL_ioport_shared.VirtoolsParams):
|
||||
@ -344,10 +344,12 @@ def _import_virtools_groups(
|
||||
reader: bmap.BMFileReader,
|
||||
progress: ProgressReport,
|
||||
obj3d_cret_map: dict[bmap.BM3dObject, bpy.types.Object]
|
||||
) -> dict[bmap.BM3dObject, bpy.types.Object]:
|
||||
) -> None:
|
||||
# we need iterate all groups to construct a reversed map
|
||||
# to indicate which groups should this 3dobject be grouped into.
|
||||
reverse_map: dict[bmap.BM3dObject, set[str]] = {}
|
||||
# sector counter to record the maximum sector we have processed.
|
||||
sector_count: int = 1
|
||||
|
||||
# prepare progress
|
||||
progress.enter_substeps(reader.get_material_count(), "Loading Groups")
|
||||
@ -357,6 +359,12 @@ def _import_virtools_groups(
|
||||
group_name: str | None = vtgroup.get_name()
|
||||
if group_name is None: continue
|
||||
|
||||
# try extracting sector info
|
||||
potential_sector_count: int | None = UTIL_naming_convension.extract_sector_from_name(group_name)
|
||||
if potential_sector_count is not None:
|
||||
sector_count = max(sector_count, potential_sector_count)
|
||||
|
||||
# creating map
|
||||
for item in vtgroup.get_objects():
|
||||
# get or create set
|
||||
objgroups: set[str] = reverse_map.get(item, None)
|
||||
@ -370,6 +378,11 @@ def _import_virtools_groups(
|
||||
# step
|
||||
progress.step()
|
||||
|
||||
# assign to ballance map info according to gotten sector count
|
||||
map_info: PROP_ballance_map_info.RawBallanceMapInfo = PROP_ballance_map_info.get_raw_ballance_map_info(bpy.context.scene)
|
||||
map_info.mSectorCount = max(map_info.mSectorCount, sector_count)
|
||||
PROP_ballance_map_info.set_raw_ballance_map_info(bpy.context.scene, map_info)
|
||||
|
||||
# leave progress
|
||||
progress.leave_substeps()
|
||||
|
||||
|
81
bbp_ng/PROP_ballance_map_info.py
Normal file
81
bbp_ng/PROP_ballance_map_info.py
Normal file
@ -0,0 +1,81 @@
|
||||
import bpy
|
||||
import typing
|
||||
from . import UTIL_functions
|
||||
|
||||
class RawBallanceMapInfo():
|
||||
cSectorCount: typing.ClassVar[int] = 1
|
||||
|
||||
mSectorCount: int
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
self.mSectorCount = kwargs.get("mSectorCount", RawBallanceMapInfo.cSectorCount)
|
||||
|
||||
def regulate(self):
|
||||
self.mSectorCount = UTIL_functions.clamp_int(self.mSectorCount, 1, 999)
|
||||
|
||||
#region Prop Decl & Getter Setter
|
||||
|
||||
class BBP_PG_ballance_map_info(bpy.types.PropertyGroup):
|
||||
sector_count: bpy.props.IntProperty(
|
||||
name = "Sector",
|
||||
description = "The sector count of this Ballance map which is used in exporting map and may be changed when importing map.",
|
||||
default = 1,
|
||||
max = 999, min = 1,
|
||||
soft_max = 8, soft_min = 1,
|
||||
step = 1
|
||||
) # type: ignore
|
||||
|
||||
def get_ballance_map_info(scene: bpy.types.Scene) -> BBP_PG_ballance_map_info:
|
||||
return scene.ballance_map_info
|
||||
|
||||
def get_raw_ballance_map_info(scene: bpy.types.Scene) -> RawBallanceMapInfo:
|
||||
props: BBP_PG_ballance_map_info = get_ballance_map_info(scene)
|
||||
rawdata: RawBallanceMapInfo = RawBallanceMapInfo()
|
||||
|
||||
rawdata.mSectorCount = props.sector_count
|
||||
|
||||
rawdata.regulate()
|
||||
return rawdata
|
||||
|
||||
def set_raw_ballance_map_info(scene: bpy.types.Scene, rawdata: RawBallanceMapInfo) -> None:
|
||||
props: BBP_PG_ballance_map_info = get_ballance_map_info(scene)
|
||||
|
||||
props.sector_count = rawdata.mSectorCount
|
||||
|
||||
#endregion
|
||||
|
||||
class BBP_PT_ballance_map_info(bpy.types.Panel):
|
||||
"""Show Ballance Map Infos."""
|
||||
bl_label = "Ballance Map"
|
||||
bl_idname = "BBP_PT_ballance_map_info"
|
||||
bl_space_type = 'PROPERTIES'
|
||||
bl_region_type = 'WINDOW'
|
||||
bl_context = "scene"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return context.scene is not None
|
||||
|
||||
def draw(self, context):
|
||||
layout: bpy.types.UILayout = self.layout
|
||||
target: bpy.types.Scene = context.scene
|
||||
props: BBP_PG_ballance_map_info = get_ballance_map_info(target)
|
||||
|
||||
# show map sector count numberbox
|
||||
layout.prop(props, 'sector_count')
|
||||
|
||||
def register() -> None:
|
||||
# register
|
||||
bpy.utils.register_class(BBP_PG_ballance_map_info)
|
||||
bpy.utils.register_class(BBP_PT_ballance_map_info)
|
||||
|
||||
# add into scene metadata
|
||||
bpy.types.Scene.ballance_map_info = bpy.props.PointerProperty(type = BBP_PG_ballance_map_info)
|
||||
|
||||
def unregister() -> None:
|
||||
# del from scene metadata
|
||||
del bpy.types.Scene.ballance_map_info
|
||||
|
||||
# unregister
|
||||
bpy.utils.unregister_class(BBP_PG_ballance_map_info)
|
||||
bpy.utils.unregister_class(BBP_PT_ballance_map_info)
|
@ -8,7 +8,7 @@ class BBP_PG_virtools_group(bpy.types.PropertyGroup):
|
||||
group_name: bpy.props.StringProperty(
|
||||
name = "Group Name",
|
||||
default = ""
|
||||
)
|
||||
) # type: ignore
|
||||
|
||||
def get_virtools_groups(obj: bpy.types.Object) -> bpy.types.CollectionProperty:
|
||||
return obj.virtools_groups
|
||||
|
@ -184,6 +184,42 @@ class BallanceObjectInfo():
|
||||
|
||||
#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((
|
||||
@ -227,7 +263,6 @@ _g_BlcWood: set[str] = set((
|
||||
))
|
||||
|
||||
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])$')
|
||||
@ -255,9 +290,9 @@ class VirtoolsGroupConvention():
|
||||
counter: int = 0
|
||||
last_matched_sector: int = 0
|
||||
for i in gps:
|
||||
regex_result = VirtoolsGroupConvention.cRegexGroupSector.match(i)
|
||||
regex_result: int | None = extract_sector_from_name(i)
|
||||
if regex_result is not None:
|
||||
last_matched_sector = int(regex_result.group(1))
|
||||
last_matched_sector = regex_result
|
||||
counter += 1
|
||||
|
||||
if counter != 1: return None
|
||||
@ -377,12 +412,8 @@ class VirtoolsGroupConvention():
|
||||
# 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
|
||||
if info.mSector == 9:
|
||||
gp.add_group('Sector_9')
|
||||
else:
|
||||
gp.add_group(f'Sector_{info.mSector:0>2d}')
|
||||
gp.add_group(build_name_from_sector_index(typing.cast(int, info.mSector)))
|
||||
|
||||
case _:
|
||||
if reporter is not None:
|
||||
|
@ -29,7 +29,8 @@ from . import UTIL_icons_manager
|
||||
UTIL_icons_manager.register()
|
||||
|
||||
# then load other modules
|
||||
from . import PROP_preferences, PROP_ptrprop_resolver, PROP_virtools_material, PROP_virtools_texture, PROP_virtools_mesh, PROP_virtools_group, PROP_ballance_element, PROP_bme_material
|
||||
from . import PROP_preferences, PROP_ptrprop_resolver, PROP_virtools_material, PROP_virtools_texture, PROP_virtools_mesh, PROP_virtools_group
|
||||
from . import PROP_ballance_element, PROP_bme_material, PROP_ballance_map_info
|
||||
from . import OP_IMPORT_bmfile, OP_EXPORT_bmfile, OP_IMPORT_virtools, OP_EXPORT_virtools
|
||||
from . import OP_UV_flatten_uv, OP_UV_rail_uv
|
||||
from . import OP_MTL_fix_material
|
||||
@ -217,6 +218,7 @@ def register() -> None:
|
||||
PROP_virtools_group.register()
|
||||
PROP_ballance_element.register()
|
||||
PROP_bme_material.register()
|
||||
PROP_ballance_map_info.register()
|
||||
|
||||
OP_IMPORT_bmfile.register()
|
||||
OP_EXPORT_bmfile.register()
|
||||
@ -275,6 +277,7 @@ def unregister() -> None:
|
||||
OP_EXPORT_bmfile.unregister()
|
||||
OP_IMPORT_bmfile.unregister()
|
||||
|
||||
PROP_ballance_map_info.unregister()
|
||||
PROP_bme_material.unregister()
|
||||
PROP_ballance_element.unregister()
|
||||
PROP_virtools_group.unregister()
|
||||
|
Loading…
Reference in New Issue
Block a user