[SM64] Rework actor load exports for level scripts (#564)

* 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 <[email protected]>
This commit is contained in:
Anoliah
2025-08-15 14:12:09 -04:00
committed by GitHub
co-authored by Lila
parent a53681e8da
commit 6dd14a0d8b
3 changed files with 97 additions and 59 deletions
+8 -2
View File
@@ -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"),
]
+52 -41
View File
@@ -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
+37 -16
View File
@@ -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)