From 6dd14a0d8bc43020d8ca06f3a78fa8c10be2d358 Mon Sep 17 00:00:00 2001 From: Anoliah <173389381+Anoliah@users.noreply.github.com> Date: Fri, 15 Aug 2025 11:12:09 -0700 Subject: [PATCH] [SM64] Rework actor load exports for level scripts (#564) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * seg 8 loading for yay0 and geo segments * update replaceScriptLoads to add new script loads if missing * change grid layout * add global write/don't write toggle for actor loads and 'None' option for individual segment loads * reappend any JUMP_LINK commands that do not contain 'script_func_global' * formatting lol * some code quality changes * move write actor loads to the correct place, clean up the UI a little (grid wasn´t even being used properly aha) * replace yay0 or mio0 equivelent commands to current compression method --------- Co-authored-by: Lila --- fast64_internal/sm64/sm64_constants.py | 10 ++- fast64_internal/sm64/sm64_level_writer.py | 93 +++++++++++++---------- fast64_internal/sm64/sm64_objects.py | 53 +++++++++---- 3 files changed, 97 insertions(+), 59 deletions(-) diff --git a/fast64_internal/sm64/sm64_constants.py b/fast64_internal/sm64/sm64_constants.py index 8d802ef..1d07ce5 100644 --- a/fast64_internal/sm64/sm64_constants.py +++ b/fast64_internal/sm64/sm64_constants.py @@ -2101,7 +2101,7 @@ groupsSeg5 = [ ("group9", "group9", "Haunted Objects (Boo, Mad Piano etc.)"), ("group10", "group10", "Peach/Yoshi"), ("group11", "group11", "THI Ojbects (Lakitu, Wiggler, Bubba)"), - ("Do Not Write", "Do Not Write", "Do Not Write"), + ("None", "None", "None"), ("Custom", "Custom", "Custom"), ] @@ -2113,7 +2113,13 @@ groupsSeg6 = [ ("group15", "group15", "Castle Objects (MIPS, Toad etc.)"), ("group16", "group16", "Ice Objects (Chill Bully, Moneybags)"), ("group17", "group17", "Cave Objects (Swoop, Scuttlebug, Dorrie etc.)"), - ("Do Not Write", "Do Not Write", "Do Not Write"), + ("None", "None", "None"), + ("Custom", "Custom", "Custom"), +] + +groups_seg8 = [ + ("common0", "common0", "Generic course objects (Goomba, Bob-ombs, Cannon etc.)"), + ("None", "None", "None"), ("Custom", "Custom", "Custom"), ] diff --git a/fast64_internal/sm64/sm64_level_writer.py b/fast64_internal/sm64/sm64_level_writer.py index 712569a..f075b22 100644 --- a/fast64_internal/sm64/sm64_level_writer.py +++ b/fast64_internal/sm64/sm64_level_writer.py @@ -440,19 +440,25 @@ def parseZoomMasks(filepath): return ZoomOutMasks(zoomMacros, cameraData) -def replaceSegmentLoad(levelscript, segmentName, command, changedSegment): +def replace_segment_load(level: LevelScript, name: str, commands: list[str], new_segment: int): + args = ["0x0{:X}".format(new_segment), name + "SegmentRomStart", name + "SegmentRomEnd"] changedLoad = None - for segmentLoad in levelscript.segmentLoads: + for i, segmentLoad in enumerate(level.segmentLoads): segmentString = segmentLoad[1][0].lower() - segment = int(segmentString, 16 if "x" in segmentString else 10) - if segmentLoad[0] == command and segment == changedSegment: + segment = int(segmentString, 0) + if segmentLoad[0] in commands and segment == new_segment: + level.segmentLoads[i] = Macro(commands[0], args, segmentLoad[2]) changedLoad = segmentLoad if changedLoad is None: - changedLoad = Macro(command, [hex(changedSegment), "", ""], "") - levelscript.segmentLoads.append(changedLoad) + changedLoad = Macro(commands[0], args, "") + level.segmentLoads.append(changedLoad) - changedLoad[1][1] = segmentName + "SegmentRomStart" - changedLoad[1][2] = segmentName + "SegmentRomEnd" + +def remove_segment_load(levelscript: LevelScript, segment_num: int): + for segment_load in levelscript.segmentLoads: + segment_string = segment_load[1][0].lower() + if int(segment_string, 0) == segment_num: + levelscript.segmentLoads.remove(segment_load) def replaceScriptLoads(levelscript, obj): @@ -462,19 +468,13 @@ def replaceScriptLoads(levelscript, obj): if "script_func_global_" not in target: newFuncs.append(jumpLink) continue - scriptNum = int(re.findall(r"\d+", target)[-1]) - # this is common0 - if scriptNum == 1: - newFuncs.append(jumpLink) - continue - if scriptNum < 13: - newNum = obj.fast64.sm64.segment_loads.group5 - else: - newNum = obj.fast64.sm64.segment_loads.group6 - if newNum == "Do Not Write": - newFuncs.append(jumpLink) - continue - newFuncs.append(Macro("JUMP_LINK", [newNum], jumpLink.comment)) + + group_seg_loads = obj.fast64.sm64.segment_loads + scriptFuncs = (group_seg_loads.group8, group_seg_loads.group5, group_seg_loads.group6) + for func in scriptFuncs: + if func is not None: + newFuncs.append(Macro("JUMP_LINK", [func], "")) + levelscript.levelFunctions = newFuncs @@ -818,38 +818,49 @@ def export_area_c( def export_level_script_c(obj, prev_level_script, level_name, level_data, level_dir, uses_env_fx): compressionFmt = bpy.context.scene.fast64.sm64.compression_format + + def replace_compressed_segment_load(name: str, segment: int, add_compression_fmt=True): + compression_fmts = [compressionFmt.upper()] + list({"MIO0", "YAY0", "RAW"} - {compressionFmt.upper()}) + valid_cmds = [f"LOAD_{fmt}" for fmt in compression_fmts] + if add_compression_fmt: + name += f"_{compressionFmt}" + replace_segment_load(prev_level_script, f"_{name}", valid_cmds, segment) + # replace level loads - replaceSegmentLoad(prev_level_script, f"_{level_name}_segment_7", f"LOAD_{compressionFmt.upper()}", 0x07) + replace_compressed_segment_load(f"{level_name}_segment_7", 0x07, False) if uses_env_fx: - replaceSegmentLoad(prev_level_script, f"_effect_{compressionFmt}", f"LOAD_{compressionFmt.upper()}", 0x0B) + replace_compressed_segment_load("effect", 0x0B) if not obj.useBackgroundColor: if obj.background == "CUSTOM": segment = obj.fast64.sm64.level.backgroundSegment else: segment = f"{backgroundSegments[obj.background]}_skybox" - replaceSegmentLoad(prev_level_script, f"_{segment}_{compressionFmt}", f"LOAD_{compressionFmt.upper()}", 0x0A) + replace_compressed_segment_load(segment, 0x0A) # replace actor loads group_seg_loads = obj.fast64.sm64.segment_loads - if group_seg_loads.seg5_enum != "Do Not Write": - replaceSegmentLoad( - prev_level_script, - f"_{group_seg_loads.seg5}_{compressionFmt}", - f"LOAD_{compressionFmt.upper()}", - 0x05, - ) - replaceSegmentLoad(prev_level_script, f"_{group_seg_loads.seg5}_geo", "LOAD_RAW", 0x0C) - if group_seg_loads.seg6_enum != "Do Not Write": - replaceSegmentLoad( - prev_level_script, - f"_{group_seg_loads.seg6}_{compressionFmt}", - f"LOAD_{compressionFmt.upper()}", - 0x06, - ) - replaceSegmentLoad(prev_level_script, f"_{group_seg_loads.seg6}_geo", "LOAD_RAW", 0x0D) + if group_seg_loads.write_actor_loads: + if group_seg_loads.seg5_enum != "None": + replace_compressed_segment_load(group_seg_loads.seg5, 0x05) + replace_segment_load(prev_level_script, f"_{group_seg_loads.seg5}_geo", ["LOAD_RAW"], 0x0C) + else: + remove_segment_load(prev_level_script, 0x05) + remove_segment_load(prev_level_script, 0x0C) + if group_seg_loads.seg6_enum != "None": + replace_compressed_segment_load(group_seg_loads.seg6, 0x06) + replace_segment_load(prev_level_script, f"_{group_seg_loads.seg6}_geo", ["LOAD_RAW"], 0x0D) + else: + remove_segment_load(prev_level_script, 0x06) + remove_segment_load(prev_level_script, 0x0D) + if group_seg_loads.seg8_enum != "None": + replace_compressed_segment_load(group_seg_loads.seg8, 0x08) + replace_segment_load(prev_level_script, f"_{group_seg_loads.seg8}_geo", ["LOAD_RAW"], 0x0F) + else: + remove_segment_load(prev_level_script, 0x08) + remove_segment_load(prev_level_script, 0x0F) + replaceScriptLoads(prev_level_script, obj) # write data - replaceScriptLoads(prev_level_script, obj) saveDataToFile(os.path.join(level_dir, "script.c"), prev_level_script.to_c(level_data.area_data)) return level_data diff --git a/fast64_internal/sm64/sm64_objects.py b/fast64_internal/sm64/sm64_objects.py index 27c16b1..f2243a2 100644 --- a/fast64_internal/sm64/sm64_objects.py +++ b/fast64_internal/sm64/sm64_objects.py @@ -46,6 +46,7 @@ from .sm64_constants import ( obj_group_enums, groupsSeg5, groupsSeg6, + groups_seg8, groups_obj_export, ) from .sm64_utility import convert_addr_to_func @@ -1254,8 +1255,7 @@ class SM64ObjectPanel(bpy.types.Panel): # box.box().label(text = 'Background IDs defined in include/geo_commands.h.') box.prop(obj, "actSelectorIgnore") box.prop(obj, "setAsStartLevel") - grid = box.grid_flow(columns=2) - obj.fast64.sm64.segment_loads.draw(grid) + obj.fast64.sm64.segment_loads.draw_props(box) prop_split(box, obj, "acousticReach", "Acoustic Reach") obj.starGetCutscenes.draw(box) @@ -2776,28 +2776,35 @@ class SM64_GameObjectProperties(bpy.types.PropertyGroup): class SM64_SegmentProperties(bpy.types.PropertyGroup): + write_actor_loads: bpy.props.BoolProperty(name="Write Actor Loads") seg5_load_custom: bpy.props.StringProperty(name="Segment 5 Seg") seg5_group_custom: bpy.props.StringProperty(name="Segment 5 Group") seg6_load_custom: bpy.props.StringProperty(name="Segment 6 Seg") seg6_group_custom: bpy.props.StringProperty(name="Segment 6 Group") - seg5_enum: bpy.props.EnumProperty(name="Segment 5 Group", default="Do Not Write", items=groupsSeg5) - seg6_enum: bpy.props.EnumProperty(name="Segment 6 Group", default="Do Not Write", items=groupsSeg6) + seg8_load_custom: bpy.props.StringProperty(name="Segment 8 Seg") + seg8_group_custom: bpy.props.StringProperty(name="Segment 8 Group") + seg5_enum: bpy.props.EnumProperty(name="Segment 5 Group", default="None", items=groupsSeg5) + seg6_enum: bpy.props.EnumProperty(name="Segment 6 Group", default="None", items=groupsSeg6) + seg8_enum: bpy.props.EnumProperty(name="Segment 8 Group", default="None", items=groups_seg8) - def draw(self, layout): + def draw_props(self, layout): col = layout.column() - prop_split(col, self, "seg5_enum", "Segment 5 Select") - if self.seg5_enum == "Custom": - prop_split(col, self, "seg5_load_custom", "Segment 5 Seg") - prop_split(col, self, "seg5_group_custom", "Segment 5 Group") - col = layout.column() - prop_split(col, self, "seg6_enum", "Segment 6 Select") - if self.seg6_enum == "Custom": - prop_split(col, self, "seg6_load_custom", "Segment 6 Seg") - prop_split(col, self, "seg6_group_custom", "Segment 6 Group") + col.prop(self, "write_actor_loads") + if not self.write_actor_loads: + return + + for seg in (5, 6, 8): + prop_split(col, self, f"seg{seg}_enum", f"Segment {seg} Select") + if getattr(self, f"seg{seg}_enum") == "Custom": + prop_split(col, self, f"seg{seg}_load_custom", "Segment") + prop_split(col, self, f"seg{seg}_group_custom", "Group") + col.separator() def jump_link_from_enum(self, grp): - if grp == "Do Not Write": - return grp + if grp == "None": + return None + elif grp == "common0": + return "script_func_global_1" num = int(grp.removeprefix("group")) + 1 return f"script_func_global_{num}" @@ -2815,6 +2822,13 @@ class SM64_SegmentProperties(bpy.types.PropertyGroup): else: return self.seg6_enum + @property + def seg8(self): + if self.seg8_enum == "Custom": + return self.seg8_load_custom + else: + return self.seg8_enum + @property def group5(self): if self.seg5_enum == "Custom": @@ -2829,6 +2843,13 @@ class SM64_SegmentProperties(bpy.types.PropertyGroup): else: return self.jump_link_from_enum(self.seg6_enum) + @property + def group8(self): + if self.seg8_enum == "Custom": + return self.seg8_group_custom + else: + return self.jump_link_from_enum(self.seg8_enum) + class SM64_ObjectProperties(bpy.types.PropertyGroup): version: bpy.props.IntProperty(name="SM64_ObjectProperties Version", default=0)