diff --git a/.gitignore b/.gitignore
index 0cb2f6e73..6c84a5c69 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,3 +5,6 @@ __pycache__/
fast64_updater/
.python-version
.idea
+
+# temp? just exporting my stuff in my fast64 clone because reasons
+export/
diff --git a/README.md b/README.md
index 39eaccd93..67784b9db 100644
--- a/README.md
+++ b/README.md
@@ -22,7 +22,7 @@ We have a Discord server for support as well as development [here](https://disco
### Links to Docs / Guides for Each Game
1. [ Super Mario 64 ](/fast64_internal/sm64/README.md)
-2. [ Ocarina Of Time ](/fast64_internal/oot/README.md)
+2. [ Ocarina Of Time and Majora's Mask ](/fast64_internal/z64/README.md)
### Installation
Download the repository as a zip file. In Blender, go to Edit -> Preferences -> Add-Ons and click the "Install" button to install the plugin from the zip file. Find the Fast64 addon in the addon list and enable it. If it does not show up, go to Edit -> Preferences -> Save&Load and make sure 'Auto Run Python Scripts' is enabled.
diff --git a/__init__.py b/__init__.py
index b18a0079f..f3a5a1e77 100644
--- a/__init__.py
+++ b/__init__.py
@@ -1,4 +1,6 @@
import bpy
+from .fast64_internal.game_data import game_data
+
from bpy.utils import register_class, unregister_class
from bpy.path import abspath
@@ -19,10 +21,10 @@
from .fast64_internal.sm64.sm64_geolayout_bone import SM64_BoneProperties
from .fast64_internal.sm64.sm64_objects import SM64_ObjectProperties
-from .fast64_internal.oot import OOT_Properties, oot_register, oot_unregister
-from .fast64_internal.oot.oot_constants import oot_world_defaults
-from .fast64_internal.oot.props_panel_main import OOT_ObjectProperties
-from .fast64_internal.oot.actor.properties import initOOTActorProperties
+from .fast64_internal.z64 import OOT_Properties, oot_register, oot_unregister, z64_register_on_enable
+from .fast64_internal.z64.constants import oot_world_defaults
+from .fast64_internal.z64.props_panel_main import OOT_ObjectProperties
+from .fast64_internal.z64.utility import getObjectList
from .fast64_internal.utility_anim import utility_anim_register, utility_anim_unregister, ArmatureApplyWithMeshOperator
from .fast64_internal.mk64 import MK64_Properties, mk64_register, mk64_unregister
@@ -59,7 +61,7 @@
# info about add on
bl_info = {
"name": "Fast64",
- "version": (2, 3, 0),
+ "version": (2, 3, 1),
"author": "kurethedead",
"location": "3DView",
"description": "Plugin for exporting F3D display lists and other game data related to Nintendo 64 games.",
@@ -70,6 +72,7 @@
gameEditorEnum = (
("SM64", "SM64", "Super Mario 64", 0),
("OOT", "OOT", "Ocarina Of Time", 1),
+ ("MM", "MM", "Majora's Mask", 4),
("MK64", "MK64", "Mario Kart 64", 3),
("Homebrew", "Homebrew", "Homebrew", 2),
)
@@ -241,6 +244,9 @@ class Fast64_Properties(bpy.types.PropertyGroup):
settings: bpy.props.PointerProperty(type=Fast64Settings_Properties, name="Fast64 Settings")
renderSettings: bpy.props.PointerProperty(type=Fast64RenderSettings_Properties, name="Fast64 Render Settings")
+ def get_addon_version(self):
+ return bl_info["version"]
+
class Fast64_BoneProperties(bpy.types.PropertyGroup):
"""
@@ -299,24 +305,6 @@ def execute(self, context: "bpy.types.Context"):
return {"FINISHED"}
-# def updateGameEditor(scene, context):
-# if scene.currentGameEditorMode == 'SM64':
-# sm64_panel_unregister()
-# elif scene.currentGameEditorMode == 'Z64':
-# oot_panel_unregister()
-# else:
-# raise PluginError("Unhandled game editor mode " + str(scene.currentGameEditorMode))
-#
-# if scene.gameEditorMode == 'SM64':
-# sm64_panel_register()
-# elif scene.gameEditorMode == 'Z64':
-# oot_panel_register()
-# else:
-# raise PluginError("Unhandled game editor mode " + str(scene.gameEditorMode))
-#
-# scene.currentGameEditorMode = scene.gameEditorMode
-
-
class ExampleAddonPreferences(bpy.types.AddonPreferences, addon_updater_ops.AddonUpdaterPreferences):
bl_idname = __package__
@@ -371,6 +359,8 @@ def upgrade_scene_props_node():
@bpy.app.handlers.persistent
def after_load(_a, _b):
+ game_data.update(bpy.context.scene.gameEditorMode)
+
settings = bpy.context.scene.fast64.settings
if any(mat.is_f3d for mat in bpy.data.materials):
check_or_ask_color_management(bpy.context)
@@ -396,7 +386,7 @@ def set_game_defaults(scene: bpy.types.Scene, set_ucode=True):
elif scene.gameEditorMode == "MK64":
f3d_type = "F3DEX"
world_defaults = mk64_world_defaults
- elif scene.gameEditorMode == "OOT":
+ elif scene.gameEditorMode in {"OOT", "MM"}:
f3d_type = "F3DEX2/LX2"
world_defaults = oot_world_defaults
elif scene.gameEditorMode == "MK64":
@@ -406,13 +396,25 @@ def set_game_defaults(scene: bpy.types.Scene, set_ucode=True):
world_defaults = {} # This will set some pretty bad defaults, but trust the user
if set_ucode:
scene.f3d_type = f3d_type
- if scene.world is not None:
+ if scene.world is not None and world_defaults is not None:
scene.world.rdp_defaults.from_dict(world_defaults)
def gameEditorUpdate(scene: bpy.types.Scene, _context):
+ game_data.update(scene.gameEditorMode)
set_game_defaults(scene)
+ # reset `currentCutsceneIndex` when switching games
+ if scene.gameEditorMode in {"OOT", "MM"}:
+ for scene_obj in bpy.data.objects:
+ scene_obj.ootAlternateSceneHeaders.currentCutsceneIndex = game_data.z64.cs_index_start
+
+ if scene_obj.type == "EMPTY" and scene_obj.ootEmptyType == "Scene":
+ room_obj_list = getObjectList(scene_obj.children_recursive, "EMPTY", "Room")
+
+ for room_obj in room_obj_list:
+ room_obj.ootAlternateRoomHeaders.currentCutsceneIndex = game_data.z64.cs_index_start
+
# called on add-on enabling
# register operators and panels here
@@ -435,17 +437,19 @@ def register():
register_class(ExampleAddonPreferences)
addon_updater_ops.register(bl_info)
- initOOTActorProperties()
utility_anim_register()
mat_register()
render_engine_register()
bsdf_conv_register()
sm64_register(True)
- oot_register(True)
+ oot_register(True, True)
mk64_register(True)
repo_settings_operators_register()
+ for cls in z64_register_on_enable:
+ register_class(cls)
+
for cls in classes:
register_class(cls)
@@ -488,7 +492,7 @@ def unregister():
f3d_writer_unregister()
f3d_parser_unregister()
sm64_unregister(True)
- oot_unregister(True)
+ oot_unregister(True, True)
mk64_unregister(True)
mat_unregister()
bsdf_conv_unregister()
@@ -508,7 +512,10 @@ def unregister():
repo_settings_operators_unregister()
- for cls in classes:
+ for cls in reversed(classes):
+ unregister_class(cls)
+
+ for cls in reversed(z64_register_on_enable):
unregister_class(cls)
bpy.app.handlers.load_post.remove(after_load)
diff --git a/fast64_internal/__init__.py b/fast64_internal/__init__.py
deleted file mode 100644
index 6bb605c2a..000000000
--- a/fast64_internal/__init__.py
+++ /dev/null
@@ -1,5 +0,0 @@
-from .f3d_material_converter import *
-from .f3d import *
-from .sm64 import *
-from .oot import * # is this really needed?
-from .panels import *
diff --git a/fast64_internal/data/__init__.py b/fast64_internal/data/__init__.py
new file mode 100644
index 000000000..514ce8cf3
--- /dev/null
+++ b/fast64_internal/data/__init__.py
@@ -0,0 +1,2 @@
+from .z64.data import Z64_Data
+from .z64.object_data import Z64_ObjectData
diff --git a/fast64_internal/oot/data/oot_actor_data.py b/fast64_internal/data/z64/actor_data.py
similarity index 85%
rename from fast64_internal/oot/data/oot_actor_data.py
rename to fast64_internal/data/z64/actor_data.py
index f6a732f90..e1de70d89 100644
--- a/fast64_internal/oot/data/oot_actor_data.py
+++ b/fast64_internal/data/z64/actor_data.py
@@ -1,11 +1,11 @@
from os import path
from dataclasses import dataclass
-from .oot_getters import getXMLRoot
-from .oot_data import OoT_BaseElement
+from pathlib import Path
+from .common import Z64_BaseElement, get_xml_root
@dataclass
-class OoT_ParameterElement:
+class Z64_ParameterElement:
type: str # bool, enum, type, property, etc...
index: int
mask: int
@@ -18,34 +18,34 @@ class OoT_ParameterElement:
@dataclass
-class OoT_ListElement:
+class Z64_ListElement:
key: str
name: str
value: int
@dataclass
-class OoT_ActorElement(OoT_BaseElement):
+class Z64_ActorElement(Z64_BaseElement):
category: str
tiedObjects: list[str]
- params: list[OoT_ParameterElement]
+ params: list[Z64_ParameterElement]
-class OoT_ActorData:
+class Z64_ActorData:
"""Everything related to OoT Actors"""
- def __init__(self):
+ def __init__(self, game: str):
# Path to the ``ActorList.xml`` file
- actorXML = path.dirname(path.abspath(__file__)) + "/xml/ActorList.xml"
- actorRoot = getXMLRoot(actorXML)
+ xml_path = Path(f"{path.dirname(path.abspath(__file__))}/xml/{game.lower()}_actor_list.xml")
+ actor_root = get_xml_root(xml_path.resolve())
# general actor list
- self.actorList: list[OoT_ActorElement] = []
+ self.actorList: list[Z64_ActorElement] = []
# list elements
- self.chestItems: list[OoT_ListElement] = []
- self.collectibleItems: list[OoT_ListElement] = []
- self.messageItems: list[OoT_ListElement] = []
+ self.chestItems: list[Z64_ListElement] = []
+ self.collectibleItems: list[Z64_ListElement] = []
+ self.messageItems: list[Z64_ListElement] = []
listNameToList = {
"Chest Content": self.chestItems,
@@ -53,15 +53,15 @@ def __init__(self):
"Elf_Msg Message ID": self.messageItems,
}
- for elem in actorRoot.iterfind("List"):
+ for elem in actor_root.iterfind("List"):
listName = elem.get("Name")
if listName is not None:
for item in elem:
listNameToList[listName].append(
- OoT_ListElement(item.get("Key"), item.get("Name"), int(item.get("Value"), base=16))
+ Z64_ListElement(item.get("Key"), item.get("Name"), int(item.get("Value"), base=16))
)
- for actor in actorRoot.iterfind("Actor"):
+ for actor in actor_root.iterfind("Actor"):
tiedObjects = []
objKey = actor.get("ObjectKey")
actorName = f"{actor.attrib['Name']} - {actor.attrib['ID'].removeprefix('ACTOR_')}"
@@ -70,7 +70,7 @@ def __init__(self):
tiedObjects = objKey.split(",")
# parameters
- params: list[OoT_ParameterElement] = []
+ params: list[Z64_ParameterElement] = []
for elem in actor:
elemType = elem.tag
if elemType != "Notes":
@@ -91,7 +91,7 @@ def __init__(self):
defaultName = f"{elem.get('Type')} {elemType}"
valueRange = elem.get("ValueRange")
params.append(
- OoT_ParameterElement(
+ Z64_ParameterElement(
elemType,
int(elem.get("Index", "1")),
int(elem.get("Mask", "0xFFFF"), base=16),
@@ -105,7 +105,7 @@ def __init__(self):
)
self.actorList.append(
- OoT_ActorElement(
+ Z64_ActorElement(
actor.attrib["ID"],
actor.attrib["Key"],
actorName,
@@ -129,7 +129,7 @@ def __init__(self):
# list of tuples used by Blender's enum properties
- lastIndex = max(1, *(actor.index for actor in self.actorList))
+ lastIndex = max(1, *(actor.index for actor in self.actorList)) + 1
self.ootEnumActorID = [("None", f"{i} (Deleted from the XML)", "None") for i in range(lastIndex)]
self.ootEnumActorID.insert(0, ("Custom", "Custom Actor", "Custom"))
@@ -142,7 +142,7 @@ def __init__(self):
i = 1
for actor in self.actorList:
- self.ootEnumActorID[actor.index] = (actor.id, actor.name, actor.id)
+ self.ootEnumActorID[actor.index + 1] = (actor.id, actor.name, actor.id)
if actor.category == "ACTORCAT_DOOR":
self.ootEnumTransitionActorID[i] = (actor.id, actor.name, actor.id)
i += 1
diff --git a/fast64_internal/oot/data/oot_getters.py b/fast64_internal/data/z64/common.py
similarity index 64%
rename from fast64_internal/oot/data/oot_getters.py
rename to fast64_internal/data/z64/common.py
index fb4fbfe09..7cd3b5159 100644
--- a/fast64_internal/oot/data/oot_getters.py
+++ b/fast64_internal/data/z64/common.py
@@ -1,7 +1,16 @@
from xml.etree.ElementTree import parse as parseXML, Element
+from dataclasses import dataclass
-def getXMLRoot(xmlPath: str) -> Element:
+@dataclass
+class Z64_BaseElement:
+ id: str
+ key: str
+ name: str
+ index: int
+
+
+def get_xml_root(xmlPath: str) -> Element:
"""Parse an XML file and return its root element"""
try:
return parseXML(xmlPath).getroot()
diff --git a/fast64_internal/data/z64/data.py b/fast64_internal/data/z64/data.py
new file mode 100644
index 000000000..e3de403c0
--- /dev/null
+++ b/fast64_internal/data/z64/data.py
@@ -0,0 +1,946 @@
+import bpy
+
+from collections import OrderedDict
+from dataclasses import dataclass
+from typing import Optional
+from bpy.types import Context
+from .enum_data import Z64_EnumData
+from .object_data import Z64_ObjectData
+from .actor_data import Z64_ActorData
+
+# ---
+
+# TODO: get this from XML
+
+ootEnumNightSeq = [
+ ("Custom", "Custom", "Custom"),
+ ("0x00", "General Night", "NATURE_ID_GENERAL_NIGHT"),
+ ("0x01", "Market Entrance", "NATURE_ID_MARKET_ENTRANCE"),
+ ("0x02", "Kakariko Region", "NATURE_ID_KAKARIKO_REGION"),
+ ("0x03", "Market Ruins", "NATURE_ID_MARKET_RUINS"),
+ ("0x04", "Kokiri Region", "NATURE_ID_KOKIRI_REGION"),
+ ("0x05", "Market Night", "NATURE_ID_MARKET_NIGHT"),
+ ("0x06", "NATURE_ID_06", "NATURE_ID_06"),
+ ("0x07", "Ganon's Lair", "NATURE_ID_GANONS_LAIR"),
+ ("0x08", "NATURE_ID_08", "NATURE_ID_08"),
+ ("0x09", "NATURE_ID_09", "NATURE_ID_09"),
+ ("0x0A", "Wasteland", "NATURE_ID_WASTELAND"),
+ ("0x0B", "Colossus", "NATURE_ID_COLOSSUS"),
+ ("0x0C", "Nature DMT", "NATURE_ID_DEATH_MOUNTAIN_TRAIL"),
+ ("0x0D", "NATURE_ID_0D", "NATURE_ID_0D"),
+ ("0x0E", "NATURE_ID_0E", "NATURE_ID_0E"),
+ ("0x0F", "NATURE_ID_0F", "NATURE_ID_0F"),
+ ("0x10", "NATURE_ID_10", "NATURE_ID_10"),
+ ("0x11", "NATURE_ID_11", "NATURE_ID_11"),
+ ("0x12", "NATURE_ID_12", "NATURE_ID_12"),
+ ("0x13", "None", "NATURE_ID_NONE"),
+ ("0xFF", "Disabled", "NATURE_ID_DISABLED"),
+]
+
+enum_ambiance_id = [
+ ("Custom", "Custom", "Custom"),
+ ("0x00", "AMBIENCE_ID_00", "AMBIENCE_ID_00"),
+ ("0x01", "AMBIENCE_ID_01", "AMBIENCE_ID_01"),
+ ("0x02", "AMBIENCE_ID_02", "AMBIENCE_ID_02"),
+ ("0x03", "AMBIENCE_ID_03", "AMBIENCE_ID_03"),
+ ("0x04", "AMBIENCE_ID_04", "AMBIENCE_ID_04"),
+ ("0x05", "AMBIENCE_ID_05", "AMBIENCE_ID_05"),
+ ("0x06", "AMBIENCE_ID_06", "AMBIENCE_ID_06"),
+ ("0x07", "AMBIENCE_ID_07", "AMBIENCE_ID_07"),
+ ("0x08", "AMBIENCE_ID_08", "AMBIENCE_ID_08"),
+ ("0x09", "AMBIENCE_ID_09", "AMBIENCE_ID_09"),
+ ("0x0A", "AMBIENCE_ID_0A", "AMBIENCE_ID_0A"),
+ ("0x0B", "AMBIENCE_ID_0B", "AMBIENCE_ID_0B"),
+ ("0x0C", "AMBIENCE_ID_0C", "AMBIENCE_ID_0C"),
+ ("0x0D", "AMBIENCE_ID_0D", "AMBIENCE_ID_0D"),
+ ("0x0E", "AMBIENCE_ID_0E", "AMBIENCE_ID_0E"),
+ ("0x0F", "AMBIENCE_ID_0F", "AMBIENCE_ID_0F"),
+ ("0x10", "AMBIENCE_ID_10", "AMBIENCE_ID_10"),
+ ("0x11", "AMBIENCE_ID_11", "AMBIENCE_ID_11"),
+ ("0x12", "AMBIENCE_ID_12", "AMBIENCE_ID_12"),
+ ("0x13", "AMBIENCE_ID_13", "AMBIENCE_ID_13"),
+ ("0xFF", "AMBIENCE_ID_DISABLED", "AMBIENCE_ID_DISABLED"),
+]
+
+# ---
+
+ootEnumSkybox = [
+ ("Custom", "Custom", "Custom"),
+ ("0x00", "None", "None"),
+ ("0x01", "Standard Sky", "Standard Sky"),
+ ("0x02", "Hylian Bazaar", "Hylian Bazaar"),
+ ("0x03", "Brown Cloudy Sky", "Brown Cloudy Sky"),
+ ("0x04", "Market Ruins", "Market Ruins"),
+ ("0x05", "Black Cloudy Night", "Black Cloudy Night"),
+ ("0x07", "Link's House", "Link's House"),
+ ("0x09", "Market (Main Square, Day)", "Market (Main Square, Day)"),
+ ("0x0A", "Market (Main Square, Night)", "Market (Main Square, Night)"),
+ ("0x0B", "Happy Mask Shop", "Happy Mask Shop"),
+ ("0x0C", "Know-It-All Brothers' House", "Know-It-All Brothers' House"),
+ ("0x0E", "Kokiri Twins' House", "Kokiri Twins' House"),
+ ("0x0F", "Stable", "Stable"),
+ ("0x10", "Stew Lady's House", "Stew Lady's House"),
+ ("0x11", "Kokiri Shop", "Kokiri Shop"),
+ ("0x13", "Goron Shop", "Goron Shop"),
+ ("0x14", "Zora Shop", "Zora Shop"),
+ ("0x16", "Kakariko Potions Shop", "Kakariko Potions Shop"),
+ ("0x17", "Hylian Potions Shop", "Hylian Potions Shop"),
+ ("0x18", "Bomb Shop", "Bomb Shop"),
+ ("0x1A", "Dog Lady's House", "Dog Lady's House"),
+ ("0x1B", "Impa's House", "Impa's House"),
+ ("0x1C", "Gerudo Tent", "Gerudo Tent"),
+ ("0x1D", "Environment Color", "Environment Color"),
+ ("0x20", "Mido's House", "Mido's House"),
+ ("0x21", "Saria's House", "Saria's House"),
+ ("0x22", "Dog Guy's House", "Dog Guy's House"),
+]
+
+mm_enum_skybox = [
+ ("Custom", "Custom", "Custom"),
+ ("SKYBOX_NONE", "None", "0x00"),
+ ("SKYBOX_NORMAL_SKY", "Standard Sky", "0x01"),
+ ("SKYBOX_2", "SKYBOX_2", "0x02"),
+ ("SKYBOX_3", "SKYBOX_3", "0x03"),
+ ("SKYBOX_CUTSCENE_MAP", "Cutscene Map", "0x05"),
+]
+
+ootEnumCloudiness = [
+ ("Custom", "Custom", "Custom"),
+ ("0x00", "Sunny", "Sunny"),
+ ("0x01", "Cloudy", "Cloudy"),
+]
+
+mm_enum_skybox_config = [
+ ("Custom", "Custom", "Custom"),
+ ("SKYBOX_CONFIG_0", "SKYBOX_CONFIG_0", "0x00"),
+ ("SKYBOX_CONFIG_1", "SKYBOX_CONFIG_1", "0x01"),
+ ("SKYBOX_CONFIG_2", "SKYBOX_CONFIG_2", "0x02"),
+ ("SKYBOX_CONFIG_3", "SKYBOX_CONFIG_3", "0x03"),
+ ("SKYBOX_CONFIG_4", "SKYBOX_CONFIG_4", "0x04"),
+ ("SKYBOX_CONFIG_5", "SKYBOX_CONFIG_5", "0x05"),
+ ("SKYBOX_CONFIG_6", "SKYBOX_CONFIG_6", "0x06"),
+ ("SKYBOX_CONFIG_7", "SKYBOX_CONFIG_7", "0x07"),
+ ("SKYBOX_CONFIG_8", "SKYBOX_CONFIG_8", "0x08"),
+ ("SKYBOX_CONFIG_9", "SKYBOX_CONFIG_9", "0x09"),
+ ("SKYBOX_CONFIG_10", "SKYBOX_CONFIG_10", "0x0A"),
+ ("SKYBOX_CONFIG_11", "SKYBOX_CONFIG_11", "0x0B"),
+ ("SKYBOX_CONFIG_12", "SKYBOX_CONFIG_12", "0x0C"),
+ ("SKYBOX_CONFIG_13", "SKYBOX_CONFIG_13", "0x0D"),
+ ("SKYBOX_CONFIG_14", "SKYBOX_CONFIG_14", "0x0E"),
+ ("SKYBOX_CONFIG_15", "SKYBOX_CONFIG_15", "0x0F"),
+ ("SKYBOX_CONFIG_16", "SKYBOX_CONFIG_16", "0x10"),
+ ("SKYBOX_CONFIG_17", "SKYBOX_CONFIG_17", "0x11"),
+ ("SKYBOX_CONFIG_18", "SKYBOX_CONFIG_18", "0x12"),
+ ("SKYBOX_CONFIG_19", "SKYBOX_CONFIG_19", "0x13"),
+ ("SKYBOX_CONFIG_20", "SKYBOX_CONFIG_20", "0x14"),
+ ("SKYBOX_CONFIG_21", "SKYBOX_CONFIG_21", "0x15"),
+ ("SKYBOX_CONFIG_22", "SKYBOX_CONFIG_22", "0x16"),
+ ("SKYBOX_CONFIG_23", "SKYBOX_CONFIG_23", "0x17"),
+ ("SKYBOX_CONFIG_24", "SKYBOX_CONFIG_24", "0x18"),
+ ("SKYBOX_CONFIG_25", "SKYBOX_CONFIG_25", "0x19"),
+ ("SKYBOX_CONFIG_26", "SKYBOX_CONFIG_26", "0x1A"),
+ ("SKYBOX_CONFIG_27", "SKYBOX_CONFIG_27", "0x1B"),
+]
+
+ootEnumLinkIdle = [
+ ("Custom", "Custom", "Custom"),
+ ("0x00", "Default", "Default"),
+ ("0x01", "Sneezing", "Sneezing"),
+ ("0x02", "Wiping Forehead", "Wiping Forehead"),
+ ("0x04", "Yawning", "Yawning"),
+ ("0x07", "Gasping For Breath", "Gasping For Breath"),
+ ("0x09", "Brandish Sword", "Brandish Sword"),
+ ("0x0A", "Adjust Tunic", "Adjust Tunic"),
+ ("0xFF", "Hops On Epona", "Hops On Epona"),
+]
+
+mm_enum_environment_type = [
+ ("Custom", "Custom", "Custom"),
+ ("ROOM_ENV_DEFAULT", "Default", "0x00"),
+ ("ROOM_ENV_COLD", "Cold", "0x01"),
+ ("ROOM_ENV_WARM", "Warm", "0x02"),
+ ("ROOM_ENV_HOT", "Hot", "0x03"),
+ ("ROOM_ENV_UNK_STRETCH_1", "Unknown Stretch 1", "0x04"),
+ ("ROOM_ENV_UNK_STRETCH_2", "Unknown Stretch 2", "0x05"),
+ ("ROOM_ENV_UNK_STRETCH_3", "Unknown Stretch 3", "0x06"),
+]
+
+# see RoomType enum
+ootEnumRoomBehaviour = [
+ ("Custom", "Custom", "Custom"),
+ ("0x00", "Default", "Default"),
+ ("0x01", "Dungeon Behavior (Z-Target, Sun's Song)", "Dungeon Behavior (Z-Target, Sun's Song)"),
+ ("0x02", "Disable Backflips/Sidehops", "Disable Backflips/Sidehops"),
+ ("0x03", "Disable Color Dither", "Disable Color Dither"),
+ ("0x04", "(?) Horse Camera Related", "(?) Horse Camera Related"),
+ ("0x05", "Disable Darker Screen Effect (NL/Spins)", "Disable Darker Screen Effect (NL/Spins)"),
+]
+
+mm_enum_room_type = [
+ ("Custom", "Custom", "Custom"),
+ ("ROOM_TYPE_NORMAL", "Normal", "0x00"),
+ ("ROOM_TYPE_DUNGEON", "Dungeon", "0x01"),
+ ("ROOM_TYPE_INDOORS", "Indoors", "0x02"),
+ ("ROOM_TYPE_3", "Type 3", "0x03"),
+ ("ROOM_TYPE_4", "Type 4 (Horse related)", "0x04"),
+ ("ROOM_TYPE_BOSS", "Boss", "0x05"),
+]
+
+ootEnumFloorSetting = [
+ ("Custom", "Custom", "Custom"),
+ ("0x00", "Default", "Default"),
+ ("0x05", "Trigger Respawn", "Trigger Respawn"),
+ ("0x06", "Grab Wall", "Grab Wall"),
+ ("0x08", "Stop Air Momentum", "Stop Air Momentum"),
+ ("0x09", "Fall Instead Of Jumping", "Fall Instead Of Jumping"),
+ ("0x0B", "Dive Animation", "Dive Animation"),
+ ("0x0C", "Trigger Void", "Trigger Void"),
+]
+
+mm_enum_floor_property = [
+ ("Custom", "Custom", "Custom"),
+ ("0x00", "Default", "FLOOR_PROPERTY_0"),
+ ("0x01", "Frontflip Jump Animation", "FLOOR_PROPERTY_1"),
+ ("0x02", "Sideflip Jump Animation", "FLOOR_PROPERTY_2"),
+ ("0x05", "Trigger Respawn (sets human no mask)", "FLOOR_PROPERTY_5"),
+ ("0x06", "Grab Wall", "FLOOR_PROPERTY_6"),
+ ("0x07", "Unknown (sets speed to 0)", "FLOOR_PROPERTY_7"),
+ ("0x08", "Stop Air Momentum", "FLOOR_PROPERTY_8"),
+ ("0x09", "Fall Instead Of Jumping", "FLOOR_PROPERTY_9"),
+ ("0x0B", "Dive Animation", "FLOOR_PROPERTY_11"),
+ ("0x0C", "Trigger Void", "FLOOR_PROPERTY_12"),
+ ("0x0D", "Trigger Void (runs `Player_Action_1`)", "FLOOR_PROPERTY_13"),
+]
+
+ootEnumFloorProperty = [
+ ("Custom", "Custom", "Custom"),
+ ("0x00", "Default", "Default"),
+ ("0x01", "Haunted Wasteland Camera", "Haunted Wasteland Camera"),
+ ("0x02", "Fire (damages every 6s)", "Fire (damages every 6s)"),
+ ("0x03", "Fire (damages every 3s)", "Fire (damages every 3s)"),
+ ("0x04", "Shallow Sand", "Shallow Sand"),
+ ("0x05", "Slippery", "Slippery"),
+ ("0x06", "Ignore Fall Damage", "Ignore Fall Damage"),
+ ("0x07", "Quicksand Crossing (Blocks Epona)", "Quicksand Crossing (Epona Uncrossable)"),
+ ("0x08", "Jabu Jabu's Belly Floor", "Jabu Jabu's Belly Floor"),
+ ("0x09", "Trigger Void", "Trigger Void"),
+ ("0x0A", "Stops Air Momentum", "Stops Air Momentum"),
+ ("0x0B", "Grotto Exit Animation", "Link Looks Up"),
+ ("0x0C", "Quicksand Crossing (Epona Crossable)", "Quicksand Crossing (Epona Crossable)"),
+]
+
+mm_enum_floor_type = [
+ ("Custom", "Custom", "Custom"),
+ ("0x00", "Default", "FLOOR_TYPE_0"),
+ ("0x01", "Unused (?)", "FLOOR_TYPE_1"),
+ ("0x02", "Fire Damages (burns Player every second)", "FLOOR_TYPE_2"),
+ ("0x03", "Fire Damages 2 (burns Player every second)", "FLOOR_TYPE_3"),
+ ("0x04", "Shallow Sand", "FLOOR_TYPE_4"),
+ ("0x05", "Ice (Slippery)", "FLOOR_TYPE_5"),
+ ("0x06", "Ignore Fall Damages", "FLOOR_TYPE_6"),
+ ("0x07", "Quicksand (blocks Epona)", "FLOOR_TYPE_7"),
+ ("0x08", "Jabu Jabu's Belly Floor (Unused)", "FLOOR_TYPE_8"),
+ ("0x09", "Triggers Void", "FLOOR_TYPE_9"),
+ ("0x0A", "Stops Air Momentum", "FLOOR_TYPE_10"),
+ ("0x0B", "Grotto Exit Animation", "FLOOR_TYPE_11"),
+ ("0x0C", "Quicksand (doesn't block Epona)", "FLOOR_TYPE_12"),
+ ("0x0D", "Deeper Shallow Sand", "FLOOR_TYPE_13"),
+ ("0x0E", "Shallow Snow", "FLOOR_TYPE_14"),
+ ("0x0F", "Deeper Shallow Snow", "FLOOR_TYPE_15"),
+]
+
+enum_floor_effect = [
+ ("Custom", "Custom", "Custom"),
+ ("0x00", "Default", "FLOOR_EFFECT_0"),
+ ("0x01", "Steep/Slippery Slope", "FLOOR_EFFECT_1"),
+ ("0x02", "Walkable (Preserves Exit Flags)", "FLOOR_EFFECT_2"),
+]
+
+ootEnumCameraSType = [
+ ("Custom", "Custom", "Custom"),
+ ("CAM_SET_NONE", "None", "None"),
+ ("CAM_SET_NORMAL0", "Normal0", "Normal0"),
+ ("CAM_SET_NORMAL1", "Normal1", "Normal1"),
+ ("CAM_SET_DUNGEON0", "Dungeon0", "Dungeon0"),
+ ("CAM_SET_DUNGEON1", "Dungeon1", "Dungeon1"),
+ ("CAM_SET_NORMAL3", "Normal3", "Normal3"),
+ ("CAM_SET_HORSE0", "Horse", "Horse"),
+ ("CAM_SET_BOSS_GOMA", "Boss_gohma", "Boss_gohma"),
+ ("CAM_SET_BOSS_DODO", "Boss_dodongo", "Boss_dodongo"),
+ ("CAM_SET_BOSS_BARI", "Boss_barinade", "Boss_barinade"),
+ ("CAM_SET_BOSS_FGANON", "Boss_phantom_ganon", "Boss_phantom_ganon"),
+ ("CAM_SET_BOSS_BAL", "Boss_volvagia", "Boss_volvagia"),
+ ("CAM_SET_BOSS_SHADES", "Boss_bongo", "Boss_bongo"),
+ ("CAM_SET_BOSS_MOFA", "Boss_morpha", "Boss_morpha"),
+ ("CAM_SET_TWIN0", "Twinrova_platform", "Twinrova_platform"),
+ ("CAM_SET_TWIN1", "Twinrova_floor", "Twinrova_floor"),
+ ("CAM_SET_BOSS_GANON1", "Boss_ganondorf", "Boss_ganondorf"),
+ ("CAM_SET_BOSS_GANON2", "Boss_ganon", "Boss_ganon"),
+ ("CAM_SET_TOWER0", "Tower_climb", "Tower_climb"),
+ ("CAM_SET_TOWER1", "Tower_unused", "Tower_unused"),
+ ("CAM_SET_FIXED0", "Market_balcony", "Market_balcony"),
+ ("CAM_SET_FIXED1", "Chu_bowling", "Chu_bowling"),
+ ("CAM_SET_CIRCLE0", "Pivot_crawlspace", "Pivot_crawlspace"),
+ ("CAM_SET_CIRCLE2", "Pivot_shop_browsing", "Pivot_shop_browsing"),
+ ("CAM_SET_CIRCLE3", "Pivot_in_front", "Pivot_in_front"),
+ ("CAM_SET_PREREND0", "Prerend_fixed", "Prerend_fixed"),
+ ("CAM_SET_PREREND1", "Prerend_pivot", "Prerend_pivot"),
+ ("CAM_SET_PREREND3", "Prerend_side_scroll", "Prerend_side_scroll"),
+ ("CAM_SET_DOOR0", "Door0", "Door0"),
+ ("CAM_SET_DOORC", "Doorc", "Doorc"),
+ ("CAM_SET_RAIL3", "Crawlspace", "Crawlspace"),
+ ("CAM_SET_START0", "Start0", "Start0"),
+ ("CAM_SET_START1", "Start1", "Start1"),
+ ("CAM_SET_FREE0", "Free0", "Free0"),
+ ("CAM_SET_FREE2", "Free2", "Free2"),
+ ("CAM_SET_CIRCLE4", "Pivot_corner", "Pivot_corner"),
+ ("CAM_SET_CIRCLE5", "Pivot_water_surface", "Pivot_water_surface"),
+ ("CAM_SET_DEMO0", "Cs_0", "Cs_0"),
+ ("CAM_SET_DEMO1", "Twisted_Hallway", "Twisted_Hallway"),
+ ("CAM_SET_MORI1", "Forest_birds_eye", "Forest_birds_eye"),
+ ("CAM_SET_ITEM0", "Slow_chest_cs", "Slow_chest_cs"),
+ ("CAM_SET_ITEM1", "Item_unused", "Item_unused"),
+ ("CAM_SET_DEMO3", "Cs_3", "Cs_3"),
+ ("CAM_SET_DEMO4", "Cs_attention", "Cs_attention"),
+ ("CAM_SET_UFOBEAN", "Bean_generic", "Bean_generic"),
+ ("CAM_SET_LIFTBEAN", "Bean_lost_woods", "Bean_lost_woods"),
+ ("CAM_SET_SCENE0", "Scene_unused", "Scene_unused"),
+ ("CAM_SET_SCENE1", "Scene_transition", "Scene_transition"),
+ ("CAM_SET_HIDAN1", "Fire_platform", "Fire_platform"),
+ ("CAM_SET_HIDAN2", "Fire_staircase", "Fire_staircase"),
+ ("CAM_SET_MORI2", "Forest_unused", "Forest_unused"),
+ ("CAM_SET_MORI3", "Defeat_poe", "Defeat_poe"),
+ ("CAM_SET_TAKO", "Big_octo", "Big_octo"),
+ ("CAM_SET_SPOT05A", "Meadow_birds_eye", "Meadow_birds_eye"),
+ ("CAM_SET_SPOT05B", "Meadow_unused", "Meadow_unused"),
+ ("CAM_SET_HIDAN3", "Fire_birds_eye", "Fire_birds_eye"),
+ ("CAM_SET_ITEM2", "Turn_around", "Turn_around"),
+ ("CAM_SET_CIRCLE6", "Pivot_vertical", "Pivot_vertical"),
+ ("CAM_SET_NORMAL2", "Normal2", "Normal2"),
+ ("CAM_SET_FISHING", "Fishing", "Fishing"),
+ ("CAM_SET_DEMOC", "Cs_c", "Cs_c"),
+ ("CAM_SET_UO_FIBER", "Jabu_tentacle", "Jabu_tentacle"),
+ ("CAM_SET_DUNGEON2", "Dungeon2", "Dungeon2"),
+ ("CAM_SET_TEPPEN", "Directed_yaw", "Directed_yaw"),
+ ("CAM_SET_CIRCLE7", "Pivot_from_side", "Pivot_from_side"),
+ ("CAM_SET_NORMAL4", "Normal4", "Normal4"),
+]
+
+mm_enum_camera_setting_type = [
+ ("Custom", "Custom", "Custom"),
+ ("CAM_SET_NONE", "None", "None"),
+ ("CAM_SET_NORMAL0", "Normal0", "Generic camera 0, used in various places 'NORMAL0'"),
+ ("CAM_SET_NORMAL3", "Normal3", "Generic camera 3, used in various places 'NORMAL3'"),
+ (
+ "CAM_SET_PIVOT_DIVING",
+ "Pivot_Diving",
+ "Player diving from the surface of the water to underwater not as zora 'CIRCLE5'",
+ ),
+ ("CAM_SET_HORSE", "Horse", "Reiding a horse 'HORSE0'"),
+ (
+ "CAM_SET_ZORA_DIVING",
+ "Zora_Diving",
+ "Parallel's Pivot Diving, but as Zora. However, Zora does not dive like a human. So this setting appears to not be used 'ZORA0'",
+ ),
+ (
+ "CAM_SET_PREREND_FIXED",
+ "Prerend_Fixed",
+ "Unused remnant of OoT: camera is fixed in position and rotation 'PREREND0'",
+ ),
+ (
+ "CAM_SET_PREREND_PIVOT",
+ "Prerend_Pivot",
+ "Unused remnant of OoT: Camera is fixed in position with fixed pitch, but is free to rotate in the yaw direction 360 degrees 'PREREND1'",
+ ),
+ (
+ "CAM_SET_DOORC",
+ "Doorc",
+ "Generic room door transitions, camera moves and follows player as the door is open and closed 'DOORC'",
+ ),
+ ("CAM_SET_DEMO0", "Demo0", "Unknown, possibly related to treasure chest game as goron? 'DEMO0'"),
+ ("CAM_SET_FREE0", "Free0", "Free Camera, manual control is given, no auto-updating eye or at 'FREE0'"),
+ ("CAM_SET_BIRDS_EYE_VIEW_0", "Birds_Eye_View_0", "Appears unused. Camera is a top-down view 'FUKAN0'"),
+ ("CAM_SET_NORMAL1", "Normal1", "Generic camera 1, used in various places 'NORMAL1'"),
+ (
+ "CAM_SET_NANAME",
+ "Naname",
+ "Unknown, slanted or tilted. Behaves identical to Normal0 except with added roll 'NANAME'",
+ ),
+ ("CAM_SET_CIRCLE0", "Circle0", "Used in Curiosity Shop, Pirates Fortress, Mayor's Residence 'CIRCLE0'"),
+ ("CAM_SET_FIXED0", "Fixed0", "Used in Sakon's Hideout puzzle rooms, milk bar stage 'FIXED0'"),
+ ("CAM_SET_SPIRAL_DOOR", "Spiral_Door", "Exiting a Spiral Staircase 'SPIRAL'"),
+ ("CAM_SET_DUNGEON0", "Dungeon0", "Generic dungeon camera 0, used in various places 'DUNGEON0'"),
+ (
+ "CAM_SET_ITEM0",
+ "Item0",
+ "Getting an item and holding it above Player's head (from small chest, freestanding, npc, ...) 'ITEM0'",
+ ),
+ ("CAM_SET_ITEM1", "Item1", "Looking at player while playing the ocarina 'ITEM1'"),
+ ("CAM_SET_ITEM2", "Item2", "Bottles: drinking, releasing fairy, dropping fish 'ITEM2'"),
+ ("CAM_SET_ITEM3", "Item3", "Bottles: catching fish or bugs, showing an item 'ITEM3'"),
+ ("CAM_SET_NAVI", "Navi", "Song of Soaring, variations of playing Song of Time 'NAVI'"),
+ ("CAM_SET_WARP_PAD_MOON", "Warp_Pad_Moon", "Warp circles from Goron Trial on the moon 'WARP0'"),
+ ("CAM_SET_DEATH", "Death", "Player death animation when health goes to 0 'DEATH'"),
+ ("CAM_SET_REBIRTH", "Rebirth", "Unknown set with camDataId = -9 (it's not being revived by a fairy) 'REBIRTH'"),
+ (
+ "CAM_SET_LONG_CHEST_OPENING",
+ "Long_Chest_Opening",
+ "Long cutscene when opening a big chest with a major item 'TREASURE'",
+ ),
+ ("CAM_SET_MASK_TRANSFORMATION", "Mask_Transformation", "Putting on a transformation mask 'TRANSFORM'"),
+ ("CAM_SET_ATTENTION", "Attention", "Unknown, set with camDataId = -15 'ATTENTION'"),
+ ("CAM_SET_WARP_PAD_ENTRANCE", "Warp_Pad_Entrance", "Warp pad from start of a dungeon to the boss-room 'WARP1'"),
+ ("CAM_SET_DUNGEON1", "Dungeon1", "Generic dungeon camera 1, used in various places 'DUNGEON1'"),
+ (
+ "CAM_SET_FIXED1",
+ "Fixed1",
+ "Fixes camera in place, used in various places eg. entering Stock Pot Inn, hiting a switch, giving witch a red potion, shop browsing 'FIXED1'",
+ ),
+ (
+ "CAM_SET_FIXED2",
+ "Fixed2",
+ "Used in Pinnacle Rock after defeating Sea Monsters, and by Tatl in Fortress 'FIXED2'",
+ ),
+ ("CAM_SET_MAZE", "Maze", "Unused. Set to use Camera_Parallel2(), which is only Camera_Noop() 'MAZE'"),
+ (
+ "CAM_SET_REMOTEBOMB",
+ "Remotebomb",
+ "Unused. Set to use Camera_Parallel2(), which is only Camera_Noop(). But also related to Play_ChangeCameraSetting? 'REMOTEBOMB'",
+ ),
+ ("CAM_SET_CIRCLE1", "Circle1", "Unknown 'CIRCLE1'"),
+ (
+ "CAM_SET_CIRCLE2",
+ "Circle2",
+ "Looking at far-away NPCs eg. Garo in Road to Ikana, Hungry Goron, Tingle 'CIRCLE2'",
+ ),
+ (
+ "CAM_SET_CIRCLE3",
+ "Circle3",
+ "Used in curiosity shop, goron racetrack, final room in Sakon's hideout, other places 'CIRCLE3'",
+ ),
+ ("CAM_SET_CIRCLE4", "Circle4", "Used during the races on the doggy racetrack 'CIRCLE4'"),
+ ("CAM_SET_FIXED3", "Fixed3", "Used in Stock Pot Inn Toilet and Tatl cutscene after woodfall 'FIXED3'"),
+ (
+ "CAM_SET_TOWER_ASCENT",
+ "Tower_Ascent",
+ "Various climbing structures (Snowhead climb to the temple entrance) 'TOWER0'",
+ ),
+ ("CAM_SET_PARALLEL0", "Parallel0", "Unknown 'PARALLEL0'"),
+ ("CAM_SET_NORMALD", "Normald", "Unknown, set with camDataId = -20 'NORMALD'"),
+ ("CAM_SET_SUBJECTD", "Subjectd", "Unknown, set with camDataId = -21 'SUBJECTD'"),
+ (
+ "CAM_SET_START0",
+ "Start0",
+ "Entering a room, either Dawn of a New Day reload, or entering a door where the camera is fixed on the other end 'START0'",
+ ),
+ (
+ "CAM_SET_START2",
+ "Start2",
+ "Entering a scene, camera is put at a low angle eg. Grottos, Deku Palace, Stock Pot Inn 'START2'",
+ ),
+ ("CAM_SET_STOP0", "Stop0", "Called in z_play 'STOP0'"),
+ ("CAM_SET_BOAT_CRUISE", "Boat_Cruise", " Koume's boat cruise 'JCRUISING'"),
+ (
+ "CAM_SET_VERTICAL_CLIMB",
+ "Vertical_Climb",
+ "Large vertical climbs, such as Mountain Village wall or Pirates Fortress ladder. 'CLIMBMAZE'",
+ ),
+ ("CAM_SET_SIDED", "Sided", "Unknown, set with camDataId = -24 'SIDED'"),
+ ("CAM_SET_DUNGEON2", "Dungeon2", "Generic dungeon camera 2, used in various places 'DUNGEON2'"),
+ ("CAM_SET_BOSS_ODOLWA", "Boss_Odolwa", "Odolwa's Lair, also used in GBT entrance: 'BOSS_SHIGE'"),
+ ("CAM_SET_KEEPBACK", "Keepback", "Unknown. Possibly related to climbing something? 'KEEPBACK'"),
+ ("CAM_SET_CIRCLE6", "Circle6", "Used in select regions from Ikana 'CIRCLE6'"),
+ ("CAM_SET_CIRCLE7", "Circle7", "Unknown 'CIRCLE7'"),
+ ("CAM_SET_MINI_BOSS", "Mini_Boss", "Used during the various minibosses of the 'CHUBOSS'"),
+ ("CAM_SET_RFIXED1", "Rfixed1", "Talking to Koume stuck on the floor in woods of mystery 'RFIXED1'"),
+ (
+ "CAM_SET_TREASURE_CHEST_MINIGAME",
+ "Treasure_Chest_Minigame",
+ "Treasure Chest Shop in East Clock Town, minigame location 'TRESURE1'",
+ ),
+ ("CAM_SET_HONEY_AND_DARLING_1", "Honey_And_Darling_1", "Honey and Darling Minigames 'BOMBBASKET'"),
+ (
+ "CAM_SET_CIRCLE8",
+ "Circle8",
+ "Used by Stone Tower moving platforms, Falling eggs in Marine Lab, Bugs into soilpatch cutscene 'CIRCLE8'",
+ ),
+ (
+ "CAM_SET_BIRDS_EYE_VIEW_1",
+ "Birds_Eye_View_1",
+ "Camera is a top-down view. Used in Fisherman's minigame and Deku Palace 'FUKAN1'",
+ ),
+ ("CAM_SET_DUNGEON3", "Dungeon3", "Generic dungeon camera 3, used in various places 'DUNGEON3'"),
+ ("CAM_SET_TELESCOPE", "Telescope", "Observatory telescope and Curiosity Shop Peep-Hole 'TELESCOPE'"),
+ ("CAM_SET_ROOM0", "Room0", "Certain rooms eg. inside the clock tower 'ROOM0'"),
+ ("CAM_SET_RCIRC0", "Rcirc0", "Used by a few NPC cutscenes, focus close on the NPC 'RCIRC0'"),
+ ("CAM_SET_CIRCLE9", "Circle9", "Used by Sakon Hideout entrance and Deku Palace Maze 'CIRCLE9'"),
+ ("CAM_SET_ONTHEPOLE", "Onthepole", "Somewhere in Snowhead Temple and Woodfall Temple 'ONTHEPOLE'"),
+ (
+ "CAM_SET_INBUSH",
+ "Inbush",
+ "Various bush environments eg. grottos, Swamp Spider House, Termina Field grass bushes, Deku Palace near bean 'INBUSH'",
+ ),
+ ("CAM_SET_BOSS_MAJORA", "Boss_Majora", "Majora's Lair: 'BOSS_LAST'"),
+ ("CAM_SET_BOSS_TWINMOLD", "Boss_Twinmold", "Twinmold's Lair: 'BOSS_INI'"),
+ ("CAM_SET_BOSS_GOHT", "Boss_Goht", "Goht's Lair: 'BOSS_HAK'"),
+ ("CAM_SET_BOSS_GYORG", "Boss_Gyorg", "Gyorg's Lair: 'BOSS_KON'"),
+ ("CAM_SET_CONNECT0", "Connect0", "Smoothly and gradually return camera to Player after a cutscene 'CONNECT0'"),
+ ("CAM_SET_PINNACLE_ROCK", "Pinnacle_Rock", "Pinnacle Rock pit 'MORAY'"),
+ ("CAM_SET_NORMAL2", "Normal2", "Generic camera 2, used in various places 'NORMAL2'"),
+ ("CAM_SET_HONEY_AND_DARLING_2", "Honey_And_Darling_2", "'BOMBBOWL'"),
+ ("CAM_SET_CIRCLEA", "Circlea", "Unknown, Circle 10 'CIRCLEA'"),
+ ("CAM_SET_WHIRLPOOL", "Whirlpool", "Great Bay Temple Central Room Whirlpool 'WHIRLPOOL'"),
+ ("CAM_SET_CUCCO_SHACK", "Cucco_Shack", "'KOKKOGAME'"),
+ ("CAM_SET_GIANT", "Giant", "Giants Mask in Twinmold's Lair 'GIANT'"),
+ ("CAM_SET_SCENE0", "Scene0", "Entering doors to a new scene 'SCENE0'"),
+ ("CAM_SET_ROOM1", "Room1", "Certain rooms eg. some rooms in Stock Pot Inn 'ROOM1'"),
+ ("CAM_SET_WATER2", "Water2", "Swimming as Zora in Great Bay Temple 'WATER2'"),
+ ("CAM_SET_WOODFALL_SWAMP", "Woodfall_Swamp", "Woodfall inside the swamp, but not on the platforms, 'SOKONASI'"),
+ ("CAM_SET_FORCEKEEP", "Forcekeep", "Unknown 'FORCEKEEP'"),
+ ("CAM_SET_PARALLEL1", "Parallel1", "Unknown 'PARALLEL1'"),
+ ("CAM_SET_START1", "Start1", "Used when entering the lens cave 'START1'"),
+ ("CAM_SET_ROOM2", "Room2", "Certain rooms eg. Deku King's Chamber, Ocean Spider House 'ROOM2'"),
+ ("CAM_SET_NORMAL4", "Normal4", "Generic camera 4, used in Ikana Graveyard 'NORMAL4'"),
+ ("CAM_SET_ELEGY_SHELL", "Elegy_Shell", "cutscene after playing elegy of emptyness and spawning a shell 'SHELL'"),
+ ("CAM_SET_DUNGEON4", "Dungeon4", "Used in Pirates Fortress Interior, hidden room near hookshot 'DUNGEON4'"),
+]
+
+# order here sets order on the UI
+ootEnumCSListType = [
+ # Col 1
+ ("TextList", "Text List", "Textbox", "ALIGN_BOTTOM", 0),
+ ("MiscList", "Misc List", "Misc", "OPTIONS", 7),
+ ("RumbleList", "Rumble List", "Rumble Controller", "OUTLINER_OB_FORCE_FIELD", 8),
+ # Col 2
+ ("Transition", "Transition List", "Transition List", "COLORSET_10_VEC", 1),
+ ("LightSettingsList", "Light Settings List", "Lighting", "LIGHT_SUN", 2),
+ ("TimeList", "Time List", "Time", "TIME", 3),
+ # Col 3
+ ("StartSeqList", "Start Seq List", "Play BGM", "PLAY", 4),
+ ("StopSeqList", "Stop Seq List", "Stop BGM", "SNAP_FACE", 5),
+ ("FadeOutSeqList", "Fade-Out Seq List", "Fade BGM", "IPO_EASE_IN_OUT", 6),
+]
+
+mm_enum_cs_list_type = [
+ # Col 1
+ ("TextList", "Text List", "Textbox", "ALIGN_BOTTOM", 0),
+ ("MiscList", "Misc List", "Misc", "OPTIONS", 7),
+ ("RumbleList", "Rumble List", "Rumble Controller", "OUTLINER_OB_FORCE_FIELD", 8),
+ ("MotionBlurList", "Motion Blur List", "Motion Blur", "ONIONSKIN_ON", 9),
+ ("CreditsSceneList", "Choose Credits Scene List", "Choose Credits Scene", "WORLD", 11),
+ # Col 2
+ ("Transition", "Transition", "Transition", "COLORSET_10_VEC", 1),
+ ("LightSettingsList", "Light Settings List", "Lighting", "LIGHT_SUN", 2),
+ ("TimeList", "Time List", "Time", "TIME", 3),
+ ("TransitionGeneralList", "Transition General List", "Transition General", "COLORSET_06_VEC", 12),
+ ("ModifySeqList", "Modify Seq List", "Modify Seq", "IPO_CONSTANT", 10),
+ # Col 3
+ ("StartSeqList", "Start Seq List", "Play BGM", "PLAY", 4),
+ ("StopSeqList", "Stop Seq List", "Stop BGM", "SNAP_FACE", 5),
+ ("FadeOutSeqList", "Fade-Out Seq List", "Fade BGM", "IPO_EASE_IN_OUT", 6),
+ ("StartAmbienceList", "Start Ambience List", "Start Ambience", "SNAP_FACE", 13),
+ ("FadeOutAmbienceList", "Fade-Out Ambience List", "Fade-Out Ambience", "IPO_EASE_IN_OUT", 14),
+]
+
+# Adding new rest pose entry:
+# 1. Import a generic skeleton
+# 2. Pose into a usable rest pose
+# 3. Select skeleton, then run bpy.ops.object.oot_save_rest_pose()
+# 4. Copy array data from console into an OOTSkeletonImportInfo object
+# - list of tuples, first is root position, rest are euler XYZ rotations
+# 5. Add object to oot_skeleton_dict/mm_skeleton_dict
+
+link_skeleton_names = {
+ "gLinkAdultSkel",
+ "gLinkChildSkel",
+ "gLinkHumanSkel",
+ "gLinkDekuSkel",
+ "gLinkGoronSkel",
+ "gLinkZoraSkel",
+ "gLinkFierceDeitySkel",
+}
+
+
+# Link overlay will be "", since Link texture array data is handled as a special case.
+class OOTSkeletonImportInfo:
+ def __init__(
+ self,
+ skeletonName: str,
+ folderName: str,
+ actorOverlayName: str,
+ flipbookArrayIndex2D: int | None,
+ restPoseData: list[tuple[float, float, float]] | None,
+ ):
+ self.skeletonName = skeletonName
+ self.folderName = folderName
+ self.actorOverlayName = actorOverlayName # Note that overlayName = None will disable texture array reading.
+ self.flipbookArrayIndex2D = flipbookArrayIndex2D
+ self.isLink = skeletonName in link_skeleton_names
+ self.restPoseData = restPoseData
+
+
+oot_skeleton_dict = OrderedDict(
+ {
+ "Adult Link": OOTSkeletonImportInfo(
+ "gLinkAdultSkel",
+ "object_link_boy",
+ "",
+ 0,
+ [
+ (0.0, 3.6050000190734863, 0.0),
+ (0.0, -0.0, 0.0),
+ (-1.5708922147750854, -0.0, -1.5707963705062866),
+ (0.0, -0.0, 0.0),
+ (0.0, 0.05235987901687622, 0.0),
+ (0.0, -0.0, 0.0),
+ (0.0, 0.0, -1.5707964897155762),
+ (0.0, -0.05235987901687622, 0.0),
+ (0.0, -0.0, 0.0),
+ (0.0, 0.0, -1.5707964897155762),
+ (1.5707963705062866, -0.0, 1.5707963705062866),
+ (-4.740638548383913e-09, -5.356494803265832e-09, 1.4546878337860107),
+ (-4.114889869409654e-15, -1.1733899984468776e-14, 1.9080803394317627),
+ (0.0, -0.0, 0.0),
+ (1.0222795112391236e-15, -0.6981316804885864, -3.141592502593994),
+ (0.0, -0.0, 0.0),
+ (0.0, 0.0, -1.5707964897155762),
+ (-1.0222795112391236e-15, 0.6981316804885864, -3.141592502593994),
+ (0.0, -0.0, 0.0),
+ (0.0, 0.0, -1.5707964897155762),
+ (-1.5707963705062866, 2.611602306365967, -0.08726644515991211),
+ (0.0, -0.0, 0.0),
+ ],
+ ),
+ "Child Link": OOTSkeletonImportInfo(
+ "gLinkChildSkel",
+ "object_link_child",
+ "",
+ 1,
+ [
+ (0.0, 2.3559017181396484, 0.0),
+ (0.0, -0.0, 0.0),
+ (-1.5708922147750854, -0.0, -1.5707963705062866),
+ (0.0, -0.0, 0.0),
+ (0.0, 0.05235987901687622, 0.0),
+ (0.0, -0.0, 0.0),
+ (0.0, 0.0, -1.5707964897155762),
+ (0.0, -0.05235987901687622, 0.0),
+ (0.0, -0.0, 0.0),
+ (0.0, 0.0, -1.5707964897155762),
+ (1.5707963705062866, -0.0, 1.5707963705062866),
+ (-4.740638548383913e-09, -5.356494803265832e-09, 1.4546878337860107),
+ (-4.114889869409654e-15, -1.1733899984468776e-14, 1.9080803394317627),
+ (0.0, -0.0, 0.0),
+ (1.0222795112391236e-15, -0.6981316804885864, -3.141592502593994),
+ (0.0, -0.0, 0.0),
+ (0.0, 0.0, -1.5707964897155762),
+ (-1.0222795112391236e-15, 0.6981316804885864, -3.141592502593994),
+ (0.0, -0.0, 0.0),
+ (0.0, 0.0, -1.5707964897155762),
+ (-1.5707963705062866, 2.611602306365967, -0.08726644515991211),
+ (0.0, -0.0, 0.0),
+ ],
+ ),
+ }
+)
+oot_enum_skeleton_mode = [
+ ("Generic", "Generic", "Generic"),
+]
+for name, info in oot_skeleton_dict.items():
+ oot_enum_skeleton_mode.append((name, name, name))
+
+mm_skeleton_dict = OrderedDict(
+ {
+ "Human Link": OOTSkeletonImportInfo(
+ "gLinkHumanSkel",
+ "object_link_child",
+ "",
+ 4,
+ [
+ (0.0, 2.3559017181396484, 0.0),
+ (0.0, -0.0, 0.0),
+ (-1.5708922147750854, -0.0, -1.5707963705062866),
+ (0.0, -0.0, 0.0),
+ (0.0, 0.05235987901687622, 0.0),
+ (0.0, -0.0, 0.0),
+ (0.0, 0.0, -1.5707964897155762),
+ (0.0, -0.05235987901687622, 0.0),
+ (0.0, -0.0, 0.0),
+ (0.0, 0.0, -1.5707964897155762),
+ (1.5707963705062866, -0.0, 1.5707963705062866),
+ (-4.740638548383913e-09, -5.356494803265832e-09, 1.4546878337860107),
+ (-4.114889869409654e-15, -1.1733899984468776e-14, 1.9080803394317627),
+ (0.0, -0.0, 0.0),
+ (1.0222795112391236e-15, -0.6981316804885864, -3.141592502593994),
+ (0.0, -0.0, 0.0),
+ (0.0, 0.0, -1.5707964897155762),
+ (-1.0222795112391236e-15, 0.6981316804885864, -3.141592502593994),
+ (0.0, -0.0, 0.0),
+ (0.0, 0.0, -1.5707964897155762),
+ (-1.5707963705062866, 2.611602306365967, -0.08726644515991211),
+ (0.0, -0.0, 0.0),
+ ],
+ ),
+ "Deku Link": OOTSkeletonImportInfo(
+ "gLinkDekuSkel",
+ "object_link_nuts",
+ None,
+ 3,
+ [
+ (0.0, 2.3559017181396484, 0.0),
+ (0.0, -0.0, 0.0),
+ (-1.5708922147750854, -0.0, -1.5707963705062866),
+ (0.0, -0.0, 0.0),
+ (0.0, 0.05235987901687622, 0.0),
+ (0.0, -0.0, 0.0),
+ (0.0, 0.0, -1.5707964897155762),
+ (0.0, -0.05235987901687622, 0.0),
+ (0.0, -0.0, 0.0),
+ (0.0, 0.0, -1.5707964897155762),
+ (1.5707963705062866, -0.0, 1.5707963705062866),
+ (-4.740638548383913e-09, -5.356494803265832e-09, 1.4546878337860107),
+ (-4.114889869409654e-15, -1.1733899984468776e-14, 1.9080803394317627),
+ (0.0, -0.0, 0.0),
+ (1.0222795112391236e-15, -0.6981316804885864, -3.141592502593994),
+ (0.0, -0.0, 0.0),
+ (0.0, 0.0, -1.5707964897155762),
+ (-1.0222795112391236e-15, 0.6981316804885864, -3.141592502593994),
+ (0.0, -0.0, 0.0),
+ (0.0, 0.0, -1.5707964897155762),
+ (-1.5707963705062866, 2.611602306365967, -0.08726644515991211),
+ (0.0, -0.0, 0.0),
+ ],
+ ),
+ "Goron Link": OOTSkeletonImportInfo(
+ "gLinkGoronSkel",
+ "object_link_goron",
+ "",
+ 1,
+ [
+ (0.0, 2.3559017181396484, 0.0),
+ (0.0, -0.0, 0.0),
+ (-1.5708922147750854, -0.0, -1.5707963705062866),
+ (0.0, -0.0, 0.0),
+ (0.0, 0.05235987901687622, 0.0),
+ (0.0, -0.0, 0.0),
+ (0.0, 0.0, -1.5707964897155762),
+ (0.0, -0.05235987901687622, 0.0),
+ (0.0, -0.0, 0.0),
+ (0.0, 0.0, -1.5707964897155762),
+ (1.5707963705062866, -0.0, 1.5707963705062866),
+ (-4.740638548383913e-09, -5.356494803265832e-09, 1.4546878337860107),
+ (-4.114889869409654e-15, -1.1733899984468776e-14, 1.9080803394317627),
+ (0.0, -0.0, 0.0),
+ (1.0222795112391236e-15, -0.6981316804885864, -3.141592502593994),
+ (0.0, -0.0, 0.0),
+ (0.0, 0.0, -1.5707964897155762),
+ (-1.0222795112391236e-15, 0.6981316804885864, -3.141592502593994),
+ (0.0, -0.0, 0.0),
+ (0.0, 0.0, -1.5707964897155762),
+ (-1.5707963705062866, 2.611602306365967, -0.08726644515991211),
+ (0.0, -0.0, 0.0),
+ ],
+ ),
+ "Zora Link": OOTSkeletonImportInfo(
+ "gLinkZoraSkel",
+ "object_link_zora",
+ "",
+ 2,
+ [
+ (0.0, 2.3559017181396484, 0.0),
+ (0.0, -0.0, 0.0),
+ (-1.5708922147750854, -0.0, -1.5707963705062866),
+ (0.0, -0.0, 0.0),
+ (0.0, 0.05235987901687622, 0.0),
+ (0.0, -0.0, 0.0),
+ (0.0, 0.0, -1.5707964897155762),
+ (0.0, -0.05235987901687622, 0.0),
+ (0.0, -0.0, 0.0),
+ (0.0, 0.0, -1.5707964897155762),
+ (1.5707963705062866, -0.0, 1.5707963705062866),
+ (-4.740638548383913e-09, -5.356494803265832e-09, 1.4546878337860107),
+ (-4.114889869409654e-15, -1.1733899984468776e-14, 1.9080803394317627),
+ (0.0, -0.0, 0.0),
+ (1.0222795112391236e-15, -0.6981316804885864, -3.141592502593994),
+ (0.0, -0.0, 0.0),
+ (0.0, 0.0, -1.5707964897155762),
+ (-1.0222795112391236e-15, 0.6981316804885864, -3.141592502593994),
+ (0.0, -0.0, 0.0),
+ (0.0, 0.0, -1.5707964897155762),
+ (-1.5707963705062866, 2.611602306365967, -0.08726644515991211),
+ (0.0, -0.0, 0.0),
+ ],
+ ),
+ "Fierce Deity Link": OOTSkeletonImportInfo(
+ "gLinkFierceDeitySkel",
+ "object_link_boy",
+ None,
+ 0,
+ [
+ (0.0, 3.6050000190734863, 0.0),
+ (0.0, -0.0, 0.0),
+ (-1.5708922147750854, -0.0, -1.5707963705062866),
+ (0.0, -0.0, 0.0),
+ (0.0, 0.05235987901687622, 0.0),
+ (0.0, -0.0, 0.0),
+ (0.0, 0.0, -1.5707964897155762),
+ (0.0, -0.05235987901687622, 0.0),
+ (0.0, -0.0, 0.0),
+ (0.0, 0.0, -1.5707964897155762),
+ (1.5707963705062866, -0.0, 1.5707963705062866),
+ (-4.740638548383913e-09, -5.356494803265832e-09, 1.4546878337860107),
+ (-4.114889869409654e-15, -1.1733899984468776e-14, 1.9080803394317627),
+ (0.0, -0.0, 0.0),
+ (1.0222795112391236e-15, -0.6981316804885864, -3.141592502593994),
+ (0.0, -0.0, 0.0),
+ (0.0, 0.0, -1.5707964897155762),
+ (-1.0222795112391236e-15, 0.6981316804885864, -3.141592502593994),
+ (0.0, -0.0, 0.0),
+ (0.0, 0.0, -1.5707964897155762),
+ (-1.5707963705062866, 2.611602306365967, -0.08726644515991211),
+ (0.0, -0.0, 0.0),
+ ],
+ ),
+ }
+)
+mm_enum_skeleton_mode = [
+ ("Generic", "Generic", "Generic"),
+]
+for name, info in mm_skeleton_dict.items():
+ mm_enum_skeleton_mode.append((name, name, name))
+
+# ---
+
+
+@dataclass
+class Z64_Data:
+ """Contains data related to OoT, like actors or objects"""
+
+ def __init__(self, game: str):
+ self.game = game
+ self.is_registering = True
+ self.update(None, game, True) # forcing the update as we're in the init function
+
+ self.enum_floor_effect = enum_floor_effect
+
+ def is_oot(self):
+ self.update(bpy.context, None)
+ return self.game == "OOT"
+
+ def is_mm(self):
+ self.update(bpy.context, None)
+ return self.game == "MM"
+
+ def update(self, context: Optional[Context], game: Optional[str], force: bool = False):
+ if context is not None and self.is_registering:
+ self.is_registering = False
+
+ if not force and self.is_registering:
+ next_game = "OOT"
+ elif game is not None:
+ next_game = game
+ elif context is not None:
+ next_game = context.scene.gameEditorMode
+ else:
+ raise ValueError("ERROR: invalid values for context and game")
+
+ # don't update if the game is the same (or we don't want to force one)
+ if not force and next_game == self.game:
+ return
+
+ self.cs_list_type_to_cmd = {
+ "TextList": "CS_TEXT_LIST",
+ "LightSettingsList": "CS_LIGHT_SETTING_LIST",
+ "TimeList": "CS_TIME_LIST",
+ "StartSeqList": "CS_START_SEQ_LIST",
+ "StopSeqList": "CS_STOP_SEQ_LIST",
+ "FadeOutSeqList": "CS_FADE_OUT_SEQ_LIST",
+ "MiscList": "CS_MISC_LIST",
+ "DestinationList": "CS_DESTINATION_LIST",
+ "MotionBlurList": "CS_MOTION_BLUR_LIST",
+ "ModifySeqList": "CS_MODIFY_SEQ_LIST",
+ "CreditsSceneList": "CS_CHOOSE_CREDITS_SCENES_LIST",
+ "TransitionGeneralList": "CS_TRANSITION_GENERAL_LIST",
+ "GiveTatlList": "CS_GIVE_TATL_LIST",
+ }
+
+ self.game = next_game
+ self.enums = Z64_EnumData(self.game)
+ self.objects = Z64_ObjectData(self.game)
+ self.actors = Z64_ActorData(self.game)
+
+ if self.game == "OOT":
+ self.cs_index_start = 4
+ self.cs_list_type_to_cmd["Transition"] = "CS_TRANSITION"
+ self.cs_list_type_to_cmd["RumbleList"] = "CS_RUMBLE_CONTROLLER_LIST"
+ self.ootEnumNightSeq = ootEnumNightSeq
+ self.ootEnumSkybox = ootEnumSkybox
+ self.ootEnumCloudiness = ootEnumCloudiness
+ self.ootEnumLinkIdle = ootEnumLinkIdle
+ self.ootEnumRoomBehaviour = ootEnumRoomBehaviour
+ self.ootEnumFloorSetting = ootEnumFloorSetting
+ self.ootEnumFloorProperty = ootEnumFloorProperty
+ self.ootEnumCameraSType = ootEnumCameraSType
+ self.ootEnumCSListType = ootEnumCSListType
+ self.skeleton_dict = oot_skeleton_dict
+ self.enum_skeleton_mode = oot_enum_skeleton_mode
+ self.default_time_speed = 1.0
+ elif self.game == "MM":
+ self.cs_index_start = 1
+ self.cs_list_type_to_cmd["Transition"] = "CS_TRANSITION_LIST"
+ self.cs_list_type_to_cmd["RumbleList"] = "CS_RUMBLE_LIST"
+ self.ootEnumNightSeq = enum_ambiance_id
+ self.ootEnumSkybox = mm_enum_skybox
+ self.ootEnumCloudiness = mm_enum_skybox_config
+ self.ootEnumLinkIdle = mm_enum_environment_type
+ self.ootEnumRoomBehaviour = mm_enum_room_type
+ self.ootEnumFloorSetting = mm_enum_floor_property
+ self.ootEnumFloorProperty = mm_enum_floor_type
+ self.ootEnumCameraSType = mm_enum_camera_setting_type
+ self.ootEnumCSListType = mm_enum_cs_list_type
+ self.skeleton_dict = mm_skeleton_dict
+ self.enum_skeleton_mode = mm_enum_skeleton_mode
+ self.default_time_speed = 0.3
+ else:
+ raise ValueError(f"ERROR: unsupported game {repr(self.game)}")
+
+ self.enum_map: dict[str, list[tuple[str, str, str]]] = {
+ "globalObject": self.enums.enum_global_object,
+ "musicSeq": self.enums.enum_seq_id,
+ "drawConfig": self.enums.enum_draw_config,
+ "sound": self.enums.enum_surface_material,
+ "csDestination": self.enums.enum_cs_destination,
+ "seqId": self.enums.enum_seq_id,
+ "playerCueID": self.enums.enum_cs_player_cue_id,
+ "ocarinaAction": self.enums.enum_ocarina_song_action_id,
+ "csTextType": self.enums.enum_cs_text_type,
+ "csSeqPlayer": self.enums.enum_cs_fade_out_seq_player,
+ "csMiscType": self.enums.enum_cs_misc_type,
+ "transitionType": self.enums.enum_cs_transition_type,
+ "actor_cue_list_cmd_type": self.enums.enum_cs_actor_cue_list_cmd_type,
+ "spline_interp_type": self.enums.enum_cs_spline_interp_type,
+ "spline_rel_to": self.enums.enum_cs_spline_rel,
+ "trans_general": self.enums.enum_cs_transition_general,
+ "blur_type": self.enums.enum_cs_motion_blur_type,
+ "credits_scene_type": self.enums.enum_cs_credits_scene_type,
+ "mod_seq_type": self.enums.enum_cs_modify_seq_type,
+ "objectKey": self.objects.ootEnumObjectKey,
+ "actor_id": self.actors.ootEnumActorID,
+ "chest_content": self.actors.ootEnumChestContent,
+ "navi_msg_id": self.actors.ootEnumNaviMessageData,
+ "collectibles": self.actors.ootEnumCollectibleItems,
+ "skyboxID": self.ootEnumSkybox,
+ "skyboxCloudiness": self.ootEnumCloudiness,
+ "nightSeq": self.ootEnumNightSeq,
+ "roomBehaviour": self.ootEnumRoomBehaviour,
+ "linkIdleMode": self.ootEnumLinkIdle,
+ "floorSetting": self.ootEnumFloorSetting,
+ "floorProperty": self.ootEnumFloorProperty,
+ "camSType": self.ootEnumCameraSType,
+ "cs_list_type": self.ootEnumCSListType,
+ "skeleton_mode": self.enum_skeleton_mode,
+ }
+
+ def get_enum(self, prop_name: str):
+ self.update(bpy.context, None)
+ return self.enum_map[prop_name]
diff --git a/fast64_internal/data/z64/enum_data.py b/fast64_internal/data/z64/enum_data.py
new file mode 100644
index 000000000..987688a54
--- /dev/null
+++ b/fast64_internal/data/z64/enum_data.py
@@ -0,0 +1,156 @@
+from dataclasses import dataclass, field
+from os import path
+from pathlib import Path
+from .common import Z64_BaseElement, get_xml_root
+
+
+@dataclass
+class Z64_ItemElement(Z64_BaseElement):
+ parentKey: str
+ game: str
+ desc: str
+
+ def __post_init__(self):
+ # generate the name from the id
+
+ if self.name is None:
+ keyToPrefix = {
+ "cs_cmd": "CS_CMD",
+ "cs_misc_type": "CS_MISC",
+ "cs_text_type": "CS_TEXT",
+ "cs_fade_out_seq_player": "CS_FADE_OUT",
+ "cs_transition_type": "CS_TRANS",
+ "cs_destination": ("CS_DESTINATION" if self.game == "MM" else "CS_DEST"),
+ "cs_player_cue_id": "PLAYER_CUEID",
+ "cs_modify_seq_type": "CS_MOD",
+ "cs_credits_scene_type": "CS_CREDITS",
+ "cs_motion_blur_type": "CS_MOTION_BLUR",
+ "cs_rumble_type": "CS_RUMBLE",
+ "cs_transition_general": "CS_TRANS_GENERAL",
+ "cs_spline_interp_type": "CS_CAM_INTERP",
+ "cs_spline_rel": "", # TODO: set the value to `CS_CAM_REL` once this is documented
+ "cs_spawn_flag": "CS_SPAWN_FLAG",
+ "actor_cs_end_sfx": "CS_END_SFX",
+ "navi_quest_hint_type": "NAVI_QUEST_HINTS",
+ "ocarina_song_action_id": "OCARINA_ACTION",
+ "seq_id": "NA_BGM",
+ "draw_config": ("SCENE_DRAW_CFG" if self.game == "MM" else "SDC"),
+ "surface_material": "SURFACE_MATERIAL",
+ "global_object": "OBJECT",
+ }
+
+ self.name = self.id.removeprefix(f"{keyToPrefix[self.parentKey]}_")
+
+ if self.parentKey in ["cs_cmd", "cs_player_cue_id"]:
+ split = self.name.split("_")
+ if self.parentKey == "cs_cmd" and "ACTOR_CUE" in self.id:
+ self.name = f"Actor Cue {split[-2]}_{split[-1]}"
+ else:
+ self.name = f"Player Cue Id {split[-1]}"
+ else:
+ self.name = self.name.replace("_", " ").title()
+
+
+@dataclass
+class Z64_EnumElement(Z64_BaseElement):
+ items: list[Z64_ItemElement]
+ item_by_key: dict[str, Z64_ItemElement] = field(default_factory=dict)
+ item_by_index: dict[int, Z64_ItemElement] = field(default_factory=dict)
+ item_by_id: dict[int, Z64_ItemElement] = field(default_factory=dict)
+
+ def __post_init__(self):
+ self.item_by_key = {item.key: item for item in self.items}
+ self.item_by_index = {item.index: item for item in self.items}
+ self.item_by_id = {item.id: item for item in self.items}
+
+
+class Z64_EnumData:
+ """Cutscene and misc enum data"""
+
+ def __init__(self, game: str):
+ # general enumData list
+ self.enumDataList: list[Z64_EnumElement] = []
+
+ # Path to the ``EnumData.xml`` file
+ xml_path = Path(f"{path.dirname(path.abspath(__file__))}/xml/{game.lower()}_enum_data.xml")
+ enum_data_root = get_xml_root(xml_path.resolve())
+
+ for enum in enum_data_root.iterfind("Enum"):
+ self.enumDataList.append(
+ Z64_EnumElement(
+ enum.attrib["ID"],
+ enum.attrib["Key"],
+ None,
+ None,
+ [
+ Z64_ItemElement(
+ item.attrib["ID"],
+ item.attrib["Key"],
+ # note: the name sets automatically after the init if None
+ item.attrib["Name"] if enum.attrib["Key"] == "seqId" else None,
+ int(item.attrib["Index"]),
+ enum.attrib["Key"],
+ game,
+ item.attrib.get("Description", "Unset"),
+ )
+ for item in enum
+ ],
+ )
+ )
+
+ # create list of tuples used by Blender's enum properties
+ self.deletedEntry = ("None", "(Deleted from the XML)", "None")
+
+ self.enum_cs_cmd: list[tuple[str, str, str]] = []
+ self.enum_cs_misc_type: list[tuple[str, str, str]] = []
+ self.enum_cs_text_type: list[tuple[str, str, str]] = []
+ self.enum_cs_fade_out_seq_player: list[tuple[str, str, str]] = []
+ self.enum_cs_transition_type: list[tuple[str, str, str]] = []
+ self.enum_cs_destination: list[tuple[str, str, str]] = []
+ self.enum_cs_player_cue_id: list[tuple[str, str, str]] = []
+ self.enum_cs_modify_seq_type: list[tuple[str, str, str]] = []
+ self.enum_cs_credits_scene_type: list[tuple[str, str, str]] = []
+ self.enum_cs_motion_blur_type: list[tuple[str, str, str]] = []
+ self.enum_cs_rumble_type: list[tuple[str, str, str]] = []
+ self.enum_cs_transition_general: list[tuple[str, str, str]] = []
+ self.enum_cs_spline_interp_type: list[tuple[str, str, str]] = []
+ self.enum_cs_spline_rel: list[tuple[str, str, str]] = []
+ self.enum_cs_spawn_flag: list[tuple[str, str, str]] = []
+ self.enum_actor_cs_end_sfx: list[tuple[str, str, str]] = []
+ self.enum_navi_quest_hint_type: list[tuple[str, str, str]] = []
+ self.enum_ocarina_song_action_id: list[tuple[str, str, str]] = []
+ self.enum_seq_id: list[tuple[str, str, str]] = []
+ self.enum_draw_config: list[tuple[str, str, str]] = []
+ self.enum_surface_material: list[tuple[str, str, str]] = []
+ self.enum_global_object: list[tuple[str, str, str]] = []
+
+ self.enumByID = {enum.id: enum for enum in self.enumDataList}
+ self.enumByKey = {enum.key: enum for enum in self.enumDataList}
+
+ for key in self.enumByKey.keys():
+ setattr(self, f"enum_{key}", self.get_enum_data(key))
+
+ self.enum_cs_actor_cue_list_cmd_type = [
+ item for item in self.enum_cs_cmd if "actor_cue" in item[0] or "player_cue" in item[0]
+ ]
+ self.enum_cs_actor_cue_list_cmd_type.sort()
+ self.enum_cs_actor_cue_list_cmd_type.insert(0, ("Custom", "Custom", "Custom"))
+
+ def get_enum_data(self, enumKey: str):
+ enum = self.enumByKey[enumKey]
+ firstIndex = min(1, *(item.index for item in enum.items))
+ lastIndex = max(1, *(item.index for item in enum.items)) + 1
+ enumData = [self.deletedEntry] * lastIndex
+ custom = ("Custom", "Custom", "Custom")
+
+ for item in enum.items:
+ if item.index < lastIndex:
+ identifier = item.key
+ enumData[item.index] = (identifier, item.name, item.id)
+
+ if firstIndex > 0:
+ enumData[0] = custom
+ else:
+ enumData.insert(0, custom)
+
+ return enumData
diff --git a/fast64_internal/data/z64/object_data.py b/fast64_internal/data/z64/object_data.py
new file mode 100644
index 000000000..8c42f90ff
--- /dev/null
+++ b/fast64_internal/data/z64/object_data.py
@@ -0,0 +1,63 @@
+from dataclasses import dataclass
+from os import path
+from pathlib import Path
+from ...utility import PluginError
+from .common import Z64_BaseElement, get_xml_root
+
+# Note: "object" in this context refers to an OoT Object file (like ``gameplay_keep``)
+
+
+@dataclass
+class Z64_ObjectElement(Z64_BaseElement):
+ pass
+
+
+class Z64_ObjectData:
+ """Everything related to OoT objects"""
+
+ def __init__(self, game: str):
+ # general object list
+ self.objectList: list[Z64_ObjectElement] = []
+
+ # Path to the ``ObjectList.xml`` file
+ xml_path = Path(f"{path.dirname(path.abspath(__file__))}/xml/{game.lower()}_object_list.xml")
+ object_root = get_xml_root(xml_path.resolve())
+
+ for obj in object_root.iterfind("Object"):
+ objName = f"{obj.attrib['Name']} - {obj.attrib['ID'].removeprefix('OBJECT_')}"
+ self.objectList.append(
+ Z64_ObjectElement(obj.attrib["ID"], obj.attrib["Key"], objName, int(obj.attrib["Index"]))
+ )
+
+ self.objects_by_id = {obj.id: obj for obj in self.objectList}
+ self.objects_by_key = {obj.key: obj for obj in self.objectList}
+
+ # list of tuples used by Blender's enum properties
+ self.deletedEntry = ("None", "(Deleted from the XML)", "None")
+ lastIndex = max(1, *(obj.index for obj in self.objectList))
+ self.ootEnumObjectKey = self.getObjectIDList(lastIndex + 1, False, game)
+
+ # create the legacy object list for old blends
+ if game == "OOT":
+ self.ootEnumObjectIDLegacy = self.getObjectIDList(
+ self.objects_by_key["obj_timeblock"].index + 1, True, game
+ )
+
+ # validate the legacy list, if there's any None element then something's wrong
+ if self.deletedEntry in self.ootEnumObjectIDLegacy:
+ raise PluginError("ERROR: Legacy Object List doesn't match!")
+ else:
+ self.ootEnumObjectIDLegacy = []
+
+ def getObjectIDList(self, max: int, isLegacy: bool, game: str):
+ """Generates and returns the object list in the right order"""
+ objList = [self.deletedEntry] * max
+ for obj in self.objectList:
+ if obj.index < max:
+ identifier = obj.id if isLegacy else obj.key
+ objList[obj.index] = (identifier, obj.name, obj.id)
+ if game == "OOT":
+ objList[0] = ("Custom", "Custom Object", "Custom")
+ else:
+ objList.insert(0, ("Custom", "Custom Object", "Custom"))
+ return objList
diff --git a/fast64_internal/data/z64/xml/mm_actor_list.xml b/fast64_internal/data/z64/xml/mm_actor_list.xml
new file mode 100644
index 000000000..20f762697
--- /dev/null
+++ b/fast64_internal/data/z64/xml/mm_actor_list.xml
@@ -0,0 +1,979 @@
+
+
+
+
+
+
+
+
+
+
+
+ - Large Orange Flame
+ - Large Orange Flame
+ - Large Blue Flame
+ - Large Green Flame
+ - Small Orange Flame
+ - Large Orange Flame
+ - Large Green Flame
+ - Large Blue Flame
+ - Large Magenta Flame
+ - Large Pale Orange Flame
+ - Large Pale Yellow Flame
+ - Large Pale Green Flame
+ - Large Pale Pink Flame
+ - Large Pale Purple Flame
+ - Large Pale Indigo Flame
+ - Large Pale Blue Flame
+
+
+
+
+
+
+
+ - Whole Day ('ENDOOR_TYPE_WHOLE_DAY')
+ - Locked ('ENDOOR_TYPE_LOCKED')
+ - Day ('ENDOOR_TYPE_DAY')
+ - Night ('ENDOOR_TYPE_NIGHT')
+ - Ajar ('ENDOOR_TYPE_AJAR')
+ - Schedule ('ENDOOR_TYPE_SCHEDULE')
+ - Unknown ('ENDOOR_TYPE_6')
+ - Framed ('ENDOOR_TYPE_FRAMED')
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ - Golden
+ - Golden - Appears - Clear Flag
+ - Boss Key Chest
+ - Golden - Falls - Switch Flag
+ - Golden - Invisible
+ - Wooden
+ - Wooden - Invisible
+ - Wooden - Clear Flag
+ - Wooden - Falls - Switch Flag
+ - Crash
+ - Crash
+ - Golden - Appears - Switch Flag
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ - Regular
+ - Large
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ - Default
+ - Invisible
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/fast64_internal/data/z64/xml/mm_enum_data.xml b/fast64_internal/data/z64/xml/mm_enum_data.xml
new file mode 100644
index 000000000..e5c4782fb
--- /dev/null
+++ b/fast64_internal/data/z64/xml/mm_enum_data.xml
@@ -0,0 +1,756 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/fast64_internal/data/z64/xml/mm_object_list.xml b/fast64_internal/data/z64/xml/mm_object_list.xml
new file mode 100644
index 000000000..71706d83c
--- /dev/null
+++ b/fast64_internal/data/z64/xml/mm_object_list.xml
@@ -0,0 +1,653 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/fast64_internal/oot/data/xml/ActorList.xml b/fast64_internal/data/z64/xml/oot_actor_list.xml
similarity index 100%
rename from fast64_internal/oot/data/xml/ActorList.xml
rename to fast64_internal/data/z64/xml/oot_actor_list.xml
diff --git a/fast64_internal/oot/data/xml/EnumData.xml b/fast64_internal/data/z64/xml/oot_enum_data.xml
similarity index 78%
rename from fast64_internal/oot/data/xml/EnumData.xml
rename to fast64_internal/data/z64/xml/oot_enum_data.xml
index 8575b4a4b..b57abbf4f 100644
--- a/fast64_internal/oot/data/xml/EnumData.xml
+++ b/fast64_internal/data/z64/xml/oot_enum_data.xml
@@ -20,7 +20,7 @@
- Player Cue Ids got their own enum but not regular Actor Cues.
This is because Actor Cues are among cutscene commands (``csCmd``)
-->
-
+
@@ -152,7 +152,7 @@
-
+
@@ -191,7 +191,7 @@
-
+
@@ -199,12 +199,12 @@
-
+
-
+
@@ -220,7 +220,7 @@
-
+
@@ -343,7 +343,7 @@
-
+
@@ -424,13 +424,13 @@
-
+
-
+
@@ -483,7 +483,7 @@
-
+
@@ -598,4 +598,142 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/fast64_internal/oot/data/xml/ObjectList.xml b/fast64_internal/data/z64/xml/oot_object_list.xml
similarity index 100%
rename from fast64_internal/oot/data/xml/ObjectList.xml
rename to fast64_internal/data/z64/xml/oot_object_list.xml
diff --git a/fast64_internal/f3d/f3d_gbi.py b/fast64_internal/f3d/f3d_gbi.py
index a07dd137b..890739efb 100644
--- a/fast64_internal/f3d/f3d_gbi.py
+++ b/fast64_internal/f3d/f3d_gbi.py
@@ -93,6 +93,7 @@ class GfxMatWriteMethod(enum.Enum):
default_draw_layers = {
"SM64": sm64_default_draw_layers,
"OOT": oot_default_draw_layers,
+ "MM": oot_default_draw_layers,
}
CCMUXDict = {
diff --git a/fast64_internal/f3d/f3d_material.py b/fast64_internal/f3d/f3d_material.py
index 3acbc9aaa..9db8a6f1b 100644
--- a/fast64_internal/f3d/f3d_material.py
+++ b/fast64_internal/f3d/f3d_material.py
@@ -149,8 +149,8 @@ def menu_items_enum(_self, context):
]
defaultMaterialPresets = {
- "Shaded Solid": {"SM64": "Shaded Solid", "OOT": "oot_shaded_solid"},
- "Shaded Texture": {"SM64": "Shaded Texture", "OOT": "oot_shaded_texture"},
+ "Shaded Solid": {"SM64": "Shaded Solid", "OOT": "oot_shaded_solid", "MM": "oot_shaded_solid"},
+ "Shaded Texture": {"SM64": "Shaded Texture", "OOT": "oot_shaded_texture", "MM": "oot_shaded_texture"},
}
F3D_GEO_MODES = {
@@ -232,7 +232,7 @@ def update_draw_layer(self, context):
drawLayer = material.f3d_mat.draw_layer
if context.scene.gameEditorMode == "SM64":
drawLayer.oot = drawLayerSM64toOOT[drawLayer.sm64]
- elif context.scene.gameEditorMode == "OOT":
+ elif context.scene.gameEditorMode in {"OOT", "MM"}:
if material.f3d_mat.draw_layer.oot == "Opaque":
if int(material.f3d_mat.draw_layer.sm64) > 4:
material.f3d_mat.draw_layer.sm64 = "1"
@@ -253,7 +253,7 @@ def get_world_layer_defaults(scene, game_mode: str, layer: str):
getattr(world, f"draw_layer_{layer}_cycle_1", ""),
getattr(world, f"draw_layer_{layer}_cycle_2", ""),
)
- elif game_mode == "OOT":
+ elif game_mode in {"OOT", "MM"}:
return (
getattr(world.ootDefaultRenderModes, f"{layer.lower()}Cycle1", ""),
getattr(world.ootDefaultRenderModes, f"{layer.lower()}Cycle2", ""),
@@ -1053,7 +1053,7 @@ def ui_uvCheck(self, layout, context):
def ui_draw_layer(self, material, layout, context):
if context.scene.gameEditorMode == "SM64":
prop_split(layout, material.f3d_mat.draw_layer, "sm64", "Draw Layer")
- elif context.scene.gameEditorMode == "OOT":
+ elif context.scene.gameEditorMode in {"OOT", "MM"}:
prop_split(layout, material.f3d_mat.draw_layer, "oot", "Draw Layer")
def ui_misc(self, f3dMat: "F3DMaterialProperty", inputCol: UILayout, showCheckBox: bool) -> None:
@@ -3996,7 +3996,10 @@ def execute(self, context):
def getCurrentPresetDir():
- return "f3d/" + bpy.context.scene.gameEditorMode.lower()
+ if bpy.context.scene.gameEditorMode in {"OOT", "MM"}:
+ return f"f3d/oot"
+ else:
+ return f"f3d/{bpy.context.scene.gameEditorMode.lower()}"
class ApplyMaterialPresetOperator(Operator):
@@ -4010,10 +4013,6 @@ def execute(self, context: Context):
return {"FINISHED"}
-def getCurrentPresetDir():
- return "f3d/" + bpy.context.scene.gameEditorMode.lower()
-
-
# modules/bpy_types.py -> Menu
class MATERIAL_MT_f3d_presets(Menu):
bl_label = "F3D Material Presets"
@@ -4861,7 +4860,7 @@ def draw(self, context):
prop_split(globalSettingsBox, renderSettings, "light1SpecSize", "Light 1 Specular Size")
prop_split(globalSettingsBox, renderSettings, "useWorldSpaceLighting", "Use World Space Lighting")
- if context.scene.gameEditorMode in ["SM64", "OOT"]:
+ if context.scene.gameEditorMode in ["SM64", "OOT", "MM"]:
layout.separator(factor=0.5)
gameSettingsBox = layout.box()
gameSettingsBox.label(text="Preview Context")
@@ -4873,7 +4872,7 @@ def draw(self, context):
gameSettingsBox.prop(renderSettings, "sm64Area")
- case "OOT":
+ case "OOT" | "MM":
if renderSettings.ootSceneObject is not None:
gameSettingsBox.prop(renderSettings, "useObjectRenderPreview", text="Use Scene for Preview")
diff --git a/fast64_internal/f3d/f3d_parser.py b/fast64_internal/f3d/f3d_parser.py
index d206dd2f9..500efd4ce 100644
--- a/fast64_internal/f3d/f3d_parser.py
+++ b/fast64_internal/f3d/f3d_parser.py
@@ -1983,7 +1983,7 @@ def parseVertexData(dlData: str, vertexDataName: str, f3dContext: F3DContext):
pathMatch = re.search(r'\#include\s*"([^"]*)"', data)
if pathMatch is not None:
path = pathMatch.group(1)
- if bpy.context.scene.gameEditorMode == "OOT":
+ if bpy.context.scene.gameEditorMode in {"OOT", "MM"}:
path = f"{bpy.context.scene.fast64.oot.get_extracted_path()}/{path}"
data = readFile(f3dContext.getVTXPathFromInclude(path))
@@ -2087,7 +2087,7 @@ def parseTextureData(dlData, textureName, f3dContext, imageFormat, imageSize, wi
pathMatch = re.search(r'\#include\s*"(.*?)"', data, re.DOTALL)
if pathMatch is not None:
path = pathMatch.group(1)
- if bpy.context.scene.gameEditorMode == "OOT":
+ if bpy.context.scene.gameEditorMode in {"OOT", "MM"}:
path = f"{bpy.context.scene.fast64.oot.get_extracted_path()}/{path}"
originalImage = bpy.data.images.load(f3dContext.getImagePathFromInclude(path))
image = originalImage.copy()
diff --git a/fast64_internal/f3d/flipbook.py b/fast64_internal/f3d/flipbook.py
index 86e9f3bdb..39b61371b 100644
--- a/fast64_internal/f3d/flipbook.py
+++ b/fast64_internal/f3d/flipbook.py
@@ -256,7 +256,7 @@ def ootFlipbookAnimUpdate(self, armatureObj: bpy.types.Object, segment: str, ind
# we use a handler since update functions are not called when a property is animated.
@persistent
def flipbookAnimHandler(dummy):
- if bpy.context.scene.gameEditorMode == "OOT":
+ if bpy.context.scene.gameEditorMode in {"OOT", "MM"}:
for obj in bpy.data.objects:
if obj.type == "ARMATURE":
# we only want to update texture on keyframed armatures.
@@ -284,14 +284,14 @@ class Flipbook_MaterialPanel(bpy.types.Panel):
@classmethod
def poll(cls, context):
- return context.material is not None and context.scene.gameEditorMode in ["OOT"]
+ return context.material is not None and context.scene.gameEditorMode in ["OOT", "MM"]
def draw(self, context):
layout = self.layout
mat = context.material
col = layout.column()
- if context.scene.gameEditorMode == "OOT":
+ if context.scene.gameEditorMode in {"OOT", "MM"}:
checkFlipbookReference = ootFlipbookReferenceIsValid
drawFlipbookRequirementMessage = ootFlipbookRequirementMessage
else:
diff --git a/fast64_internal/game_data.py b/fast64_internal/game_data.py
new file mode 100644
index 000000000..1c8222c29
--- /dev/null
+++ b/fast64_internal/game_data.py
@@ -0,0 +1,21 @@
+from typing import Optional
+
+
+class GameData:
+ def __init__(self, game_editor_mode: Optional[str] = None):
+ from .data import Z64_Data
+
+ self.z64 = Z64_Data("OOT")
+
+ if game_editor_mode is not None:
+ self.update(game_editor_mode)
+
+ def update(self, game_editor_mode: str):
+ if game_editor_mode is not None and game_editor_mode in {"OOT", "MM"}:
+ self.z64.update(None, game_editor_mode, True)
+
+ if game_editor_mode in {"OOT", "MM"} and game_editor_mode != self.z64.game:
+ raise ValueError(f"ERROR: Z64 game mismatch: {game_editor_mode}, {game_data.z64.game}")
+
+
+game_data = GameData()
diff --git a/fast64_internal/mm/data/xml/ActorList.xml b/fast64_internal/mm/data/xml/ActorList.xml
deleted file mode 100644
index 11e286839..000000000
--- a/fast64_internal/mm/data/xml/ActorList.xml
+++ /dev/null
@@ -1,928 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
- - Large Orange Flame
-
- Large Orange Flame
-
- Large Blue Flame
-
- Large Green Flame
-
- Small Orange Flame
-
- Large Orange Flame
-
- Large Green Flame
-
- Large Blue Flame
-
- Large Magenta Flame
-
- Large Pale Orange Flame
-
- Large Pale Yellow Flame
-
- Large Pale Green Flame
-
- Large Pale Pink Flame
-
- Large Pale Purple Flame
-
- Large Pale Indigo Flame
-
- Large Pale Blue Flame
-
-
-
-
-
-
-
-
-
- - Golden
- - Golden - Appears - Clear Flag
- - Boss Key Chest
- - Golden - Falls - Switch Flag
- - Golden - Invisible
- - Wooden
- - Wooden - Invisible
- - Wooden - Clear Flag
- - Wooden - Falls - Switch Flag
- - Crash
- - Crash
- - Golden - Appears - Switch Flag
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- - Regular
- - Large
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- - Default
- - Invisible
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/fast64_internal/oot/collision/constants.py b/fast64_internal/oot/collision/constants.py
deleted file mode 100644
index 031125a54..000000000
--- a/fast64_internal/oot/collision/constants.py
+++ /dev/null
@@ -1,206 +0,0 @@
-ootEnumConveyer = [
- ("None", "None", "None"),
- ("Land", "Land", "Land"),
- ("Water", "Water", "Water"),
-]
-
-ootEnumFloorSetting = [
- ("Custom", "Custom", "Custom"),
- ("0x00", "Default", "Default"),
- ("0x05", "Void (Small)", "Void (Small)"),
- ("0x06", "Grab Wall", "Grab Wall"),
- ("0x08", "Stop Air Momentum", "Stop Air Momentum"),
- ("0x09", "Fall Instead Of Jumping", "Fall Instead Of Jumping"),
- ("0x0B", "Dive", "Dive"),
- ("0x0C", "Void (Large)", "Void (Large)"),
-]
-
-ootEnumWallSetting = [
- ("Custom", "Custom", "Custom"),
- ("0x00", "None", "None"),
- ("0x01", "No Ledge Grab", "No Ledge Grab"),
- ("0x02", "Ladder", "Ladder"),
- ("0x03", "Ladder Top", "Ladder Top"),
- ("0x04", "Vines", "Vines"),
- ("0x05", "Crawl Space", "Crawl Space"),
- ("0x06", "Crawl Space 2", "Crawl Space 2"),
- ("0x07", "Push Block", "Push Block"),
-]
-
-ootEnumFloorProperty = [
- ("Custom", "Custom", "Custom"),
- ("0x00", "None", "None"),
- ("0x01", "Haunted Wasteland Camera", "Haunted Wasteland Camera"),
- ("0x02", "Hurt Floor (Spikes)", "Hurt Floor (Spikes)"),
- ("0x03", "Hurt Floor (Lava)", "Hurt Floor (Lava)"),
- ("0x04", "Shallow Sand", "Shallow Sand"),
- ("0x05", "Slippery", "Slippery"),
- ("0x06", "No Fall Damage", "No Fall Damage"),
- ("0x07", "Quicksand Crossing (Epona Uncrossable)", "Quicksand Crossing (Epona Uncrossable)"),
- ("0x08", "Jabu Jabu's Belly", "Jabu Jabu's Belly"),
- ("0x09", "Void", "Void"),
- ("0x0A", "Link Looks Up", "Link Looks Up"),
- ("0x0B", "Quicksand Crossing (Epona Crossable)", "Quicksand Crossing (Epona Crossable)"),
-]
-
-ootEnumCollisionTerrain = [
- ("Custom", "Custom", "Custom"),
- ("0x00", "Walkable", "Walkable"),
- ("0x01", "Steep", "Steep"),
- ("0x02", "Walkable (Preserves Exit Flags)", "Walkable (Preserves Exit Flags)"),
- ("0x03", "Walkable (?)", "Walkable (?)"),
-]
-
-ootEnumCollisionSound = [
- ("Custom", "Custom", "Custom"),
- ("0x00", "Dirt", "Dirt (aka Earth)"),
- ("0x01", "Sand", "Sand"),
- ("0x02", "Stone", "Stone"),
- ("0x03", "Jabu", "Jabu-Jabu flesh (aka Wet Stone)"),
- ("0x04", "Shallow Water", "Shallow Water"),
- ("0x05", "Deep Water", "Deep Water"),
- ("0x06", "Tall Grass", "Tall Grass"),
- ("0x07", "Lava", "Lava (aka Goo)"),
- ("0x08", "Grass", "Grass (aka Earth 2)"),
- ("0x09", "Bridge", "Bridge (aka Wooden Plank)"),
- ("0x0A", "Wood", "Wood (aka Packed Earth)"),
- ("0x0B", "Soft Dirt", "Soft Dirt (aka Earth 3)"),
- ("0x0C", "Ice", "Ice (aka Ceramic)"),
- ("0x0D", "Carpet", "Carpet (aka Loose Earth)"),
-]
-
-ootEnumConveyorSpeed = [
- ("Custom", "Custom", "Custom"),
- ("0x00", "None", "None"),
- ("0x01", "Slow", "Slow"),
- ("0x02", "Medium", "Medium"),
- ("0x03", "Fast", "Fast"),
-]
-
-ootEnumCameraCrawlspaceSType = [
- ("Custom", "Custom", "Custom"),
- ("CAM_SET_CRAWLSPACE", "Crawlspace", "Crawlspace"),
-]
-
-ootEnumCameraSType = [
- ("Custom", "Custom", "Custom"),
- ("CAM_SET_NONE", "None", "None"),
- ("CAM_SET_NORMAL0", "Normal0", "Normal0"),
- ("CAM_SET_NORMAL1", "Normal1", "Normal1"),
- ("CAM_SET_DUNGEON0", "Dungeon0", "Dungeon0"),
- ("CAM_SET_DUNGEON1", "Dungeon1", "Dungeon1"),
- ("CAM_SET_NORMAL3", "Normal3", "Normal3"),
- ("CAM_SET_HORSE0", "Horse", "Horse"),
- ("CAM_SET_BOSS_GOMA", "Boss_gohma", "Boss_gohma"),
- ("CAM_SET_BOSS_DODO", "Boss_dodongo", "Boss_dodongo"),
- ("CAM_SET_BOSS_BARI", "Boss_barinade", "Boss_barinade"),
- ("CAM_SET_BOSS_FGANON", "Boss_phantom_ganon", "Boss_phantom_ganon"),
- ("CAM_SET_BOSS_BAL", "Boss_volvagia", "Boss_volvagia"),
- ("CAM_SET_BOSS_SHADES", "Boss_bongo", "Boss_bongo"),
- ("CAM_SET_BOSS_MOFA", "Boss_morpha", "Boss_morpha"),
- ("CAM_SET_TWIN0", "Twinrova_platform", "Twinrova_platform"),
- ("CAM_SET_TWIN1", "Twinrova_floor", "Twinrova_floor"),
- ("CAM_SET_BOSS_GANON1", "Boss_ganondorf", "Boss_ganondorf"),
- ("CAM_SET_BOSS_GANON2", "Boss_ganon", "Boss_ganon"),
- ("CAM_SET_TOWER0", "Tower_climb", "Tower_climb"),
- ("CAM_SET_TOWER1", "Tower_unused", "Tower_unused"),
- ("CAM_SET_FIXED0", "Market_balcony", "Market_balcony"),
- ("CAM_SET_FIXED1", "Chu_bowling", "Chu_bowling"),
- ("CAM_SET_CIRCLE0", "Pivot_crawlspace", "Pivot_crawlspace"),
- ("CAM_SET_CIRCLE2", "Pivot_shop_browsing", "Pivot_shop_browsing"),
- ("CAM_SET_CIRCLE3", "Pivot_in_front", "Pivot_in_front"),
- ("CAM_SET_PREREND0", "Prerend_fixed", "Prerend_fixed"),
- ("CAM_SET_PREREND1", "Prerend_pivot", "Prerend_pivot"),
- ("CAM_SET_PREREND3", "Prerend_side_scroll", "Prerend_side_scroll"),
- ("CAM_SET_DOOR0", "Door0", "Door0"),
- ("CAM_SET_DOORC", "Doorc", "Doorc"),
- ("CAM_SET_RAIL3", "Crawlspace", "Crawlspace"),
- ("CAM_SET_START0", "Start0", "Start0"),
- ("CAM_SET_START1", "Start1", "Start1"),
- ("CAM_SET_FREE0", "Free0", "Free0"),
- ("CAM_SET_FREE2", "Free2", "Free2"),
- ("CAM_SET_CIRCLE4", "Pivot_corner", "Pivot_corner"),
- ("CAM_SET_CIRCLE5", "Pivot_water_surface", "Pivot_water_surface"),
- ("CAM_SET_DEMO0", "Cs_0", "Cs_0"),
- ("CAM_SET_DEMO1", "Twisted_Hallway", "Twisted_Hallway"),
- ("CAM_SET_MORI1", "Forest_birds_eye", "Forest_birds_eye"),
- ("CAM_SET_ITEM0", "Slow_chest_cs", "Slow_chest_cs"),
- ("CAM_SET_ITEM1", "Item_unused", "Item_unused"),
- ("CAM_SET_DEMO3", "Cs_3", "Cs_3"),
- ("CAM_SET_DEMO4", "Cs_attention", "Cs_attention"),
- ("CAM_SET_UFOBEAN", "Bean_generic", "Bean_generic"),
- ("CAM_SET_LIFTBEAN", "Bean_lost_woods", "Bean_lost_woods"),
- ("CAM_SET_SCENE0", "Scene_unused", "Scene_unused"),
- ("CAM_SET_SCENE1", "Scene_transition", "Scene_transition"),
- ("CAM_SET_HIDAN1", "Fire_platform", "Fire_platform"),
- ("CAM_SET_HIDAN2", "Fire_staircase", "Fire_staircase"),
- ("CAM_SET_MORI2", "Forest_unused", "Forest_unused"),
- ("CAM_SET_MORI3", "Defeat_poe", "Defeat_poe"),
- ("CAM_SET_TAKO", "Big_octo", "Big_octo"),
- ("CAM_SET_SPOT05A", "Meadow_birds_eye", "Meadow_birds_eye"),
- ("CAM_SET_SPOT05B", "Meadow_unused", "Meadow_unused"),
- ("CAM_SET_HIDAN3", "Fire_birds_eye", "Fire_birds_eye"),
- ("CAM_SET_ITEM2", "Turn_around", "Turn_around"),
- ("CAM_SET_CIRCLE6", "Pivot_vertical", "Pivot_vertical"),
- ("CAM_SET_NORMAL2", "Normal2", "Normal2"),
- ("CAM_SET_FISHING", "Fishing", "Fishing"),
- ("CAM_SET_DEMOC", "Cs_c", "Cs_c"),
- ("CAM_SET_UO_FIBER", "Jabu_tentacle", "Jabu_tentacle"),
- ("CAM_SET_DUNGEON2", "Dungeon2", "Dungeon2"),
- ("CAM_SET_TEPPEN", "Directed_yaw", "Directed_yaw"),
- ("CAM_SET_CIRCLE7", "Pivot_from_side", "Pivot_from_side"),
- ("CAM_SET_NORMAL4", "Normal4", "Normal4"),
-]
-
-decomp_compat_map_CameraSType = {
- "CAM_SET_HORSE0": "CAM_SET_HORSE",
- "CAM_SET_BOSS_GOMA": "CAM_SET_BOSS_GOHMA",
- "CAM_SET_BOSS_DODO": "CAM_SET_BOSS_DODONGO",
- "CAM_SET_BOSS_BARI": "CAM_SET_BOSS_BARINADE",
- "CAM_SET_BOSS_FGANON": "CAM_SET_BOSS_PHANTOM_GANON",
- "CAM_SET_BOSS_BAL": "CAM_SET_BOSS_VOLVAGIA",
- "CAM_SET_BOSS_SHADES": "CAM_SET_BOSS_BONGO",
- "CAM_SET_BOSS_MOFA": "CAM_SET_BOSS_MORPHA",
- "CAM_SET_TWIN0": "CAM_SET_BOSS_TWINROVA_PLATFORM",
- "CAM_SET_TWIN1": "CAM_SET_BOSS_TWINROVA_FLOOR",
- "CAM_SET_BOSS_GANON1": "CAM_SET_BOSS_GANONDORF",
- "CAM_SET_BOSS_GANON2": "CAM_SET_BOSS_GANON",
- "CAM_SET_TOWER0": "CAM_SET_TOWER_CLIMB",
- "CAM_SET_TOWER1": "CAM_SET_TOWER_UNUSED",
- "CAM_SET_FIXED0": "CAM_SET_MARKET_BALCONY",
- "CAM_SET_FIXED1": "CAM_SET_CHU_BOWLING",
- "CAM_SET_CIRCLE0": "CAM_SET_PIVOT_CRAWLSPACE",
- "CAM_SET_CIRCLE2": "CAM_SET_PIVOT_SHOP_BROWSING",
- "CAM_SET_CIRCLE3": "CAM_SET_PIVOT_IN_FRONT",
- "CAM_SET_PREREND0": "CAM_SET_PREREND_FIXED",
- "CAM_SET_PREREND1": "CAM_SET_PREREND_PIVOT",
- "CAM_SET_PREREND3": "CAM_SET_PREREND_SIDE_SCROLL",
- "CAM_SET_RAIL3": "CAM_SET_CRAWLSPACE",
- "CAM_SET_CIRCLE4": "CAM_SET_PIVOT_CORNER",
- "CAM_SET_CIRCLE5": "CAM_SET_PIVOT_WATER_SURFACE",
- "CAM_SET_DEMO0": "CAM_SET_CS_0",
- "CAM_SET_DEMO1": "CAM_SET_CS_TWISTED_HALLWAY",
- "CAM_SET_MORI1": "CAM_SET_FOREST_BIRDS_EYE",
- "CAM_SET_ITEM0": "CAM_SET_SLOW_CHEST_CS",
- "CAM_SET_ITEM1": "CAM_SET_ITEM_UNUSED",
- "CAM_SET_DEMO3": "CAM_SET_CS_3",
- "CAM_SET_DEMO4": "CAM_SET_CS_ATTENTION",
- "CAM_SET_UFOBEAN": "CAM_SET_BEAN_GENERIC",
- "CAM_SET_LIFTBEAN": "CAM_SET_BEAN_LOST_WOODS",
- "CAM_SET_SCENE0": "CAM_SET_SCENE_UNUSED",
- "CAM_SET_SCENE1": "CAM_SET_SCENE_TRANSITION",
- "CAM_SET_HIDAN1": "CAM_SET_ELEVATOR_PLATFORM",
- "CAM_SET_HIDAN2": "CAM_SET_FIRE_STAIRCASE",
- "CAM_SET_MORI2": "CAM_SET_FOREST_UNUSED",
- "CAM_SET_MORI3": "CAM_SET_FOREST_DEFEAT_POE",
- "CAM_SET_TAKO": "CAM_SET_BIG_OCTO",
- "CAM_SET_SPOT05A": "CAM_SET_MEADOW_BIRDS_EYE",
- "CAM_SET_SPOT05B": "CAM_SET_MEADOW_UNUSED",
- "CAM_SET_HIDAN3": "CAM_SET_FIRE_BIRDS_EYE",
- "CAM_SET_ITEM2": "CAM_SET_TURN_AROUND",
- "CAM_SET_CIRCLE6": "CAM_SET_PIVOT_VERTICAL",
- "CAM_SET_DEMOC": "CAM_SET_CS_C",
- "CAM_SET_UO_FIBER": "CAM_SET_JABU_TENTACLE",
- "CAM_SET_TEPPEN": "CAM_SET_DIRECTED_YAW",
- "CAM_SET_CIRCLE7": "CAM_SET_PIVOT_FROM_SIDE",
-}
diff --git a/fast64_internal/oot/cutscene/importer/classes.py b/fast64_internal/oot/cutscene/importer/classes.py
deleted file mode 100644
index aa1ac254d..000000000
--- a/fast64_internal/oot/cutscene/importer/classes.py
+++ /dev/null
@@ -1,563 +0,0 @@
-import bpy
-import re
-
-from dataclasses import dataclass
-from typing import Optional, TYPE_CHECKING
-from bpy.types import Object, Armature
-from ....utility import PluginError
-from ..motion.utility import setupCutscene, getBlenderPosition, getInteger
-
-if TYPE_CHECKING:
- from ..properties import OOTCSListProperty, OOTCutsceneProperty
-
-from ..constants import (
- ootCSLegacyToNewCmdNames,
- ootCSListCommands,
- ootCutsceneCommandsC,
- ootCSListEntryCommands,
- ootCSSingleCommands,
- ootCSListAndSingleCommands,
- cmdToClass,
-)
-
-from ..classes import (
- CutsceneCmdActorCueList,
- CutsceneCmdCamPoint,
- Cutscene,
- CutsceneObjectFactory,
-)
-
-
-@dataclass
-class ParsedCutscene:
- """Local class used to order the parsed cutscene properly"""
-
- csName: str
- csData: list[str] # contains every command lists or standalone ones like ``CS_TRANSITION()``
-
-
-@dataclass
-class PropertyData:
- listType: str
- subPropsData: dict[str, str]
- useEndFrame: bool
-
-
-@dataclass
-class CutsceneImport(CutsceneObjectFactory):
- """This class contains functions to create the new cutscene Blender data"""
-
- filePath: Optional[str] # used when importing from the panel
- fileData: Optional[str] # used when importing the cutscenes when importing a scene
- csName: Optional[str] # used when import a specific cutscene
-
- def getCmdParams(self, data: str, cmdName: str, paramNumber: int):
- """Returns the list of every parameter of the given command"""
-
- parenthesis = "(" if not cmdName.endswith("(") else ""
- data = data.strip().removeprefix(f"{cmdName}{parenthesis}").replace(" ", "").removesuffix(")")
- if "CS_FLOAT" in data:
- data = re.sub(r"CS_FLOAT\([a-fA-F0-9x]*,([0-9e+-.f]*)\)", r"\1", data, re.DOTALL)
- data = re.sub(r"CS_FLOAT\([a-fA-F0-9x]*,([0-9e+-.f]*)", r"\1", data, re.DOTALL)
- params = data.split(",")
- validTimeCmd = cmdName == "CS_TIME" and len(params) == 6 and paramNumber == 5
- if len(params) != paramNumber and not validTimeCmd:
- raise PluginError(
- f"ERROR: The number of expected parameters for `{cmdName}` "
- + "and the number of found ones is not the same!"
- )
- return params
-
- def getNewCutscene(self, csData: str, name: str):
- params = self.getCmdParams(csData, "CS_HEADER", Cutscene.paramNumber)
- return Cutscene(name, getInteger(params[0]), getInteger(params[1]))
-
- def getParsedCutscenes(self):
- """Returns the parsed commands read from every cutscene we can find"""
-
- fileData = ""
-
- if self.fileData is not None:
- fileData = self.fileData
- elif self.filePath is not None:
- with open(self.filePath, "r") as inputFile:
- fileData = inputFile.read()
- else:
- raise PluginError("ERROR: File data can't be found!")
-
- # replace old names
- oldNames = list(ootCSLegacyToNewCmdNames.keys())
- fileData = fileData.replace("CS_CMD_CONTINUE", "CS_CAM_CONTINUE")
- fileData = fileData.replace("CS_CMD_STOP", "CS_CAM_STOP")
- for oldName in oldNames:
- fileData = fileData.replace(f"{oldName}(", f"{ootCSLegacyToNewCmdNames[oldName]}(")
-
- # make a list of existing cutscene names, to skip importing them if found
- existingCutsceneNames = [
- csObj.name.removeprefix("Cutscene.")
- for csObj in bpy.data.objects
- if csObj.type == "EMPTY" and csObj.ootEmptyType == "Cutscene"
- ]
-
- fileLines: list[str] = []
- for line in fileData.split("\n"):
- fileLines.append(line.strip())
-
- # parse cutscenes
- csData = []
- cutsceneList: list[list[str]] = []
- foundCutscene = False
- for line in fileLines:
- if not line.startswith("//") and not line.startswith("/*"):
- if "CutsceneData " in line:
- # split with "[" just in case the array has a set size
- csName = line.split(" ")[1].split("[")[0]
- if csName in existingCutsceneNames:
- print(f"WARNING: Cutscene '{csName}' already exists in this blend's data.")
- foundCutscene = True
- print(f"INFO: Found cutscene '{csName}' in the file data.")
-
- if foundCutscene:
- sLine = line.strip()
- csCmd = sLine.split("(")[0]
- if "CutsceneData " not in line and "};" not in line and csCmd not in ootCutsceneCommandsC:
- if len(csData) > 0:
- csData[-1] += line
-
- if len(csData) == 0 or sLine.startswith("CS_") and not sLine.startswith("CS_FLOAT"):
- if self.csName is None or self.csName == csName:
- csData.append(line)
-
- if "};" in line:
- foundCutscene = False
- if len(csData) > 0:
- cutsceneList.append(csData)
- csData = []
-
- if len(cutsceneList) == 0:
- print("INFO: Found no cutscenes in this file!")
- return None
-
- # parse the commands from every cutscene we found
- parsedCutscenes: list[ParsedCutscene] = []
- for cutscene in cutsceneList:
- cmdListFound = False
- curCmdPrefix = None
- parsedCS = []
- parsedData = ""
- csName = None
-
- for line in cutscene:
- curCmd = line.strip().split("(")[0]
- index = cutscene.index(line) + 1
- nextCmd = cutscene[index].strip().split("(")[0] if index < len(cutscene) else None
- line = line.strip()
- if "CutsceneData" in line:
- csName = line.split(" ")[1][:-2]
-
- # NOTE: ``CS_UNK_DATA()`` are commands that are completely useless, so we're ignoring those
- if csName is not None and not "CS_UNK_DATA" in curCmd:
- if curCmd in ootCutsceneCommandsC:
- line = line.removesuffix(",") + "\n"
-
- if curCmd in ootCSSingleCommands and curCmd != "CS_END_OF_SCRIPT":
- parsedData += line
-
- if not cmdListFound and curCmd in ootCSListCommands:
- cmdListFound = True
- parsedData = ""
-
- # camera and lighting have "non-standard" list names
- if curCmd.startswith("CS_CAM"):
- curCmdPrefix = "CS_CAM"
- elif curCmd.startswith("CS_LIGHT") or curCmd.startswith("L_CS_LIGHT"):
- curCmdPrefix = "CS_LIGHT"
- else:
- curCmdPrefix = curCmd[:-5]
-
- if curCmdPrefix is not None:
- if curCmdPrefix in curCmd:
- parsedData += line
- elif not cmdListFound and curCmd in ootCSListEntryCommands:
- print(f"{csName}, command:\n{line}")
- raise PluginError(f"ERROR: Found a list entry outside a list inside ``{csName}``!")
-
- if cmdListFound and nextCmd == "CS_END_OF_SCRIPT" or nextCmd in ootCSListAndSingleCommands:
- cmdListFound = False
- parsedCS.append(parsedData)
- parsedData = ""
- elif not "CutsceneData" in curCmd and not "};" in curCmd:
- print(f"WARNING: Unknown command found: ``{curCmd}``")
- cmdListFound = False
-
- if csName is not None and len(parsedCS) > 0:
- parsedCutscenes.append(ParsedCutscene(csName, parsedCS))
- else:
- raise PluginError("ERROR: Something wrong happened during the parsing of the cutscene.")
-
- return parsedCutscenes
-
- def getCutsceneList(self):
- """Returns the list of cutscenes with the data processed"""
-
- parsedCutscenes = self.getParsedCutscenes()
-
- if parsedCutscenes is None:
- # if it's none then there's no cutscene in the file
- return None
-
- if len(parsedCutscenes) == 0:
- raise PluginError("ERROR: No cutscene was found.")
-
- cutsceneList: list[Cutscene] = []
-
- # for each cutscene from the list returned by getParsedCutscenes(),
- # create classes containing the cutscene's informations
- # that will be used later when creating Blender objects to complete the import
- for parsedCS in parsedCutscenes:
- cutscene = None
- for data in parsedCS.csData:
- cmdData = data.removesuffix("\n").split("\n")
- cmdListData = cmdData.pop(0)
- cmdListName = cmdListData.strip().split("(")[0]
-
- # create a new cutscene data
- if cmdListName == "CS_HEADER":
- cutscene = self.getNewCutscene(data, parsedCS.csName)
-
- # if we have a cutscene, create and add the commands data in it
- elif cutscene is not None and data.startswith(f"{cmdListName}("):
- isPlayer = cmdListData.startswith("CS_PLAYER_CUE_LIST(")
- isStartSeq = cmdListData.startswith("CS_START_SEQ_LIST(")
- isStopSeq = cmdListData.startswith("CS_STOP_SEQ_LIST(")
-
- cmd = cmdToClass.get(cmdListName)
- if cmd is not None:
- cmdList = getattr(cutscene, "playerCueList" if isPlayer else cmd.listName)
-
- paramNumber = cmd.paramNumber - 1 if isPlayer else cmd.paramNumber
- params = self.getCmdParams(cmdListData, cmdListName, paramNumber)
- if isStartSeq or isStopSeq:
- commandData = cmd(params, type="start" if isStartSeq else "stop")
- elif cmdListData.startswith("CS_ACTOR_CUE_LIST(") or isPlayer:
- commandData = cmd(params, isPlayer=isPlayer)
- else:
- commandData = cmd(params)
-
- if cmdListName != "CS_TRANSITION" and cmdListName != "CS_DESTINATION":
- foundEndCmd = False
- for d in cmdData:
- cmdEntryName = d.strip().split("(")[0]
- isLegacy = d.startswith("L_")
- if isLegacy:
- cmdEntryName = cmdEntryName.removeprefix("L_")
- d = d.removeprefix("L_")
-
- if "CAM" in cmdListName:
- flag = d.removeprefix("CS_CAM_POINT(").split(",")[0]
- if foundEndCmd:
- raise PluginError("ERROR: More camera commands after last one!")
- foundEndCmd = "CS_CAM_STOP" in flag or "-1" in flag
-
- entryCmd = cmdToClass[cmdEntryName]
- params = self.getCmdParams(d, cmdEntryName, entryCmd.paramNumber)
-
- if "CS_LIGHT_SETTING(" in d or isStartSeq or isStopSeq:
- listEntry = entryCmd(params, isLegacy=isLegacy)
- else:
- listEntry = entryCmd(params)
- commandData.entries.append(listEntry)
- if cmdListName == "CS_DESTINATION":
- cutscene.destination = commandData
- else:
- cmdList.append(commandData)
- else:
- print(f"WARNING: `{cmdListName}` is not implemented yet!")
-
- # after processing the commands we can add the cutscene to the cutscene list
- if cutscene is not None:
- cutsceneList.append(cutscene)
- return cutsceneList
-
- def setActorCueData(self, csObj: Object, actorCueList: list[CutsceneCmdActorCueList], cueName: str, csNbr: int):
- """Creates the objects from the Actor Cue List data"""
-
- cueObjList = []
- cueEndFrames = []
- for i, entry in enumerate(actorCueList, 1):
- if len(entry.entries) == 0:
- raise PluginError("ERROR: Actor Cue List does not have any Actor Cue!")
-
- lastFrame = lastPos = None
- actorCueListObj = self.getNewActorCueListObject(
- f"CS_{csNbr:02}.{cueName} Cue List {i:02}", entry.commandType, csObj
- )
-
- for j, actorCue in enumerate(entry.entries, 1):
- if lastFrame is not None and lastFrame != actorCue.startFrame:
- raise PluginError("ERROR: Actor Cues are not temporally continuous!")
-
- if lastPos is not None and lastPos != actorCue.startPos:
- raise PluginError("ERROR: Actor Cues are not spatially continuous!")
-
- cueObjList.append(
- self.getNewActorCueObject(
- f"CS_{csNbr:02}.{cueName} Cue {i}.{j:02}",
- actorCue.startFrame,
- actorCue.actionID,
- actorCue.startPos,
- actorCue.rot,
- actorCueListObj,
- )
- )
- lastFrame = actorCue.endFrame
- lastPos = actorCue.endPos
- cueEndFrames.append(lastFrame)
-
- # we need a dummy actor cue to get the end position of the last real one
- if lastFrame is not None:
- cueObjList.append(
- self.getNewActorCueObject(
- f"CS_{csNbr:02}.{cueName} Cue {i}.999 (D)",
- lastFrame,
- "DUMMY",
- lastPos,
- actorCue.rot,
- actorCueListObj,
- )
- )
- cueEndFrames.append(lastFrame + 1)
-
- # updating the end frames
- if len(cueEndFrames) != len(cueObjList):
- raise PluginError("ERROR: Lists lengths do not match!")
-
- for obj, endFrame in zip(cueObjList, cueEndFrames):
- # reading this value will trigger the "get" function
- getEndFrame = obj.ootCSMotionProperty.actorCueProp.cueEndFrame
-
- if endFrame != getEndFrame and obj.ootEmptyType != "CS Dummy Cue":
- print(f"WARNING: `{obj.name}`'s end frame do not match the one from the script!")
-
- def validateCameraData(self, cutscene: Cutscene):
- """Safety checks to make sure the camera data is correct"""
-
- camLists: list[tuple[str, list, list]] = [
- ("Eye and AT Spline", cutscene.camEyeSplineList, cutscene.camATSplineList),
- ("Eye and AT Spline Rel to Player", cutscene.camEyeSplineRelPlayerList, cutscene.camATSplineRelPlayerList),
- ("Eye and AT", cutscene.camEyeList, cutscene.camATList),
- ]
-
- for camType, eyeList, atList in camLists:
- for eyeListEntry, atListEntry in zip(eyeList, atList):
- eyeTotal = len(eyeListEntry.entries)
- atTotal = len(atListEntry.entries)
-
- # Eye -> bone's head, AT -> bone's tail, that's why both lists requires the same length
- if eyeTotal != atTotal:
- raise PluginError(f"ERROR: Found {eyeTotal} Eye lists but {atTotal} AT lists in {camType}!")
-
- if eyeTotal < 4:
- raise PluginError(f"ERROR: Only {eyeTotal} cam point in this command!")
-
- if eyeTotal > 4:
- # NOTE: There is a bug in the game where when incrementing to the next set of key points,
- # the key point which checked for whether it's the last point or not is the last point
- # of the next set, not the last point of the old set. This means we need to remove
- # the extra point at the end that will only tell the game that this camera shot stops.
- del eyeListEntry.entries[-1]
- del atListEntry.entries[-1]
-
- def setBoneData(
- self, cameraShotObj: Object, boneData: list[tuple[CutsceneCmdCamPoint, CutsceneCmdCamPoint]], csNbr: int
- ):
- """Creates the bones from the Camera Point data"""
-
- scale = bpy.context.scene.ootBlenderScale
- for i, (eyePoint, atPoint) in enumerate(boneData, 1):
- # we need the edit mode to be able to change the bone's location
- bpy.ops.object.mode_set(mode="EDIT")
- armatureData: Armature = cameraShotObj.data
- boneName = f"CS_{csNbr:02}.Camera Point {i:02}"
- newEditBone = armatureData.edit_bones.new(boneName)
- newEditBone.head = getBlenderPosition(eyePoint.pos, scale)
- newEditBone.tail = getBlenderPosition(atPoint.pos, scale)
- bpy.ops.object.mode_set(mode="OBJECT")
- newBone = armatureData.bones[boneName]
-
- if eyePoint.frame != 0:
- print("WARNING: Frames must be 0!")
-
- # using the "AT" (look-at) data since this is what determines where the camera is looking
- # the "Eye" only sets the location of the camera
- newBone.ootCamShotPointProp.shotPointFrame = atPoint.frame
- newBone.ootCamShotPointProp.shotPointViewAngle = atPoint.viewAngle
- newBone.ootCamShotPointProp.shotPointRoll = atPoint.camRoll
-
- def setCameraShotData(
- self, csObj: Object, eyePoints: list, atPoints: list, camMode: str, startIndex: int, csNbr: int
- ):
- """Creates the armatures from the Camera Shot data"""
-
- endIndex = 0
-
- # this is required to be able to change the object mode
- if bpy.context.mode != "OBJECT":
- bpy.ops.object.mode_set(mode="OBJECT")
-
- for i, (camEyeSpline, camATSpline) in enumerate(zip(eyePoints, atPoints), startIndex):
- cameraShotObj = self.getNewArmatureObject(f"CS_{csNbr:02}.Camera Shot {i:02}", True, csObj)
-
- if camEyeSpline.endFrame < camEyeSpline.startFrame + 2 or camATSpline.endFrame < camATSpline.startFrame + 2:
- print("WARNING: Non-standard end frame")
-
- cameraShotObj.data.ootCamShotProp.shotStartFrame = camEyeSpline.startFrame
- cameraShotObj.data.ootCamShotProp.shotCamMode = camMode
- boneData = [(eyePoint, atPoint) for eyePoint, atPoint in zip(camEyeSpline.entries, camATSpline.entries)]
- self.setBoneData(cameraShotObj, boneData, csNbr)
- endIndex = i
-
- return endIndex + 1
-
- def setPropOrCustom(self, prop, propName: str, value):
- try:
- setattr(prop, propName, value)
- except TypeError:
- setattr(prop, propName, "Custom")
- setattr(prop, f"{propName}Custom", value)
-
- def setSubPropertyData(self, subPropsData: dict[str, str], newSubElem, entry):
- customNames = [
- "csMiscType",
- "csTextType",
- "ocarinaAction",
- "transitionType",
- "csSeqID",
- "csSeqPlayer",
- "transition",
- ]
-
- for key, value in subPropsData.items():
- if value is not None:
- if key in customNames:
- valueToSet = getattr(entry, value)
- self.setPropOrCustom(newSubElem, key, valueToSet)
- else:
- setattr(newSubElem, key, getattr(entry, value))
-
- def setPropertyData(self, csProp: "OOTCutsceneProperty", cutscene: Cutscene, propDataList: list[PropertyData]):
- for data in propDataList:
- listName = f"{data.listType[0].lower() + data.listType[1:]}List"
- dataList = getattr(cutscene, (listName if data.listType != "FadeOutSeq" else "fadeSeqList"))
- for list in dataList:
- newElem: "OOTCSListProperty" = csProp.csLists.add()
-
- if data.listType == "Seq":
- type = "StartSeqList" if list.type == "start" else "StopSeqList"
- else:
- type = f"{data.listType}List" if data.listType != "Transition" else data.listType
- newElem.listType = type
-
- if data.listType == "Transition":
- newElem.transitionStartFrame = list.startFrame
- newElem.transitionEndFrame = list.endFrame
- self.setSubPropertyData(data.subPropsData, newElem, list)
- else:
- list.entries.sort(key=lambda elem: elem.startFrame)
- for entry in list.entries:
- newSubElem = getattr(newElem, "seqList" if "fadeOut" in listName else listName).add()
- newSubElem.startFrame = entry.startFrame
-
- if data.useEndFrame:
- newSubElem.endFrame = entry.endFrame
-
- if data.listType == "Text":
- self.setPropOrCustom(newSubElem, "textboxType", entry.id)
- match entry.id:
- case "Text":
- newSubElem.textID = f"0x{entry.textId:04X}"
- self.setPropOrCustom(newSubElem, "csTextType", entry.type)
- case "None":
- pass
- case "OcarinaAction":
- newSubElem.ocarinaMessageId = f"0x{entry.messageId:04X}"
- self.setPropOrCustom(newSubElem, "ocarinaAction", entry.ocarinaActionId)
- case _:
- raise PluginError("ERROR: Unknown text type!")
- self.setSubPropertyData(data.subPropsData, newSubElem, entry)
-
- def setCutsceneData(self, csNumber):
- """Creates the cutscene empty objects from the file data"""
-
- cutsceneList = self.getCutsceneList()
-
- if cutsceneList is None:
- # if it's none then there's no cutscene in the file
- return csNumber
-
- if len(cutsceneList) == 0:
- raise PluginError("ERROR: No cutscene was found.")
-
- for i, cutscene in enumerate(cutsceneList, csNumber):
- print(f'Found Cutscene "{cutscene.name}"! Importing...')
- self.validateCameraData(cutscene)
- csName = f"Cutscene.{cutscene.name}"
- csObj = self.getNewCutsceneObject(csName, cutscene.frameCount, None)
- csProp = csObj.ootCutsceneProperty
- csNumber = i
-
- self.setActorCueData(csObj, cutscene.actorCueList, "Actor", i)
- self.setActorCueData(csObj, cutscene.playerCueList, "Player", i)
-
- if len(cutscene.camEyeSplineList) > 0:
- lastIndex = self.setCameraShotData(
- csObj, cutscene.camEyeSplineList, cutscene.camATSplineList, "splineEyeOrAT", 1, i
- )
-
- if len(cutscene.camEyeSplineRelPlayerList) > 0:
- lastIndex = self.setCameraShotData(
- csObj,
- cutscene.camEyeSplineRelPlayerList,
- cutscene.camATSplineRelPlayerList,
- "splineEyeOrATRelPlayer",
- lastIndex,
- i,
- )
-
- if len(cutscene.camEyeList) > 0:
- lastIndex = self.setCameraShotData(
- csObj, cutscene.camEyeList, cutscene.camATList, "eyeOrAT", lastIndex, i
- )
-
- if cutscene.destination is not None:
- csProp.csUseDestination = True
- csProp.csDestinationStartFrame = cutscene.destination.startFrame
- self.setPropOrCustom(csProp, "csDestination", cutscene.destination.id)
-
- propDataList = [
- PropertyData("Text", {"textboxType": "id"}, True),
- PropertyData("Misc", {"csMiscType": "type"}, True),
- PropertyData("Transition", {"transitionType": "type"}, True),
- PropertyData("LightSettings", {"lightSettingsIndex": "lightSetting"}, False),
- PropertyData("Time", {"hour": "hour", "minute": "minute"}, False),
- PropertyData("Seq", {"csSeqID": "seqId"}, False),
- PropertyData("FadeOutSeq", {"csSeqPlayer": "seqPlayer"}, True),
- PropertyData(
- "Rumble",
- {
- "rumbleSourceStrength": "sourceStrength",
- "rumbleDuration": "duration",
- "rumbleDecreaseRate": "decreaseRate",
- },
- False,
- ),
- ]
- self.setPropertyData(csProp, cutscene, propDataList)
-
- # Init camera + preview objects and setup the scene
- setupCutscene(csObj)
- bpy.ops.object.select_all(action="DESELECT")
- print("Success!")
-
- # ``csNumber`` makes sure there's no duplicates
- return csNumber + 1
diff --git a/fast64_internal/oot/cutscene/importer/functions.py b/fast64_internal/oot/cutscene/importer/functions.py
deleted file mode 100644
index 20e562f76..000000000
--- a/fast64_internal/oot/cutscene/importer/functions.py
+++ /dev/null
@@ -1,11 +0,0 @@
-import bpy
-
-from typing import Optional
-from .classes import CutsceneImport
-
-
-def importCutsceneData(filePath: Optional[str], sceneData: Optional[str], csName: Optional[str] = None):
- """Initialises and imports the cutscene data from either a file or the scene data"""
- # NOTE: ``sceneData`` is the data read when importing a scene
- csMotionImport = CutsceneImport(filePath, sceneData, csName)
- return csMotionImport.setCutsceneData(bpy.context.scene.ootCSNumber)
diff --git a/fast64_internal/oot/data/__init__.py b/fast64_internal/oot/data/__init__.py
deleted file mode 100644
index ce6e88b62..000000000
--- a/fast64_internal/oot/data/__init__.py
+++ /dev/null
@@ -1,2 +0,0 @@
-from .oot_data import OoT_Data
-from .oot_object_data import OoT_ObjectData
diff --git a/fast64_internal/oot/data/oot_data.py b/fast64_internal/oot/data/oot_data.py
deleted file mode 100644
index c4619a925..000000000
--- a/fast64_internal/oot/data/oot_data.py
+++ /dev/null
@@ -1,23 +0,0 @@
-from dataclasses import dataclass
-
-
-@dataclass
-class OoT_BaseElement:
- id: str
- key: str
- name: str
- index: int
-
-
-@dataclass
-class OoT_Data:
- """Contains data related to OoT, like actors or objects"""
-
- def __init__(self):
- from .oot_enum_data import OoT_EnumData
- from .oot_object_data import OoT_ObjectData
- from .oot_actor_data import OoT_ActorData
-
- self.enumData = OoT_EnumData()
- self.objectData = OoT_ObjectData()
- self.actorData = OoT_ActorData()
diff --git a/fast64_internal/oot/data/oot_enum_data.py b/fast64_internal/oot/data/oot_enum_data.py
deleted file mode 100644
index 0644c6f4b..000000000
--- a/fast64_internal/oot/data/oot_enum_data.py
+++ /dev/null
@@ -1,123 +0,0 @@
-from dataclasses import dataclass, field
-from os import path
-from .oot_getters import getXMLRoot
-from .oot_data import OoT_BaseElement
-
-# Note: "enumData" in this context refers to an OoT Object file (like ``gameplay_keep``)
-
-
-@dataclass
-class OoT_ItemElement(OoT_BaseElement):
- parentKey: str
-
- def __post_init__(self):
- # generate the name from the id
-
- if self.name is None:
- keyToPrefix = {
- "csCmd": "CS_CMD",
- "csMiscType": "CS_MISC",
- "csTextType": "CS_TEXT",
- "csFadeOutSeqPlayer": "CS_FADE_OUT",
- "csTransitionType": "CS_TRANS",
- "csDestination": "CS_DEST",
- "csPlayerCueId": "PLAYER_CUEID",
- "naviQuestHintType": "NAVI_QUEST_HINTS",
- "ocarinaSongActionId": "OCARINA_ACTION",
- }
-
- self.name = self.id.removeprefix(f"{keyToPrefix[self.parentKey]}_")
-
- if self.parentKey in ["csCmd", "csPlayerCueId"]:
- split = self.name.split("_")
- if self.parentKey == "csCmd" and "ACTOR_CUE" in self.id:
- self.name = f"Actor Cue {split[-2]}_{split[-1]}"
- else:
- self.name = f"Player Cue Id {split[-1]}"
- else:
- self.name = self.name.replace("_", " ").title()
-
-
-@dataclass
-class OoT_EnumElement(OoT_BaseElement):
- items: list[OoT_ItemElement]
- itemByKey: dict[str, OoT_ItemElement] = field(default_factory=dict)
- itemByIndex: dict[int, OoT_ItemElement] = field(default_factory=dict)
- itemById: dict[int, OoT_ItemElement] = field(default_factory=dict)
-
- def __post_init__(self):
- self.itemByKey = {item.key: item for item in self.items}
- self.itemByIndex = {item.index: item for item in self.items}
- self.itemById = {item.id: item for item in self.items}
-
-
-class OoT_EnumData:
- """Cutscene and misc enum data"""
-
- def __init__(self):
- # general enumData list
- self.enumDataList: list[OoT_EnumElement] = []
-
- # Path to the ``EnumData.xml`` file
- enumDataXML = path.dirname(path.abspath(__file__)) + "/xml/EnumData.xml"
- enumDataRoot = getXMLRoot(enumDataXML)
-
- for enum in enumDataRoot.iterfind("Enum"):
- self.enumDataList.append(
- OoT_EnumElement(
- enum.attrib["ID"],
- enum.attrib["Key"],
- None,
- None,
- [
- OoT_ItemElement(
- item.attrib["ID"],
- item.attrib["Key"],
- # note: the name sets automatically after the init if None
- item.attrib["Name"] if enum.attrib["Key"] == "seqId" else None,
- int(item.attrib["Index"]),
- enum.attrib["Key"],
- )
- for item in enum
- ],
- )
- )
-
- # create list of tuples used by Blender's enum properties
- self.deletedEntry = ("None", "(Deleted from the XML)", "None")
-
- self.ootEnumCsCmd: list[tuple[str, str, str]] = []
- self.ootEnumCsMiscType: list[tuple[str, str, str]] = []
- self.ootEnumCsTextType: list[tuple[str, str, str]] = []
- self.ootEnumCsFadeOutSeqPlayer: list[tuple[str, str, str]] = []
- self.ootEnumCsTransitionType: list[tuple[str, str, str]] = []
- self.ootEnumCsDestination: list[tuple[str, str, str]] = []
- self.ootEnumCsPlayerCueId: list[tuple[str, str, str]] = []
- self.ootEnumNaviQuestHintType: list[tuple[str, str, str]] = []
- self.ootEnumOcarinaSongActionId: list[tuple[str, str, str]] = []
- self.ootEnumSeqId: list[tuple[str, str, str]] = []
-
- self.enumByID = {enum.id: enum for enum in self.enumDataList}
- self.enumByKey = {enum.key: enum for enum in self.enumDataList}
-
- for key in self.enumByKey.keys():
- setattr(self, "ootEnum" + key[0].upper() + key[1:], self.getOoTEnumData(key))
-
- def getOoTEnumData(self, enumKey: str):
- enum = self.enumByKey[enumKey]
- firstIndex = min(1, *(item.index for item in enum.items))
- lastIndex = max(1, *(item.index for item in enum.items)) + 1
- enumData = [self.deletedEntry] * lastIndex
- custom = ("Custom", "Custom", "Custom")
-
- for item in enum.items:
- if item.index < lastIndex:
- identifier = item.key
- enumData[item.index] = (identifier, item.name, item.id)
-
- if firstIndex > 0:
- enumData[0] = custom
- else:
- enumData.insert(0, custom)
-
- return enumData
diff --git a/fast64_internal/oot/data/oot_object_data.py b/fast64_internal/oot/data/oot_object_data.py
deleted file mode 100644
index 28970a80c..000000000
--- a/fast64_internal/oot/data/oot_object_data.py
+++ /dev/null
@@ -1,55 +0,0 @@
-from dataclasses import dataclass
-from os import path
-from ...utility import PluginError
-from .oot_getters import getXMLRoot
-from .oot_data import OoT_BaseElement
-
-# Note: "object" in this context refers to an OoT Object file (like ``gameplay_keep``)
-
-
-@dataclass
-class OoT_ObjectElement(OoT_BaseElement):
- pass
-
-
-class OoT_ObjectData:
- """Everything related to OoT objects"""
-
- def __init__(self):
- # general object list
- self.objectList: list[OoT_ObjectElement] = []
-
- # Path to the ``ObjectList.xml`` file
- objectXML = path.dirname(path.abspath(__file__)) + "/xml/ObjectList.xml"
- objectRoot = getXMLRoot(objectXML)
-
- for obj in objectRoot.iterfind("Object"):
- objName = f"{obj.attrib['Name']} - {obj.attrib['ID'].removeprefix('OBJECT_')}"
- self.objectList.append(
- OoT_ObjectElement(obj.attrib["ID"], obj.attrib["Key"], objName, int(obj.attrib["Index"]))
- )
-
- self.objectsByID = {obj.id: obj for obj in self.objectList}
- self.objectsByKey = {obj.key: obj for obj in self.objectList}
-
- # list of tuples used by Blender's enum properties
- self.deletedEntry = ("None", "(Deleted from the XML)", "None")
- lastIndex = max(1, *(obj.index for obj in self.objectList))
- self.ootEnumObjectKey = self.getObjectIDList(lastIndex + 1, False)
-
- # create the legacy object list for old blends
- self.ootEnumObjectIDLegacy = self.getObjectIDList(self.objectsByKey["obj_timeblock"].index + 1, True)
-
- # validate the legacy list, if there's any None element then something's wrong
- if self.deletedEntry in self.ootEnumObjectIDLegacy:
- raise PluginError("ERROR: Legacy Object List doesn't match!")
-
- def getObjectIDList(self, max: int, isLegacy: bool):
- """Generates and returns the object list in the right order"""
- objList = [self.deletedEntry] * max
- for obj in self.objectList:
- if obj.index < max:
- identifier = obj.id if isLegacy else obj.key
- objList[obj.index] = (identifier, obj.name, obj.id)
- objList[0] = ("Custom", "Custom Object", "Custom")
- return objList
diff --git a/fast64_internal/oot/exporter/cutscene/__init__.py b/fast64_internal/oot/exporter/cutscene/__init__.py
deleted file mode 100644
index a9d8c5b1a..000000000
--- a/fast64_internal/oot/exporter/cutscene/__init__.py
+++ /dev/null
@@ -1,141 +0,0 @@
-import bpy
-
-from dataclasses import dataclass, field
-from typing import Optional
-from bpy.types import Object
-from ....utility import PluginError, CData, indent
-from ...oot_utility import getCustomProperty
-from ...scene.properties import OOTSceneHeaderProperty
-from .data import CutsceneData
-
-
-# NOTE: ``paramNumber`` is the expected number of parameters inside the parsed commands,
-# this account for the unused parameters. Every classes are based on the commands arguments from ``z64cutscene_commands.h``
-
-# NOTE: ``params`` is the list of parsed parameters, it can't be ``None`` if we're importing a scene,
-# when it's ``None`` it will get the data from the cutscene objects
-
-
-@dataclass
-class Cutscene:
- """This class defines a cutscene, including its data and its informations"""
-
- name: str
- data: CutsceneData
- totalEntries: int
- frameCount: int
- useMacros: bool
- motionOnly: bool
-
- paramNumber: int = field(init=False, default=2)
-
- @staticmethod
- def new(name: Optional[str], csObj: Optional[Object], useMacros: bool, motionOnly: bool):
- # when csObj is None it means we're in import context
- if csObj is not None:
- if name is None:
- name = csObj.name.removeprefix("Cutscene.").replace(".", "_")
- data = CutsceneData.new(csObj, useMacros, motionOnly)
- return Cutscene(name, data, data.totalEntries, data.frameCount, useMacros, motionOnly)
-
- def getC(self):
- """Returns the cutscene data"""
-
- if self.data is not None:
- csData = CData()
- declarationBase = f"CutsceneData {self.name}[]"
-
- # this list's order defines the order of the commands in the cutscene array
- dataListNames = []
-
- if not self.motionOnly:
- dataListNames = [
- "textList",
- "miscList",
- "rumbleList",
- "transitionList",
- "lightSettingsList",
- "timeList",
- "seqList",
- "fadeSeqList",
- ]
-
- dataListNames.extend(
- [
- "playerCueList",
- "actorCueList",
- "camEyeSplineList",
- "camATSplineList",
- "camEyeSplineRelPlayerList",
- "camATSplineRelPlayerList",
- "camEyeList",
- "camATList",
- ]
- )
-
- if self.data.motionFrameCount > self.frameCount:
- self.frameCount += self.data.motionFrameCount - self.frameCount
-
- # .h
- csData.header = f"extern {declarationBase};\n"
-
- # .c
- csData.source = (
- declarationBase
- + " = {\n"
- + (indent + f"CS_HEADER({self.totalEntries}, {self.frameCount}),\n")
- + (self.data.destination.getCmd() if self.data.destination is not None else "")
- + "".join(entry.getCmd() for curList in dataListNames for entry in getattr(self.data, curList))
- + (indent + "CS_END_OF_SCRIPT(),\n")
- + "};\n\n"
- )
-
- return csData
- else:
- raise PluginError("ERROR: CutsceneData not initialised!")
-
-
-@dataclass
-class SceneCutscene:
- """This class hosts cutscene data"""
-
- entries: list[Cutscene]
-
- @staticmethod
- def new(props: OOTSceneHeaderProperty, headerIndex: int, useMacros: bool):
- csObj: Object = props.csWriteObject
- cutsceneObjects: list[Object] = [csObj for csObj in props.extraCutscenes]
- entries: list[Cutscene] = []
-
- if headerIndex > 0 and len(cutsceneObjects) > 0:
- raise PluginError("ERROR: Extra cutscenes can only belong to the main header!")
-
- cutsceneObjects.insert(0, csObj)
- for csObj in cutsceneObjects:
- if csObj is not None:
- if csObj.ootEmptyType != "Cutscene":
- raise PluginError(
- "ERROR: Object selected as cutscene is wrong type, must be empty with Cutscene type"
- )
- elif csObj.parent is not None:
- raise PluginError("ERROR: Cutscene empty object should not be parented to anything")
-
- writeType = props.csWriteType
- csWriteCustom = None
- if writeType == "Custom":
- csWriteCustom = getCustomProperty(props, "csWriteCustom")
-
- if props.writeCutscene:
- # if csWriteCustom is None then the name will auto-set from the csObj passed in the class
- entries.append(
- Cutscene.new(csWriteCustom, csObj, useMacros, bpy.context.scene.fast64.oot.exportMotionOnly)
- )
- return SceneCutscene(entries)
-
- def getCmd(self):
- """Returns the cutscene data scene command"""
- if len(self.entries) == 0:
- raise PluginError("ERROR: Cutscene entry list is empty!")
-
- # entry No. 0 is always self.csObj
- return indent + f"SCENE_CMD_CUTSCENE_DATA({self.entries[0].name}),\n"
diff --git a/fast64_internal/oot/exporter/cutscene/data.py b/fast64_internal/oot/exporter/cutscene/data.py
deleted file mode 100644
index 25a92db7d..000000000
--- a/fast64_internal/oot/exporter/cutscene/data.py
+++ /dev/null
@@ -1,413 +0,0 @@
-import bpy
-import math
-
-from dataclasses import dataclass, field
-from typing import TYPE_CHECKING
-from bpy.types import Object, Bone
-from ....utility import PluginError
-from ...oot_constants import ootData
-from .actor_cue import CutsceneCmdActorCueList, CutsceneCmdActorCue
-from .seq import CutsceneCmdStartStopSeqList, CutsceneCmdFadeSeqList, CutsceneCmdStartStopSeq, CutsceneCmdFadeSeq
-from .text import CutsceneCmdTextList, CutsceneCmdText, CutsceneCmdTextNone, CutsceneCmdTextOcarinaAction
-
-from .misc import (
- CutsceneCmdLightSetting,
- CutsceneCmdTime,
- CutsceneCmdMisc,
- CutsceneCmdRumbleController,
- CutsceneCmdDestination,
- CutsceneCmdMiscList,
- CutsceneCmdRumbleControllerList,
- CutsceneCmdTransition,
- CutsceneCmdLightSettingList,
- CutsceneCmdTimeList,
-)
-
-from .camera import (
- CutsceneCmdCamPoint,
- CutsceneCmdCamEyeSpline,
- CutsceneCmdCamATSpline,
- CutsceneCmdCamEyeSplineRelToPlayer,
- CutsceneCmdCamATSplineRelToPlayer,
- CutsceneCmdCamEye,
- CutsceneCmdCamAT,
-)
-
-if TYPE_CHECKING:
- from ...cutscene.properties import OOTCutsceneProperty, OOTCSTextProperty
-
-
-cmdToClass = {
- "TextList": CutsceneCmdTextList,
- "LightSettingsList": CutsceneCmdLightSettingList,
- "TimeList": CutsceneCmdTimeList,
- "MiscList": CutsceneCmdMiscList,
- "RumbleList": CutsceneCmdRumbleControllerList,
- "StartSeqList": CutsceneCmdStartStopSeqList,
- "StopSeqList": CutsceneCmdStartStopSeqList,
- "FadeOutSeqList": CutsceneCmdFadeSeqList,
- "StartSeq": CutsceneCmdStartStopSeq,
- "StopSeq": CutsceneCmdStartStopSeq,
- "FadeOutSeq": CutsceneCmdFadeSeq,
-}
-
-cmdToList = {
- "TextList": "textList",
- "LightSettingsList": "lightSettingsList",
- "TimeList": "timeList",
- "MiscList": "miscList",
- "RumbleList": "rumbleList",
-}
-
-
-@dataclass
-class CutsceneData:
- """This class defines the command data inside a cutscene"""
-
- useMacros: bool
- motionOnly: bool
-
- totalEntries: int = field(init=False, default=0)
- frameCount: int = field(init=False, default=0)
- motionFrameCount: int = field(init=False, default=0)
- camEndFrame: int = field(init=False, default=0)
- destination: CutsceneCmdDestination = field(init=False, default=None)
- actorCueList: list[CutsceneCmdActorCueList] = field(init=False, default_factory=list)
- playerCueList: list[CutsceneCmdActorCueList] = field(init=False, default_factory=list)
- camEyeSplineList: list[CutsceneCmdCamEyeSpline] = field(init=False, default_factory=list)
- camATSplineList: list[CutsceneCmdCamATSpline] = field(init=False, default_factory=list)
- camEyeSplineRelPlayerList: list[CutsceneCmdCamEyeSplineRelToPlayer] = field(init=False, default_factory=list)
- camATSplineRelPlayerList: list[CutsceneCmdCamATSplineRelToPlayer] = field(init=False, default_factory=list)
- camEyeList: list[CutsceneCmdCamEye] = field(init=False, default_factory=list)
- camATList: list[CutsceneCmdCamAT] = field(init=False, default_factory=list)
- textList: list[CutsceneCmdTextList] = field(init=False, default_factory=list)
- miscList: list[CutsceneCmdMiscList] = field(init=False, default_factory=list)
- rumbleList: list[CutsceneCmdRumbleControllerList] = field(init=False, default_factory=list)
- transitionList: list[CutsceneCmdTransition] = field(init=False, default_factory=list)
- lightSettingsList: list[CutsceneCmdLightSettingList] = field(init=False, default_factory=list)
- timeList: list[CutsceneCmdTimeList] = field(init=False, default_factory=list)
- seqList: list[CutsceneCmdStartStopSeqList] = field(init=False, default_factory=list)
- fadeSeqList: list[CutsceneCmdFadeSeqList] = field(init=False, default_factory=list)
-
- @staticmethod
- def new(csObj: Object, useMacros: bool, motionOnly: bool):
- csProp: "OOTCutsceneProperty" = csObj.ootCutsceneProperty
- csObjects = {
- "CS Actor Cue List": [],
- "CS Player Cue List": [],
- "camShot": [],
- }
-
- for obj in csObj.children_recursive:
- if obj.type == "EMPTY" and obj.ootEmptyType in csObjects.keys():
- csObjects[obj.ootEmptyType].append(obj)
- elif obj.type == "ARMATURE":
- csObjects["camShot"].append(obj)
- csObjects["camShot"].sort(key=lambda obj: obj.name)
-
- newCutsceneData = CutsceneData(useMacros, motionOnly)
- newCutsceneData.setCutsceneData(csObjects, csProp)
- return newCutsceneData
-
- def getOoTRotation(self, obj: Object):
- """Returns the converted Blender rotation"""
-
- def conv(r):
- r /= 2.0 * math.pi
- r -= math.floor(r)
- r = round(r * 0x10000)
-
- if r >= 0x8000:
- r += 0xFFFF0000
-
- assert r >= 0 and r <= 0xFFFFFFFF and (r <= 0x7FFF or r >= 0xFFFF8000)
-
- return hex(r & 0xFFFF)
-
- rotXYZ = [conv(obj.rotation_euler[0]), conv(obj.rotation_euler[2]), conv(obj.rotation_euler[1])]
- return [f"DEG_TO_BINANG({(int(rot, base=16) * (180 / 0x8000)):.3f})" for rot in rotXYZ]
-
- def getOoTPosition(self, pos):
- """Returns the converted Blender position"""
-
- scale = bpy.context.scene.ootBlenderScale
-
- x = round(pos[0] * scale)
- y = round(pos[2] * scale)
- z = round(-pos[1] * scale)
-
- if any(v < -0x8000 or v >= 0x8000 for v in (x, y, z)):
- raise RuntimeError(f"Position(s) too large, out of range: {x}, {y}, {z}")
-
- return [x, y, z]
-
- def getEnumValueFromProp(self, enumKey: str, owner, propName: str):
- item = ootData.enumData.enumByKey[enumKey].itemByKey.get(getattr(owner, propName))
- return item.id if item is not None else getattr(owner, f"{propName}Custom")
-
- def setActorCueListData(self, csObjects: dict[str, list[Object]], isPlayer: bool):
- """Returns the Actor Cue List commands from the corresponding objects"""
-
- playerOrActor = f"{'Player' if isPlayer else 'Actor'}"
- actorCueListObjects = csObjects[f"CS {playerOrActor} Cue List"]
- actorCueListObjects.sort(key=lambda o: o.ootCSMotionProperty.actorCueProp.cueStartFrame)
-
- self.totalEntries += len(actorCueListObjects)
- for obj in actorCueListObjects:
- entryTotal = len(obj.children)
-
- if entryTotal == 0:
- raise PluginError("ERROR: The Actor Cue List does not contain any child Actor Cue objects")
-
- if obj.children[-1].ootEmptyType != "CS Dummy Cue":
- # we need an extra point that won't be exported to get the real last cue's
- # end frame and end position
- raise PluginError("ERROR: The Actor Cue List is missing the extra dummy point!")
-
- commandType = obj.ootCSMotionProperty.actorCueListProp.commandType
-
- if commandType == "Custom":
- commandType = obj.ootCSMotionProperty.actorCueListProp.commandTypeCustom
- elif self.useMacros:
- commandType = ootData.enumData.enumByKey["csCmd"].itemByKey[commandType].id
-
- # ignoring dummy cue
- newActorCueList = CutsceneCmdActorCueList(None, None, isPlayer, commandType, entryTotal - 1)
-
- for i, childObj in enumerate(obj.children, 1):
- startFrame = childObj.ootCSMotionProperty.actorCueProp.cueStartFrame
- if i < len(obj.children) and childObj.ootEmptyType != "CS Dummy Cue":
- endFrame = obj.children[i].ootCSMotionProperty.actorCueProp.cueStartFrame
- actionID = None
-
- if isPlayer:
- cueID = childObj.ootCSMotionProperty.actorCueProp.playerCueID
- if cueID != "Custom":
- actionID = ootData.enumData.enumByKey["csPlayerCueId"].itemByKey[cueID].id
-
- if actionID is None:
- actionID = childObj.ootCSMotionProperty.actorCueProp.cueActionID
-
- newActorCueList.entries.append(
- CutsceneCmdActorCue(
- startFrame,
- endFrame,
- actionID,
- self.getOoTRotation(childObj),
- self.getOoTPosition(childObj.location),
- self.getOoTPosition(obj.children[i].location),
- isPlayer,
- )
- )
-
- self.actorCueList.append(newActorCueList)
-
- def getCameraShotPointData(self, bones: list[Bone], useAT: bool):
- """Returns the Camera Point data from the bone data"""
-
- shotPoints: list[CutsceneCmdCamPoint] = []
-
- if len(bones) < 4:
- raise RuntimeError("Camera Armature needs at least 4 bones!")
-
- for bone in bones:
- if bone.parent is not None:
- raise RuntimeError("Camera Armature bones are not allowed to have parent bones!")
-
- shotPoints.append(
- CutsceneCmdCamPoint(
- None,
- None,
- ("CS_CAM_CONTINUE" if self.useMacros else "0"),
- bone.ootCamShotPointProp.shotPointRoll if useAT else 0,
- bone.ootCamShotPointProp.shotPointFrame,
- bone.ootCamShotPointProp.shotPointViewAngle,
- self.getOoTPosition(bone.head if not useAT else bone.tail),
- )
- )
-
- # NOTE: because of the game's bug explained in the importer we need to add an extra dummy point when exporting
- shotPoints.append(
- CutsceneCmdCamPoint(None, None, "CS_CAM_STOP" if self.useMacros else "-1", 0, 0, 0.0, [0, 0, 0])
- )
- return shotPoints
-
- def getCamCmdFunc(self, camMode: str, useAT: bool):
- """Returns the camera get function depending on the camera mode"""
-
- camCmdFuncMap = {
- "splineEyeOrAT": self.getCamATSplineCmd if useAT else self.getCamEyeSplineCmd,
- "splineEyeOrATRelPlayer": self.getCamATSplineRelToPlayerCmd
- if useAT
- else self.getCamEyeSplineRelToPlayerCmd,
- "eyeOrAT": self.getCamATCmd if useAT else self.getCamEyeCmd,
- }
-
- return camCmdFuncMap[camMode]
-
- def getCamClassOrList(self, isClass: bool, camMode: str, useAT: bool):
- """Returns the camera dataclass depending on the camera mode"""
-
- # TODO: improve this
- if isClass:
- camCmdClassMap = {
- "splineEyeOrAT": CutsceneCmdCamATSpline if useAT else CutsceneCmdCamEyeSpline,
- "splineEyeOrATRelPlayer": CutsceneCmdCamATSplineRelToPlayer
- if useAT
- else CutsceneCmdCamEyeSplineRelToPlayer,
- "eyeOrAT": CutsceneCmdCamAT if useAT else CutsceneCmdCamEye,
- }
- else:
- camCmdClassMap = {
- "splineEyeOrAT": "camATSplineList" if useAT else "camEyeSplineList",
- "splineEyeOrATRelPlayer": "camATSplineRelPlayerList" if useAT else "camEyeSplineRelPlayerList",
- "eyeOrAT": "camATList" if useAT else "camEyeList",
- }
-
- return camCmdClassMap[camMode]
-
- def getNewCamData(self, shotObj: Object, useAT: bool):
- """Returns the Camera Shot data from the corresponding Armatures"""
-
- entries = self.getCameraShotPointData(shotObj.data.bones, useAT)
- startFrame = shotObj.data.ootCamShotProp.shotStartFrame
-
- # "fake" end frame
- endFrame = startFrame + max(2, sum(point.frame for point in entries)) + (entries[-2].frame if useAT else 1)
-
- if not useAT:
- for pointData in entries:
- pointData.frame = 0
- self.camEndFrame = endFrame
-
- return self.getCamClassOrList(True, shotObj.data.ootCamShotProp.shotCamMode, useAT)(
- startFrame, endFrame, entries
- )
-
- def setCameraShotData(self, csObjects: dict[str, list[Object]]):
- shotObjects = csObjects["camShot"]
-
- if len(shotObjects) > 0:
- motionFrameCount = -1
- for shotObj in shotObjects:
- camMode = shotObj.data.ootCamShotProp.shotCamMode
-
- eyeCamList = getattr(self, self.getCamClassOrList(False, camMode, False))
- eyeCamList.append(self.getNewCamData(shotObj, False))
-
- atCamList = getattr(self, self.getCamClassOrList(False, camMode, True))
- atCamList.append(self.getNewCamData(shotObj, True))
-
- motionFrameCount = max(motionFrameCount, self.camEndFrame + 1)
- self.motionFrameCount += motionFrameCount
- self.totalEntries += len(shotObjects) * 2
-
- def getNewTextCmd(self, textEntry: "OOTCSTextProperty"):
- match textEntry.textboxType:
- case "Text":
- return CutsceneCmdText(
- textEntry.startFrame,
- textEntry.endFrame,
- textEntry.textID,
- self.getEnumValueFromProp("csTextType", textEntry, "csTextType"),
- textEntry.topOptionTextID,
- textEntry.bottomOptionTextID,
- )
- case "None":
- return CutsceneCmdTextNone(textEntry.startFrame, textEntry.endFrame)
- case "OcarinaAction":
- return CutsceneCmdTextOcarinaAction(
- textEntry.startFrame,
- textEntry.endFrame,
- self.getEnumValueFromProp("ocarinaSongActionId", textEntry, "ocarinaAction"),
- textEntry.ocarinaMessageId,
- )
- raise PluginError("ERROR: Unknown text type!")
-
- def setCutsceneData(self, csObjects: dict[str, list[Object]], csProp: "OOTCutsceneProperty"):
- self.setActorCueListData(csObjects, True)
- self.setActorCueListData(csObjects, False)
- self.setCameraShotData(csObjects)
-
- # don't process the cutscene empty if we don't want its data
- if self.motionOnly:
- return
-
- if csProp.csUseDestination:
- self.destination = CutsceneCmdDestination(
- csProp.csDestinationStartFrame,
- None,
- self.getEnumValueFromProp("csDestination", csProp, "csDestination"),
- )
- self.totalEntries += 1
-
- self.frameCount = csProp.csEndFrame
- self.totalEntries += len(csProp.csLists)
-
- for entry in csProp.csLists:
- match entry.listType:
- case "StartSeqList" | "StopSeqList" | "FadeOutSeqList":
- isFadeOutSeq = entry.listType == "FadeOutSeqList"
- cmdList = cmdToClass[entry.listType](None, None)
- cmdList.entryTotal = len(entry.seqList)
- if not isFadeOutSeq:
- cmdList.type = "start" if entry.listType == "StartSeqList" else "stop"
- for elem in entry.seqList:
- data = cmdToClass[entry.listType.removesuffix("List")](elem.startFrame, elem.endFrame)
- if isFadeOutSeq:
- data.seqPlayer = self.getEnumValueFromProp("csFadeOutSeqPlayer", elem, "csSeqPlayer")
- else:
- data.type = cmdList.type
- data.seqId = self.getEnumValueFromProp("seqId", elem, "csSeqID")
- cmdList.entries.append(data)
- if isFadeOutSeq:
- self.fadeSeqList.append(cmdList)
- else:
- self.seqList.append(cmdList)
- case "Transition":
- self.transitionList.append(
- CutsceneCmdTransition(
- entry.transitionStartFrame,
- entry.transitionEndFrame,
- self.getEnumValueFromProp("csTransitionType", entry, "transitionType"),
- )
- )
- case _:
- curList = getattr(entry, (entry.listType[0].lower() + entry.listType[1:]))
- cmdList = cmdToClass[entry.listType](None, None)
- cmdList.entryTotal = len(curList)
- for elem in curList:
- match entry.listType:
- case "TextList":
- cmdList.entries.append(self.getNewTextCmd(elem))
- case "LightSettingsList":
- cmdList.entries.append(
- CutsceneCmdLightSetting(
- elem.startFrame, elem.endFrame, False, elem.lightSettingsIndex
- )
- )
- case "TimeList":
- cmdList.entries.append(
- CutsceneCmdTime(elem.startFrame, elem.endFrame, elem.hour, elem.minute)
- )
- case "MiscList":
- cmdList.entries.append(
- CutsceneCmdMisc(
- elem.startFrame,
- elem.endFrame,
- self.getEnumValueFromProp("csMiscType", elem, "csMiscType"),
- )
- )
- case "RumbleList":
- cmdList.entries.append(
- CutsceneCmdRumbleController(
- elem.startFrame,
- elem.endFrame,
- elem.rumbleSourceStrength,
- elem.rumbleDuration,
- elem.rumbleDecreaseRate,
- )
- )
- case _:
- raise PluginError("ERROR: Unknown Cutscene List Type!")
- getattr(self, cmdToList[entry.listType]).append(cmdList)
diff --git a/fast64_internal/oot/exporter/cutscene/misc.py b/fast64_internal/oot/exporter/cutscene/misc.py
deleted file mode 100644
index a332dbf6c..000000000
--- a/fast64_internal/oot/exporter/cutscene/misc.py
+++ /dev/null
@@ -1,228 +0,0 @@
-from dataclasses import dataclass, field
-from typing import Optional
-from ....utility import PluginError, indent
-from ...cutscene.motion.utility import getInteger
-from .common import CutsceneCmdBase
-
-
-@dataclass
-class CutsceneCmdMisc(CutsceneCmdBase):
- """This class contains a single misc command entry"""
-
- type: str # see ``CutsceneMiscType`` in decomp
-
- paramNumber: int = field(init=False, default=14)
-
- @staticmethod
- def from_params(params: list[str]):
- return CutsceneCmdMisc(
- getInteger(params[1]), getInteger(params[2]), CutsceneCmdBase.getEnumValue("csMiscType", params[0])
- )
-
- def getCmd(self):
- self.validateFrames()
- return indent * 3 + (f"CS_MISC({self.type}, {self.startFrame}, {self.endFrame}" + ", 0" * 11 + "),\n")
-
-
-@dataclass
-class CutsceneCmdLightSetting(CutsceneCmdBase):
- """This class contains Light Setting command data"""
-
- isLegacy: bool
- lightSetting: int
-
- paramNumber: int = field(init=False, default=11)
-
- @staticmethod
- def from_params(params: list[str], isLegacy: bool):
- lightSetting = getInteger(params[0])
- return CutsceneCmdLightSetting(
- getInteger(params[1]), getInteger(params[2]), isLegacy, lightSetting - 1 if isLegacy else lightSetting
- )
-
- def getCmd(self):
- self.validateFrames(False)
- return indent * 3 + (f"CS_LIGHT_SETTING({self.lightSetting}, {self.startFrame}" + ", 0" * 12 + "),\n")
-
-
-@dataclass
-class CutsceneCmdTime(CutsceneCmdBase):
- """This class contains Time Ocarina Action command data"""
-
- hour: int
- minute: int
-
- paramNumber: int = field(init=False, default=5)
-
- @staticmethod
- def from_params(params: list[str]):
- return CutsceneCmdTime(
- getInteger(params[1]),
- getInteger(params[2]),
- getInteger(params[3]),
- getInteger(params[4]),
- )
-
- def getCmd(self):
- self.validateFrames(False)
- return indent * 3 + f"CS_TIME(0, {self.startFrame}, 0, {self.hour}, {self.minute}),\n"
-
-
-@dataclass
-class CutsceneCmdRumbleController(CutsceneCmdBase):
- """This class contains Rumble Controller command data"""
-
- sourceStrength: int
- duration: int
- decreaseRate: int
-
- paramNumber: int = field(init=False, default=8)
-
- @staticmethod
- def from_params(params: list[str]):
- return CutsceneCmdRumbleController(
- getInteger(params[1]),
- getInteger(params[2]),
- getInteger(params[3]),
- getInteger(params[4]),
- getInteger(params[5]),
- )
-
- def getCmd(self):
- self.validateFrames(False)
- return indent * 3 + (
- f"CS_RUMBLE_CONTROLLER("
- + f"0, {self.startFrame}, 0, {self.sourceStrength}, {self.duration}, {self.decreaseRate}, 0, 0),\n"
- )
-
-
-@dataclass
-class CutsceneCmdMiscList(CutsceneCmdBase):
- """This class contains Misc command data"""
-
- entryTotal: Optional[int] = field(init=False, default=None)
- entries: list[CutsceneCmdMisc] = field(init=False, default_factory=list)
- paramNumber: int = field(init=False, default=1)
- listName: str = field(init=False, default="miscList")
-
- @staticmethod
- def from_params(params: list[str]):
- new = CutsceneCmdMiscList()
- new.entryTotal = getInteger(params[0])
- return new
-
- def getCmd(self):
- if len(self.entries) == 0:
- raise PluginError("ERROR: Entry list is empty!")
- return self.getGenericListCmd("CS_MISC_LIST", self.entryTotal) + "".join(
- entry.getCmd() for entry in self.entries
- )
-
-
-@dataclass
-class CutsceneCmdLightSettingList(CutsceneCmdBase):
- """This class contains Light Setting List command data"""
-
- entryTotal: Optional[int] = field(init=False, default=None)
- entries: list[CutsceneCmdLightSetting] = field(init=False, default_factory=list)
- paramNumber: int = field(init=False, default=1)
- listName: str = field(init=False, default="lightSettingsList")
-
- @staticmethod
- def from_params(params: list[str]):
- new = CutsceneCmdLightSettingList()
- new.entryTotal = getInteger(params[0])
- return new
-
- def getCmd(self):
- if len(self.entries) == 0:
- raise PluginError("ERROR: Entry list is empty!")
- return self.getGenericListCmd("CS_LIGHT_SETTING_LIST", self.entryTotal) + "".join(
- entry.getCmd() for entry in self.entries
- )
-
-
-@dataclass
-class CutsceneCmdTimeList(CutsceneCmdBase):
- """This class contains Time List command data"""
-
- entryTotal: Optional[int] = field(init=False, default=None)
- entries: list[CutsceneCmdTime] = field(init=False, default_factory=list)
- paramNumber: int = field(init=False, default=1)
- listName: str = field(init=False, default="timeList")
-
- @staticmethod
- def from_params(params: list[str]):
- new = CutsceneCmdTimeList()
- new.entryTotal = getInteger(params[0])
- return new
-
- def getCmd(self):
- if len(self.entries) == 0:
- raise PluginError("ERROR: Entry list is empty!")
- return self.getGenericListCmd("CS_TIME_LIST", self.entryTotal) + "".join(
- entry.getCmd() for entry in self.entries
- )
-
-
-@dataclass
-class CutsceneCmdRumbleControllerList(CutsceneCmdBase):
- """This class contains Rumble Controller List command data"""
-
- entryTotal: Optional[int] = field(init=False, default=None)
- entries: list[CutsceneCmdRumbleController] = field(init=False, default_factory=list)
- paramNumber: int = field(init=False, default=1)
- listName: str = field(init=False, default="rumbleList")
-
- @staticmethod
- def from_params(params: list[str]):
- new = CutsceneCmdRumbleControllerList()
- new.entryTotal = getInteger(params[0])
- return new
-
- def getCmd(self):
- if len(self.entries) == 0:
- raise PluginError("ERROR: Entry list is empty!")
- return self.getGenericListCmd("CS_RUMBLE_CONTROLLER_LIST", self.entryTotal) + "".join(
- entry.getCmd() for entry in self.entries
- )
-
-
-@dataclass
-class CutsceneCmdDestination(CutsceneCmdBase):
- """This class contains Destination command data"""
-
- id: str
-
- paramNumber: int = field(init=False, default=3)
- listName: str = field(init=False, default="destination")
-
- @staticmethod
- def from_params(params: list[str]):
- return CutsceneCmdDestination(
- getInteger(params[1]), None, CutsceneCmdBase.getEnumValue("csDestination", params[0])
- )
-
- def getCmd(self):
- self.validateFrames(False)
- return indent * 2 + f"CS_DESTINATION({self.id}, {self.startFrame}, 0),\n"
-
-
-@dataclass
-class CutsceneCmdTransition(CutsceneCmdBase):
- """This class contains Transition command data"""
-
- type: str
-
- paramNumber: int = field(init=False, default=3)
- listName: str = field(init=False, default="transitionList")
-
- @staticmethod
- def from_params(params: list[str]):
- return CutsceneCmdTransition(
- getInteger(params[1]), getInteger(params[2]), CutsceneCmdBase.getEnumValue("csTransitionType", params[0])
- )
-
- def getCmd(self):
- self.validateFrames()
- return indent * 2 + f"CS_TRANSITION({self.type}, {self.startFrame}, {self.endFrame}),\n"
diff --git a/fast64_internal/oot/exporter/cutscene/seq.py b/fast64_internal/oot/exporter/cutscene/seq.py
deleted file mode 100644
index f8afeb78b..000000000
--- a/fast64_internal/oot/exporter/cutscene/seq.py
+++ /dev/null
@@ -1,93 +0,0 @@
-from dataclasses import dataclass, field
-from typing import Optional
-from ....utility import PluginError
-from ...cutscene.motion.utility import getInteger
-from .common import CutsceneCmdBase
-
-
-@dataclass
-class CutsceneCmdStartStopSeq(CutsceneCmdBase):
- """This class contains Start/Stop Seq command data"""
-
- isLegacy: bool = field(init=False, default=False)
- seqId: Optional[str] = field(init=False, default=None)
- paramNumber: int = field(init=False, default=11)
- type: Optional[str] = field(init=False, default=None) # "start" or "stop"
-
- @staticmethod
- def from_params(params: list[str], isLegacy: bool):
- return CutsceneCmdFadeSeq(
- getInteger(params[1]), getInteger(params[2]), CutsceneCmdBase.getEnumValue("seqId", params[0], isLegacy)
- )
-
- def getCmd(self):
- self.validateFrames()
- if self.type is None:
- raise PluginError("ERROR: Type is None!")
- return self.getGenericSeqCmd(f"CS_{self.type.upper()}_SEQ", self.seqId, self.startFrame, self.endFrame)
-
-
-@dataclass
-class CutsceneCmdFadeSeq(CutsceneCmdBase):
- """This class contains Fade Seq command data"""
-
- seqPlayer: str = field(init=False, default=str())
- paramNumber: int = field(init=False, default=11)
- enumKey: str = field(init=False, default="csFadeOutSeqPlayer")
-
- @staticmethod
- def from_params(params: list[str], enumKey: str):
- return CutsceneCmdFadeSeq(
- getInteger(params[1]), getInteger(params[2]), CutsceneCmdBase.getEnumValue(enumKey, params[0])
- )
-
- def getCmd(self):
- self.validateFrames()
- return self.getGenericSeqCmd("CS_FADE_OUT_SEQ", self.seqPlayer, self.startFrame, self.endFrame)
-
-
-@dataclass
-class CutsceneCmdStartStopSeqList(CutsceneCmdBase):
- """This class contains Start/Stop Seq List command data"""
-
- entryTotal: int = field(init=False, default=0)
- type: str = field(init=False, default=str()) # "start" or "stop"
- entries: list[CutsceneCmdStartStopSeq] = field(init=False, default_factory=list)
- paramNumber: int = field(init=False, default=1)
- listName: str = field(init=False, default="seqList")
-
- @staticmethod
- def from_params(params: list[str]):
- new = CutsceneCmdStartStopSeqList()
- new.entryTotal = getInteger(params[0])
- return new
-
- def getCmd(self):
- if len(self.entries) == 0:
- raise PluginError("ERROR: Entry list is empty!")
- return self.getGenericListCmd(f"CS_{self.type.upper()}_SEQ_LIST", self.entryTotal) + "".join(
- entry.getCmd() for entry in self.entries
- )
-
-
-@dataclass
-class CutsceneCmdFadeSeqList(CutsceneCmdBase):
- """This class contains Fade Seq List command data"""
-
- entryTotal: int = field(init=False, default=0)
- entries: list[CutsceneCmdFadeSeq] = field(init=False, default_factory=list)
- paramNumber: int = field(init=False, default=1)
- listName: str = field(init=False, default="fadeSeqList")
-
- @staticmethod
- def from_params(params: list[str]):
- new = CutsceneCmdFadeSeqList()
- new.entryTotal = getInteger(params[0])
- return new
-
- def getCmd(self):
- if len(self.entries) == 0:
- raise PluginError("ERROR: Entry list is empty!")
- return self.getGenericListCmd("CS_FADE_OUT_SEQ_LIST", self.entryTotal) + "".join(
- entry.getCmd() for entry in self.entries
- )
diff --git a/fast64_internal/oot/importer/scene_header.py b/fast64_internal/oot/importer/scene_header.py
deleted file mode 100644
index 017667b33..000000000
--- a/fast64_internal/oot/importer/scene_header.py
+++ /dev/null
@@ -1,347 +0,0 @@
-import math
-import os
-import re
-import bpy
-import mathutils
-
-from ...utility import PluginError, readFile, parentObject, hexOrDecInt, gammaInverse
-from ...f3d.f3d_parser import parseMatrices
-from ..oot_model_classes import OOTF3DContext
-from ..scene.properties import OOTSceneHeaderProperty, OOTLightProperty
-from ..oot_utility import getEvalParams, setCustomProperty
-from .constants import headerNames
-from .utility import getDataMatch, stripName
-from .classes import SharedSceneData
-from .room_header import parseRoomCommands
-from .actor import parseTransActorList, parseSpawnList, parseEntranceList
-from .scene_collision import parseCollisionHeader
-from .scene_pathways import parsePathList
-
-from ..oot_constants import (
- ootEnumAudioSessionPreset,
- ootEnumNightSeq,
- ootEnumMusicSeq,
- ootEnumCameraMode,
- ootEnumMapLocation,
- ootEnumNaviHints,
- ootEnumGlobalObject,
- ootEnumSkybox,
- ootEnumCloudiness,
- ootEnumSkyboxLighting,
-)
-
-
-def parseColor(values: tuple[str, str, str]) -> tuple[float, float, float]:
- return tuple(gammaInverse([hexOrDecInt(value) / 0xFF for value in values]))
-
-
-def parseDirection(index: int, values: tuple[str, str, str]) -> tuple[float, float, float] | int:
- values = [hexOrDecInt(value) for value in values]
-
- if tuple(values) == (0, 0, 0):
- return "Zero"
- elif index == 0 and tuple(values) == (0x49, 0x49, 0x49):
- return "Default"
- elif index == 1 and tuple(values) == (0xB7, 0xB7, 0xB7):
- return "Default"
- else:
- direction = mathutils.Vector(
- [int.from_bytes(value.to_bytes(1, "big", signed=value < 127), "big", signed=True) / 127 for value in values]
- )
-
- return (
- mathutils.Euler((0, 0, math.pi)).to_quaternion()
- @ (mathutils.Euler((math.pi / 2, 0, 0)).to_quaternion() @ direction).rotation_difference(
- mathutils.Vector((0, 0, 1))
- )
- ).to_euler()
-
-
-def parseLight(
- lightHeader: OOTLightProperty, index: int, rotation: mathutils.Euler, color: mathutils.Vector
-) -> bpy.types.Object | None:
- setattr(lightHeader, f"useCustomDiffuse{index}", rotation != "Zero" and rotation != "Default")
-
- if rotation == "Zero" or rotation == "Default":
- setattr(lightHeader, f"zeroDiffuse{index}", rotation == "Zero")
- setattr(lightHeader, f"diffuse{index}", color + (1,))
- return None
- else:
- light = bpy.data.lights.new("Light", "SUN")
- lightObj = bpy.data.objects.new("Light", light)
- bpy.context.scene.collection.objects.link(lightObj)
- setattr(lightHeader, f"diffuse{index}Custom", lightObj.data)
- lightObj.rotation_euler = rotation
- lightObj.data.color = color
- lightObj.data.type = "SUN"
- return lightObj
-
-
-def parseLightList(
- sceneObj: bpy.types.Object,
- sceneHeader: OOTSceneHeaderProperty,
- sceneData: str,
- lightListName: str,
- headerIndex: int,
-):
- lightData = getDataMatch(sceneData, lightListName, ["LightSettings", "EnvLightSettings"], "light list")
-
- # I currently don't understand the light list format in respect to this lighting flag.
- # So we'll set it to custom instead.
- if sceneHeader.skyboxLighting != "Custom":
- sceneHeader.skyboxLightingCustom = sceneHeader.skyboxLighting
- sceneHeader.skyboxLighting = "Custom"
- sceneHeader.lightList.clear()
-
- # convert string to ZAPD format if using new Fast64 output
- if "// Ambient Color" in sceneData:
- i = 0
- lightData = lightData.replace("{", "").replace("}", "").replace("\n", "").replace(" ", "").replace(",,", ",")
- data = "{ "
- for part in lightData.split(","):
- if i < 20:
- if i == 18:
- part = getEvalParams(part)
- data += part + ", "
- if i == 19:
- data = data[:-2]
- else:
- data += "},\n{ " + part + ", "
- i = 0
- i += 1
- lightData = data[:-4]
-
- lightList = [
- value.replace("{", "").replace("\n", "").replace(" ", "")
- for value in lightData.split("},")
- if value.strip() != ""
- ]
-
- index = 0
- for lightEntry in lightList:
- lightParams = [value.strip() for value in lightEntry.split(",")]
-
- ambientColor = parseColor(lightParams[0:3])
- diffuseDir0 = parseDirection(0, lightParams[3:6])
- diffuseColor0 = parseColor(lightParams[6:9])
- diffuseDir1 = parseDirection(1, lightParams[9:12])
- diffuseColor1 = parseColor(lightParams[12:15])
- fogColor = parseColor(lightParams[15:18])
-
- blendFogShort = hexOrDecInt(lightParams[18])
- fogNear = blendFogShort & ((1 << 10) - 1)
- transitionSpeed = blendFogShort >> 10
- z_far = hexOrDecInt(lightParams[19])
-
- lightHeader = sceneHeader.lightList.add()
- lightHeader.ambient = ambientColor + (1,)
-
- lightObj0 = parseLight(lightHeader, 0, diffuseDir0, diffuseColor0)
- lightObj1 = parseLight(lightHeader, 1, diffuseDir1, diffuseColor1)
-
- if lightObj0 is not None:
- parentObject(sceneObj, lightObj0)
- lightObj0.location = [4 + headerIndex * 2, 0, -index * 2]
- if lightObj1 is not None:
- parentObject(sceneObj, lightObj1)
- lightObj1.location = [4 + headerIndex * 2, 2, -index * 2]
-
- lightHeader.fogColor = fogColor + (1,)
- lightHeader.fogNear = fogNear
- lightHeader.z_far = z_far
- lightHeader.transitionSpeed = transitionSpeed
-
- index += 1
-
-
-def parseExitList(sceneHeader: OOTSceneHeaderProperty, sceneData: str, exitListName: str):
- exitData = getDataMatch(sceneData, exitListName, "u16", "exit list")
-
- # see also start position list
- exitList = [value.strip() for value in exitData.split(",") if value.strip() != ""]
- for exit in exitList:
- exitProp = sceneHeader.exitList.add()
- exitProp.exitIndex = "Custom"
- exitProp.exitIndexCustom = exit
-
-
-def parseRoomList(
- sceneObj: bpy.types.Object,
- sceneData: str,
- roomListName: str,
- f3dContext: OOTF3DContext,
- sharedSceneData: SharedSceneData,
- headerIndex: int,
-):
- roomList = getDataMatch(sceneData, roomListName, "RomFile", "room list")
- index = 0
- roomObjs = []
-
- # Assumption that alternate scene headers all use the same room list.
- for roomMatch in re.finditer(
- rf"\{{([\(\)\sA-Za-z0-9\_]*),([\(\)\sA-Za-z0-9\_]*)\}}\s*,", roomList, flags=re.DOTALL
- ):
- roomName = roomMatch.group(1).strip().replace("SegmentRomStart", "")
- if "(u32)" in roomName:
- roomName = roomName[5:].strip()[1:] # includes leading underscore
- elif "(uintptr_t)" in roomName:
- roomName = roomName[11:].strip()[1:]
- else:
- roomName = roomName[1:]
-
- roomPath = os.path.join(sharedSceneData.scenePath, f"{roomName}.c")
- roomData = readFile(roomPath)
- parseMatrices(roomData, f3dContext, 1 / bpy.context.scene.ootBlenderScale)
-
- roomCommandsName = f"{roomName}Commands"
- if roomCommandsName not in roomData:
- roomCommandsName = f"{roomName}_header00" # fast64 naming
-
- # Assumption that any shared textures are stored after the CollisionHeader.
- # This is done to avoid including large collision data in regex searches.
- try:
- collisionHeaderIndex = sceneData.index("CollisionHeader ")
- except:
- collisionHeaderIndex = 0
- sharedRoomData = sceneData[collisionHeaderIndex:]
- roomObj = parseRoomCommands(
- roomName,
- None,
- sharedRoomData + roomData,
- roomCommandsName,
- index,
- f3dContext,
- sharedSceneData,
- headerIndex,
- )
- parentObject(sceneObj, roomObj)
- index += 1
- roomObjs.append(roomObj)
-
- return roomObjs
-
-
-def parseAlternateSceneHeaders(
- sceneObj: bpy.types.Object,
- roomObjs: list[bpy.types.Object],
- sceneData: str,
- altHeadersListName: str,
- f3dContext: OOTF3DContext,
- sharedSceneData: SharedSceneData,
-):
- altHeadersData = getDataMatch(sceneData, altHeadersListName, ["SceneCmd*", "SCmdBase*"], "alternate header list")
- altHeadersList = [value.strip() for value in altHeadersData.split(",") if value.strip() != ""]
-
- for i in range(len(altHeadersList)):
- if not (altHeadersList[i] == "NULL" or altHeadersList[i] == "0"):
- parseSceneCommands(
- sceneObj.name, sceneObj, roomObjs, altHeadersList[i], sceneData, f3dContext, i + 1, sharedSceneData
- )
-
-
-def parseSceneCommands(
- sceneName: str | None,
- sceneObj: bpy.types.Object | None,
- roomObjs: list[bpy.types.Object] | None,
- sceneCommandsName: str,
- sceneData: str,
- f3dContext: OOTF3DContext,
- headerIndex: int,
- sharedSceneData: SharedSceneData,
-):
- if sceneObj is None:
- sceneObj = bpy.data.objects.new(sceneCommandsName, None)
- bpy.context.scene.collection.objects.link(sceneObj)
- sceneObj.empty_display_type = "SPHERE"
- sceneObj.ootEmptyType = "Scene"
- sceneObj.name = sceneName
-
- if headerIndex == 0:
- sceneHeader = sceneObj.ootSceneHeader
- elif headerIndex < 4:
- sceneHeader = getattr(sceneObj.ootAlternateSceneHeaders, headerNames[headerIndex])
- sceneHeader.usePreviousHeader = False
- else:
- cutsceneHeaders = sceneObj.ootAlternateSceneHeaders.cutsceneHeaders
- while len(cutsceneHeaders) < headerIndex - 3:
- cutsceneHeaders.add()
- sceneHeader = cutsceneHeaders[headerIndex - 4]
-
- commands = getDataMatch(sceneData, sceneCommandsName, ["SceneCmd", "SCmdBase"], "scene commands")
- entranceList = None
- altHeadersListName = None
- for commandMatch in re.finditer(rf"(SCENE\_CMD\_[a-zA-Z0-9\_]*)\s*\((.*?)\)\s*,", commands, flags=re.DOTALL):
- command = commandMatch.group(1)
- args = [arg.strip() for arg in commandMatch.group(2).split(",")]
- if command == "SCENE_CMD_SOUND_SETTINGS":
- setCustomProperty(sceneHeader, "audioSessionPreset", args[0], ootEnumAudioSessionPreset)
- setCustomProperty(sceneHeader, "nightSeq", args[1], ootEnumNightSeq)
- setCustomProperty(sceneHeader, "musicSeq", args[2], ootEnumMusicSeq)
- elif command == "SCENE_CMD_ROOM_LIST":
- # Assumption that all scenes use the same room list.
- if headerIndex == 0:
- if roomObjs is not None:
- raise PluginError("Attempting to parse a room list while room objs already loaded.")
- roomListName = stripName(args[1])
- roomObjs = parseRoomList(sceneObj, sceneData, roomListName, f3dContext, sharedSceneData, headerIndex)
-
- # This must be handled after rooms, so that room objs can be referenced
- elif command == "SCENE_CMD_TRANSITION_ACTOR_LIST" and sharedSceneData.includeActors:
- transActorListName = stripName(args[1])
- parseTransActorList(roomObjs, sceneData, transActorListName, sharedSceneData, headerIndex)
-
- elif command == "SCENE_CMD_MISC_SETTINGS":
- setCustomProperty(sceneHeader, "cameraMode", args[0], ootEnumCameraMode)
- setCustomProperty(sceneHeader, "mapLocation", args[1], ootEnumMapLocation)
- elif command == "SCENE_CMD_COL_HEADER":
- # Assumption that all scenes use the same collision.
- if headerIndex == 0:
- collisionHeaderName = args[0][1:] # remove '&'
- parseCollisionHeader(sceneObj, roomObjs, sceneData, collisionHeaderName, sharedSceneData)
- elif command == "SCENE_CMD_ENTRANCE_LIST" and sharedSceneData.includeActors:
- if not (args[0] == "NULL" or args[0] == "0" or args[0] == "0x00"):
- entranceListName = stripName(args[0])
- entranceList = parseEntranceList(sceneHeader, roomObjs, sceneData, entranceListName)
- elif command == "SCENE_CMD_SPECIAL_FILES":
- setCustomProperty(sceneHeader, "naviCup", args[0], ootEnumNaviHints)
- setCustomProperty(sceneHeader, "globalObject", args[1], ootEnumGlobalObject)
- elif command == "SCENE_CMD_PATH_LIST" and sharedSceneData.includePaths:
- pathListName = stripName(args[0])
- parsePathList(sceneObj, sceneData, pathListName, headerIndex, sharedSceneData)
-
- # This must be handled after entrance list, so that entrance list can be referenced
- elif command == "SCENE_CMD_SPAWN_LIST" and sharedSceneData.includeActors:
- if not (args[1] == "NULL" or args[1] == "0" or args[1] == "0x00"):
- spawnListName = stripName(args[1])
- parseSpawnList(roomObjs, sceneData, spawnListName, entranceList, sharedSceneData, headerIndex)
-
- # Clear entrance list
- entranceList = None
-
- elif command == "SCENE_CMD_SKYBOX_SETTINGS":
- setCustomProperty(sceneHeader, "skyboxID", args[0], ootEnumSkybox)
- setCustomProperty(sceneHeader, "skyboxCloudiness", args[1], ootEnumCloudiness)
- setCustomProperty(sceneHeader, "skyboxLighting", args[2], ootEnumSkyboxLighting)
- elif command == "SCENE_CMD_EXIT_LIST":
- exitListName = stripName(args[0])
- parseExitList(sceneHeader, sceneData, exitListName)
- elif command == "SCENE_CMD_ENV_LIGHT_SETTINGS" and sharedSceneData.includeLights:
- if not (args[1] == "NULL" or args[1] == "0" or args[1] == "0x00"):
- lightsListName = stripName(args[1])
- parseLightList(sceneObj, sceneHeader, sceneData, lightsListName, headerIndex)
- elif command == "SCENE_CMD_CUTSCENE_DATA" and sharedSceneData.includeCutscenes:
- sceneHeader.writeCutscene = True
- sceneHeader.csWriteType = "Object"
- csObjName = f"Cutscene.{args[0]}"
- try:
- sceneHeader.csWriteObject = bpy.data.objects[csObjName]
- except:
- print(f"ERROR: Cutscene ``{csObjName}`` do not exist!")
- elif command == "SCENE_CMD_ALTERNATE_HEADER_LIST":
- # Delay until after rooms are parsed
- altHeadersListName = stripName(args[0])
-
- if altHeadersListName is not None:
- parseAlternateSceneHeaders(sceneObj, roomObjs, sceneData, altHeadersListName, f3dContext, sharedSceneData)
-
- return sceneObj
diff --git a/fast64_internal/oot/oot_spline.py b/fast64_internal/oot/oot_spline.py
deleted file mode 100644
index 05084437c..000000000
--- a/fast64_internal/oot/oot_spline.py
+++ /dev/null
@@ -1,38 +0,0 @@
-import bpy
-from ..utility import PluginError, toAlnum
-
-
-class OOTPath:
- def __init__(self, ownerName, objName: str):
- self.ownerName = toAlnum(ownerName)
- self.objName = objName
- self.points = []
-
- def pathName(self, headerIndex, index):
- return f"{self.ownerName}_pathwayList{index}_header{headerIndex}"
-
-
-def ootConvertPath(name, obj, transformMatrix):
- path = OOTPath(name, obj.name)
-
- spline = obj.data.splines[0]
- for point in spline.points:
- position = transformMatrix @ point.co.xyz
- path.points.append(position)
-
- return path
-
-
-def onSplineTypeSet(self, context):
- self.splines.active.order_u = 1
-
-
-def assertCurveValid(obj):
- curve = obj.data
- if not isinstance(curve, bpy.types.Curve) or curve.splines[0].type != "NURBS":
- # Curve was likely not intended to be exported
- return False
- if len(curve.splines) != 1:
- # Curve was intended to be exported but has multiple disconnected segments
- raise PluginError("Exported curves should have only one single segment, found " + str(len(curve.splines)))
- return True
diff --git a/fast64_internal/oot/skeleton/constants.py b/fast64_internal/oot/skeleton/constants.py
deleted file mode 100644
index b96ebfb72..000000000
--- a/fast64_internal/oot/skeleton/constants.py
+++ /dev/null
@@ -1,102 +0,0 @@
-from collections import OrderedDict
-
-
-# Adding new rest pose entry:
-# 1. Import a generic skeleton
-# 2. Pose into a usable rest pose
-# 3. Select skeleton, then run bpy.ops.object.oot_save_rest_pose()
-# 4. Copy array data from console into an OOTSkeletonImportInfo object
-# - list of tuples, first is root position, rest are euler XYZ rotations
-# 5. Add object to ootSkeletonImportDict
-
-
-# Link overlay will be "", since Link texture array data is handled as a special case.
-class OOTSkeletonImportInfo:
- def __init__(
- self,
- skeletonName: str,
- folderName: str,
- actorOverlayName: str,
- flipbookArrayIndex2D: int | None,
- restPoseData: list[tuple[float, float, float]] | None,
- ):
- self.skeletonName = skeletonName
- self.folderName = folderName
- self.actorOverlayName = actorOverlayName # Note that overlayName = None will disable texture array reading.
- self.flipbookArrayIndex2D = flipbookArrayIndex2D
- self.isLink = skeletonName == "gLinkAdultSkel" or skeletonName == "gLinkChildSkel"
- self.restPoseData = restPoseData
-
-
-ootSkeletonImportDict = OrderedDict(
- {
- "Adult Link": OOTSkeletonImportInfo(
- "gLinkAdultSkel",
- "object_link_boy",
- "",
- 0,
- [
- (0.0, 3.6050000190734863, 0.0),
- (0.0, -0.0, 0.0),
- (-1.5708922147750854, -0.0, -1.5707963705062866),
- (0.0, -0.0, 0.0),
- (0.0, 0.05235987901687622, 0.0),
- (0.0, -0.0, 0.0),
- (0.0, 0.0, -1.5707964897155762),
- (0.0, -0.05235987901687622, 0.0),
- (0.0, -0.0, 0.0),
- (0.0, 0.0, -1.5707964897155762),
- (1.5707963705062866, -0.0, 1.5707963705062866),
- (-4.740638548383913e-09, -5.356494803265832e-09, 1.4546878337860107),
- (-4.114889869409654e-15, -1.1733899984468776e-14, 1.9080803394317627),
- (0.0, -0.0, 0.0),
- (1.0222795112391236e-15, -0.6981316804885864, -3.141592502593994),
- (0.0, -0.0, 0.0),
- (0.0, 0.0, -1.5707964897155762),
- (-1.0222795112391236e-15, 0.6981316804885864, -3.141592502593994),
- (0.0, -0.0, 0.0),
- (0.0, 0.0, -1.5707964897155762),
- (-1.5707963705062866, 2.611602306365967, -0.08726644515991211),
- (0.0, -0.0, 0.0),
- ],
- ),
- "Child Link": OOTSkeletonImportInfo(
- "gLinkChildSkel",
- "object_link_child",
- "",
- 1,
- [
- (0.0, 2.3559017181396484, 0.0),
- (0.0, -0.0, 0.0),
- (-1.5708922147750854, -0.0, -1.5707963705062866),
- (0.0, -0.0, 0.0),
- (0.0, 0.05235987901687622, 0.0),
- (0.0, -0.0, 0.0),
- (0.0, 0.0, -1.5707964897155762),
- (0.0, -0.05235987901687622, 0.0),
- (0.0, -0.0, 0.0),
- (0.0, 0.0, -1.5707964897155762),
- (1.5707963705062866, -0.0, 1.5707963705062866),
- (-4.740638548383913e-09, -5.356494803265832e-09, 1.4546878337860107),
- (-4.114889869409654e-15, -1.1733899984468776e-14, 1.9080803394317627),
- (0.0, -0.0, 0.0),
- (1.0222795112391236e-15, -0.6981316804885864, -3.141592502593994),
- (0.0, -0.0, 0.0),
- (0.0, 0.0, -1.5707964897155762),
- (-1.0222795112391236e-15, 0.6981316804885864, -3.141592502593994),
- (0.0, -0.0, 0.0),
- (0.0, 0.0, -1.5707964897155762),
- (-1.5707963705062866, 2.611602306365967, -0.08726644515991211),
- (0.0, -0.0, 0.0),
- ],
- ),
- # "Gerudo": OOTSkeletonImportInfo("gGerudoRedSkel", "object_geldb", "ovl_En_GeldB", None, None),
- }
-)
-
-ootEnumSkeletonImportMode = [
- ("Generic", "Generic", "Generic"),
-]
-
-for name, info in ootSkeletonImportDict.items():
- ootEnumSkeletonImportMode.append((name, name, name))
diff --git a/fast64_internal/oot/spline/properties.py b/fast64_internal/oot/spline/properties.py
deleted file mode 100644
index b0c32f27c..000000000
--- a/fast64_internal/oot/spline/properties.py
+++ /dev/null
@@ -1,49 +0,0 @@
-from bpy.types import PropertyGroup, Object, UILayout
-from bpy.props import EnumProperty, PointerProperty, StringProperty, IntProperty
-from bpy.utils import register_class, unregister_class
-from ...utility import prop_split
-from ..oot_utility import drawEnumWithCustom
-from ..collision.constants import ootEnumCameraCrawlspaceSType
-from ..actor.properties import OOTActorHeaderProperty
-from ..scene.properties import OOTAlternateSceneHeaderProperty
-
-
-ootSplineEnum = [("Path", "Path", "Path"), ("Crawlspace", "Crawlspace", "Crawlspace")]
-
-
-class OOTSplineProperty(PropertyGroup):
- splineType: EnumProperty(items=ootSplineEnum, default="Path")
- index: IntProperty(min=0) # only used for crawlspace, not path
- headerSettings: PointerProperty(type=OOTActorHeaderProperty)
- camSType: EnumProperty(items=ootEnumCameraCrawlspaceSType, default="CAM_SET_CRAWLSPACE")
- camSTypeCustom: StringProperty(default="CAM_SET_CRAWLSPACE")
-
- def draw_props(self, layout: UILayout, altSceneProp: OOTAlternateSceneHeaderProperty, objName: str):
- camIndexName = ""
- prop_split(layout, self, "splineType", "Type")
- if self.splineType == "Path":
- headerProp: OOTActorHeaderProperty = self.headerSettings
- headerProp.draw_props(layout, "Curve", altSceneProp, objName)
- camIndexName = "Path Index"
- elif self.splineType == "Crawlspace":
- layout.label(text="This counts as a camera for index purposes.", icon="INFO")
- drawEnumWithCustom(layout, self, "camSType", "Camera S Type", "")
- camIndexName = "Camera Index"
- prop_split(layout, self, "index", camIndexName)
-
-
-oot_spline_classes = (OOTSplineProperty,)
-
-
-def spline_props_register():
- for cls in oot_spline_classes:
- register_class(cls)
-
- Object.ootSplineProperty = PointerProperty(type=OOTSplineProperty)
-
-
-def spline_props_unregister():
- for cls in reversed(oot_spline_classes):
- unregister_class(cls)
-
- del Object.ootSplineProperty
diff --git a/fast64_internal/panels.py b/fast64_internal/panels.py
index a7c986dac..812914231 100644
--- a/fast64_internal/panels.py
+++ b/fast64_internal/panels.py
@@ -38,15 +38,15 @@ def poll(cls, context):
return scene_goal == "All" or sm64_props.goal == cls.goal or cls.goal == "All"
-class OOT_Panel(bpy.types.Panel):
+class Z64_Panel(bpy.types.Panel):
bl_space_type = "VIEW_3D"
bl_region_type = "UI"
- bl_category = "OOT"
+ bl_category = "OOT/MM"
bl_options = {"DEFAULT_CLOSED"}
@classmethod
def poll(cls, context):
- return context.scene.gameEditorMode == "OOT"
+ return context.scene.gameEditorMode in {"OOT", "MM"}
class MK64_Panel(bpy.types.Panel):
diff --git a/fast64_internal/render_settings.py b/fast64_internal/render_settings.py
index 29e5a7385..40a26def6 100644
--- a/fast64_internal/render_settings.py
+++ b/fast64_internal/render_settings.py
@@ -252,7 +252,7 @@ def on_update_render_settings(self, context: bpy.types.Context):
match context.scene.gameEditorMode:
case "SM64":
on_update_sm64_render_settings(self, context)
- case "OOT":
+ case "OOT" | "MM":
on_update_oot_render_settings(self, context)
case _:
pass
@@ -396,7 +396,7 @@ class Fast64RenderSettings_Properties(bpy.types.PropertyGroup):
update=on_update_sm64_render_settings,
poll=poll_sm64_area,
)
- # OOT
+ # OOT and MM
ootSceneObject: bpy.props.PointerProperty(
name="Scene Object",
type=bpy.types.Object,
diff --git a/fast64_internal/utility.py b/fast64_internal/utility.py
index e709391f1..25093c5f2 100644
--- a/fast64_internal/utility.py
+++ b/fast64_internal/utility.py
@@ -3,9 +3,12 @@
from math import pi, ceil, degrees, radians, copysign
from mathutils import *
from .utility_anim import *
-from typing import Callable, Iterable, Any, Optional, Tuple, TypeVar, Union
+from typing import Callable, Iterable, Any, Optional, Tuple, TypeVar, Union, TYPE_CHECKING
from bpy.types import UILayout, Scene, World
+if TYPE_CHECKING:
+ from .f3d.f3d_material import F3DMaterialProperty
+
CollectionProperty = Any # collection prop as defined by using bpy.props.CollectionProperty
@@ -1654,7 +1657,7 @@ def get_blender_to_game_scale(context):
match context.scene.gameEditorMode:
case "SM64":
return context.scene.fast64.sm64.blender_to_sm64_scale
- case "OOT":
+ case "OOT" | "MM":
return context.scene.ootBlenderScale
case "F3D":
# TODO: (V5) create F3D game editor mode, utilize that scale
@@ -1683,29 +1686,34 @@ def lightDataToObj(lightData):
)
-def ootGetSceneOrRoomHeader(parent, idx, isRoom):
+def ootGetSceneOrRoomHeader(parent: bpy.types.Object, idx: int, isRoom: bool):
+ from .game_data import game_data # circular import fix
+
# This should be in oot_utility.py, but it is needed in f3d_material.py
# which creates a circular import. The real problem is that the F3D render
# settings stuff should be in a place which can import both SM64 and OoT
# code without circular dependencies.
if idx < 0:
raise PluginError("Alternate scene/room header index too low: " + str(idx))
+
target = "Room" if isRoom else "Scene"
altHeaders = getattr(parent, "ootAlternate" + target + "Headers")
+
if idx == 0:
return getattr(parent, "oot" + target + "Header")
- elif 1 <= idx <= 3:
- if idx == 1:
- ret = altHeaders.childNightHeader
- elif idx == 2:
- ret = altHeaders.adultDayHeader
- else:
- ret = altHeaders.adultNightHeader
- return None if ret.usePreviousHeader else ret
- else:
- if idx - 4 >= len(altHeaders.cutsceneHeaders):
- return None
- return altHeaders.cutsceneHeaders[idx - 4]
+ elif game_data.z64.is_oot():
+ if 1 <= idx <= (game_data.z64.cs_index_start - 1):
+ if idx == 1:
+ ret = altHeaders.childNightHeader
+ elif idx == 2:
+ ret = altHeaders.adultDayHeader
+ else:
+ ret = altHeaders.adultNightHeader
+ return None if ret.usePreviousHeader else ret
+
+ if idx - game_data.z64.cs_index_start >= len(altHeaders.cutsceneHeaders):
+ return None
+ return altHeaders.cutsceneHeaders[idx - game_data.z64.cs_index_start]
def ootGetBaseOrCustomLight(prop, idx, toExport: bool, errIfMissing: bool):
@@ -1932,6 +1940,16 @@ def set_prop_if_in_data(owner: object, prop_name: str, data: dict, data_name: st
set_if_different(owner, prop_name, data[data_name])
+def get_prop_annotations(cls):
+ prop_annotations = getattr(cls, "__annotations__", None)
+
+ if prop_annotations is None:
+ setattr(cls, "__annotations__", dict())
+ prop_annotations = getattr(cls, "__annotations__")
+
+ return prop_annotations
+
+
def wrap_func_with_error_message(error_message: Callable):
"""Decorator for big, reused functions that need generic info in errors, such as material exports."""
diff --git a/fast64_internal/oot/README.md b/fast64_internal/z64/README.md
similarity index 53%
rename from fast64_internal/oot/README.md
rename to fast64_internal/z64/README.md
index abc3f3386..37e4fe15e 100644
--- a/fast64_internal/oot/README.md
+++ b/fast64_internal/z64/README.md
@@ -1,4 +1,4 @@
-# Ocarina Of Time
+# Ocarina Of Time and Majora's Mask
## Table of Contents
1. [Getting Started](#getting-started)
@@ -12,20 +12,23 @@
9. [Custom Link Process](#custom-link-process)
10. [Custom Skeleton Mesh Process](#custom-skeleton-mesh-process)
11. [Cutscenes](#cutscenes)
+12. [Actor Cutscenes](#actor-cutscenes)
### Getting Started
-1. In the 3D view properties sidebar (default hotkey to show this is `n` in the viewport), go to the ``Fast64`` tab, then ``Fast64 Global Settings`` and set ``Game`` to ``OOT``.
-2. Switch to the ``OOT`` tab. In ``OOT File Settings``, set your decomp path to the path of your [HackerOoT (recommended)](https://github.com/HackerN64/HackerOoT) or [OoT Decomp](https://github.com/zeldaret/oot/) repository on disk. Check `Enable HackerOoT Features` if using HackerOoT.
-3. In ``OOT Tools``, click `Add Scene` to create a basic scene.
+1. In the 3D view properties sidebar (default hotkey to show this is `n` in the viewport), go to the `Fast64` tab, then `Fast64 Global Settings` and set `Game` to `OOT` or `MM`.
+2. Switch to the `OOT/MM` tab. In `Workspace Settings`, set your decomp path to the path of your [HackerOoT (recommended)](https://github.com/HackerN64/HackerOoT), [OoT Decomp](https://github.com/zeldaret/oot/) or [MM](https://github.com/zeldaret/mm) repository on disk. Check `Enable HackerOoT Features` if using HackerOoT.
+3. In `Tools`, click `Add Scene` to create a basic scene.
4. Press `a` so that everything is selected, then click `Clear Transform`.
-5. In ``OOT Scene Exporter`` you can choose the scene to replace or add. Some scenes have some hardcoded things that will cause them to break, so choose something like ``Market Entrance (Child Day) (Entra)``.
-- To add a custom scene choose ``Custom`` in the scene search box, then choose in which folder you want to export the scene and which name you want it to be (note that Fast64 will force the scene name to be lower-case).
-- Enable ``Export as Single File`` if you want to have your scene in the same format as the other ones in decomp.
-6. Make sure you selected the right scene in ``Scene Object`` then click "Export Scene" to export it. When you click ``Add Scene`` this is set automatically.
+5. In `Scenes` you can choose the scene to replace or add. Some scenes have some hardcoded things that will cause them to break, so choose something like `Market Entrance (Child Day) (Entra)`.
+- To add a custom scene choose `Custom` in the scene search box, then choose in which folder you want to export the scene and which name you want it to be (note that Fast64 will force the scene name to be lower-case).
+- Enable `Export as Single File` if you want to have your scene in the same format as the other ones in decomp.
+6. Make sure you selected the right scene in `Scene Object` then click "Export Scene" to export it. When you click `Add Scene` this is set automatically.
7. Compile and run the game.
-8. (Optional) In the ``View`` tab you may want to increase the ``Clip End`` value.
+8. (Optional) In the `View` tab you may want to increase the `Clip End` value.
9. Note: You can read [this code](https://github.com/Dragorn421/oot/tree/mod_base_for_mods) to take a glance at what you can do for quality of life for testing.
+Note: You can use features from MM with OoT by enabling the MM features (see workspace settings when the game is set to `OOT`).
+
### Scene Overview
In Blender, the "empty" object type is used to define different types of OOT data, including scenes and rooms.
For scenes, there must be a specific parenting hierarchy:
@@ -43,30 +46,42 @@ Read the "Getting Started" section for information on scene exporting.
### Actors
To add an actor you need to create a new empty object in Blender, the shape doesn't matter.
-When the empty object is created you can set the ``Actor`` object type in the ``Object Properties`` panel.
+When the empty object is created you can set the `Actor` object type in the `Object Properties` panel.
-To add actors to a scene, create a new Empty and parent it to a Room, otherwise they will not be exported in the room C code. Then in the Object Properties panel select ``Actor`` as the Object Type. Use the ``Select Actor ID`` button to choose an actor, and then set the Actor Parameter value as desired (see the list of Actor Parameters below).
+To add actors to a scene, create a new Empty and parent it to a Room, otherwise they will not be exported in the room C code. Then in the Object Properties panel select `Actor` as the Object Type. Use the `Select Actor ID` button to choose an actor, and then set the Actor Parameter value as desired (see the list of Actor Parameters below).
Finally, every actors you are using needs their assets. In OoT they're called "Objects", if an actor is missing an object the code will not spawn the actor. To do this select the Room that your actor is parented to, select the "Objects" tab in its Object Properties window, and click "Add Item".
Then "Search Object ID" to find the actor object you need. For example, if adding a Deku Baba actor (EN_DEKUBABA) you need to add the "Dekubaba" object to the Room's object dependencies. Note that the object list must not contain more than 15 items.
#### Actor Parameters
-Actor parameters can be found at https://wiki.cloudmodding.com/oot/Actor_List_(Variables). This documentation is NOT 100% accurate, you can get more informations with the OoT Decomp. Look for ``rot.z`` and ``params`` in the actor you want, some actors may use ``rot.x`` and ``rot.y``.
+Actor parameters can be found at https://wiki.cloudmodding.com/oot/Actor_List_(Variables). This documentation is NOT 100% accurate, you can get more informations with the OoT Decomp. Look for `rot.z` and `params` in the actor you want, some actors may use `rot.x` and `rot.y`.
### Exits
-The debug menu scene select can be found at ``SceneSelectEntry sScenes[]`` in ``src/overlays/gamestates/ovl_select/z_select.c``.
-The last field of a ``SceneSelectEntry`` is the index into ``gEntranceTable`` (found in ``src/code/z_scene_table.c``).
+The entrance table can be found in `tables/entrance_table.h`. For MM search `EntranceSceneId` in `include/z64scene.h`.
All exits are basically an index into this table. Due to the way it works it makes it difficult to add/remove entries without breaking everything. For now the user will have to manually manage this table. For more info check out https://wiki.cloudmodding.com/oot/Entrance_Table.
### Scene Draw Configuration And Dynamic Material Properties
-The scene object has a property called ``Draw Config``. This is an index into ``sSceneDrawHandlers`` in ``src/code/z_scene_table.c``.
-Each function in this table will load certain Gfx commands such as scrolling textures or setting color registers into the beginning of a RAM segment using ``gsSPSegment()``. In Blender, in the material properties window you can choose which commands are called at the beginning of the material drawing. For example, to get animated water you need to use segment 9 with the draw config 19 (0x13).
+The scene object has a property called `Draw Config`. This is an index into `sSceneDrawHandlers` in `src/code/z_scene_table.c`.
+Each function in this table will load certain Gfx commands such as scrolling textures or setting color registers into the beginning of a RAM segment using `gsSPSegment()`. In Blender, in the material properties window you can choose which commands are called at the beginning of the material drawing. For example, to get animated water you need to use segment 9 with the draw config 19 (0x13).

Note that there are different segments loaded between the OPA (opaque) and XLU (transparent) draw layers.
-Additionally, for functions like ``Gfx_TexScroll()``, the x,y inputs are pre-shifted by <<2. For example, a % 128 means a repeat of the texture every 32 pixels.
+Additionally, for functions like `Gfx_TexScroll()`, the x,y inputs are pre-shifted by <<2. For example, a % 128 means a repeat of the texture every 32 pixels.
+
+## Animated Materials
+
+On Majora's Mask the draw config system changed. Now you only have a few configs available. To use animated materials, you need to change the draw config to "material animated" and add an empty object with the type "Animated Materials".
+
+The "Animated Materials List" element represents one `AnimatedMaterial` array. You can set the header index at this level. Then you can add elements in the first sub-list ("Animated Materials ([item count])"). You need to select the segment number corresponding to the one your material is using, for instance for a water-like material, if you pick segment 0x0A/10 in the dynamic material properties, you'll need to choose 10 for the "Segment Number" value (and choose "Draw Two Texture Scroll" for the draw handler type).
+
+Each Draw Handler Type requires elements in a list of the current "Animated Materials" item:
+- For texture scroll types, each element represent one texture scroll. This means for "Draw Two Texture Scroll" you will need two entries here. To create a water effect, you need to set two segments and add two items, one for each segment, with two "tex scroll" elements for each items. The width and height must match the width and height of the texture you picked on the material.
+- For "color" types, you need to set the total frame count and add keyframes. Each keyframe requires a primitive color and an environment color (and the "LOD Fraction"). This is also where you set the frame where this entry starts, this value must not exceed the total frame count.
+- For "cycle" types, it's very similar to color types, except you need to choose a texture symbol instead of a color. Note: this is a GIF-like system.
+
+You can choose between exporting with the scene or exporting with an actor with the "Export To" enum. (Default: "Scene")
### Collision
Collision properties are found underneath the material properties. Water boxes and collision properties will have references the properties "Camera", "Lighting", and "Exit", which reference the indices of the scene cameras, scene light list, and scene exit list respectively. If you want separate visual/collision geometry, you can set per mesh object "Ignore Collision" or "Ignore Render" in object inspector window.
@@ -142,12 +157,12 @@ For more informations about cutscenes [click here](cutscene_docs.md)
**Creating the cutscene itself:**
-1. Start with using the ``Add Cutscene`` button from the OOT Panel. Name it ``Cutscene.YOUR_CS_NAME``, ``YOUR_CS_NAME`` being the name of your choice, it can be something like: ``Cutscene.fireTempleIntroCS``. Note that this object can't be parented to any object, also this will automatically create a new camera and it will also set the Blender scene's active camera to this one.
-2. Select the scene where you want to add the cutscene, and in the object properties go in the ``Cutscene`` tab then enable ``Write Cutscene``. In ``Data`` select ``Object`` and in ``Cutscene Object`` select the cutscene empty object you just created.
-3. Now you can create the camera shot. The ``Create Camera Shot`` button from the cutscene object's properties panel will add a basic shot with 4 bones that you can edit. To have a better idea of the position/angle of one point of the shot, you can change the display of the armature in the ``Object Data Properties`` panel (select the shot object first), choose ``Octahedral`` in ``Viewport Display`` then ``Display As``.
-4. The first and last bone of the shot won't be actual camera points, it defines the start and the end of a camera shot, this means a basic shot will only have 2 actual camera points. Either duplicate (``SHIFT+D``) or add a new bone to add more camera points. Note that these points will be exported in the order you can see in the view layer.
-5. You can edit the position and rotation of each bone in the ``Edit Mode`` after selecting the shot object. The "tail" (less large point) of a bone is the direction (the "look-at", or AT), and "head" (larger point) is the origin (the "eye").
-7. When you're done with your cutscene, exporting the scene will also export the camera shot and actor cue data. If you don't want to re-export the scene everytime, select the cutscene object you want to export and use ``Export Cutscene`` from ``OOT Cutscene Exporter`` in the OOT panel. In the ``File`` field, you can choose the scene of your choice (note that it can export into actors too). You can toggle the usage of decomp's names and macros with the ``Use Decomp for Export`` checkbox, you can also choose to insert the motion data in an existing cutscene (it will create a new array if it can't find it, it's based on the name of the object you selected). This is done by toggling the ``Export Motion Data Only`` checkbox.
+1. Start with using the `Add Cutscene` button from the OOT Panel. Name it `Cutscene.YOUR_CS_NAME`, `YOUR_CS_NAME` being the name of your choice, it can be something like: `Cutscene.fireTempleIntroCS`. Note that this object can't be parented to any object, also this will automatically create a new camera and it will also set the Blender scene's active camera to this one.
+2. Select the scene where you want to add the cutscene, and in the object properties go in the `Cutscene` tab then enable `Write Cutscene`. In `Data` select `Object` and in `Cutscene Object` select the cutscene empty object you just created.
+3. Now you can create the camera shot. The `Create Camera Shot` button from the cutscene object's properties panel will add a basic shot with 4 bones that you can edit. To have a better idea of the position/angle of one point of the shot, you can change the display of the armature in the `Object Data Properties` panel (select the shot object first), choose `Octahedral` in `Viewport Display` then `Display As`.
+4. The first and last bone of the shot won't be actual camera points, it defines the start and the end of a camera shot, this means a basic shot will only have 2 actual camera points. Either duplicate (`SHIFT+D`) or add a new bone to add more camera points. Note that these points will be exported in the order you can see in the view layer.
+5. You can edit the position and rotation of each bone in the `Edit Mode` after selecting the shot object. The "tail" (less large point) of a bone is the direction (the "look-at", or AT), and "head" (larger point) is the origin (the "eye").
+7. When you're done with your cutscene, exporting the scene will also export the camera shot and actor cue data. If you don't want to re-export the scene everytime, select the cutscene object you want to export and use `Export Cutscene` from `OOT Cutscene Exporter` in the OOT panel. In the `File` field, you can choose the scene of your choice (note that it can export into actors too). You can toggle the usage of decomp's names and macros with the `Use Decomp for Export` checkbox, you can also choose to insert the motion data in an existing cutscene (it will create a new array if it can't find it, it's based on the name of the object you selected). This is done by toggling the `Export Motion Data Only` checkbox.
8. Compile the game.
@@ -160,30 +175,30 @@ For more informations about cutscenes [click here](cutscene_docs.md)
To be able to actually watch your cutscene you need to have a way to trigger it, this can be done by an actor (for instance) or using the entrance cutscene table. This guide will be explaining how to use an entrance.
-1. Open ``src/code/z_demo.c`` and add an ``#include`` with the path of the file containing your cutscene.
-2. Add an entry at the end of ``EntranceCutscene sEntranceCutsceneTable[]``, the format is:
-``{ ENTRANCE_NUMBER, AGE_RESTRICTION, FLAG, SEGMENT_ADDRESS }``
-- ``ENTRANCE_NUMBER`` is the entrance index in ``gEntranceTable``
-- ``AGE_RESTRICTION`` defines if you want to play your cutscene only as child (set it to 1), as adult (set it to 0) or both (set it to 2)
-- ``FLAG`` is the ``event_chk_inf`` flag that will prevent playing the cutscene everytime, you can use something unused like ``0x0F`` (https://wiki.cloudmodding.com/oot/Save_Format#event_chk_inf for more informations)
-- ``SEGMENT_ADDRESS`` is the important part. This is the memory address where your cutscene is located. Using the ``#include`` will allow you to use the name of the array containing the cutscene commands in the file you exported you're cutscene, if you named the cutscene object ``Cutscene.YOUR_CS_NAME`` then this array will be named ``YOUR_CS_NAME``, use that name for the segment address.
-3. Example with: ``{ ENTR_SPOT00_3, 2, EVENTCHKINF_A0, gHyruleFieldIntroCs },``
-- ``ENTR_SPOT00_3`` is the Hyrule Field entrance from Lost Woods, see ``entrance_table.h`` to view/add entrances
-- ``2`` means this cutscene can be watched as child AND as adult
-- ``EVENTCHKINF_A0`` is the flag set in the ``event_chk_inf`` table, this is a macro but you can use raw hex: ``0xA0``
-- ``gHyruleFieldIntroCs`` is the name of the array with the cutscene commands, as defined in ``extracted/VERSION/assets/scenes/overworld/spot00_scene.c``, ``CutsceneData gHyruleFieldIntroCs[]``
-4. Compile the game again and use the entrance you chose for ``sEntranceCutsceneTable`` and your cutscene should play.
+1. Open `src/code/z_demo.c` and add an `#include` with the path of the file containing your cutscene.
+2. Add an entry at the end of `EntranceCutscene sEntranceCutsceneTable[]`, the format is:
+`{ ENTRANCE_NUMBER, AGE_RESTRICTION, FLAG, SEGMENT_ADDRESS }`
+- `ENTRANCE_NUMBER` is the entrance index in `gEntranceTable`
+- `AGE_RESTRICTION` defines if you want to play your cutscene only as child (set it to 1), as adult (set it to 0) or both (set it to 2)
+- `FLAG` is the `event_chk_inf` flag that will prevent playing the cutscene everytime, you can use something unused like `0x0F` (https://wiki.cloudmodding.com/oot/Save_Format#event_chk_inf for more informations)
+- `SEGMENT_ADDRESS` is the important part. This is the memory address where your cutscene is located. Using the `#include` will allow you to use the name of the array containing the cutscene commands in the file you exported you're cutscene, if you named the cutscene object `Cutscene.YOUR_CS_NAME` then this array will be named `YOUR_CS_NAME`, use that name for the segment address.
+3. Example with: `{ ENTR_SPOT00_3, 2, EVENTCHKINF_A0, gHyruleFieldIntroCs },`
+- `ENTR_SPOT00_3` is the Hyrule Field entrance from Lost Woods, see `entrance_table.h` to view/add entrances
+- `2` means this cutscene can be watched as child AND as adult
+- `EVENTCHKINF_A0` is the flag set in the `event_chk_inf` table, this is a macro but you can use raw hex: `0xA0`
+- `gHyruleFieldIntroCs` is the name of the array with the cutscene commands, as defined in `extracted/VERSION/assets/scenes/overworld/spot00_scene.c`, `CutsceneData gHyruleFieldIntroCs[]`
+4. Compile the game again and use the entrance you chose for `sEntranceCutsceneTable` and your cutscene should play.
Alternatively, you can use the map select to watch your cutscene, though note that this won't make it watchable during normal gameplay:
-1. Open ``src/overlays/gamestates/ovl_select/z_select.c``
-2. Either edit or add an entry inside ``SceneSelectEntry sScenes[]``, for instance: ``{ "My Scene", MapSelect_LoadGame, ENTR_MYSCENE_0 },`` (note that the entrance used is the first of the block you need to have for the scene)
-3. Compile the game, you may or may not need to run ``make clean`` first if you edited the entrance table
-4. Get on the map select then scroll until you see your new entry (in the previous example is will be called "My Scene") then press R to change the header, on the vanilla map select the first cutscene header will be called ``デモ00``, on HackerOoT it will be ``Cutscene 0`` then press A to start the cutscene.
+1. Open `src/overlays/gamestates/ovl_select/z_select.c`
+2. Either edit or add an entry inside `SceneSelectEntry sScenes[]`, for instance: `{ "My Scene", MapSelect_LoadGame, ENTR_MYSCENE_0 },` (note that the entrance used is the first of the block you need to have for the scene)
+3. Compile the game, you may or may not need to run `make clean` first if you edited the entrance table
+4. Get on the map select then scroll until you see your new entry (in the previous example is will be called "My Scene") then press R to change the header, on the vanilla map select the first cutscene header will be called `デモ00`, on HackerOoT it will be `Cutscene 0` then press A to start the cutscene.
-Note that you can have the actual address of your cutscene if you use ``sym_info.py`` from decomp. Example with ``gHyruleFieldIntroCs``:
-- Command: ``./sym_info.py gHyruleFieldIntroCs``
-- Result: ``Symbol gHyruleFieldIntroCs (RAM: 0x02013AA0, ROM: 0x27E9AA0, build/assets/scenes/overworld/spot00/spot00_scene.o)``
+Note that you can have the actual address of your cutscene if you use `sym_info.py` from decomp. Example with `gHyruleFieldIntroCs`:
+- Command: `./sym_info.py gHyruleFieldIntroCs`
+- Result: `Symbol gHyruleFieldIntroCs (RAM: 0x02013AA0, ROM: 0x27E9AA0, build/assets/scenes/overworld/spot00/spot00_scene.o)`
If you have a softlock in-game then you probably did something wrong when creating the cutscene. Make sure you set up the bones properly. The softlock means the game is playing a cutscene but it's probably reading wrong data. Make sure the cutscene is exported, if it's not export it again.
@@ -194,3 +209,38 @@ If the camera preview in Blender isn't following where you have the bones or if
2. If you moved / rotated / etc. one of the camera shots / armatures in object mode, this transformation will be ignored. You can fix this by selecting the shot / armature in object mode and clicking Object > Apply > All Transforms. That will convert the transform to actual changed positions for each bone.
If the game crashes check the transitions if you use the transition command (check both the ones from the entrance table and your cutscene script), also it will crash if you try to use the map select without having a 5th entrance (or more depending on the number of cutscenes you have) in the group for your scene.
+
+### Actor Cutscenes
+
+To get started, add an empty object with the "Actor Cutscene" type then add entries in the list.
+
+The informations required are the following:
+- `Priority`: how important the cutscene is, lower value means higher priority.
+- `Length`: the length of the cutscene, only useful when you're not using a cutscene script. Can be -1 if using a script.
+- `CS Cam ID`: the cutscene camera type. **IMPORTANT**: because of how `Play_AssignPlayerCsIdsFromScene` works, you need to match the order of the `PlayerCsId` enum (see `include/z64cutscene.h`) otherwise it won't work properly. Can be -1 if using a script.
+- `Script Index`: the index into the `CutsceneScriptEntry` array. **IMPORTANT**: this takes the priority over `CS Cam ID`, it's very important you leave this value to -1 if you don't want to use a script.
+- `Additional CS ID`: another index into `CutsceneEntry`, some actors might require using this (the actor responsible for "dawn/night of the X day", `en_test4`, requires this for instance).
+- `End Sound Effect`: the sound effect to play when the cutscene ends.
+- `Custom Value`: 0 - 99: actor-specific custom value. 100+: spawn. 255: none
+- `HUD Visibility`: which elements of the HUD to hide.
+- `End Cam`: the camera's behavior when the cutscene ends.
+- `Letterbox Size`: the size of the letterbox (the two black rectangles at the top and the bottom of the screen)
+
+If you want to use your own camera, add a camera object and enable "Is Actor CS Camera". Set the index, this is NOT the same order as normal scene cameras so choose 0 for the first actor cutscene camera you add. Finally, back in the actor cutscene entry, choose "Camera Object" as the value of `CS Cam ID` and select the camera you've just created. For the camera setting simply change that from the camera object you added.
+
+#### Three-Day Timer Actor Cutscenes
+
+This actor handles the day/night transitions (and going the "Dawn of the X Day" gamestates), to setup an actor cutscene for this actor, you need to know few things:
+- you need two different cameras
+- the cameras you will bind to the actor cutscene entry needs to use a "fixed" setting
+- you need two actor cutscene entries (one for the day, even though it's not used, and one for the night), the order doesn't really matter
+- you need to bind the `Actor CS Index` to the entry you will "use" for the day and it's required to set the `Additional CS ID` to the index of the night entry
+- it's also required to set the `Length` value, usually this is set to 90 frames, which corresponds to 4.5 seconds
+
+#### Treasure Chest Actor Cutscenes
+
+To achieve this you have two solutions:
+- enable `Use Global Actor Cutscene` and bind the `Actor CS Index` to a global entry
+- use actor-embedded actor cutscenes like the Three-Day Timer actor, but this is NOT recommended if you have several chests as it will increase the number of actor cutscene entries in the array
+
+For both cases the `CS Cam ID` of the actor cutscene entry need to be set to `Long Chest Opening` with a length of 135 frames (6.75 seconds) and the `Custom Value` need to be set to 1, the other parameters doesn't matter.
diff --git a/fast64_internal/oot/__init__.py b/fast64_internal/z64/__init__.py
similarity index 75%
rename from fast64_internal/oot/__init__.py
rename to fast64_internal/z64/__init__.py
index e688445ac..b99d548c5 100644
--- a/fast64_internal/oot/__init__.py
+++ b/fast64_internal/z64/__init__.py
@@ -1,27 +1,31 @@
import bpy
+from ..game_data import game_data
from pathlib import Path
-from bpy.utils import register_class, unregister_class
from ..utility import PluginError
from .scene.operators import scene_ops_register, scene_ops_unregister
-from .scene.properties import OOTBootupSceneOptions, scene_props_register, scene_props_unregister
+from .scene.properties import OOT_BootupSceneOptions, scene_props_register, scene_props_unregister
from .scene.panels import scene_panels_register, scene_panels_unregister
-from .props_panel_main import oot_obj_panel_register, oot_obj_panel_unregister, oot_obj_register, oot_obj_unregister
+from .props_panel_main import (
+ oot_obj_panel_register,
+ oot_obj_panel_unregister,
+ oot_obj_register,
+ oot_obj_unregister,
+ OOT_ObjectProperties,
+ OOTSceneProperties,
+)
from .skeleton.properties import OOTSkeletonImportSettings, OOTSkeletonExportSettings
from .collection_utility import collections_register, collections_unregister
-from .oot_utility import setAllActorsVisibility
+from .utility import setAllActorsVisibility
from .file_settings import file_register, file_unregister
from .collision.properties import OOTCollisionExportSettings
from .room.operators import room_ops_register, room_ops_unregister
from .room.properties import room_props_register, room_props_unregister
-from .actor.operators import actor_ops_register, actor_ops_unregister
-from .actor.properties import actor_props_register, actor_props_unregister
-
from .f3d.operators import f3d_ops_register, f3d_ops_unregister
from .f3d.properties import OOTDLExportSettings, OOTDLImportSettings, f3d_props_register, f3d_props_unregister
from .f3d.panels import f3d_panels_register, f3d_panels_unregister
@@ -56,6 +60,9 @@
from .spline.properties import spline_props_register, spline_props_unregister
from .spline.panels import spline_panels_register, spline_panels_unregister
+from .animated_mats.properties import animated_mats_register, animated_mats_unregister
+from .actor_cutscene.properties import actor_cs_register, actor_cs_unregister
+
from .tools import (
oot_operator_panel_register,
oot_operator_panel_unregister,
@@ -63,6 +70,7 @@
oot_operator_unregister,
)
+
oot_versions_items = [
("Custom", "Custom", "Custom", 0),
("ntsc-1.0", "ntsc-1.0", "ntsc-1.0", 11),
@@ -83,6 +91,12 @@
("legacy", "Legacy", "Older Decomp Version", 10),
]
+mm_versions_items = [
+ ("Custom", "Custom", "Custom"),
+ ("n64-us", "n64-us", "n64-us"),
+ ("legacy", "Legacy", "Older Decomp Version"),
+]
+
class OOT_Properties(bpy.types.PropertyGroup):
"""Global OOT Scene Properties found under scene.fast64.oot"""
@@ -92,7 +106,7 @@ class OOT_Properties(bpy.types.PropertyGroup):
headerTabAffectsVisibility: bpy.props.BoolProperty(
default=False, name="Header Sets Actor Visibility", update=setAllActorsVisibility
)
- bootupSceneOptions: bpy.props.PointerProperty(type=OOTBootupSceneOptions)
+ bootupSceneOptions: bpy.props.PointerProperty(type=OOT_BootupSceneOptions)
DLExportSettings: bpy.props.PointerProperty(type=OOTDLExportSettings)
DLImportSettings: bpy.props.PointerProperty(type=OOTDLImportSettings)
skeletonExportSettings: bpy.props.PointerProperty(type=OOTSkeletonExportSettings)
@@ -101,13 +115,20 @@ class OOT_Properties(bpy.types.PropertyGroup):
animImportSettings: bpy.props.PointerProperty(type=OOTAnimImportSettingsProperty)
collisionExportSettings: bpy.props.PointerProperty(type=OOTCollisionExportSettings)
oot_version: bpy.props.EnumProperty(name="OoT Version", items=oot_versions_items, default="gc-eu-mq-dbg")
- oot_version_custom: bpy.props.StringProperty(name="Custom Version")
+ mm_version: bpy.props.EnumProperty(name="OoT Version", items=mm_versions_items, default="n64-us")
+ version_custom: bpy.props.StringProperty(name="Custom Version")
+ mm_features: bpy.props.BoolProperty(name="Enable MM Features", default=False)
+
+ # internal
+ global_actor_cs_count: bpy.props.IntProperty(min=0, default=0)
def get_extracted_path(self):
- if self.oot_version == "legacy":
+ version = self.oot_version if game_data.z64.is_oot() else self.mm_version
+
+ if version == "legacy":
return "."
else:
- return f"extracted/{self.oot_version if self.oot_version != 'Custom' else self.oot_version_custom}"
+ return f"extracted/{version if version != 'Custom' else self.version_custom}"
def is_globalh_present(self):
decomp_path = Path(bpy.context.scene.ootDecompPath).resolve()
@@ -118,6 +139,12 @@ def is_globalh_present(self):
global_h_path = decomp_path / "include" / "global.h"
return global_h_path.exists()
+ def can_use_new_actor_panel(self):
+ if game_data.z64.is_mm():
+ return False
+
+ return self.use_new_actor_panel
+
useDecompFeatures: bpy.props.BoolProperty(
name="Use decomp for export", description="Use names and macros from decomp when exporting", default=True
)
@@ -138,7 +165,19 @@ def is_globalh_present(self):
)
-oot_classes = (OOT_Properties,)
+z64_register_on_enable = (
+ OOT_BootupSceneOptions,
+ OOTDLExportSettings,
+ OOTDLImportSettings,
+ OOTSkeletonExportSettings,
+ OOTSkeletonImportSettings,
+ OOTAnimExportSettingsProperty,
+ OOTAnimImportSettingsProperty,
+ OOTCollisionExportSettings,
+ OOT_Properties,
+ OOTSceneProperties,
+ OOT_ObjectProperties,
+)
def oot_panel_register():
@@ -165,65 +204,88 @@ def oot_panel_unregister():
skeleton_panels_unregister()
-def oot_register(registerPanels):
+def z64_register_ops():
+ from .actor.operators import actor_ops_register
+
+ collision_ops_register()
+ scene_ops_register()
+ room_ops_register()
+ actor_ops_register()
+ anim_ops_register()
+ skeleton_ops_register()
+ cutscene_ops_register()
+ f3d_ops_register()
+ csMotion_ops_register()
+
+
+def oot_register(registerPanels: bool, register_ops: bool = True):
+ from .actor.properties import actor_props_register
+
oot_operator_register()
collections_register()
- collision_ops_register() # register first, so panel goes above mat panel
- collision_props_register()
+
+ collision_props_register() # register first, so panel goes above mat panel
cutscene_props_register()
- scene_ops_register()
scene_props_register()
- room_ops_register()
room_props_register()
- actor_ops_register()
actor_props_register()
oot_obj_register()
spline_props_register()
f3d_props_register()
- anim_ops_register()
- skeleton_ops_register()
skeleton_props_register()
- cutscene_ops_register()
- f3d_ops_register()
file_register()
anim_props_register()
+ animated_mats_register()
+ actor_cs_register()
- csMotion_ops_register()
csMotion_props_register()
csMotion_panels_register()
csMotion_preview_register()
cutscene_preview_register()
- for cls in oot_classes:
- register_class(cls)
+ if register_ops:
+ z64_register_ops()
if registerPanels:
oot_panel_register()
-def oot_unregister(unregisterPanels):
- for cls in reversed(oot_classes):
- unregister_class(cls)
+def z64_unregister_ops():
+ from .actor.operators import actor_ops_unregister
+
+ collision_ops_unregister()
+ scene_ops_unregister()
+ room_ops_unregister()
+ actor_ops_unregister()
+ anim_ops_unregister()
+ skeleton_ops_unregister()
+ cutscene_ops_unregister()
+ f3d_ops_unregister()
+ csMotion_ops_unregister()
+
+
+def oot_unregister(unregisterPanels: bool, unregister_ops: bool = True):
+ from .actor.properties import actor_props_unregister
+ if unregisterPanels:
+ oot_panel_unregister()
+
+ if unregister_ops:
+ z64_unregister_ops()
+
+ actor_cs_unregister()
+ animated_mats_unregister()
oot_operator_unregister()
collections_unregister()
- collision_ops_unregister() # register first, so panel goes above mat panel
collision_props_unregister()
oot_obj_unregister()
cutscene_props_unregister()
- scene_ops_unregister()
scene_props_unregister()
- room_ops_unregister()
room_props_unregister()
- actor_ops_unregister()
actor_props_unregister()
spline_props_unregister()
f3d_props_unregister()
- anim_ops_unregister()
- skeleton_ops_unregister()
skeleton_props_unregister()
- cutscene_ops_unregister()
- f3d_ops_unregister()
file_unregister()
anim_props_unregister()
@@ -231,7 +293,3 @@ def oot_unregister(unregisterPanels):
csMotion_preview_unregister()
csMotion_panels_unregister()
csMotion_props_unregister()
- csMotion_ops_unregister()
-
- if unregisterPanels:
- oot_panel_unregister()
diff --git a/fast64_internal/oot/actor/operators.py b/fast64_internal/z64/actor/operators.py
similarity index 88%
rename from fast64_internal/oot/actor/operators.py
rename to fast64_internal/z64/actor/operators.py
index 44cc60939..6d5cfddee 100644
--- a/fast64_internal/oot/actor/operators.py
+++ b/fast64_internal/z64/actor/operators.py
@@ -3,7 +3,7 @@
from bpy.props import EnumProperty, StringProperty
from bpy.utils import register_class, unregister_class
from ...utility import PluginError
-from ..oot_constants import ootData
+from ...game_data import game_data
class OOT_SearchChestContentEnumOperator(Operator):
@@ -12,7 +12,7 @@ class OOT_SearchChestContentEnumOperator(Operator):
bl_property = "chest_content"
bl_options = {"REGISTER", "UNDO"}
- chest_content: EnumProperty(items=ootData.actorData.ootEnumChestContent, default="item_heart")
+ chest_content: EnumProperty(items=lambda self, context: game_data.z64.get_enum("chest_content"), default=1)
obj_name: StringProperty()
prop_name: StringProperty()
@@ -33,7 +33,7 @@ class OOT_SearchNaviMsgIDEnumOperator(Operator):
bl_property = "navi_msg_id"
bl_options = {"REGISTER", "UNDO"}
- navi_msg_id: EnumProperty(items=ootData.actorData.ootEnumNaviMessageData, default="msg_00")
+ navi_msg_id: EnumProperty(items=lambda self, context: game_data.z64.get_enum("navi_msg_id"), default=1)
obj_name: StringProperty()
prop_name: StringProperty()
@@ -54,7 +54,7 @@ class OOT_SearchActorIDEnumOperator(Operator):
bl_property = "actor_id"
bl_options = {"REGISTER", "UNDO"}
- actor_id: EnumProperty(items=lambda self, context: ootData.actorData.getItems(self.actor_user))
+ actor_id: EnumProperty(items=lambda self, context: game_data.z64.actors.getItems(self.actor_user))
actor_user: StringProperty(default="Actor")
obj_name: StringProperty()
diff --git a/fast64_internal/oot/actor/properties.py b/fast64_internal/z64/actor/properties.py
similarity index 54%
rename from fast64_internal/oot/actor/properties.py
rename to fast64_internal/z64/actor/properties.py
index 17b1fcb8c..7f871738c 100644
--- a/fast64_internal/oot/actor/properties.py
+++ b/fast64_internal/z64/actor/properties.py
@@ -1,13 +1,14 @@
import bpy
-from bpy.types import Object, PropertyGroup, UILayout
+from bpy.types import Object, PropertyGroup, UILayout, Context
from bpy.utils import register_class, unregister_class
from bpy.props import EnumProperty, StringProperty, IntProperty, BoolProperty, CollectionProperty, PointerProperty
-from ...utility import PluginError, prop_split, label_split
-from ..oot_constants import ootData, ootEnumCamTransition
-from ..oot_upgrade import upgradeActors
-from ..scene.properties import OOTAlternateSceneHeaderProperty
-from ..room.properties import OOTAlternateRoomHeaderProperty
+from ...utility import PluginError, prop_split, label_split, get_prop_annotations
+from ...game_data import game_data
+from ..constants import ootEnumCamTransition
+from ..upgrade import upgradeActors
+from ..scene.properties import Z64_AlternateSceneHeaderProperty
+from ..room.properties import Z64_AlternateRoomHeaderProperty
from ..collection_utility import drawAddButton, drawCollectionOps
from .operators import (
OOT_SearchActorIDEnumOperator,
@@ -15,10 +16,12 @@
OOT_SearchNaviMsgIDEnumOperator,
)
-from ..oot_utility import (
+from ..utility import (
getRoomObj,
getEnumName,
drawEnumWithCustom,
+ is_oot_features,
+ get_list_tab_text,
getEvalParams,
getEvalParamsInt,
getShiftFromMask,
@@ -31,8 +34,29 @@
("All Non-Cutscene Scene Setups", "All Non-Cutscene Scene Setups", "All Non-Cutscene Scene Setups"),
]
+enum_actor_menu = [
+ ("General", "General", "General"),
+ ("Actor Cutscene", "Actor Cutscene", "Actor Cutscene"),
+]
+
+enum_half_day = [
+ ("Custom", "Custom", "Custom"),
+ ("0-Dawn", "Day 0 (Intro) - Dawn", "Day 0 - Dawn"),
+ ("0-Night", "Day 0 (Intro) - Night", "Day 0 - Night"),
+ ("1-Dawn", "Day 1 - Dawn", "Day 1 - Dawn"),
+ ("1-Night", "Day 1 - Night", "Day 1 - Night"),
+ ("2-Dawn", "Day 2 - Dawn", "Day 2 - Dawn"),
+ ("2-Night", "Day 2 - Night", "Day 2 - Night"),
+ ("3-Dawn", "Day 3 - Dawn", "Day 3 - Dawn"),
+ ("3-Night", "Day 3 - Night", "Day 3 - Night"),
+ ("4-Dawn", "Day 4 (Credits) - Dawn", "Day 4 - Dawn"),
+ ("4-Night", "Day 4 (Credits) - Night", "Day 4 - Night"),
+]
+
-def get_prop_name(actor_key: str, param_type: str, param_subtype: str, param_index: int):
+def get_prop_name(actor_key: str, param_type: str, param_subtype: str, param_index: int, update: bool = True):
+ if update:
+ game_data.z64.update(bpy.context, None)
flag_to_prop_suffix = {"Chest": "chestFlag", "Collectible": "collectibleFlag", "Switch": "switchFlag"}
param_to_prop_suffix = {
"Type": "type",
@@ -44,26 +68,24 @@ def get_prop_name(actor_key: str, param_type: str, param_subtype: str, param_ind
"Message": "naviMsg",
}
suffix = param_to_prop_suffix[param_type] if param_type != "Flag" else flag_to_prop_suffix[param_subtype]
- return f"{actor_key}.{suffix}{param_index}" # e.g.: ``en_test.props1``
-
+ return f"{game_data.z64.game.lower()}.{actor_key}.{suffix}{param_index}" # e.g.: `oot.en_test.props1`
-def initOOTActorProperties():
- """This function is used to edit the OOTActorProperty class"""
- prop_annotations = getattr(OOTActorProperty, "__annotations__", None)
+def create_game_props(game: str):
+ """This function is used to edit the Z64_ActorProperty class"""
- if prop_annotations is None:
- OOTActorProperty.__annotations__ = prop_annotations = {}
+ game_data.z64.update(None, game, True)
+ prop_ats = get_prop_annotations(Z64_ActorProperty)
param_type_to_enum_items = {
- "ChestContent": ootData.actorData.ootEnumChestContent,
- "Collectible": ootData.actorData.ootEnumCollectibleItems,
- "Message": ootData.actorData.ootEnumNaviMessageData,
+ "ChestContent": game_data.z64.actors.ootEnumChestContent,
+ "Collectible": game_data.z64.actors.ootEnumCollectibleItems,
+ "Message": game_data.z64.actors.ootEnumNaviMessageData,
}
- for actor in ootData.actorData.actorList:
+ for actor in game_data.z64.actors.actorList:
for param in actor.params:
- prop_name = get_prop_name(actor.key, param.type, param.subType, param.index)
+ prop_name = get_prop_name(actor.key, param.type, param.subType, param.index, update=False)
enum_items = None
if len(param.items) > 0:
@@ -73,102 +95,154 @@ def initOOTActorProperties():
enum_items = param_type_to_enum_items[param.type]
if param.type in {"Property", "Flag"}:
- prop_annotations[prop_name] = StringProperty(name="", default="0x0")
+ prop_ats[prop_name] = StringProperty(name="", default="0x0")
elif param.type == "Bool":
- prop_annotations[prop_name] = BoolProperty(name="", default=False)
+ prop_ats[prop_name] = BoolProperty(name="", default=False)
elif param.type in {"Type", "Enum", "ChestContent", "Collectible", "Message"} and enum_items is not None:
- prop_annotations[prop_name] = EnumProperty(name="", items=enum_items, default=enum_items[1][0])
+ prop_ats[prop_name] = EnumProperty(name="", items=enum_items, default=enum_items[1][0])
if param.type in {"Type", "Enum", "ChestContent", "Collectible", "Message"}:
- prop_annotations[f"{prop_name}_custom"] = StringProperty(name="", default="0x0")
+ prop_ats[f"{prop_name}_custom"] = StringProperty(name="", default="0x0")
+
+
+class Z64_HalfdayItem(PropertyGroup):
+ value: EnumProperty(items=enum_half_day, default=1)
+ value_custom: StringProperty()
+
+ def draw_props(self, layout: UILayout, owner: Object, index: int):
+ layout = layout.column()
+ row = layout.row()
+ row.prop(self, "value", text="")
+ if self.value == "Custom":
+ row.prop(self, "value_custom", text="")
+ drawCollectionOps(row.row(align=True), index, "Actor Halfday", None, owner.name, compact=True)
+
+
+# TODO: remove
+def update_cutscene_index(self, context: Context):
+ if self.headerIndex < game_data.z64.cs_index_start:
+ self.headerIndex = game_data.z64.cs_index_start
-class OOTActorHeaderItemProperty(PropertyGroup):
- headerIndex: IntProperty(name="Scene Setup", min=4, default=4)
- expandTab: BoolProperty(name="Expand Tab")
+class Z64_ActorHeaderItemProperty(PropertyGroup):
+ headerIndex: IntProperty(name="Scene Setup", min=1, default=1, update=update_cutscene_index)
def draw_props(
self,
layout: UILayout,
propUser: str,
index: int,
- altProp: OOTAlternateSceneHeaderProperty | OOTAlternateRoomHeaderProperty,
+ altProp: Z64_AlternateSceneHeaderProperty | Z64_AlternateRoomHeaderProperty,
objName: str,
):
box = layout.column()
row = box.row()
row.prop(self, "headerIndex", text="")
+
drawCollectionOps(row.row(align=True), index, propUser, None, objName, compact=True)
- if altProp is not None and self.headerIndex >= len(altProp.cutsceneHeaders) + 4:
+
+ if altProp is not None and self.headerIndex >= len(altProp.cutsceneHeaders) + game_data.z64.cs_index_start:
box.label(text="Above header does not exist.", icon="QUESTION")
-class OOTActorHeaderProperty(PropertyGroup):
- sceneSetupPreset: EnumProperty(name="Scene Setup Preset", items=ootEnumSceneSetupPreset, default="All Scene Setups")
+class Z64_ActorHeaderProperty(PropertyGroup):
childDayHeader: BoolProperty(name="Child Day Header", default=True)
+ cutsceneHeaders: CollectionProperty(type=Z64_ActorHeaderItemProperty)
+
+ # OoT exclusive
+ sceneSetupPreset: EnumProperty(name="Scene Setup Preset", items=ootEnumSceneSetupPreset, default="All Scene Setups")
childNightHeader: BoolProperty(name="Child Night Header", default=True)
adultDayHeader: BoolProperty(name="Adult Day Header", default=True)
adultNightHeader: BoolProperty(name="Adult Night Header", default=True)
- cutsceneHeaders: CollectionProperty(type=OOTActorHeaderItemProperty)
+
+ # MM exclusive
+ include_in_all_setups: BoolProperty(name="Include in all scene setups")
+ expand_tab: BoolProperty(name="Expand Tab")
def checkHeader(self, index: int) -> bool:
if index == 0:
return self.childDayHeader
- elif index == 1:
- return self.childNightHeader
- elif index == 2:
- return self.adultDayHeader
- elif index == 3:
- return self.adultNightHeader
- else:
- return index in [value.headerIndex for value in self.cutsceneHeaders]
+ elif game_data.z64.is_oot():
+ if index == 1:
+ return self.childNightHeader
+ elif index == 2:
+ return self.adultDayHeader
+ elif index == 3:
+ return self.adultNightHeader
+
+ return index in [value.headerIndex for value in self.cutsceneHeaders]
def draw_props(
self,
layout: UILayout,
propUser: str,
- altProp: OOTAlternateSceneHeaderProperty | OOTAlternateRoomHeaderProperty,
+ altProp: Z64_AlternateSceneHeaderProperty | Z64_AlternateRoomHeaderProperty,
objName: str,
):
headerSetup = layout.column()
- # headerSetup.box().label(text = "Alternate Headers")
- prop_split(headerSetup, self, "sceneSetupPreset", "Scene Setup Preset")
- if self.sceneSetupPreset == "Custom":
- headerSetupBox = headerSetup.column()
- headerSetupBox.prop(self, "childDayHeader", text="Child Day")
- prevHeaderName = "childDayHeader"
- childNightRow = headerSetupBox.row()
- if altProp is None or altProp.childNightHeader.usePreviousHeader:
- # Draw previous header checkbox (so get previous state), but labeled
- # as current one and grayed out
- childNightRow.prop(self, prevHeaderName, text="Child Night")
- childNightRow.enabled = False
- else:
- childNightRow.prop(self, "childNightHeader", text="Child Night")
- prevHeaderName = "childNightHeader"
- adultDayRow = headerSetupBox.row()
- if altProp is None or altProp.adultDayHeader.usePreviousHeader:
- adultDayRow.prop(self, prevHeaderName, text="Adult Day")
- adultDayRow.enabled = False
- else:
- adultDayRow.prop(self, "adultDayHeader", text="Adult Day")
- prevHeaderName = "adultDayHeader"
- adultNightRow = headerSetupBox.row()
- if altProp is None or altProp.adultNightHeader.usePreviousHeader:
- adultNightRow.prop(self, prevHeaderName, text="Adult Night")
- adultNightRow.enabled = False
- else:
- adultNightRow.prop(self, "adultNightHeader", text="Adult Night")
- headerSetupBox.row().label(text="Cutscene headers to include this actor in:")
- for i in range(len(self.cutsceneHeaders)):
- headerItemProps: OOTActorHeaderItemProperty = self.cutsceneHeaders[i]
- headerItemProps.draw_props(headerSetup, propUser, i, altProp, objName)
- drawAddButton(headerSetup, len(self.cutsceneHeaders), propUser, None, objName)
+ if game_data.z64.is_oot():
+ prop_split(headerSetup, self, "sceneSetupPreset", "Scene Setup Preset")
+
+ if self.sceneSetupPreset == "Custom":
+ headerSetupBox = headerSetup.column()
+ headerSetupBox.prop(self, "childDayHeader", text="Child Day")
+ prevHeaderName = "childDayHeader"
+ childNightRow = headerSetupBox.row()
+ if altProp is None or altProp.childNightHeader.usePreviousHeader:
+ # Draw previous header checkbox (so get previous state), but labeled
+ # as current one and grayed out
+ childNightRow.prop(self, prevHeaderName, text="Child Night")
+ childNightRow.enabled = False
+ else:
+ childNightRow.prop(self, "childNightHeader", text="Child Night")
+ prevHeaderName = "childNightHeader"
+ adultDayRow = headerSetupBox.row()
+ if altProp is None or altProp.adultDayHeader.usePreviousHeader:
+ adultDayRow.prop(self, prevHeaderName, text="Adult Day")
+ adultDayRow.enabled = False
+ else:
+ adultDayRow.prop(self, "adultDayHeader", text="Adult Day")
+ prevHeaderName = "adultDayHeader"
+ adultNightRow = headerSetupBox.row()
+ if altProp is None or altProp.adultNightHeader.usePreviousHeader:
+ adultNightRow.prop(self, prevHeaderName, text="Adult Night")
+ adultNightRow.enabled = False
+ else:
+ adultNightRow.prop(self, "adultNightHeader", text="Adult Night")
+
+ headerSetupBox.row().label(text="Cutscene headers to include this actor in:")
+ for i in range(len(self.cutsceneHeaders)):
+ headerItemProps: Z64_ActorHeaderItemProperty = self.cutsceneHeaders[i]
+ headerItemProps.draw_props(headerSetup, propUser, i, altProp, objName)
+ drawAddButton(headerSetup, len(self.cutsceneHeaders), propUser, None, objName)
+ else:
+ header_settings_box = headerSetup.column().box()
+
+ header_settings_box.label(text="Header Settings")
+ header_settings_box.prop(self, "include_in_all_setups")
+
+ if not self.include_in_all_setups:
+ header_settings_box.prop(self, "childDayHeader", text="Default Header")
+ cs_header_box = header_settings_box.box()
+ cs_header_box.row().prop(
+ self,
+ "expand_tab",
+ text="Cutscene Headers",
+ icon="TRIA_DOWN" if self.expand_tab else "TRIA_RIGHT",
+ )
-class OOTActorProperty(PropertyGroup):
- actor_id: EnumProperty(name="Actor", items=ootData.actorData.ootEnumActorID, default="ACTOR_PLAYER")
+ if self.expand_tab:
+ for i in range(len(self.cutsceneHeaders)):
+ headerItemProps: Z64_ActorHeaderItemProperty = self.cutsceneHeaders[i]
+ headerItemProps.draw_props(cs_header_box, propUser, i, altProp, objName)
+
+ drawAddButton(cs_header_box, len(self.cutsceneHeaders), propUser, None, objName)
+
+
+class Z64_ActorProperty(PropertyGroup):
+ actor_id: EnumProperty(name="Actor", items=lambda self, context: game_data.z64.get_enum("actor_id"), default=1)
actor_id_custom: StringProperty(name="Actor ID", default="ACTOR_PLAYER")
# only used for actors with the id "Custom"
@@ -186,7 +260,6 @@ class OOTActorProperty(PropertyGroup):
get=lambda self: self.get_param_value("Params"),
set=lambda self, value: self.set_param_value(value, "Params"),
)
-
rot_x: StringProperty(
name="Rot X",
default="0",
@@ -206,35 +279,40 @@ class OOTActorProperty(PropertyGroup):
set=lambda self, value: self.set_param_value(value, "ZRot"),
)
- headerSettings: PointerProperty(type=OOTActorHeaderProperty)
+ headerSettings: PointerProperty(type=Z64_ActorHeaderProperty)
eval_params: BoolProperty(name="Eval Params", default=False)
+ menu_tab: EnumProperty(items=enum_actor_menu)
+ halfday_show_entries: BoolProperty(default=True)
+ halfday_all: BoolProperty(default=True)
+ halfday_all_dawns: BoolProperty(default=False)
+ halfday_all_nights: BoolProperty(default=False)
+ halfday_bits: CollectionProperty(type=Z64_HalfdayItem)
+ use_global_actor_cs: BoolProperty(name="Use Global Actor Cutscene", default=False)
+ actor_cs_index: IntProperty(min=0, max=127, default=127)
@staticmethod
def upgrade_object(obj: Object):
- print(f"Processing '{obj.name}'...")
- upgradeActors(obj)
+ if game_data.z64.is_oot():
+ print(f"Processing '{obj.name}'...")
+ upgradeActors(obj)
def is_rotation_used(self, target: str):
- actor = ootData.actorData.actorsByID[self.actor_id]
+ game_data.z64.update(bpy.context, None)
+ actor = game_data.z64.actors.actorsByID[self.actor_id]
selected_type = None
-
for param in actor.params:
if param.type == "Type":
prop_name = get_prop_name(actor.key, param.type, param.subType, param.index)
base_val = getattr(self, prop_name)
-
if base_val == "Custom":
base_val = getattr(self, f"{prop_name}_custom")
-
selected_type = getEvalParamsInt(base_val)
-
# the first parameter type is always the "Actor Type"
# because of that we need to make sure the current "Actor Type" value
# is included in type list of the property as not all properties are used sometimes
if selected_type is not None and selected_type in param.tiedTypes or len(param.tiedTypes) == 0:
if param.target != "Params" and target == param.target:
return True
-
return False
def is_value_in_range(self, value: int, min: int, max: int):
@@ -243,10 +321,10 @@ def is_value_in_range(self, value: int, min: int, max: int):
return True
def set_param_value(self, base_value: str | bool, target: str):
- actor = ootData.actorData.actorsByID[self.actor_id]
+ game_data.z64.update(bpy.context, None)
+ actor = game_data.z64.actors.actorsByID[self.actor_id]
base_value = getEvalParamsInt(base_value)
found_type = None
-
for param in actor.params:
if target == param.target:
shift = getShiftFromMask(param.mask)
@@ -254,30 +332,25 @@ def set_param_value(self, base_value: str | bool, target: str):
value = (base_value & param.mask) >> shift
else:
value = base_value & param.mask
-
if "Rot" in target:
attr = getattr(self, get_prop_name(actor.key, "Type", None, 1), None)
found_type = getEvalParamsInt(attr) if attr is not None else None
else:
found_type = value
-
is_in_range = self.is_value_in_range(value, param.valueRange[0], param.valueRange[1])
found_type_in_tied_types = found_type is not None and found_type in param.tiedTypes
-
if is_in_range and (found_type_in_tied_types or len(param.tiedTypes) == 0):
prop_name = get_prop_name(actor.key, param.type, param.subType, param.index)
-
if param.type == "ChestContent":
- prop_value = ootData.actorData.chestItemByValue[value].key
+ prop_value = game_data.z64.actors.chestItemByValue[value].key
elif param.type == "Collectible":
- prop_value = ootData.actorData.collectibleItemsByValue[value].key
+ prop_value = game_data.z64.actors.collectibleItemsByValue[value].key
elif param.type == "Message":
- prop_value = ootData.actorData.messageItemsByValue[value].key
+ prop_value = game_data.z64.actors.messageItemsByValue[value].key
elif param.type == "Bool":
prop_value = bool(value)
else:
prop_value = f"0x{value:04X}"
-
try:
setattr(self, prop_name, prop_value)
except:
@@ -290,25 +363,22 @@ def set_param_value(self, base_value: str | bool, target: str):
)
def get_param_value(self, target: str):
- actor = ootData.actorData.actorsByID[self.actor_id]
+ game_data.z64.update(bpy.context, None)
+ actor = game_data.z64.actors.actorsByID[self.actor_id]
param_list = []
type_value = None
have_custom_value = False
-
for param in actor.params:
if target == param.target:
param_val = None
prop_name = get_prop_name(actor.key, param.type, param.subType, param.index)
cur_prop_value = getattr(self, prop_name)
-
if param.type not in {"Type", "Enum", "ChestContent", "Collectible", "Message"}:
if param.type == "Bool":
value_to_eval = "1" if cur_prop_value else "0"
else:
value_to_eval = cur_prop_value
-
param_val = getEvalParamsInt(value_to_eval)
-
# treat any invalid value as a custom value
if param_val is None:
param_list.append(value_to_eval)
@@ -325,16 +395,14 @@ def get_param_value(self, target: str):
type_value = getEvalParamsInt(cur_prop_value)
else:
param_val = 0
-
if param.type == "ChestContent":
- param_val = ootData.actorData.chestItemByKey[cur_prop_value].value
+ param_val = game_data.z64.actors.chestItemByKey[cur_prop_value].value
elif param.type == "Collectible":
- param_val = ootData.actorData.collectibleItemsByKey[cur_prop_value].value
+ param_val = game_data.z64.actors.collectibleItemsByKey[cur_prop_value].value
elif param.type == "Message":
- param_val = ootData.actorData.messageItemsByKey[cur_prop_value].value
+ param_val = game_data.z64.actors.messageItemsByKey[cur_prop_value].value
elif param.type == "Enum":
param_val = getEvalParamsInt(cur_prop_value)
-
if "Rot" in target:
attr = getattr(self, get_prop_name(actor.key, "Type", None, 1), None)
type_value = getEvalParamsInt(attr) if attr is not None else None
@@ -342,29 +410,22 @@ def get_param_value(self, target: str):
if type_value is not None and type_value in param.tiedTypes or len(param.tiedTypes) == 0:
val = ((param_val if param_val is not None else -1) & param.mask) >> getShiftFromMask(param.mask)
is_in_range = self.is_value_in_range(val, param.valueRange[0], param.valueRange[1])
-
if is_in_range and param.type != "Type" and param_val is not None:
value = getFormattedParams(param.mask, param_val, param.type == "Bool")
-
if value is not None:
param_list.append(value)
-
if len(param_list) > 0:
param_str = " | ".join(val for val in param_list)
else:
param_str = "0x0"
-
if "Rot" in target:
type_value = None
-
eval_type_value = type_value if type_value is not None else 0
-
# don't evaluate the params if there's a custom value
if not have_custom_value:
eval_param_value = getEvalParamsInt(param_str)
else:
eval_param_value = 0
-
if eval_type_value and (eval_param_value != 0 or have_custom_value) and type_value is not None:
param_str = f"(0x{type_value:04X} | ({param_str}))"
elif eval_type_value and not (eval_param_value != 0 or have_custom_value) and type_value is not None:
@@ -373,7 +434,6 @@ def get_param_value(self, target: str):
param_str = f"({param_str})"
else:
param_str = "0x0"
-
if self.eval_params:
# return `param_str` if the eval failed
# should only happen if the user inputs invalid numbers (hex or dec)
@@ -383,24 +443,19 @@ def get_param_value(self, target: str):
return value if value is not None else param_str
except:
pass
-
return param_str
def draw_params(self, layout: UILayout, obj: Object):
- actor = ootData.actorData.actorsByID[self.actor_id]
+ game_data.z64.update(bpy.context, None)
+ actor = game_data.z64.actors.actorsByID[self.actor_id]
selected_type = None
-
for param in actor.params:
prop_name = get_prop_name(actor.key, param.type, param.subType, param.index)
-
if param.type == "Type":
base_val = getattr(self, prop_name)
-
if base_val == "Custom":
base_val = getattr(self, f"{prop_name}_custom")
-
selected_type = getEvalParamsInt(base_val)
-
# the first parameter type is always the "Actor Type"
# because of that we need to make sure the current "Actor Type" value
# is included in type list of the property as not all properties are used sometimes
@@ -408,86 +463,142 @@ def draw_params(self, layout: UILayout, obj: Object):
if is_type_in_tied_types or param.type == "Type" or len(param.tiedTypes) == 0:
if param.type in {"ChestContent", "Message"}:
key: str = getattr(self, prop_name)
-
if param.type == "ChestContent":
search_op = layout.operator(OOT_SearchChestContentEnumOperator.bl_idname)
label_name = "Chest Content"
- item_map = ootData.actorData.chestItemByKey
+ item_map = game_data.z64.actors.chestItemByKey
else:
search_op = layout.operator(OOT_SearchNaviMsgIDEnumOperator.bl_idname)
label_name = "Navi Message ID"
- item_map = ootData.actorData.messageItemsByKey
-
+ item_map = game_data.z64.actors.messageItemsByKey
search_op.obj_name = obj.name
search_op.prop_name = prop_name
-
if key != "Custom":
label_split(layout, label_name, item_map[key].name)
else:
prop_split(layout, self, f"{prop_name}_custom", f"{label_name} Custom")
else:
prop_split(layout, self, prop_name, param.name)
-
if param.type in {"Type", "Enum", "Collectible"}:
if getattr(self, prop_name) == "Custom":
prop_split(layout, self, f"{prop_name}_custom", f"{param.name} Custom")
- def draw_props(self, layout: UILayout, altRoomProp: OOTAlternateRoomHeaderProperty, obj: Object):
+ def draw_props(
+ self,
+ layout: UILayout,
+ owner: Object,
+ alt_scene_props: Z64_AlternateSceneHeaderProperty,
+ altRoomProp: Z64_AlternateRoomHeaderProperty,
+ ):
actorIDBox = layout.column()
- searchOp = actorIDBox.operator(OOT_SearchActorIDEnumOperator.bl_idname, icon="VIEWZOOM")
- searchOp.actor_user = "Actor"
- searchOp.obj_name = obj.name
-
- split = actorIDBox.split(factor=0.5)
-
- if self.actor_id == "None":
- actorIDBox.box().label(text="This Actor was deleted from the XML file.")
- return
- split.label(text="Actor ID")
- split.label(text=getEnumName(ootData.actorData.ootEnumActorID, self.actor_id))
+ actorIDBox.row().prop(self, "menu_tab", expand=True)
+ actor_cs_props = owner.z64_actor_cs_property
- if bpy.context.scene.fast64.oot.use_new_actor_panel and self.actor_id != "Custom":
- self.draw_params(actorIDBox, obj)
+ if self.menu_tab == "General":
+ searchOp = actorIDBox.operator(OOT_SearchActorIDEnumOperator.bl_idname, icon="VIEWZOOM")
+ searchOp.actor_user = "Actor"
+ searchOp.obj_name = owner.name
- if self.actor_id == "Custom":
- prop_split(actorIDBox, self, "actor_id_custom", "")
+ split = actorIDBox.split(factor=0.5)
- paramBox = actorIDBox.box()
- paramBox.label(text="Actor Parameter")
+ if self.actor_id == "None":
+ actorIDBox.box().label(text="This Actor was deleted from the XML file.")
+ return
- if bpy.context.scene.fast64.oot.use_new_actor_panel and self.actor_id != "Custom":
- paramBox.prop(self, "eval_params")
- paramBox.prop(self, "params", text="")
- else:
- paramBox.prop(self, "params_custom", text="")
+ split.label(text="Actor ID")
+ split.label(text=getEnumName(game_data.z64.get_enum("actor_id"), self.actor_id))
- rotations_used = []
+ if (
+ game_data.z64.is_oot()
+ and bpy.context.scene.fast64.oot.can_use_new_actor_panel()
+ and self.actor_id != "Custom"
+ ):
+ self.draw_params(actorIDBox, owner)
- if bpy.context.scene.fast64.oot.use_new_actor_panel and self.actor_id != "Custom":
- if self.is_rotation_used("XRot"):
- rotations_used.append("X")
- if self.is_rotation_used("YRot"):
- rotations_used.append("Y")
- if self.is_rotation_used("ZRot"):
- rotations_used.append("Z")
- elif self.rot_override:
- rotations_used = ["X", "Y", "Z"]
+ if self.actor_id == "Custom":
+ prop_split(actorIDBox, self, "actor_id_custom", "Actor ID Custom")
- if not bpy.context.scene.fast64.oot.use_new_actor_panel or self.actor_id == "Custom":
- paramBox.prop(self, "rot_override", text="Override Rotation (ignore Blender rot)")
+ paramBox = actorIDBox.box()
+ paramBox.label(text="Actor Parameter")
- for rot in rotations_used:
- custom = (
- "_custom" if not bpy.context.scene.fast64.oot.use_new_actor_panel or self.actor_id == "Custom" else ""
- )
- prop_split(paramBox, self, f"rot_{rot.lower()}{custom}", f"Rot {rot}")
+ if (
+ game_data.z64.is_oot()
+ and bpy.context.scene.fast64.oot.can_use_new_actor_panel()
+ and self.actor_id != "Custom"
+ ):
+ paramBox.prop(self, "eval_params")
+ paramBox.prop(self, "params", text="")
+ else:
+ paramBox.prop(self, "params_custom", text="")
+
+ rotations_used = []
+
+ if bpy.context.scene.fast64.oot.can_use_new_actor_panel() and self.actor_id != "Custom":
+ if self.is_rotation_used("XRot"):
+ rotations_used.append("X")
+ if self.is_rotation_used("YRot"):
+ rotations_used.append("Y")
+ if self.is_rotation_used("ZRot"):
+ rotations_used.append("Z")
+ elif self.rot_override:
+ rotations_used = ["X", "Y", "Z"]
+
+ if self.actor_id == "Custom":
+ paramBox.prop(self, "rot_override", text="Override Rotation (ignore Blender rot)")
+
+ for rot in rotations_used:
+ custom = (
+ "_custom"
+ if not bpy.context.scene.fast64.oot.can_use_new_actor_panel() or self.actor_id == "Custom"
+ else ""
+ )
+ prop_split(paramBox, self, f"rot_{rot.lower()}{custom}", f"Rot {rot}")
+
+ if not is_oot_features():
+ layout_halfday = actorIDBox.box().column()
+ layout_halfday.label(text="Spawn Schedule")
+ row = layout_halfday.row(align=True)
+ row.prop(self, "halfday_all", text="Always Spawn")
+
+ if not self.halfday_all:
+ row.prop(self, "halfday_all_dawns", text="All Dawns")
+ row.prop(self, "halfday_all_nights", text="All Nights")
+
+ if not self.halfday_all_dawns and not self.halfday_all_nights:
+ prop_text = get_list_tab_text("Entries", len(self.halfday_bits))
+ layout_halfday.prop(
+ self,
+ "halfday_show_entries",
+ text=prop_text,
+ icon="TRIA_DOWN" if self.halfday_show_entries else "TRIA_RIGHT",
+ )
+
+ if self.halfday_show_entries:
+ for i, item in enumerate(self.halfday_bits):
+ item.draw_props(layout_halfday, owner, i)
+ drawAddButton(layout_halfday, len(self.halfday_bits), "Actor Halfday", None, owner.name)
+ elif self.menu_tab == "Actor Cutscene":
+ actorIDBox.prop(self, "use_global_actor_cs")
+ prop_split(actorIDBox, self, "actor_cs_index", "Actor CS Index")
+
+ if self.actor_cs_index > 119 and self.actor_cs_index < 127:
+ actorIDBox.label(text="The index can't be between 120 and 126!", icon="ERROR")
+
+ if self.use_global_actor_cs:
+ label_box = actorIDBox.box()
+ label_box.label(text="This should match the 'CutsceneEntry' array entry of", icon="INFO")
+ label_box.label(text="the actor cutscene you want to use. For instance with chests, ")
+ label_box.label(text="it should be the index of the entry where there's")
+ label_box.label(text="'CS_CAM_ID_GLOBAL_LONG_CHEST_OPENING'.")
+ else:
+ actor_cs_props.draw_props(actorIDBox, owner, alt_scene_props, False)
- headerProp: OOTActorHeaderProperty = self.headerSettings
- headerProp.draw_props(actorIDBox, "Actor", altRoomProp, obj.name)
+ headerProp: Z64_ActorHeaderProperty = self.headerSettings
+ headerProp.draw_props(actorIDBox, "Actor", altRoomProp, owner.name)
-class OOTTransitionActorProperty(PropertyGroup):
+class Z64_TransitionActorProperty(PropertyGroup):
fromRoom: PointerProperty(type=Object, poll=lambda self, object: self.isRoomEmptyObject(object))
toRoom: PointerProperty(type=Object, poll=lambda self, object: self.isRoomEmptyObject(object))
cameraTransitionFront: EnumProperty(items=ootEnumCamTransition, default="0x00")
@@ -496,24 +607,25 @@ class OOTTransitionActorProperty(PropertyGroup):
cameraTransitionBackCustom: StringProperty(default="0x00")
isRoomTransition: BoolProperty(name="Is Room Transition", default=True)
- actor: PointerProperty(type=OOTActorProperty)
+ actor: PointerProperty(type=Z64_ActorProperty)
def isRoomEmptyObject(self, obj: Object):
return obj.type == "EMPTY" and obj.ootEmptyType == "Room"
def draw_props(
- self, layout: UILayout, altSceneProp: OOTAlternateSceneHeaderProperty, roomObj: Object, objName: str
+ self, layout: UILayout, altSceneProp: Z64_AlternateSceneHeaderProperty, roomObj: Object, objName: str
):
actorIDBox = layout.column()
+
searchOp = actorIDBox.operator(OOT_SearchActorIDEnumOperator.bl_idname, icon="VIEWZOOM")
searchOp.actor_user = "Transition Actor"
searchOp.obj_name = objName
split = actorIDBox.split(factor=0.5)
split.label(text="Actor ID")
- split.label(text=getEnumName(ootData.actorData.ootEnumActorID, self.actor.actor_id))
+ split.label(text=getEnumName(game_data.z64.get_enum("actor_id"), self.actor.actor_id))
- if bpy.context.scene.fast64.oot.use_new_actor_panel and self.actor.actor_id != "Custom":
+ if bpy.context.scene.fast64.oot.can_use_new_actor_panel() and self.actor.actor_id != "Custom":
self.actor.draw_params(actorIDBox, roomObj)
if self.actor.actor_id == "Custom":
@@ -521,12 +633,19 @@ def draw_props(
paramBox = actorIDBox.box()
paramBox.label(text="Actor Parameter")
- if bpy.context.scene.fast64.oot.use_new_actor_panel and self.actor.actor_id != "Custom":
+ if (
+ is_oot_features()
+ and bpy.context.scene.fast64.oot.can_use_new_actor_panel()
+ and self.actor.actor_id != "Custom"
+ ):
paramBox.prop(self.actor, "eval_params")
paramBox.prop(self.actor, "params", text="")
else:
paramBox.prop(self.actor, "params_custom", text="")
+ if not is_oot_features():
+ prop_split(actorIDBox, self.actor, "actor_cs_index", "Actor CS Index")
+
if roomObj is None:
actorIDBox.label(text="This must be part of a Room empty's hierarchy.", icon="OUTLINER")
else:
@@ -540,15 +659,15 @@ def draw_props(
drawEnumWithCustom(actorIDBox, self, "cameraTransitionFront", "Camera Transition Front", "")
drawEnumWithCustom(actorIDBox, self, "cameraTransitionBack", "Camera Transition Back", "")
- headerProps: OOTActorHeaderProperty = self.actor.headerSettings
+ headerProps: Z64_ActorHeaderProperty = self.actor.headerSettings
headerProps.draw_props(actorIDBox, "Transition Actor", altSceneProp, objName)
-class OOTEntranceProperty(PropertyGroup):
+class Z64_EntranceProperty(PropertyGroup):
# This is also used in entrance list.
spawnIndex: IntProperty(min=0)
customActor: BoolProperty(name="Use Custom Actor")
- actor: PointerProperty(type=OOTActorProperty)
+ actor: PointerProperty(type=Z64_ActorProperty)
tiedRoom: PointerProperty(
type=Object,
@@ -559,51 +678,60 @@ class OOTEntranceProperty(PropertyGroup):
def isRoomEmptyObject(self, obj: Object):
return obj.type == "EMPTY" and obj.ootEmptyType == "Room"
- def draw_props(self, layout: UILayout, obj: Object, altSceneProp: OOTAlternateSceneHeaderProperty, objName: str):
+ def draw_props(self, layout: UILayout, obj: Object, altSceneProp: Z64_AlternateSceneHeaderProperty, objName: str):
box = layout.column()
+
roomObj = getRoomObj(obj)
if roomObj is None:
box.label(text="This must be part of a Room empty's hierarchy.", icon="OUTLINER")
entranceProp = obj.ootEntranceProperty
- prop_split(box, entranceProp, "tiedRoom", "Room")
- prop_split(box, entranceProp, "spawnIndex", "Spawn Index")
-
box.prop(entranceProp, "customActor")
+
if entranceProp.customActor:
prop_split(box, entranceProp.actor, "actor_id_custom", "Actor ID Custom")
- if bpy.context.scene.fast64.oot.use_new_actor_panel and not self.customActor:
+ prop_split(box, entranceProp, "tiedRoom", "Room")
+ prop_split(box, entranceProp, "spawnIndex", "Spawn Index")
+
+ if bpy.context.scene.fast64.oot.can_use_new_actor_panel() and not self.customActor:
self.actor.draw_params(box, obj)
paramBox = box.box()
paramBox.label(text="Actor Parameter")
- if bpy.context.scene.fast64.oot.use_new_actor_panel and not self.customActor:
+ if is_oot_features() and bpy.context.scene.fast64.oot.can_use_new_actor_panel() and not self.customActor:
paramBox.prop(self.actor, "eval_params")
paramBox.prop(self.actor, "params", text="")
else:
paramBox.prop(self.actor, "params_custom", text="")
- headerProps: OOTActorHeaderProperty = entranceProp.actor.headerSettings
+ headerProps: Z64_ActorHeaderProperty = entranceProp.actor.headerSettings
headerProps.draw_props(box, "Entrance", altSceneProp, objName)
classes = (
- OOTActorHeaderItemProperty,
- OOTActorHeaderProperty,
- OOTActorProperty,
- OOTTransitionActorProperty,
- OOTEntranceProperty,
+ Z64_HalfdayItem,
+ Z64_ActorHeaderItemProperty,
+ Z64_ActorHeaderProperty,
+ Z64_ActorProperty,
+ Z64_TransitionActorProperty,
+ Z64_EntranceProperty,
)
def actor_props_register():
+ # generate props for OoT
+ create_game_props("OOT")
+
+ # generate props for MM
+ create_game_props("MM")
+
for cls in classes:
register_class(cls)
- Object.ootActorProperty = PointerProperty(type=OOTActorProperty)
- Object.ootTransitionActorProperty = PointerProperty(type=OOTTransitionActorProperty)
- Object.ootEntranceProperty = PointerProperty(type=OOTEntranceProperty)
+ Object.ootActorProperty = PointerProperty(type=Z64_ActorProperty)
+ Object.ootTransitionActorProperty = PointerProperty(type=Z64_TransitionActorProperty)
+ Object.ootEntranceProperty = PointerProperty(type=Z64_EntranceProperty)
def actor_props_unregister():
diff --git a/fast64_internal/z64/actor_cutscene/properties.py b/fast64_internal/z64/actor_cutscene/properties.py
new file mode 100644
index 000000000..7dcb63547
--- /dev/null
+++ b/fast64_internal/z64/actor_cutscene/properties.py
@@ -0,0 +1,217 @@
+import bpy
+
+from bpy.props import (
+ IntProperty,
+ PointerProperty,
+ BoolProperty,
+ EnumProperty,
+ StringProperty,
+ CollectionProperty,
+ FloatVectorProperty,
+)
+from bpy.utils import register_class, unregister_class
+from bpy.types import PropertyGroup, UILayout, Object
+from ...utility import prop_split
+from ..collection_utility import drawAddButton, drawCollectionOps, getCollection
+from ..utility import get_list_tab_text
+from ..actor.properties import Z64_ActorHeaderProperty
+from ..scene.properties import Z64_AlternateSceneHeaderProperty
+
+
+enum_cs_cam_id = [
+ ("Custom", "Custom", "Custom"),
+ ("Camera", "Camera Object", "Camera Object"),
+ ("CS_CAM_ID_GLOBAL_ELEGY", "Elegy of Emptiness", "-25 (CAM_SET_ELEGY_SHELL)"),
+ ("CS_CAM_ID_GLOBAL_SIDED", "Sided", "-24 (CAM_SET_SIDED)"),
+ ("CS_CAM_ID_GLOBAL_BOAT_CRUISE", "Boat Cruise", "-23 (CAM_SET_BOAT_CRUISE)"),
+ ("CS_CAM_ID_GLOBAL_N16", "N16", "-22 (CAM_SET_NONE)"),
+ ("CS_CAM_ID_GLOBAL_SUBJECTD", "Subjectd", "-21 (CAM_SET_SUBJECTD)"),
+ ("CS_CAM_ID_GLOBAL_NORMALD", "Normald", "-20 (CAM_SET_NORMALD)"),
+ ("CS_CAM_ID_GLOBAL_N13", "N13", "-19 (CAM_SET_NONE)"),
+ ("CS_CAM_ID_GLOBAL_N12", "N12", "-18 (CAM_SET_NONE)"),
+ ("CS_CAM_ID_GLOBAL_N11", "N11", "-17 (CAM_SET_NONE)"),
+ ("CS_CAM_ID_GLOBAL_WARP_PAD_ENTRANCE", "Warp Pad Entrance", "-16 (CAM_SET_WARP_PAD_ENTRANCE)"),
+ ("CS_CAM_ID_GLOBAL_ATTENTION", "Attention", "-15 (CAM_SET_ATTENTION)"),
+ ("CS_CAM_ID_GLOBAL_CONNECT", "Connect", "-14 (CAM_SET_CONNECT0)"),
+ ("CS_CAM_ID_GLOBAL_REMOTE_BOMB", "Remote Bomb", "-13 (CAM_SET_REMOTEBOMB)"),
+ ("CS_CAM_ID_GLOBAL_N0C", "N0C", "-12 (CAM_SET_NONE)"),
+ ("CS_CAM_ID_GLOBAL_MASK_TRANSFORMATION", "Mask Transformation", "-11 (CAM_SET_MASK_TRANSFORMATION)"),
+ ("CS_CAM_ID_GLOBAL_LONG_CHEST_OPENING", "Long Chest Opening", "-10 (CAM_SET_LONG_CHEST_OPENING)"),
+ ("CS_CAM_ID_GLOBAL_REVIVE", "Revive", "-9 (CAM_SET_REBIRTH)"),
+ ("CS_CAM_ID_GLOBAL_DEATH", "Death", "-8 (CAM_SET_DEATH)"),
+ ("CS_CAM_ID_GLOBAL_WARP_PAD_MOON", "Warp Pad Moon", "-7 (CAM_SET_WARP_PAD_MOON)"),
+ ("CS_CAM_ID_GLOBAL_SONG_WARP", "Song Warp", "-6 (CAM_SET_NAVI)"),
+ ("CS_CAM_ID_GLOBAL_ITEM_SHOW", "Item Show", "-5 (CAM_SET_ITEM3)"),
+ ("CS_CAM_ID_GLOBAL_ITEM_BOTTLE", "Item Bottle", "-4 (CAM_SET_ITEM2)"),
+ ("CS_CAM_ID_GLOBAL_ITEM_OCARINA", "Item Ocarina", "-3 (CAM_SET_ITEM1)"),
+ ("CS_CAM_ID_GLOBAL_ITEM_GET", "Item Get", "-2 (CAM_SET_ITEM0)"),
+ ("CS_CAM_ID_NONE", "None", "-1"),
+]
+
+enum_end_sfx = [
+ ("Custom", "Custom", "Custom"),
+ ("CS_END_SFX_NONE", "None", "0"),
+ ("CS_END_SFX_TRE_BOX_APPEAR", "Chest Appear", "1"),
+ ("CS_END_SFX_CORRECT_CHIME", "Correct Chime", "2"),
+ ("CS_END_SFX_NONE_ALT", "None Alt", "255"),
+]
+
+enum_hud_visibility = [
+ ("Custom", "Custom", "Custom"),
+ ("CS_HUD_VISIBILITY_NONE", "None", "0"),
+ ("CS_HUD_VISIBILITY_ALL", "All", "1"),
+ ("CS_HUD_VISIBILITY_A_HEARTS_MAGIC", "Only A Button, Hearts and Magic Meter", "2"),
+ ("CS_HUD_VISIBILITY_C_HEARTS_MAGIC", "Only C Buttons, Hearts and Magic Meter", "3"),
+ ("CS_HUD_VISIBILITY_ALL_NO_MINIMAP", "All without Minimap", "4"),
+ ("CS_HUD_VISIBILITY_A_B_C", "Only A Button, B Button and C Buttons", "5"),
+ ("CS_HUD_VISIBILITY_B_MINIMAP", "Only B Button and Minimap", "6"),
+ ("CS_HUD_VISIBILITY_A", "Only A Button", "7"),
+ ("CS_HUD_VISIBILITY_ALL_ALT", "All 2", "-1"),
+]
+
+enum_end_cam = [
+ ("Custom", "Custom", "Custom"),
+ ("CS_END_CAM_0", "Cam 0", "0"),
+ ("CS_END_CAM_1", "Cam 1", "1"),
+ ("CS_END_CAM_SMOOTH", "Cam Smooth", "2"),
+]
+
+
+def poll_camera_obj(self, obj: Object):
+ return obj.type == "CAMERA" and obj.ootCameraPositionProperty.is_actor_cs_cam
+
+
+class Z64_ActorCutscene(PropertyGroup):
+ priority: IntProperty(name="Priority", default=700, description="Lower number means higher priority")
+ length: IntProperty(name="Length", min=-1, default=-1)
+ cs_cam_id: EnumProperty(
+ name="CS Cam ID",
+ items=enum_cs_cam_id,
+ default=2,
+ description="Index of `CsCameraEntry` to use. Negative indices use `sGlobalCamDataSettings`. Indices 0 and above use `CsCameraEntry` from a sceneLayer",
+ )
+ cs_cam_id_custom: StringProperty(name="CS Cam ID Custom", default="CS_CAM_ID_NONE")
+ cs_cam_obj: PointerProperty(type=Object, poll=poll_camera_obj)
+ script_index: IntProperty(name="Script Index", min=-1, default=-1, description="Gets the priority over 'CS Cam ID'")
+ additional_cs_id: IntProperty(name="Additional CS ID", min=-1, default=-1)
+ end_sfx: EnumProperty(name="End Sound Effect", items=enum_end_sfx, default=1)
+ end_sfx_custom: StringProperty(name="End Sound Effect Custom")
+ custom_value: StringProperty(
+ name="Custom Value", default="255", description="0 - 99: actor-specific custom value. 100+: spawn. 255: none"
+ )
+ hud_visibility: EnumProperty(name="HUD Visibility", items=enum_hud_visibility, default=1)
+ hud_visibility_custom: StringProperty(name="HUD Visibility Custom")
+ end_cam: EnumProperty(name="End Cam", items=enum_end_cam, default=1)
+ end_cam_custom: StringProperty(name="End Cam Custom")
+ letterbox_size: IntProperty(name="Letterbox Size", min=0, max=255, default=30)
+
+ # ui only props
+ show_item: BoolProperty()
+
+ def draw_props(self, layout: UILayout, owner: Object, index: int, array_index: int):
+ layout = layout.column()
+ layout.prop(
+ self,
+ "show_item",
+ text=f"Entry No. {array_index + 1} (Array Index {array_index})",
+ icon="TRIA_DOWN" if self.show_item else "TRIA_RIGHT",
+ )
+
+ if self.show_item:
+ drawCollectionOps(layout, index, "Actor CS", None, owner.name)
+
+ prop_split(layout, self, "priority", "Priority")
+ prop_split(layout, self, "length", "Length")
+
+ prop_split(layout, self, "cs_cam_id", "CS Cam ID")
+ if self.cs_cam_id == "Custom":
+ prop_split(layout, self, "cs_cam_id_custom", "CS Cam ID Custom")
+ elif self.cs_cam_id == "Camera":
+ prop_split(layout, self, "cs_cam_obj", "Camera Object")
+
+ prop_split(layout, self, "script_index", "Script Index")
+ prop_split(layout, self, "additional_cs_id", "Additional CS ID")
+
+ prop_split(layout, self, "end_sfx", "End Sound Effect")
+ if self.end_sfx == "Custom":
+ prop_split(layout, self, "end_sfx_custom", "End Sound Effect Custom")
+
+ prop_split(layout, self, "custom_value", "Custom Value")
+
+ prop_split(layout, self, "hud_visibility", "HUD Visibility")
+ if self.hud_visibility == "Custom":
+ prop_split(layout, self, "hud_visibility_custom", "HUD Visibility Custom")
+
+ prop_split(layout, self, "end_cam", "End Cam")
+ if self.end_cam == "Custom":
+ prop_split(layout, self, "end_cam_custom", "End Cam Custom")
+
+ prop_split(layout, self, "letterbox_size", "Letterbox Size")
+
+
+class Z64_ActorCutsceneProperty(PropertyGroup):
+ entries: CollectionProperty(type=Z64_ActorCutscene)
+ header_settings: PointerProperty(type=Z64_ActorHeaderProperty)
+
+ # ui only props
+ show_entries: BoolProperty(default=True)
+
+ def get_count(self, obj_name: str):
+ count = 0
+ for obj in bpy.data.objects:
+ if obj.ootEmptyType == "Actor" and not obj.ootActorProperty.use_global_actor_cs:
+ if obj.name == obj_name:
+ break
+
+ count += len(getCollection(obj.name, "Actor CS", 0))
+
+ return count
+
+ def draw_props(
+ self,
+ layout: UILayout,
+ owner: Object,
+ alt_header_props: Z64_AlternateSceneHeaderProperty,
+ draw_header: bool = True,
+ ):
+ layout_entries = layout.box().column()
+
+ prop_text = get_list_tab_text("Entries", len(self.entries))
+ layout_entries.prop(
+ self, "show_entries", text=prop_text, icon="TRIA_DOWN" if self.show_entries else "TRIA_RIGHT"
+ )
+
+ if self.show_entries:
+ if owner.ootEmptyType == "Actor Cutscene":
+ start = 0
+ else:
+ global_actor_cs_count = bpy.context.scene.fast64.oot.global_actor_cs_count
+ start = global_actor_cs_count + self.get_count(owner.name)
+
+ for i, actor_cs in enumerate(self.entries):
+ actor_cs.draw_props(layout_entries.box(), owner, i, start + i)
+
+ drawAddButton(layout_entries, len(self.entries), "Actor CS", None, owner.name)
+
+ if draw_header:
+ self.header_settings.draw_props(layout, "Actor CS Headers", alt_header_props, owner.name)
+
+
+classes = (
+ Z64_ActorCutscene,
+ Z64_ActorCutsceneProperty,
+)
+
+
+def actor_cs_register():
+ for cls in classes:
+ register_class(cls)
+
+ Object.z64_actor_cs_property = PointerProperty(type=Z64_ActorCutsceneProperty)
+
+
+def actor_cs_unregister():
+ del Object.z64_actor_cs_property
+
+ for cls in reversed(classes):
+ unregister_class(cls)
diff --git a/fast64_internal/z64/animated_mats/properties.py b/fast64_internal/z64/animated_mats/properties.py
new file mode 100644
index 000000000..6d7b433d9
--- /dev/null
+++ b/fast64_internal/z64/animated_mats/properties.py
@@ -0,0 +1,274 @@
+from bpy.props import (
+ IntProperty,
+ PointerProperty,
+ BoolProperty,
+ EnumProperty,
+ StringProperty,
+ CollectionProperty,
+ FloatVectorProperty,
+)
+from bpy.utils import register_class, unregister_class
+from bpy.types import PropertyGroup, UILayout, Object
+from ...utility import prop_split
+from ..collection_utility import drawAddButton, drawCollectionOps
+from ..utility import get_list_tab_text
+
+
+# no custom since we only need to know where to export the data
+enum_mode = [
+ ("Scene", "Scene", "Scene"),
+ ("Actor", "Actor", "Actor"),
+]
+
+# see `sMatAnimDrawHandlers` in `z_scene_proc.c`
+enum_anim_mat_type = [
+ ("Custom", "Custom", "Custom"),
+ ("tex_scroll", "Draw Texture Scroll", "Draw Texture Scroll"),
+ ("two_tex_scroll", "Draw Two Texture Scroll", "Draw Two Texture Scroll"),
+ ("color", "Draw Color", "Draw Color"),
+ ("color_lerp", "Draw Color Lerp", "Draw Color Lerp"),
+ ("color_nonlinear_interp", "Draw Color Non-Linear Interp", "Draw Color Non-Linear Interp"),
+ ("tex_cycle", "Draw Texture Cycle", "Draw Texture Cycle"),
+]
+
+
+class Z64_AnimatedMatColorKeyFrame(PropertyGroup):
+ frame_num: IntProperty(name="Frame No.", min=0)
+
+ prim_lod_frac: IntProperty(name="Primitive LOD Frac", min=0, max=255)
+ prim_color: FloatVectorProperty(
+ name="Primitive Color",
+ subtype="COLOR",
+ size=4,
+ min=0,
+ max=1,
+ default=(1, 1, 1, 1),
+ )
+
+ env_color: FloatVectorProperty(
+ name="Environment Color",
+ subtype="COLOR",
+ size=4,
+ min=0,
+ max=1,
+ default=(1, 1, 1, 1),
+ )
+
+ def draw_props(self, layout: UILayout, owner: Object, parent_index: int, index: int):
+ drawCollectionOps(layout, index, "Animated Mat. Color", None, owner.name, collection_index=parent_index)
+ prop_split(layout, self, "frame_num", "Frame No.")
+ prop_split(layout, self, "prim_lod_frac", "Primitive LOD Frac")
+ prop_split(layout, self, "prim_color", "Primitive Color")
+ prop_split(layout, self, "env_color", "Environment Color")
+
+
+class Z64_AnimatedMatColorParams(PropertyGroup):
+ frame_count: IntProperty(name="Frame Count", min=0)
+ keyframes: CollectionProperty(type=Z64_AnimatedMatColorKeyFrame)
+
+ # ui only props
+ show_entries: BoolProperty(default=False)
+
+ def draw_props(self, layout: UILayout, owner: Object, parent_index: int):
+ prop_split(layout, self, "frame_count", "Frame Count")
+
+ prop_text = get_list_tab_text("Keyframes", len(self.keyframes))
+ layout.prop(self, "show_entries", text=prop_text, icon="TRIA_DOWN" if self.show_entries else "TRIA_RIGHT")
+
+ if self.show_entries:
+ for i, keyframe in enumerate(self.keyframes):
+ keyframe.draw_props(layout, owner, parent_index, i)
+
+ drawAddButton(layout, len(self.keyframes), "Animated Mat. Color", None, owner.name, parent_index)
+
+
+class Z64_AnimatedMatTexScrollItem(PropertyGroup):
+ step_x: IntProperty(default=0)
+ step_y: IntProperty(default=0)
+ width: IntProperty(min=0)
+ height: IntProperty(min=0)
+
+ def draw_props(self, layout: UILayout, owner: Object, parent_index: int, index: int):
+ drawCollectionOps(layout, index, "Animated Mat. Scroll", None, owner.name, collection_index=parent_index)
+ prop_split(layout, self, "step_x", "Step X")
+ prop_split(layout, self, "step_y", "Step Y")
+ prop_split(layout, self, "width", "Texture Width")
+ prop_split(layout, self, "height", "Texture Height")
+
+
+class Z64_AnimatedMatTexScrollParams(PropertyGroup):
+ entries: CollectionProperty(type=Z64_AnimatedMatTexScrollItem)
+
+ # ui only props
+ show_entries: BoolProperty(default=False)
+
+ def draw_props(self, layout: UILayout, owner: Object, parent_index: int):
+ prop_text = get_list_tab_text("Tex. Scroll", len(self.entries))
+ layout.prop(self, "show_entries", text=prop_text, icon="TRIA_DOWN" if self.show_entries else "TRIA_RIGHT")
+
+ if self.show_entries:
+ for i, item in enumerate(self.entries):
+ item.draw_props(layout, owner, parent_index, i)
+
+ drawAddButton(layout, len(self.entries), "Animated Mat. Scroll", None, owner.name, parent_index)
+
+
+class Z64_AnimatedMatTexCycleKeyFrame(PropertyGroup):
+ frame_num: IntProperty(name="Frame No.", min=0)
+ texture: StringProperty(name="Texture Symbol")
+
+ def draw_props(self, layout: UILayout, owner: Object, parent_index: int, index: int):
+ drawCollectionOps(layout, index, "Animated Mat. Cycle", None, owner.name, collection_index=parent_index)
+ prop_split(layout, self, "frame_num", "Frame No.")
+ prop_split(layout, self, "texture", "Texture Symbol")
+
+
+class Z64_AnimatedMatTexCycleParams(PropertyGroup):
+ frame_count: IntProperty(name="Frame Count", min=0)
+ keyframes: CollectionProperty(type=Z64_AnimatedMatTexCycleKeyFrame)
+
+ # ui only props
+ show_entries: BoolProperty(default=False)
+
+ def draw_props(self, layout: UILayout, owner: Object, parent_index: int):
+ prop_split(layout, self, "frame_count", "Frame Count")
+
+ prop_text = get_list_tab_text("Keyframes", len(self.keyframes))
+ layout.prop(self, "show_entries", text=prop_text, icon="TRIA_DOWN" if self.show_entries else "TRIA_RIGHT")
+
+ if self.show_entries:
+ for i, keyframe in enumerate(self.keyframes):
+ keyframe.draw_props(layout, owner, parent_index, i)
+
+ drawAddButton(layout, len(self.keyframes), "Animated Mat. Cycle", None, owner.name, parent_index)
+
+
+class Z64_AnimatedMaterialItem(PropertyGroup):
+ """see the `AnimatedMaterial` struct from `z64scene.h`"""
+
+ segment_num: IntProperty(name="Segment Number", min=8, max=13, default=8)
+ type: EnumProperty(
+ name="Draw Handler Type", items=enum_anim_mat_type, default=2, description="Index to `sMatAnimDrawHandlers`"
+ )
+ type_custom: StringProperty(name="Custom Draw Handler Index", default="2")
+
+ color_params: PointerProperty(type=Z64_AnimatedMatColorParams)
+ tex_scroll_params: PointerProperty(type=Z64_AnimatedMatTexScrollParams)
+ tex_cycle_params: PointerProperty(type=Z64_AnimatedMatTexCycleParams)
+
+ # ui only props
+ show_item: BoolProperty(default=False)
+
+ def draw_props(self, layout: UILayout, owner: Object, index: int):
+ layout.prop(
+ self, "show_item", text=f"Item No.{index + 1}", icon="TRIA_DOWN" if self.show_item else "TRIA_RIGHT"
+ )
+
+ if self.show_item:
+ drawCollectionOps(layout, index, "Animated Mat.", None, owner.name)
+
+ prop_split(layout, self, "segment_num", "Segment Number")
+
+ layout_type = layout.column()
+ prop_split(layout_type, self, "type", "Draw Handler Type")
+
+ if self.type == "Custom":
+ layout_type.label(
+ text="This only allows you to choose a custom index for the function handler.", icon="ERROR"
+ )
+ prop_split(layout_type, self, "type_custom", "Custom Draw Handler Index")
+ elif self.type in {"tex_scroll", "two_tex_scroll"}:
+ self.tex_scroll_params.draw_props(layout_type, owner, index)
+ elif self.type in {"color", "color_lerp", "color_nonlinear_interp"}:
+ self.color_params.draw_props(layout_type, owner, index)
+ elif self.type == "tex_cycle":
+ self.tex_cycle_params.draw_props(layout_type, owner, index)
+
+
+class Z64_AnimatedMaterial(PropertyGroup):
+ """Defines an Animated Material array"""
+
+ header_index: IntProperty(name="Header Index", min=-1, default=-1, description="Header Index, -1 means all headers")
+ entries: CollectionProperty(type=Z64_AnimatedMaterialItem)
+
+ # ui only props
+ show_list: BoolProperty(default=True)
+ show_entries: BoolProperty(default=True)
+
+ def draw_props(self, layout: UILayout, owner: Object, index: int):
+ layout.prop(
+ self, "show_list", text=f"List No.{index + 1}", icon="TRIA_DOWN" if self.show_list else "TRIA_RIGHT"
+ )
+
+ if self.show_list:
+ drawCollectionOps(layout, index, "Animated Mat. List", None, owner.name)
+ prop_split(layout, self, "header_index", "Header Index")
+
+ prop_text = get_list_tab_text("Animated Materials", len(self.entries))
+ layout_entries = layout.column()
+ layout_entries.prop(
+ self, "show_entries", text=prop_text, icon="TRIA_DOWN" if self.show_entries else "TRIA_RIGHT"
+ )
+
+ if self.show_entries:
+ for i, item in enumerate(self.entries):
+ item.draw_props(layout_entries.box().column(), owner, i)
+
+ drawAddButton(layout_entries, len(self.entries), "Animated Mat.", None, owner.name)
+
+
+class Z64_AnimatedMaterialProperty(PropertyGroup):
+ """List of Animated Material arrays"""
+
+ mode: EnumProperty(name="Export To", items=enum_mode)
+
+ # this is probably useless since usually you wouldn't use different animated materials
+ # on different headers but it's better to give users the choice
+ items: CollectionProperty(type=Z64_AnimatedMaterial)
+
+ # ui only props
+ show_entries: BoolProperty(default=True)
+
+ def draw_props(self, layout: UILayout, owner: Object):
+ layout = layout.column()
+
+ prop_split(layout, self, "mode", "Export To")
+
+ prop_text = get_list_tab_text("Animated Materials List", len(self.items))
+ layout_entries = layout.box().column()
+ layout_entries.prop(
+ self, "show_entries", text=prop_text, icon="TRIA_DOWN" if self.show_entries else "TRIA_RIGHT"
+ )
+
+ if self.show_entries:
+ for i, item in enumerate(self.items):
+ item.draw_props(layout_entries.box().column(), owner, i)
+
+ drawAddButton(layout_entries, len(self.items), "Animated Mat. List", None, owner.name)
+
+
+classes = (
+ Z64_AnimatedMatColorKeyFrame,
+ Z64_AnimatedMatColorParams,
+ Z64_AnimatedMatTexScrollItem,
+ Z64_AnimatedMatTexScrollParams,
+ Z64_AnimatedMatTexCycleKeyFrame,
+ Z64_AnimatedMatTexCycleParams,
+ Z64_AnimatedMaterialItem,
+ Z64_AnimatedMaterial,
+ Z64_AnimatedMaterialProperty,
+)
+
+
+def animated_mats_register():
+ for cls in classes:
+ register_class(cls)
+
+ Object.z64_anim_mats_property = PointerProperty(type=Z64_AnimatedMaterialProperty)
+
+
+def animated_mats_unregister():
+ del Object.z64_anim_mats_property
+
+ for cls in reversed(classes):
+ unregister_class(cls)
diff --git a/fast64_internal/oot/animation/exporter/__init__.py b/fast64_internal/z64/animation/exporter/__init__.py
similarity index 100%
rename from fast64_internal/oot/animation/exporter/__init__.py
rename to fast64_internal/z64/animation/exporter/__init__.py
diff --git a/fast64_internal/oot/animation/exporter/classes.py b/fast64_internal/z64/animation/exporter/classes.py
similarity index 100%
rename from fast64_internal/oot/animation/exporter/classes.py
rename to fast64_internal/z64/animation/exporter/classes.py
diff --git a/fast64_internal/oot/animation/exporter/functions.py b/fast64_internal/z64/animation/exporter/functions.py
similarity index 99%
rename from fast64_internal/oot/animation/exporter/functions.py
rename to fast64_internal/z64/animation/exporter/functions.py
index 4c639257c..b0bb2b540 100644
--- a/fast64_internal/oot/animation/exporter/functions.py
+++ b/fast64_internal/z64/animation/exporter/functions.py
@@ -14,7 +14,7 @@
stashActionInArmature,
)
-from ...oot_utility import (
+from ...utility import (
checkForStartBone,
getStartBone,
getSortedChildren,
diff --git a/fast64_internal/oot/animation/importer/__init__.py b/fast64_internal/z64/animation/importer/__init__.py
similarity index 100%
rename from fast64_internal/oot/animation/importer/__init__.py
rename to fast64_internal/z64/animation/importer/__init__.py
diff --git a/fast64_internal/oot/animation/importer/functions.py b/fast64_internal/z64/animation/importer/functions.py
similarity index 99%
rename from fast64_internal/oot/animation/importer/functions.py
rename to fast64_internal/z64/animation/importer/functions.py
index da35f233e..680b987ad 100644
--- a/fast64_internal/oot/animation/importer/functions.py
+++ b/fast64_internal/z64/animation/importer/functions.py
@@ -4,7 +4,7 @@
import math
from ....utility import PluginError, hexOrDecInt
from ....f3d.f3d_parser import getImportData
-from ...oot_model_classes import ootGetIncludedAssetData
+from ...model_classes import ootGetIncludedAssetData
from ....utility_anim import (
getTranslationRelativeToRest,
@@ -12,7 +12,7 @@
stashActionInArmature,
)
-from ...oot_utility import (
+from ...utility import (
getStartBone,
getNextBone,
)
diff --git a/fast64_internal/oot/animation/operators.py b/fast64_internal/z64/animation/operators.py
similarity index 99%
rename from fast64_internal/oot/animation/operators.py
rename to fast64_internal/z64/animation/operators.py
index 7db7a57db..192d434d4 100644
--- a/fast64_internal/oot/animation/operators.py
+++ b/fast64_internal/z64/animation/operators.py
@@ -8,7 +8,7 @@
from .exporter import ootExportLinkAnimation, ootExportNonLinkAnimation
from .importer import ootImportLinkAnimationC, ootImportNonLinkAnimationC
-from ..oot_utility import (
+from ..utility import (
ootGetPath,
addIncludeFiles,
checkEmptyName,
diff --git a/fast64_internal/oot/animation/panels.py b/fast64_internal/z64/animation/panels.py
similarity index 83%
rename from fast64_internal/oot/animation/panels.py
rename to fast64_internal/z64/animation/panels.py
index ce3e8f00e..975900442 100644
--- a/fast64_internal/oot/animation/panels.py
+++ b/fast64_internal/z64/animation/panels.py
@@ -1,14 +1,14 @@
from bpy.types import Panel, Armature
from bpy.utils import register_class, unregister_class
from ...utility import prop_split
-from ...panels import OOT_Panel
+from ...panels import Z64_Panel
from .operators import OOT_ExportAnim, OOT_ImportAnim
from .properties import OOTAnimExportSettingsProperty, OOTAnimImportSettingsProperty, OOTLinkTextureAnimProperty
class OOT_LinkAnimPanel(Panel):
- bl_idname = "OOT_PT_link_anim"
- bl_label = "OOT Link Animation Properties"
+ bl_idname = "Z64_PT_link_anim"
+ bl_label = "Link Animation Properties"
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "object"
@@ -17,7 +17,7 @@ class OOT_LinkAnimPanel(Panel):
@classmethod
def poll(cls, context):
return (
- context.scene.gameEditorMode == "OOT"
+ context.scene.gameEditorMode in {"OOT", "MM"}
and hasattr(context, "object")
and context.object is not None
and isinstance(context.object.data, Armature)
@@ -26,15 +26,15 @@ def poll(cls, context):
# called every frame
def draw(self, context):
col = self.layout.box().column()
- col.box().label(text="OOT Link Animation Inspector")
+ col.box().label(text="Link Animation Inspector")
linkTextureAnim: OOTLinkTextureAnimProperty = context.object.ootLinkTextureAnim
linkTextureAnim.draw_props(col)
col.label(text="Index 0 is for auto, flipbook starts at index 1.", icon="INFO")
-class OOT_ExportAnimPanel(OOT_Panel):
- bl_idname = "OOT_PT_export_anim"
- bl_label = "OOT Animation Exporter"
+class OOT_ExportAnimPanel(Z64_Panel):
+ bl_idname = "Z64_PT_export_anim"
+ bl_label = "Animations"
# called every frame
def draw(self, context):
diff --git a/fast64_internal/oot/animation/properties.py b/fast64_internal/z64/animation/properties.py
similarity index 96%
rename from fast64_internal/oot/animation/properties.py
rename to fast64_internal/z64/animation/properties.py
index ab3a641c9..97a510128 100644
--- a/fast64_internal/oot/animation/properties.py
+++ b/fast64_internal/z64/animation/properties.py
@@ -68,11 +68,7 @@ def draw_props(self, layout: UILayout):
prop_split(layout, self, "mouth", "Mouth")
-classes = (
- OOTAnimExportSettingsProperty,
- OOTAnimImportSettingsProperty,
- OOTLinkTextureAnimProperty,
-)
+classes = (OOTLinkTextureAnimProperty,)
def anim_props_register():
diff --git a/fast64_internal/oot/collection_utility.py b/fast64_internal/z64/collection_utility.py
similarity index 70%
rename from fast64_internal/oot/collection_utility.py
rename to fast64_internal/z64/collection_utility.py
index 98da2cc66..e3a40939e 100644
--- a/fast64_internal/oot/collection_utility.py
+++ b/fast64_internal/z64/collection_utility.py
@@ -14,13 +14,19 @@ class OOTCollectionAdd(Operator):
option: IntProperty()
collectionType: StringProperty(default="Actor")
subIndex: IntProperty(default=0)
+ collection_index: IntProperty(default=0)
objName: StringProperty()
def execute(self, context):
- collection = getCollection(self.objName, self.collectionType, self.subIndex)
+ collection = getCollection(self.objName, self.collectionType, self.subIndex, self.collection_index)
collection.add()
collection.move(len(collection) - 1, self.option)
+
+ owner = bpy.data.objects[self.objName]
+ if self.collectionType == "Actor CS" and owner.ootEmptyType == "Actor Cutscene":
+ context.scene.fast64.oot.global_actor_cs_count = len(collection)
+
return {"FINISHED"}
@@ -32,11 +38,17 @@ class OOTCollectionRemove(Operator):
option: IntProperty()
collectionType: StringProperty(default="Actor")
subIndex: IntProperty(default=0)
+ collection_index: IntProperty(default=0)
objName: StringProperty()
def execute(self, context):
- collection = getCollection(self.objName, self.collectionType, self.subIndex)
+ collection = getCollection(self.objName, self.collectionType, self.subIndex, self.collection_index)
collection.remove(self.option)
+
+ owner = bpy.data.objects[self.objName]
+ if self.collectionType == "Actor CS" and owner.ootEmptyType == "Actor Cutscene":
+ context.scene.fast64.oot.global_actor_cs_count = len(collection)
+
return {"FINISHED"}
@@ -48,12 +60,13 @@ class OOTCollectionMove(Operator):
option: IntProperty()
offset: IntProperty()
subIndex: IntProperty(default=0)
+ collection_index: IntProperty(default=0)
objName: StringProperty()
collectionType: StringProperty(default="Actor")
def execute(self, context):
- collection = getCollection(self.objName, self.collectionType, self.subIndex)
+ collection = getCollection(self.objName, self.collectionType, self.subIndex, self.collection_index)
collection.move(self.option, self.option + self.offset)
return {"FINISHED"}
@@ -66,7 +79,7 @@ def getCollectionFromIndex(obj, prop, subIndex, isRoom):
# Operators cannot store mutable references (?), so to reuse PropertyCollection modification code we do this.
# Save a string identifier in the operator, then choose the member variable based on that.
# subIndex is for a collection within a collection element
-def getCollection(objName, collectionType, subIndex):
+def getCollection(objName, collectionType, subIndex: int, collection_index: int = 0):
obj = bpy.data.objects[objName]
if collectionType == "Actor":
collection = obj.ootActorProperty.headerSettings.cutsceneHeaders
@@ -82,8 +95,28 @@ def getCollection(objName, collectionType, subIndex):
collection = getCollectionFromIndex(obj, "lightList", subIndex, False)
elif collectionType == "Exit":
collection = getCollectionFromIndex(obj, "exitList", subIndex, False)
+ elif collectionType == "Minimap Chest":
+ collection = getCollectionFromIndex(obj, "minimap_chest_list", subIndex, False)
+ elif collectionType == "Minimap Room":
+ collection = getCollectionFromIndex(obj, "minimap_room_list", subIndex, False)
elif collectionType == "Object":
collection = getCollectionFromIndex(obj, "objectList", subIndex, True)
+ elif collectionType == "Animated Mat. List":
+ collection = obj.z64_anim_mats_property.items
+ elif collectionType == "Animated Mat.":
+ collection = obj.z64_anim_mats_property.items[subIndex].entries
+ elif collectionType == "Animated Mat. Color":
+ collection = obj.z64_anim_mats_property.items[subIndex].entries[collection_index].color_params.keyframes
+ elif collectionType == "Animated Mat. Scroll":
+ collection = obj.z64_anim_mats_property.items[subIndex].entries[collection_index].tex_scroll_params.entries
+ elif collectionType == "Animated Mat. Cycle":
+ collection = obj.z64_anim_mats_property.items[subIndex].entries[collection_index].tex_cycle_params.keyframes
+ elif collectionType == "Actor CS":
+ collection = obj.z64_actor_cs_property.entries
+ elif collectionType == "Actor CS Headers":
+ collection = obj.z64_actor_cs_property.header_settings.cutsceneHeaders
+ elif collectionType == "Actor Halfday":
+ collection = obj.ootActorProperty.halfday_bits
elif collectionType == "Curve":
collection = obj.ootSplineProperty.headerSettings.cutsceneHeaders
elif collectionType.startswith("CSHdr."):
@@ -113,7 +146,7 @@ def getCollection(objName, collectionType, subIndex):
return collection
-def drawAddButton(layout, index, collectionType, subIndex, objName):
+def drawAddButton(layout, index, collectionType, subIndex, objName, collection_index: int = 0):
if subIndex is None:
subIndex = 0
addOp = layout.operator(OOTCollectionAdd.bl_idname)
@@ -121,9 +154,12 @@ def drawAddButton(layout, index, collectionType, subIndex, objName):
addOp.collectionType = collectionType
addOp.subIndex = subIndex
addOp.objName = objName
+ addOp.collection_index = collection_index
-def drawCollectionOps(layout, index, collectionType, subIndex, objName, allowAdd=True, compact=False):
+def drawCollectionOps(
+ layout, index, collectionType, subIndex, objName, allowAdd=True, compact=False, collection_index: int = 0
+):
if subIndex is None:
subIndex = 0
@@ -138,12 +174,14 @@ def drawCollectionOps(layout, index, collectionType, subIndex, objName, allowAdd
addOp.collectionType = collectionType
addOp.subIndex = subIndex
addOp.objName = objName
+ addOp.collection_index = collection_index
removeOp = buttons.operator(OOTCollectionRemove.bl_idname, text="Delete" if not compact else "", icon="REMOVE")
removeOp.option = index
removeOp.collectionType = collectionType
removeOp.subIndex = subIndex
removeOp.objName = objName
+ removeOp.collection_index = collection_index
moveUp = buttons.operator(OOTCollectionMove.bl_idname, text="Up" if not compact else "", icon="TRIA_UP")
moveUp.option = index
@@ -151,6 +189,7 @@ def drawCollectionOps(layout, index, collectionType, subIndex, objName, allowAdd
moveUp.collectionType = collectionType
moveUp.subIndex = subIndex
moveUp.objName = objName
+ moveUp.collection_index = collection_index
moveDown = buttons.operator(OOTCollectionMove.bl_idname, text="Down" if not compact else "", icon="TRIA_DOWN")
moveDown.option = index
@@ -158,6 +197,7 @@ def drawCollectionOps(layout, index, collectionType, subIndex, objName, allowAdd
moveDown.collectionType = collectionType
moveDown.subIndex = subIndex
moveDown.objName = objName
+ moveDown.collection_index = collection_index
collections_classes = (
diff --git a/fast64_internal/z64/collision/constants.py b/fast64_internal/z64/collision/constants.py
new file mode 100644
index 000000000..88d33de28
--- /dev/null
+++ b/fast64_internal/z64/collision/constants.py
@@ -0,0 +1,83 @@
+ootEnumConveyer = [
+ ("None", "None", "None"),
+ ("Land", "Land", "Land"),
+ ("Water", "Water", "Water"),
+]
+
+ootEnumWallSetting = [
+ ("Custom", "Custom", "Custom"),
+ ("0x00", "Default", "Default"),
+ ("0x01", "No Ledge Climb", "No Ledge Climb"),
+ ("0x02", "Ladder Bottom", "Ladder Bottom"),
+ ("0x03", "Ladder Top", "Ladder Top"),
+ ("0x04", "Vines", "Vines"),
+ ("0x05", "Crawl Space 1", "Crawl Space 1"),
+ ("0x06", "Crawl Space 2", "Crawl Space 2"),
+ ("0x07", "Push Block", "Push Block"),
+]
+
+ootEnumConveyorSpeed = [
+ ("Custom", "Custom", "Custom"),
+ ("0x00", "None", "None"),
+ ("0x01", "Slow", "Slow"),
+ ("0x02", "Medium", "Medium"),
+ ("0x03", "Fast", "Fast"),
+]
+
+enum_camera_crawlspace_stype = [
+ ("Custom", "Custom", "Custom"),
+ ("CAM_SET_CRAWLSPACE", "Crawlspace", "Crawlspace"),
+]
+
+decomp_compat_map_CameraSType = {
+ "CAM_SET_HORSE0": "CAM_SET_HORSE",
+ "CAM_SET_BOSS_GOMA": "CAM_SET_BOSS_GOHMA",
+ "CAM_SET_BOSS_DODO": "CAM_SET_BOSS_DODONGO",
+ "CAM_SET_BOSS_BARI": "CAM_SET_BOSS_BARINADE",
+ "CAM_SET_BOSS_FGANON": "CAM_SET_BOSS_PHANTOM_GANON",
+ "CAM_SET_BOSS_BAL": "CAM_SET_BOSS_VOLVAGIA",
+ "CAM_SET_BOSS_SHADES": "CAM_SET_BOSS_BONGO",
+ "CAM_SET_BOSS_MOFA": "CAM_SET_BOSS_MORPHA",
+ "CAM_SET_TWIN0": "CAM_SET_BOSS_TWINROVA_PLATFORM",
+ "CAM_SET_TWIN1": "CAM_SET_BOSS_TWINROVA_FLOOR",
+ "CAM_SET_BOSS_GANON1": "CAM_SET_BOSS_GANONDORF",
+ "CAM_SET_BOSS_GANON2": "CAM_SET_BOSS_GANON",
+ "CAM_SET_TOWER0": "CAM_SET_TOWER_CLIMB",
+ "CAM_SET_TOWER1": "CAM_SET_TOWER_UNUSED",
+ "CAM_SET_FIXED0": "CAM_SET_MARKET_BALCONY",
+ "CAM_SET_FIXED1": "CAM_SET_CHU_BOWLING",
+ "CAM_SET_CIRCLE0": "CAM_SET_PIVOT_CRAWLSPACE",
+ "CAM_SET_CIRCLE2": "CAM_SET_PIVOT_SHOP_BROWSING",
+ "CAM_SET_CIRCLE3": "CAM_SET_PIVOT_IN_FRONT",
+ "CAM_SET_PREREND0": "CAM_SET_PREREND_FIXED",
+ "CAM_SET_PREREND1": "CAM_SET_PREREND_PIVOT",
+ "CAM_SET_PREREND3": "CAM_SET_PREREND_SIDE_SCROLL",
+ "CAM_SET_RAIL3": "CAM_SET_CRAWLSPACE",
+ "CAM_SET_CIRCLE4": "CAM_SET_PIVOT_CORNER",
+ "CAM_SET_CIRCLE5": "CAM_SET_PIVOT_WATER_SURFACE",
+ "CAM_SET_DEMO0": "CAM_SET_CS_0",
+ "CAM_SET_DEMO1": "CAM_SET_CS_TWISTED_HALLWAY",
+ "CAM_SET_MORI1": "CAM_SET_FOREST_BIRDS_EYE",
+ "CAM_SET_ITEM0": "CAM_SET_SLOW_CHEST_CS",
+ "CAM_SET_ITEM1": "CAM_SET_ITEM_UNUSED",
+ "CAM_SET_DEMO3": "CAM_SET_CS_3",
+ "CAM_SET_DEMO4": "CAM_SET_CS_ATTENTION",
+ "CAM_SET_UFOBEAN": "CAM_SET_BEAN_GENERIC",
+ "CAM_SET_LIFTBEAN": "CAM_SET_BEAN_LOST_WOODS",
+ "CAM_SET_SCENE0": "CAM_SET_SCENE_UNUSED",
+ "CAM_SET_SCENE1": "CAM_SET_SCENE_TRANSITION",
+ "CAM_SET_HIDAN1": "CAM_SET_ELEVATOR_PLATFORM",
+ "CAM_SET_HIDAN2": "CAM_SET_FIRE_STAIRCASE",
+ "CAM_SET_MORI2": "CAM_SET_FOREST_UNUSED",
+ "CAM_SET_MORI3": "CAM_SET_FOREST_DEFEAT_POE",
+ "CAM_SET_TAKO": "CAM_SET_BIG_OCTO",
+ "CAM_SET_SPOT05A": "CAM_SET_MEADOW_BIRDS_EYE",
+ "CAM_SET_SPOT05B": "CAM_SET_MEADOW_UNUSED",
+ "CAM_SET_HIDAN3": "CAM_SET_FIRE_BIRDS_EYE",
+ "CAM_SET_ITEM2": "CAM_SET_TURN_AROUND",
+ "CAM_SET_CIRCLE6": "CAM_SET_PIVOT_VERTICAL",
+ "CAM_SET_DEMOC": "CAM_SET_CS_C",
+ "CAM_SET_UO_FIBER": "CAM_SET_JABU_TENTACLE",
+ "CAM_SET_TEPPEN": "CAM_SET_DIRECTED_YAW",
+ "CAM_SET_CIRCLE7": "CAM_SET_PIVOT_FROM_SIDE",
+}
diff --git a/fast64_internal/oot/collision/exporter/__init__.py b/fast64_internal/z64/collision/exporter/__init__.py
similarity index 100%
rename from fast64_internal/oot/collision/exporter/__init__.py
rename to fast64_internal/z64/collision/exporter/__init__.py
diff --git a/fast64_internal/oot/collision/exporter/classes.py b/fast64_internal/z64/collision/exporter/classes.py
similarity index 98%
rename from fast64_internal/oot/collision/exporter/classes.py
rename to fast64_internal/z64/collision/exporter/classes.py
index af8ba58ec..25898b3c9 100644
--- a/fast64_internal/oot/collision/exporter/classes.py
+++ b/fast64_internal/z64/collision/exporter/classes.py
@@ -1,6 +1,6 @@
import math
from ....utility import PluginError
-from ...oot_utility import BoxEmpty, convertIntTo2sComplement, getCustomProperty
+from ...utility import BoxEmpty, convertIntTo2sComplement, getCustomProperty
class OOTCollisionVertex:
@@ -193,9 +193,9 @@ def getPolygonType(collisionProp):
polygonType.ignoreProjectileCollision = collisionProp.ignoreProjectileCollision
polygonType.eponaBlock = collisionProp.eponaBlock
polygonType.decreaseHeight = collisionProp.decreaseHeight
- polygonType.floorSetting = getCustomProperty(collisionProp, "floorSetting")
+ polygonType.floorSetting = getCustomProperty(collisionProp, "floorSetting", "floorSettingCustom")
polygonType.wallSetting = getCustomProperty(collisionProp, "wallSetting")
- polygonType.floorProperty = getCustomProperty(collisionProp, "floorProperty")
+ polygonType.floorProperty = getCustomProperty(collisionProp, "floorProperty", "floorPropertyCustom")
polygonType.exitID = collisionProp.exitID
polygonType.cameraID = collisionProp.cameraID
polygonType.isWallDamage = collisionProp.isWallDamage
diff --git a/fast64_internal/oot/collision/exporter/functions.py b/fast64_internal/z64/collision/exporter/functions.py
similarity index 99%
rename from fast64_internal/oot/collision/exporter/functions.py
rename to fast64_internal/z64/collision/exporter/functions.py
index 19364ddd9..616127a51 100644
--- a/fast64_internal/oot/collision/exporter/functions.py
+++ b/fast64_internal/z64/collision/exporter/functions.py
@@ -2,7 +2,7 @@
import mathutils
from ....utility import PluginError
-from ...oot_utility import convertIntTo2sComplement
+from ...utility import convertIntTo2sComplement
from .classes import OOTCollisionVertex, OOTCollisionPolygon, getPolygonType
diff --git a/fast64_internal/oot/collision/exporter/to_c/__init__.py b/fast64_internal/z64/collision/exporter/to_c/__init__.py
similarity index 100%
rename from fast64_internal/oot/collision/exporter/to_c/__init__.py
rename to fast64_internal/z64/collision/exporter/to_c/__init__.py
diff --git a/fast64_internal/oot/collision/exporter/to_c/collision.py b/fast64_internal/z64/collision/exporter/to_c/collision.py
similarity index 99%
rename from fast64_internal/oot/collision/exporter/to_c/collision.py
rename to fast64_internal/z64/collision/exporter/to_c/collision.py
index 16a6533b9..e93559898 100644
--- a/fast64_internal/oot/collision/exporter/to_c/collision.py
+++ b/fast64_internal/z64/collision/exporter/to_c/collision.py
@@ -16,7 +16,7 @@
toAlnum,
)
-from ....oot_utility import (
+from ....utility import (
OOTObjectCategorizer,
addIncludeFiles,
ootDuplicateHierarchy,
diff --git a/fast64_internal/oot/collision/operators.py b/fast64_internal/z64/collision/operators.py
similarity index 97%
rename from fast64_internal/oot/collision/operators.py
rename to fast64_internal/z64/collision/operators.py
index 38fe2a5df..861ffab6d 100644
--- a/fast64_internal/oot/collision/operators.py
+++ b/fast64_internal/z64/collision/operators.py
@@ -3,7 +3,7 @@
from bpy.ops import object
from mathutils import Matrix
from ...utility import PluginError, raisePluginError
-from ..oot_utility import getOOTScale
+from ..utility import getOOTScale
from ..collision.exporter.to_c import exportCollisionToC
from .properties import OOTCollisionExportSettings
diff --git a/fast64_internal/oot/collision/panels.py b/fast64_internal/z64/collision/panels.py
similarity index 84%
rename from fast64_internal/oot/collision/panels.py
rename to fast64_internal/z64/collision/panels.py
index ee98b0745..8749c4c8d 100644
--- a/fast64_internal/oot/collision/panels.py
+++ b/fast64_internal/z64/collision/panels.py
@@ -1,6 +1,6 @@
from bpy.types import Panel, Camera
from bpy.utils import register_class, unregister_class
-from ...panels import OOT_Panel
+from ...panels import Z64_Panel
from .properties import OOTCollisionExportSettings, OOTCameraPositionProperty, OOTMaterialCollisionProperty
from .operators import OOT_ExportCollision
@@ -15,7 +15,7 @@ class OOT_CameraPosPanel(Panel):
@classmethod
def poll(cls, context):
- return context.scene.gameEditorMode == "OOT" and isinstance(context.object.data, Camera)
+ return context.scene.gameEditorMode in {"OOT", "MM"} and isinstance(context.object.data, Camera)
def draw(self, context):
box = self.layout.box().column()
@@ -36,7 +36,7 @@ class OOT_CollisionPanel(Panel):
@classmethod
def poll(cls, context):
- return context.scene.gameEditorMode == "OOT" and context.material is not None
+ return context.scene.gameEditorMode in {"OOT", "MM"} and context.material is not None
def draw(self, context):
box = self.layout.box().column()
@@ -45,9 +45,9 @@ def draw(self, context):
collisionProp.draw_props(box)
-class OOT_ExportCollisionPanel(OOT_Panel):
- bl_idname = "OOT_PT_export_collision"
- bl_label = "OOT Collision Exporter"
+class OOT_ExportCollisionPanel(Z64_Panel):
+ bl_idname = "Z64_PT_export_collision"
+ bl_label = "Collisions"
# called every frame
def draw(self, context):
diff --git a/fast64_internal/oot/collision/properties.py b/fast64_internal/z64/collision/properties.py
similarity index 68%
rename from fast64_internal/oot/collision/properties.py
rename to fast64_internal/z64/collision/properties.py
index 2b2373773..eb9ce3fcd 100644
--- a/fast64_internal/oot/collision/properties.py
+++ b/fast64_internal/z64/collision/properties.py
@@ -1,19 +1,16 @@
import math
+
from bpy.props import StringProperty, PointerProperty, IntProperty, EnumProperty, BoolProperty, FloatProperty
-from bpy.types import PropertyGroup, Camera, Object, Material, UILayout
+from bpy.types import PropertyGroup, Object, Material, UILayout
from bpy.utils import register_class, unregister_class
from ...utility import prop_split
-from ..oot_utility import drawEnumWithCustom
-from ..oot_constants import ootEnumSceneID
+from ...game_data import game_data
+from ..utility import drawEnumWithCustom
+
from .constants import (
- ootEnumFloorSetting,
ootEnumWallSetting,
- ootEnumFloorProperty,
ootEnumConveyer,
ootEnumConveyorSpeed,
- ootEnumCollisionTerrain,
- ootEnumCollisionSound,
- ootEnumCameraSType,
)
@@ -45,16 +42,24 @@ def draw_props(self, layout: UILayout):
class OOTCameraPositionProperty(PropertyGroup):
index: IntProperty(min=0)
bgImageOverrideIndex: IntProperty(default=-1, min=-1)
- camSType: EnumProperty(items=ootEnumCameraSType, default="CAM_SET_NONE")
- camSTypeCustom: StringProperty(default="CAM_SET_NONE")
+ camSType: EnumProperty(items=lambda self, context: game_data.z64.get_enum("camSType"), default=2)
+ camSTypeCustom: StringProperty(default="CAM_SET_NORMAL0")
hasPositionData: BoolProperty(default=True, name="Has Position Data")
+ is_actor_cs_cam: BoolProperty(default=False, name="Is Actor CS Camera")
+ use_setting_default_fov: BoolProperty(name="Use the default FoV from the camera setting", default=False)
def draw_props(self, layout: UILayout, cameraObj: Object):
- drawEnumWithCustom(layout, self, "camSType", "Camera S Type", "")
+ drawEnumWithCustom(layout, self, "camSType", "Camera S Type", "", "camSTypeCustom")
prop_split(layout, self, "index", "Camera Index")
- layout.prop(self, "hasPositionData")
- if self.hasPositionData:
- prop_split(layout, cameraObj.data, "angle", "Field Of View")
+ layout.prop(self, "is_actor_cs_cam")
+
+ if not self.is_actor_cs_cam:
+ layout.prop(self, "hasPositionData")
+
+ layout.prop(self, "use_setting_default_fov")
+ if not self.is_actor_cs_cam and self.hasPositionData:
+ if not self.use_setting_default_fov:
+ prop_split(layout, cameraObj.data, "angle", "Field Of View")
prop_split(layout, self, "bgImageOverrideIndex", "BG Index Override")
@@ -68,60 +73,63 @@ class OOTMaterialCollisionProperty(PropertyGroup):
eponaBlock: BoolProperty()
decreaseHeight: BoolProperty()
floorSettingCustom: StringProperty(default="0x00")
- floorSetting: EnumProperty(items=ootEnumFloorSetting, default="0x00")
+ floorSetting: EnumProperty(items=lambda self, context: game_data.z64.get_enum("floorSetting"), default=1)
wallSettingCustom: StringProperty(default="0x00")
- wallSetting: EnumProperty(items=ootEnumWallSetting, default="0x00")
+ wallSetting: EnumProperty(items=ootEnumWallSetting, default=1)
floorPropertyCustom: StringProperty(default="0x00")
- floorProperty: EnumProperty(items=ootEnumFloorProperty, default="0x00")
+ floorProperty: EnumProperty(items=lambda self, context: game_data.z64.get_enum("floorProperty"), default=1)
exitID: IntProperty(default=0, min=0)
cameraID: IntProperty(default=0, min=0)
isWallDamage: BoolProperty()
conveyorOption: EnumProperty(items=ootEnumConveyer)
conveyorRotation: FloatProperty(min=0, max=2 * math.pi, subtype="ANGLE")
- conveyorSpeed: EnumProperty(items=ootEnumConveyorSpeed, default="0x00")
+ conveyorSpeed: EnumProperty(items=ootEnumConveyorSpeed, default=1)
conveyorSpeedCustom: StringProperty(default="0x00")
conveyorKeepMomentum: BoolProperty()
hookshotable: BoolProperty()
echo: StringProperty(default="0x00")
lightingSetting: IntProperty(default=0, min=0)
terrainCustom: StringProperty(default="0x00")
- terrain: EnumProperty(items=ootEnumCollisionTerrain, default="0x00")
+ terrain: EnumProperty(items=game_data.z64.enum_floor_effect, default=1)
soundCustom: StringProperty(default="0x00")
- sound: EnumProperty(items=ootEnumCollisionSound, default="0x00")
+ sound: EnumProperty(items=lambda self, context: game_data.z64.get_enum("sound"), default=1)
def draw_props(self, layout: UILayout):
layout.prop(
self,
"expandTab",
- text="OOT Collision Properties",
+ text="Collision Properties",
icon="TRIA_DOWN" if self.expandTab else "TRIA_RIGHT",
)
if self.expandTab:
- prop_split(layout, self, "exitID", "Exit ID")
- prop_split(layout, self, "cameraID", "Camera ID")
+ prop_split(layout, self, "exitID", "Exit Index")
+ prop_split(layout, self, "cameraID", "Camera Index")
+ prop_split(layout, self, "lightingSetting", "Lighting Index")
prop_split(layout, self, "echo", "Echo")
- prop_split(layout, self, "lightingSetting", "Lighting")
- drawEnumWithCustom(layout, self, "terrain", "Terrain", "")
- drawEnumWithCustom(layout, self, "sound", "Sound", "")
+
+ enum_box = layout.box().column()
+ enum_box.label(text="Surface Settings")
+ drawEnumWithCustom(enum_box, self, "floorProperty", "Floor Type", "", "floorPropertyCustom")
+ drawEnumWithCustom(enum_box, self, "floorSetting", "Floor Property", "", "floorSettingCustom")
+ drawEnumWithCustom(enum_box, self, "terrain", "Floor Effect", "", "terrainCustom")
+ drawEnumWithCustom(enum_box, self, "sound", "Surface Material", "", "soundCustom")
+ drawEnumWithCustom(enum_box, self, "wallSetting", "Wall Type", "")
layout.prop(self, "eponaBlock", text="Blocks Epona")
layout.prop(self, "decreaseHeight", text="Decrease Height 1 Unit")
layout.prop(self, "isWallDamage", text="Is Wall Damage")
layout.prop(self, "hookshotable", text="Hookshotable")
-
- drawEnumWithCustom(layout, self, "floorSetting", "Floor Setting", "")
- drawEnumWithCustom(layout, self, "wallSetting", "Wall Setting", "")
- drawEnumWithCustom(layout, self, "floorProperty", "Floor Property", "")
-
layout.prop(self, "ignoreCameraCollision", text="Ignore Camera Collision")
layout.prop(self, "ignoreActorCollision", text="Ignore Actor Collision")
layout.prop(self, "ignoreProjectileCollision", text="Ignore Projectile Collision")
- prop_split(layout, self, "conveyorOption", "Conveyor Option")
+
+ conveyor_box = layout.box().column()
+ prop_split(conveyor_box, self, "conveyorOption", "Conveyor Option")
if self.conveyorOption != "None":
- prop_split(layout, self, "conveyorRotation", "Conveyor Rotation")
- drawEnumWithCustom(layout, self, "conveyorSpeed", "Conveyor Speed", "")
+ prop_split(conveyor_box, self, "conveyorRotation", "Conveyor Rotation")
+ drawEnumWithCustom(conveyor_box, self, "conveyorSpeed", "Conveyor Speed", "")
if self.conveyorSpeed != "Custom":
- layout.prop(self, "conveyorKeepMomentum", text="Keep Momentum")
+ conveyor_box.prop(self, "conveyorKeepMomentum", text="Keep Momentum")
class OOTWaterBoxProperty(PropertyGroup):
@@ -139,7 +147,6 @@ def draw_props(self, layout: UILayout):
oot_col_classes = (
- OOTCollisionExportSettings,
OOTCameraPositionProperty,
OOTMaterialCollisionProperty,
OOTWaterBoxProperty,
diff --git a/fast64_internal/oot/oot_constants.py b/fast64_internal/z64/constants.py
similarity index 50%
rename from fast64_internal/oot/oot_constants.py
rename to fast64_internal/z64/constants.py
index a0b33813c..793d62e51 100644
--- a/fast64_internal/oot/oot_constants.py
+++ b/fast64_internal/z64/constants.py
@@ -1,12 +1,9 @@
-from .data import OoT_Data
-
-ootData = OoT_Data()
-
ootEnumRoomShapeType = [
# ("Custom", "Custom", "Custom"),
("ROOM_SHAPE_TYPE_NORMAL", "Normal", "Normal"),
("ROOM_SHAPE_TYPE_IMAGE", "Image", "Image"),
("ROOM_SHAPE_TYPE_CULLABLE", "Cullable", "Cullable"),
+ ("ROOM_SHAPE_TYPE_NONE", "None", "None"),
]
ootEnumHeaderMenu = [
@@ -19,23 +16,6 @@
("Child Day", "Child Day", "Child Day"),
] + ootEnumHeaderMenu
-ootEnumLinkIdle = [
- ("Custom", "Custom", "Custom"),
- ("0x00", "Default", "Default"),
- ("0x01", "Sneezing", "Sneezing"),
- ("0x02", "Wiping Forehead", "Wiping Forehead"),
- ("0x04", "Yawning", "Yawning"),
- ("0x07", "Gasping For Breath", "Gasping For Breath"),
- ("0x09", "Brandish Sword", "Brandish Sword"),
- ("0x0A", "Adjust Tunic", "Adjust Tunic"),
- ("0xFF", "Hops On Epona", "Hops On Epona"),
-]
-
-ootEnumCloudiness = [
- ("Custom", "Custom", "Custom"),
- ("0x00", "Sunny", "Sunny"),
- ("0x01", "Cloudy", "Cloudy"),
-]
ootEnumCameraMode = [
("Custom", "Custom", "Custom"),
@@ -74,37 +54,6 @@
("0x16", "Grottos & Fairy Fountains", "Grottos & Fairy Fountains"),
]
-ootEnumSkybox = [
- ("Custom", "Custom", "Custom"),
- ("0x00", "None", "None"),
- ("0x01", "Standard Sky", "Standard Sky"),
- ("0x02", "Hylian Bazaar", "Hylian Bazaar"),
- ("0x03", "Brown Cloudy Sky", "Brown Cloudy Sky"),
- ("0x04", "Market Ruins", "Market Ruins"),
- ("0x05", "Black Cloudy Night", "Black Cloudy Night"),
- ("0x07", "Link's House", "Link's House"),
- ("0x09", "Market (Main Square, Day)", "Market (Main Square, Day)"),
- ("0x0A", "Market (Main Square, Night)", "Market (Main Square, Night)"),
- ("0x0B", "Happy Mask Shop", "Happy Mask Shop"),
- ("0x0C", "Know-It-All Brothers' House", "Know-It-All Brothers' House"),
- ("0x0E", "Kokiri Twins' House", "Kokiri Twins' House"),
- ("0x0F", "Stable", "Stable"),
- ("0x10", "Stew Lady's House", "Stew Lady's House"),
- ("0x11", "Kokiri Shop", "Kokiri Shop"),
- ("0x13", "Goron Shop", "Goron Shop"),
- ("0x14", "Zora Shop", "Zora Shop"),
- ("0x16", "Kakariko Potions Shop", "Kakariko Potions Shop"),
- ("0x17", "Hylian Potions Shop", "Hylian Potions Shop"),
- ("0x18", "Bomb Shop", "Bomb Shop"),
- ("0x1A", "Dog Lady's House", "Dog Lady's House"),
- ("0x1B", "Impa's House", "Impa's House"),
- ("0x1C", "Gerudo Tent", "Gerudo Tent"),
- ("0x1D", "Environment Color", "Environment Color"),
- ("0x20", "Mido's House", "Mido's House"),
- ("0x21", "Saria's House", "Saria's House"),
- ("0x22", "Dog Guy's House", "Dog Guy's House"),
-]
-
ootEnumSkyboxLighting = [
# see ``LightMode`` enum in ``z64environment.h``
("Custom", "Custom", "Custom"),
@@ -117,174 +66,11 @@
("0x00", "0x00", "0x00"),
]
-ootEnumMusicSeq = [
- # see https://github.com/zeldaret/oot/blob/9f09505d34619883748a7dab05071883281c14fd/include/sequence.h#L4-L118
- ("Custom", "Custom", "Custom"),
- ("NA_BGM_GENERAL_SFX", "General Sound Effects", "General Sound Effects"),
- ("NA_BGM_NATURE_AMBIENCE", "Nature Ambiance", "Nature Ambiance"),
- ("NA_BGM_FIELD_LOGIC", "Hyrule Field", "Hyrule Field"),
- (
- "NA_BGM_FIELD_INIT",
- "Hyrule Field (Initial Segment From Loading Area)",
- "Hyrule Field (Initial Segment From Loading Area)",
- ),
- ("NA_BGM_FIELD_DEFAULT_1", "Hyrule Field (Moving Segment 1)", "Hyrule Field (Moving Segment 1)"),
- ("NA_BGM_FIELD_DEFAULT_2", "Hyrule Field (Moving Segment 2)", "Hyrule Field (Moving Segment 2)"),
- ("NA_BGM_FIELD_DEFAULT_3", "Hyrule Field (Moving Segment 3)", "Hyrule Field (Moving Segment 3)"),
- ("NA_BGM_FIELD_DEFAULT_4", "Hyrule Field (Moving Segment 4)", "Hyrule Field (Moving Segment 4)"),
- ("NA_BGM_FIELD_DEFAULT_5", "Hyrule Field (Moving Segment 5)", "Hyrule Field (Moving Segment 5)"),
- ("NA_BGM_FIELD_DEFAULT_6", "Hyrule Field (Moving Segment 6)", "Hyrule Field (Moving Segment 6)"),
- ("NA_BGM_FIELD_DEFAULT_7", "Hyrule Field (Moving Segment 7)", "Hyrule Field (Moving Segment 7)"),
- ("NA_BGM_FIELD_DEFAULT_8", "Hyrule Field (Moving Segment 8)", "Hyrule Field (Moving Segment 8)"),
- ("NA_BGM_FIELD_DEFAULT_9", "Hyrule Field (Moving Segment 9)", "Hyrule Field (Moving Segment 9)"),
- ("NA_BGM_FIELD_DEFAULT_A", "Hyrule Field (Moving Segment 10)", "Hyrule Field (Moving Segment 10)"),
- ("NA_BGM_FIELD_DEFAULT_B", "Hyrule Field (Moving Segment 11)", "Hyrule Field (Moving Segment 11)"),
- ("NA_BGM_FIELD_ENEMY_INIT", "Hyrule Field (Enemy Approaches)", "Hyrule Field (Enemy Approaches)"),
- ("NA_BGM_FIELD_ENEMY_1", "Hyrule Field (Enemy Near Segment 1)", "Hyrule Field (Enemy Near Segment 1)"),
- ("NA_BGM_FIELD_ENEMY_2", "Hyrule Field (Enemy Near Segment 2)", "Hyrule Field (Enemy Near Segment 2)"),
- ("NA_BGM_FIELD_ENEMY_3", "Hyrule Field (Enemy Near Segment 3)", "Hyrule Field (Enemy Near Segment 3)"),
- ("NA_BGM_FIELD_ENEMY_4", "Hyrule Field (Enemy Near Segment 4)", "Hyrule Field (Enemy Near Segment 4)"),
- ("NA_BGM_FIELD_STILL_1", "Hyrule Field (Standing Still Segment 1)", "Hyrule Field (Standing Still Segment 1)"),
- ("NA_BGM_FIELD_STILL_2", "Hyrule Field (Standing Still Segment 2)", "Hyrule Field (Standing Still Segment 2)"),
- ("NA_BGM_FIELD_STILL_3", "Hyrule Field (Standing Still Segment 3)", "Hyrule Field (Standing Still Segment 3)"),
- ("NA_BGM_FIELD_STILL_4", "Hyrule Field (Standing Still Segment 4)", "Hyrule Field (Standing Still Segment 4)"),
- ("NA_BGM_DUNGEON", "Dodongo's Cavern", "Dodongo's Cavern"),
- ("NA_BGM_KAKARIKO_ADULT", "Kakariko Village (Adult)", "Kakariko Village (Adult)"),
- ("NA_BGM_ENEMY", "Enemy Battle", "Enemy Battle"),
- ("NA_BGM_BOSS", "Boss Battle 00", "Boss Battle 00"),
- ("NA_BGM_INSIDE_DEKU_TREE", "Inside the Deku Tree", "Inside the Deku Tree"),
- ("NA_BGM_MARKET", "Market", "Market"),
- ("NA_BGM_TITLE", "Title Theme", "Title Theme"),
- ("NA_BGM_LINK_HOUSE", "Link's House", "Link's House"),
- ("NA_BGM_GAME_OVER", "Game Over", "Game Over"),
- ("NA_BGM_BOSS_CLEAR", "Boss Clear", "Boss Clear"),
- ("NA_BGM_ITEM_GET", "Item Get", "Item Get"),
- ("NA_BGM_OPENING_GANON", "Opening Ganon", "Opening Ganon"),
- ("NA_BGM_HEART_GET", "Heart Get", "Heart Get"),
- ("NA_BGM_OCA_LIGHT", "Prelude Of Light", "Prelude Of Light"),
- ("NA_BGM_JABU_JABU", "Inside Jabu-Jabu's Belly", "Inside Jabu-Jabu's Belly"),
- ("NA_BGM_KAKARIKO_KID", "Kakariko Village (Child)", "Kakariko Village (Child)"),
- ("NA_BGM_GREAT_FAIRY", "Great Fairy's Fountain", "Great Fairy's Fountain"),
- ("NA_BGM_ZELDA_THEME", "Zelda's Theme", "Zelda's Theme"),
- ("NA_BGM_FIRE_TEMPLE", "Fire Temple", "Fire Temple"),
- ("NA_BGM_OPEN_TRE_BOX", "Open Treasure Chest", "Open Treasure Chest"),
- ("NA_BGM_FOREST_TEMPLE", "Forest Temple", "Forest Temple"),
- ("NA_BGM_COURTYARD", "Hyrule Castle Courtyard", "Hyrule Castle Courtyard"),
- ("NA_BGM_GANON_TOWER", "Ganondorf's Theme", "Ganondorf's Theme"),
- ("NA_BGM_LONLON", "Lon Lon Ranch", "Lon Lon Ranch"),
- ("NA_BGM_GORON_CITY", "Goron City", "Goron City"),
- ("NA_BGM_FIELD_MORNING", "Hyrule Field Morning Theme", "Hyrule Field Morning Theme"),
- ("NA_BGM_SPIRITUAL_STONE", "Spiritual Stone Get", "Spiritual Stone Get"),
- ("NA_BGM_OCA_BOLERO", "Bolero of Fire", "Bolero of Fire"),
- ("NA_BGM_OCA_MINUET", "Minuet of Woods", "Minuet of Woods"),
- ("NA_BGM_OCA_SERENADE", "Serenade of Water", "Serenade of Water"),
- ("NA_BGM_OCA_REQUIEM", "Requiem of Spirit", "Requiem of Spirit"),
- ("NA_BGM_OCA_NOCTURNE", "Nocturne of Shadow", "Nocturne of Shadow"),
- ("NA_BGM_MINI_BOSS", "Mini-Boss Battle", "Mini-Boss Battle"),
- ("NA_BGM_SMALL_ITEM_GET", "Obtain Small Item", "Obtain Small Item"),
- ("NA_BGM_TEMPLE_OF_TIME", "Temple of Time", "Temple of Time"),
- ("NA_BGM_EVENT_CLEAR", "Escape from Lon Lon Ranch", "Escape from Lon Lon Ranch"),
- ("NA_BGM_KOKIRI", "Kokiri Forest", "Kokiri Forest"),
- ("NA_BGM_OCA_FAIRY_GET", "Obtain Fairy Ocarina", "Obtain Fairy Ocarina"),
- ("NA_BGM_SARIA_THEME", "Lost Woods", "Lost Woods"),
- ("NA_BGM_SPIRIT_TEMPLE", "Spirit Temple", "Spirit Temple"),
- ("NA_BGM_HORSE", "Horse Race", "Horse Race"),
- ("NA_BGM_HORSE_GOAL", "Horse Race Goal", "Horse Race Goal"),
- ("NA_BGM_INGO", "Ingo's Theme", "Ingo's Theme"),
- ("NA_BGM_MEDALLION_GET", "Obtain Medallion", "Obtain Medallion"),
- ("NA_BGM_OCA_SARIA", "Ocarina Saria's Song", "Ocarina Saria's Song"),
- ("NA_BGM_OCA_EPONA", "Ocarina Epona's Song", "Ocarina Epona's Song"),
- ("NA_BGM_OCA_ZELDA", "Ocarina Zelda's Lullaby", "Ocarina Zelda's Lullaby"),
- ("NA_BGM_OCA_SUNS", "Sun's Song", "Sun's Song"),
- ("NA_BGM_OCA_TIME", "Song of Time", "Song of Time"),
- ("NA_BGM_OCA_STORM", "Song of Storms", "Song of Storms"),
- ("NA_BGM_NAVI_OPENING", "Fairy Flying", "Fairy Flying"),
- ("NA_BGM_DEKU_TREE_CS", "Deku Tree", "Deku Tree"),
- ("NA_BGM_WINDMILL", "Windmill Hut", "Windmill Hut"),
- ("NA_BGM_HYRULE_CS", "Legend of Hyrule", "Legend of Hyrule"),
- ("NA_BGM_MINI_GAME", "Shooting Gallery", "Shooting Gallery"),
- ("NA_BGM_SHEIK", "Sheik's Theme", "Sheik's Theme"),
- ("NA_BGM_ZORA_DOMAIN", "Zora's Domain", "Zora's Domain"),
- ("NA_BGM_APPEAR", "Enter Zelda", "Enter Zelda"),
- ("NA_BGM_ADULT_LINK", "Goodbye to Zelda", "Goodbye to Zelda"),
- ("NA_BGM_MASTER_SWORD", "Master Sword", "Master Sword"),
- ("NA_BGM_INTRO_GANON", "Ganon Intro", "Ganon Intro"),
- ("NA_BGM_SHOP", "Shop", "Shop"),
- ("NA_BGM_CHAMBER_OF_SAGES", "Chamber of the Sages", "Chamber of the Sages"),
- ("NA_BGM_FILE_SELECT", "File Select", "File Select"),
- ("NA_BGM_ICE_CAVERN", "Ice Cavern", "Ice Cavern"),
- ("NA_BGM_DOOR_OF_TIME", "Open Door of Temple of Time", "Open Door of Temple of Time"),
- ("NA_BGM_OWL", "Kaepora Gaebora's Theme", "Kaepora Gaebora's Theme"),
- ("NA_BGM_SHADOW_TEMPLE", "Shadow Temple", "Shadow Temple"),
- ("NA_BGM_WATER_TEMPLE", "Water Temple", "Water Temple"),
- ("NA_BGM_BRIDGE_TO_GANONS", "Ganon's Castle Bridge", "Ganon's Castle Bridge"),
- ("NA_BGM_OCARINA_OF_TIME", "Ocarina of Time", "Ocarina of Time"),
- ("NA_BGM_GERUDO_VALLEY", "Gerudo Valley", "Gerudo Valley"),
- ("NA_BGM_POTION_SHOP", "Potion Shop", "Potion Shop"),
- ("NA_BGM_KOTAKE_KOUME", "Kotake & Koume's Theme", "Kotake & Koume's Theme"),
- ("NA_BGM_ESCAPE", "Escape from Ganon's Castle", "Escape from Ganon's Castle"),
- ("NA_BGM_UNDERGROUND", "Ganon's Castle Under Ground", "Ganon's Castle Under Ground"),
- ("NA_BGM_GANONDORF_BOSS", "Ganondorf Battle", "Ganondorf Battle"),
- ("NA_BGM_GANON_BOSS", "Ganon Battle", "Ganon Battle"),
- ("NA_BGM_END_DEMO", "Seal of Six Sages", "Seal of Six Sages"),
- ("NA_BGM_STAFF_1", "End Credits I", "End Credits I"),
- ("NA_BGM_STAFF_2", "End Credits II", "End Credits II"),
- ("NA_BGM_STAFF_3", "End Credits III", "End Credits III"),
- ("NA_BGM_STAFF_4", "End Credits IV", "End Credits IV"),
- ("NA_BGM_FIRE_BOSS", "King Dodongo & Volvagia Boss Battle", "King Dodongo & Volvagia Boss Battle"),
- ("NA_BGM_TIMED_MINI_GAME", "Mini-Game", "Mini-Game"),
- ("NA_BGM_CUTSCENE_EFFECTS", "Various Cutscene Sounds", "Various Cutscene Sounds"),
- ("NA_BGM_NO_MUSIC", "No Music", "No Music"),
- ("NA_BGM_NATURE_SFX_RAIN", "Nature Ambiance: Rain", "Nature Ambiance: Rain"),
-]
-
-ootEnumNightSeq = [
- ("Custom", "Custom", "Custom"),
- ("0x00", "Standard night [day and night cycle]", "0x00"),
- ("0x01", "Standard night [Kakariko]", "0x01"),
- ("0x02", "Distant storm [Graveyard]", "0x02"),
- ("0x03", "Howling wind and cawing [Ganon's Castle]", "0x03"),
- ("0x04", "Wind + night birds [Kokiri]", "0x04"),
- ("0x05", "Wind + crickets", "0x05"),
- ("0x06", "Wind", "0x06"),
- ("0x07", "Howling wind", "0x07"),
- ("0x08", "Wind + crickets", "0x08"),
- ("0x09", "Wind + crickets", "0x09"),
- ("0x0A", "Tubed howling wind [Wasteland]", "0x0A"),
- ("0x0B", "Tubed howling wind [Colossus]", "0x0B"),
- ("0x0C", "Wind", "0x0C"),
- ("0x0D", "Wind + crickets", "0x0D"),
- ("0x0E", "Wind + crickets", "0x0E"),
- ("0x0F", "Wind + birds", "0x0F"),
- ("0x10", "Wind + crickets", "0x10"),
- ("0x11", "?", "0x11"),
- ("0x12", "Wind + crickets", "0x12"),
- ("0x13", "Day music always playing", "0x13"),
- ("0x14", "Silence", "0x14"),
- ("0x16", "Silence", "0x16"),
- ("0x17", "High tubed wind + rain", "0x17"),
- ("0x18", "Silence", "0x18"),
- ("0x19", "Silence", "0x19"),
- ("0x1A", "High tubed wind + rain", "0x1A"),
- ("0x1B", "Silence", "0x1B"),
- ("0x1C", "Rain", "0x1C"),
- ("0x1D", "High tubed wind + rain", "0x1D"),
- ("0x1E", "Silence", "0x1E"),
- ("0x1F", "High tubed wind + rain ", "0x1F"),
-]
-
-ootEnumGlobalObject = [
- ("Custom", "Custom", "Custom"),
- ("OBJECT_INVALID", "None", "None"),
- ("OBJECT_GAMEPLAY_FIELD_KEEP", "Overworld", "gameplay_field_keep"),
- ("OBJECT_GAMEPLAY_DANGEON_KEEP", "Dungeon", "gameplay_dangeon_keep"),
-]
-
ootEnumNaviHints = [
("Custom", "Custom", "Custom"),
- ("0x00", "None", "None"),
- ("0x01", "Overworld", "elf_message_field"),
- ("0x02", "Dungeon", "elf_message_ydan"),
+ ("NAVI_QUEST_HINTS_NONE", "None", "None"),
+ ("NAVI_QUEST_HINTS_OVERWORLD", "Overworld", "elf_message_field"),
+ ("NAVI_QUEST_HINTS_DUNGEON", "Dungeon", "elf_message_ydan"),
]
# The order of this list matters (normal OoT scene order as defined by ``scene_table.h``)
@@ -410,6 +196,113 @@
("SCENE_TESTROOM", "Treasure Chest Room (Testroom)", "Testroom"),
]
+# The order of this list matters (normal MM scene order as defined by ``scene_table.h``)
+mm_enum_scene_id = [
+ ("Custom", "Custom", "Custom"),
+ ("SCENE_20SICHITAI2", "Southern Swamp (Clear) (Z2_20SICHITAI2)", "Z2_20SICHITAI2"),
+ ("SCENE_KAKUSIANA", "Lone Peak Shrine & Grottos (KAKUSIANA)", "KAKUSIANA"),
+ ("SCENE_SPOT00", "Cutscene Scene (SPOT00)", "SPOT00"),
+ ("SCENE_WITCH_SHOP", "Magic Hags' Potion Shop (Z2_WITCH_SHOP)", "Z2_WITCH_SHOP"),
+ ("SCENE_LAST_BS", "Majora's Lair (Z2_LAST_BS)", "Z2_LAST_BS"),
+ ("SCENE_HAKASHITA", "Beneath the Graveyard (Z2_HAKASHITA)", "Z2_HAKASHITA"),
+ ("SCENE_AYASHIISHOP", "Curiosity Shop (Z2_AYASHIISHOP)", "Z2_AYASHIISHOP"),
+ ("SCENE_OMOYA", "Mama's House (Ranch House in PAL) & Barn (Z2_OMOYA)", "Z2_OMOYA"),
+ ("SCENE_BOWLING", "Honey & Darling's Shop (Z2_BOWLING)", "Z2_BOWLING"),
+ ("SCENE_SONCHONOIE", "The Mayor's Residence (Z2_SONCHONOIE)", "Z2_SONCHONOIE"),
+ ("SCENE_IKANA", "Ikana Canyon (Z2_IKANA)", "Z2_IKANA"),
+ ("SCENE_KAIZOKU", "Pirates' Fortress (Z2_KAIZOKU)", "Z2_KAIZOKU"),
+ ("SCENE_MILK_BAR", "Milk Bar (Z2_MILK_BAR)", "Z2_MILK_BAR"),
+ ("SCENE_INISIE_N", "Stone Tower Temple (Z2_INISIE_N)", "Z2_INISIE_N"),
+ ("SCENE_TAKARAYA", "Treasure Chest Shop (Z2_TAKARAYA)", "Z2_TAKARAYA"),
+ ("SCENE_INISIE_R", "Inverted Stone Tower Temple (Z2_INISIE_R)", "Z2_INISIE_R"),
+ ("SCENE_OKUJOU", "Clock Tower Rooftop (Z2_OKUJOU)", "Z2_OKUJOU"),
+ ("SCENE_OPENINGDAN", "Before Clock Town (Z2_OPENINGDAN)", "Z2_OPENINGDAN"),
+ ("SCENE_MITURIN", "Woodfall Temple (Z2_MITURIN)", "Z2_MITURIN"),
+ ("SCENE_13HUBUKINOMITI", "Path to Mountain Village (Z2_13HUBUKINOMITI)", "Z2_13HUBUKINOMITI"),
+ ("SCENE_CASTLE", "Ancient Castle of Ikana (Z2_CASTLE)", "Z2_CASTLE"),
+ ("SCENE_DEKUTES", "Deku Scrub Playground (Z2_DEKUTES)", "Z2_DEKUTES"),
+ ("SCENE_MITURIN_BS", "Odolwa's Lair (Z2_MITURIN_BS)", "Z2_MITURIN_BS"),
+ ("SCENE_SYATEKI_MIZU", "Town Shooting Gallery (Z2_SYATEKI_MIZU)", "Z2_SYATEKI_MIZU"),
+ ("SCENE_HAKUGIN", "Snowhead Temple (Z2_HAKUGIN)", "Z2_HAKUGIN"),
+ ("SCENE_ROMANYMAE", "Milk Road (Z2_ROMANYMAE)", "Z2_ROMANYMAE"),
+ ("SCENE_PIRATE", "Pirates' Fortress Interior (Z2_PIRATE)", "Z2_PIRATE"),
+ ("SCENE_SYATEKI_MORI", "Swamp Shooting Gallery (Z2_SYATEKI_MORI)", "Z2_SYATEKI_MORI"),
+ ("SCENE_SINKAI", "Pinnacle Rock (Z2_SINKAI)", "Z2_SINKAI"),
+ ("SCENE_YOUSEI_IZUMI", "Fairy's Fountain (Z2_YOUSEI_IZUMI)", "Z2_YOUSEI_IZUMI"),
+ ("SCENE_KINSTA1", "Swamp Spider House (Z2_KINSTA1)", "Z2_KINSTA1"),
+ ("SCENE_KINDAN2", "Oceanside Spider House (Z2_KINDAN2)", "Z2_KINDAN2"),
+ ("SCENE_TENMON_DAI", "Astral Observatory (Z2_TENMON_DAI)", "Z2_TENMON_DAI"),
+ ("SCENE_LAST_DEKU", "Moon Deku Trial (Z2_LAST_DEKU)", "Z2_LAST_DEKU"),
+ ("SCENE_22DEKUCITY", "Deku Palace (Z2_22DEKUCITY)", "Z2_22DEKUCITY"),
+ ("SCENE_KAJIYA", "Mountain Smithy (Z2_KAJIYA)", "Z2_KAJIYA"),
+ ("SCENE_00KEIKOKU", "Termina Field (Z2_00KEIKOKU)", "Z2_00KEIKOKU"),
+ ("SCENE_POSTHOUSE", "Post Office (Z2_POSTHOUSE)", "Z2_POSTHOUSE"),
+ ("SCENE_LABO", "Marine Research Lab (Z2_LABO)", "Z2_LABO"),
+ ("SCENE_DANPEI2TEST", "Beneath the Graveyard (Day 3) and Dampe's House (Z2_DANPEI2TEST)", "Z2_DANPEI2TEST"),
+ ("SCENE_16GORON_HOUSE", "Goron Shrine (Z2_16GORON_HOUSE)", "Z2_16GORON_HOUSE"),
+ ("SCENE_33ZORACITY", "Zora Hall (Z2_33ZORACITY)", "Z2_33ZORACITY"),
+ ("SCENE_8ITEMSHOP", "Trading Post (Z2_8ITEMSHOP)", "Z2_8ITEMSHOP"),
+ ("SCENE_F01", "Romani Ranch (Z2_F01)", "Z2_F01"),
+ ("SCENE_INISIE_BS", "Twinmold's Lair (Z2_INISIE_BS)", "Z2_INISIE_BS"),
+ ("SCENE_30GYOSON", "Great Bay Coast (Z2_30GYOSON)", "Z2_30GYOSON"),
+ ("SCENE_31MISAKI", "Zora Cape (Z2_31MISAKI)", "Z2_31MISAKI"),
+ ("SCENE_TAKARAKUJI", "Lottery Shop (Z2_TAKARAKUJI)", "Z2_TAKARAKUJI"),
+ ("SCENE_TORIDE", "Pirates' Fortress Moat (Z2_TORIDE)", "Z2_TORIDE"),
+ ("SCENE_FISHERMAN", "Fisherman's Hut (Z2_FISHERMAN)", "Z2_FISHERMAN"),
+ ("SCENE_GORONSHOP", "Goron Shop (Z2_GORONSHOP)", "Z2_GORONSHOP"),
+ ("SCENE_DEKU_KING", "Deku King's Chamber (Z2_DEKU_KING)", "Z2_DEKU_KING"),
+ ("SCENE_LAST_GORON", "Moon Goron Trial (Z2_LAST_GORON)", "Z2_LAST_GORON"),
+ ("SCENE_24KEMONOMITI", "Road to Southern Swamp (Z2_24KEMONOMITI)", "Z2_24KEMONOMITI"),
+ ("SCENE_F01_B", "Doggy Racetrack (Z2_F01_B)", "Z2_F01_B"),
+ ("SCENE_F01C", "Cucco Shack (Z2_F01C)", "Z2_F01C"),
+ ("SCENE_BOTI", "Ikana Graveyard (Z2_BOTI)", "Z2_BOTI"),
+ ("SCENE_HAKUGIN_BS", "Goht's Lair (Z2_HAKUGIN_BS)", "Z2_HAKUGIN_BS"),
+ ("SCENE_20SICHITAI", "Southern Swamp (poison) (Z2_20SICHITAI)", "Z2_20SICHITAI"),
+ ("SCENE_21MITURINMAE", "Woodfall (Z2_21MITURINMAE)", "Z2_21MITURINMAE"),
+ ("SCENE_LAST_ZORA", "Moon Zora Trial (Z2_LAST_ZORA)", "Z2_LAST_ZORA"),
+ ("SCENE_11GORONNOSATO2", "Goron Village (spring) (Z2_11GORONNOSATO2)", "Z2_11GORONNOSATO2"),
+ ("SCENE_SEA", "Great Bay Temple (Z2_SEA)", "Z2_SEA"),
+ ("SCENE_35TAKI", "Waterfall Rapids (Z2_35TAKI)", "Z2_35TAKI"),
+ ("SCENE_REDEAD", "Beneath the Well (Z2_REDEAD)", "Z2_REDEAD"),
+ ("SCENE_BANDROOM", "Zora Hall Rooms (Z2_BANDROOM)", "Z2_BANDROOM"),
+ ("SCENE_11GORONNOSATO", "Goron Village (winter) (Z2_11GORONNOSATO)", "Z2_11GORONNOSATO"),
+ ("SCENE_GORON_HAKA", "Goron Graveyard (Z2_GORON_HAKA)", "Z2_GORON_HAKA"),
+ ("SCENE_SECOM", "Sakon's Hideout (Z2_SECOM)", "Z2_SECOM"),
+ ("SCENE_10YUKIYAMANOMURA", "Mountain Village (winter) (Z2_10YUKIYAMANOMURA)", "Z2_10YUKIYAMANOMURA"),
+ ("SCENE_TOUGITES", "Ghost Hut (Z2_TOUGITES)", "Z2_TOUGITES"),
+ ("SCENE_DANPEI", "Deku Shrine (Z2_DANPEI)", "Z2_DANPEI"),
+ ("SCENE_IKANAMAE", "Road to Ikana (Z2_IKANAMAE)", "Z2_IKANAMAE"),
+ ("SCENE_DOUJOU", "Swordsman's School (Z2_DOUJOU)", "Z2_DOUJOU"),
+ ("SCENE_MUSICHOUSE", "Music Box House (Z2_MUSICHOUSE)", "Z2_MUSICHOUSE"),
+ ("SCENE_IKNINSIDE", "Igos du Ikana's Lair (Z2_IKNINSIDE)", "Z2_IKNINSIDE"),
+ ("SCENE_MAP_SHOP", "Tourist Information (Z2_MAP_SHOP)", "Z2_MAP_SHOP"),
+ ("SCENE_F40", "Stone Tower (Z2_F40)", "Z2_F40"),
+ ("SCENE_F41", "Inverted Stone Tower (Z2_F41)", "Z2_F41"),
+ ("SCENE_10YUKIYAMANOMURA2", "Mountain Village (spring) (Z2_10YUKIYAMANOMURA2)", "Z2_10YUKIYAMANOMURA2"),
+ ("SCENE_14YUKIDAMANOMITI", "Path to Snowhead (Z2_14YUKIDAMANOMITI)", "Z2_14YUKIDAMANOMITI"),
+ ("SCENE_12HAKUGINMAE", "Snowhead (Z2_12HAKUGINMAE)", "Z2_12HAKUGINMAE"),
+ ("SCENE_17SETUGEN", "Path to Goron Village (winter) (Z2_17SETUGEN)", "Z2_17SETUGEN"),
+ ("SCENE_17SETUGEN2", "Path to Goron Village (spring) (Z2_17SETUGEN2)", "Z2_17SETUGEN2"),
+ ("SCENE_SEA_BS", "Gyorg's Lair (Z2_SEA_BS)", "Z2_SEA_BS"),
+ ("SCENE_RANDOM", "Secret Shrine (Z2_RANDOM)", "Z2_RANDOM"),
+ ("SCENE_YADOYA", "Stock Pot Inn (Z2_YADOYA)", "Z2_YADOYA"),
+ ("SCENE_KONPEKI_ENT", "Great Bay Cutscene (Z2_KONPEKI_ENT)", "Z2_KONPEKI_ENT"),
+ ("SCENE_INSIDETOWER", "Clock Tower Interior (Z2_INSIDETOWER)", "Z2_INSIDETOWER"),
+ ("SCENE_26SARUNOMORI", "Woods of Mystery (Z2_26SARUNOMORI)", "Z2_26SARUNOMORI"),
+ ("SCENE_LOST_WOODS", "Lost Woods (Intro) (Z2_LOST_WOODS)", "Z2_LOST_WOODS"),
+ ("SCENE_LAST_LINK", "Moon Link Trial (Z2_LAST_LINK)", "Z2_LAST_LINK"),
+ ("SCENE_SOUGEN", "The Moon (Z2_SOUGEN)", "Z2_SOUGEN"),
+ ("SCENE_BOMYA", "Bomb Shop (Z2_BOMYA)", "Z2_BOMYA"),
+ ("SCENE_KYOJINNOMA", "Giants' Chamber (Z2_KYOJINNOMA)", "Z2_KYOJINNOMA"),
+ ("SCENE_KOEPONARACE", "Gorman Track (Z2_KOEPONARACE)", "Z2_KOEPONARACE"),
+ ("SCENE_GORONRACE", "Goron Racetrack (Z2_GORONRACE)", "Z2_GORONRACE"),
+ ("SCENE_TOWN", "East Clock Town (Z2_TOWN)", "Z2_TOWN"),
+ ("SCENE_ICHIBA", "West Clock Town (Z2_ICHIBA)", "Z2_ICHIBA"),
+ ("SCENE_BACKTOWN", "North Clock Town (Z2_BACKTOWN)", "Z2_BACKTOWN"),
+ ("SCENE_CLOCKTOWER", "South Clock Town (Z2_CLOCKTOWER)", "Z2_CLOCKTOWER"),
+ ("SCENE_ALLEY", "Laundry Pool (Z2_ALLEY)", "Z2_ALLEY"),
+]
+
ootSceneIDToName = {
"SCENE_DEKU_TREE": "ydan",
"SCENE_DODONGOS_CAVERN": "ddan",
@@ -524,6 +417,112 @@
}
ootSceneNameToID = {val: key for key, val in ootSceneIDToName.items()}
+mm_scene_id_to_name = {
+ "SCENE_20SICHITAI2": "Z2_20SICHITAI2",
+ "SCENE_KAKUSIANA": "KAKUSIANA",
+ "SCENE_SPOT00": "SPOT00",
+ "SCENE_WITCH_SHOP": "Z2_WITCH_SHOP",
+ "SCENE_LAST_BS": "Z2_LAST_BS",
+ "SCENE_HAKASHITA": "Z2_HAKASHITA",
+ "SCENE_AYASHIISHOP": "Z2_AYASHIISHOP",
+ "SCENE_OMOYA": "Z2_OMOYA",
+ "SCENE_BOWLING": "Z2_BOWLING",
+ "SCENE_SONCHONOIE": "Z2_SONCHONOIE",
+ "SCENE_IKANA": "Z2_IKANA",
+ "SCENE_KAIZOKU": "Z2_KAIZOKU",
+ "SCENE_MILK_BAR": "Z2_MILK_BAR",
+ "SCENE_INISIE_N": "Z2_INISIE_N",
+ "SCENE_TAKARAYA": "Z2_TAKARAYA",
+ "SCENE_INISIE_R": "Z2_INISIE_R",
+ "SCENE_OKUJOU": "Z2_OKUJOU",
+ "SCENE_OPENINGDAN": "Z2_OPENINGDAN",
+ "SCENE_MITURIN": "Z2_MITURIN",
+ "SCENE_13HUBUKINOMITI": "Z2_13HUBUKINOMITI",
+ "SCENE_CASTLE": "Z2_CASTLE",
+ "SCENE_DEKUTES": "Z2_DEKUTES",
+ "SCENE_MITURIN_BS": "Z2_MITURIN_BS",
+ "SCENE_SYATEKI_MIZU": "Z2_SYATEKI_MIZU",
+ "SCENE_HAKUGIN": "Z2_HAKUGIN",
+ "SCENE_ROMANYMAE": "Z2_ROMANYMAE",
+ "SCENE_PIRATE": "Z2_PIRATE",
+ "SCENE_SYATEKI_MORI": "Z2_SYATEKI_MORI",
+ "SCENE_SINKAI": "Z2_SINKAI",
+ "SCENE_YOUSEI_IZUMI": "Z2_YOUSEI_IZUMI",
+ "SCENE_KINSTA1": "Z2_KINSTA1",
+ "SCENE_KINDAN2": "Z2_KINDAN2",
+ "SCENE_TENMON_DAI": "Z2_TENMON_DAI",
+ "SCENE_LAST_DEKU": "Z2_LAST_DEKU",
+ "SCENE_22DEKUCITY": "Z2_22DEKUCITY",
+ "SCENE_KAJIYA": "Z2_KAJIYA",
+ "SCENE_00KEIKOKU": "Z2_00KEIKOKU",
+ "SCENE_POSTHOUSE": "Z2_POSTHOUSE",
+ "SCENE_LABO": "Z2_LABO",
+ "SCENE_DANPEI2TEST": "Z2_DANPEI2TEST",
+ "SCENE_16GORON_HOUSE": "Z2_16GORON_HOUSE",
+ "SCENE_33ZORACITY": "Z2_33ZORACITY",
+ "SCENE_8ITEMSHOP": "Z2_8ITEMSHOP",
+ "SCENE_F01": "Z2_F01",
+ "SCENE_INISIE_BS": "Z2_INISIE_BS",
+ "SCENE_30GYOSON": "Z2_30GYOSON",
+ "SCENE_31MISAKI": "Z2_31MISAKI",
+ "SCENE_TAKARAKUJI": "Z2_TAKARAKUJI",
+ "SCENE_TORIDE": "Z2_TORIDE",
+ "SCENE_FISHERMAN": "Z2_FISHERMAN",
+ "SCENE_GORONSHOP": "Z2_GORONSHOP",
+ "SCENE_DEKU_KING": "Z2_DEKU_KING",
+ "SCENE_LAST_GORON": "Z2_LAST_GORON",
+ "SCENE_24KEMONOMITI": "Z2_24KEMONOMITI",
+ "SCENE_F01_B": "Z2_F01_B",
+ "SCENE_F01C": "Z2_F01C",
+ "SCENE_BOTI": "Z2_BOTI",
+ "SCENE_HAKUGIN_BS": "Z2_HAKUGIN_BS",
+ "SCENE_20SICHITAI": "Z2_20SICHITAI",
+ "SCENE_21MITURINMAE": "Z2_21MITURINMAE",
+ "SCENE_LAST_ZORA": "Z2_LAST_ZORA",
+ "SCENE_11GORONNOSATO2": "Z2_11GORONNOSATO2",
+ "SCENE_SEA": "Z2_SEA",
+ "SCENE_35TAKI": "Z2_35TAKI",
+ "SCENE_REDEAD": "Z2_REDEAD",
+ "SCENE_BANDROOM": "Z2_BANDROOM",
+ "SCENE_11GORONNOSATO": "Z2_11GORONNOSATO",
+ "SCENE_GORON_HAKA": "Z2_GORON_HAKA",
+ "SCENE_SECOM": "Z2_SECOM",
+ "SCENE_10YUKIYAMANOMURA": "Z2_10YUKIYAMANOMURA",
+ "SCENE_TOUGITES": "Z2_TOUGITES",
+ "SCENE_DANPEI": "Z2_DANPEI",
+ "SCENE_IKANAMAE": "Z2_IKANAMAE",
+ "SCENE_DOUJOU": "Z2_DOUJOU",
+ "SCENE_MUSICHOUSE": "Z2_MUSICHOUSE",
+ "SCENE_IKNINSIDE": "Z2_IKNINSIDE",
+ "SCENE_MAP_SHOP": "Z2_MAP_SHOP",
+ "SCENE_F40": "Z2_F40",
+ "SCENE_F41": "Z2_F41",
+ "SCENE_10YUKIYAMANOMURA2": "Z2_10YUKIYAMANOMURA2",
+ "SCENE_14YUKIDAMANOMITI": "Z2_14YUKIDAMANOMITI",
+ "SCENE_12HAKUGINMAE": "Z2_12HAKUGINMAE",
+ "SCENE_17SETUGEN": "Z2_17SETUGEN",
+ "SCENE_17SETUGEN2": "Z2_17SETUGEN2",
+ "SCENE_SEA_BS": "Z2_SEA_BS",
+ "SCENE_RANDOM": "Z2_RANDOM",
+ "SCENE_YADOYA": "Z2_YADOYA",
+ "SCENE_KONPEKI_ENT": "Z2_KONPEKI_ENT",
+ "SCENE_INSIDETOWER": "Z2_INSIDETOWER",
+ "SCENE_26SARUNOMORI": "Z2_26SARUNOMORI",
+ "SCENE_LOST_WOODS": "Z2_LOST_WOODS",
+ "SCENE_LAST_LINK": "Z2_LAST_LINK",
+ "SCENE_SOUGEN": "Z2_SOUGEN",
+ "SCENE_BOMYA": "Z2_BOMYA",
+ "SCENE_KYOJINNOMA": "Z2_KYOJINNOMA",
+ "SCENE_KOEPONARACE": "Z2_KOEPONARACE",
+ "SCENE_GORONRACE": "Z2_GORONRACE",
+ "SCENE_TOWN": "Z2_TOWN",
+ "SCENE_ICHIBA": "Z2_ICHIBA",
+ "SCENE_BACKTOWN": "Z2_BACKTOWN",
+ "SCENE_CLOCKTOWER": "Z2_CLOCKTOWER",
+ "SCENE_ALLEY": "Z2_ALLEY",
+}
+mm_scene_name_to_id = {val: key for key, val in mm_scene_id_to_name.items()}
+
ootEnumCamTransition = [
("Custom", "Custom", "Custom"),
("0x00", "0x00", "0x00"),
@@ -531,78 +530,6 @@
# ("0xFF", "0xFF", "0xFF"),
]
-# see curRoom.behaviorType1
-ootEnumRoomBehaviour = [
- ("Custom", "Custom", "Custom"),
- ("0x00", "Default", "Default"),
- ("0x01", "Dungeon Behavior (Z-Target, Sun's Song)", "Dungeon Behavior (Z-Target, Sun's Song)"),
- ("0x02", "Disable Backflips/Sidehops", "Disable Backflips/Sidehops"),
- ("0x03", "Disable Color Dither", "Disable Color Dither"),
- ("0x04", "(?) Horse Camera Related", "(?) Horse Camera Related"),
- ("0x05", "Disable Darker Screen Effect (NL/Spins)", "Disable Darker Screen Effect (NL/Spins)"),
-]
-
-ootEnumDrawConfig = [
- ("Custom", "Custom", "Custom"),
- ("SDC_DEFAULT", "Default", "Default"),
- ("SDC_HYRULE_FIELD", "Hyrule Field (Spot00)", "Spot00"),
- ("SDC_KAKARIKO_VILLAGE", "Kakariko Village (Spot01)", "Spot01"),
- ("SDC_ZORAS_RIVER", "Zora's River (Spot03)", "Spot03"),
- ("SDC_KOKIRI_FOREST", "Kokiri Forest (Spot04)", "Spot04"),
- ("SDC_LAKE_HYLIA", "Lake Hylia (Spot06)", "Spot06"),
- ("SDC_ZORAS_DOMAIN", "Zora's Domain (Spot07)", "Spot07"),
- ("SDC_ZORAS_FOUNTAIN", "Zora's Fountain (Spot08)", "Spot08"),
- ("SDC_GERUDO_VALLEY", "Gerudo Valley (Spot09)", "Spot09"),
- ("SDC_LOST_WOODS", "Lost Woods (Spot10)", "Spot10"),
- ("SDC_DESERT_COLOSSUS", "Desert Colossus (Spot11)", "Spot11"),
- ("SDC_GERUDOS_FORTRESS", "Gerudo's Fortress (Spot12)", "Spot12"),
- ("SDC_HAUNTED_WASTELAND", "Haunted Wasteland (Spot13)", "Spot13"),
- ("SDC_HYRULE_CASTLE", "Hyrule Castle (Spot15)", "Spot15"),
- ("SDC_DEATH_MOUNTAIN_TRAIL", "Death Mountain Trail (Spot16)", "Spot16"),
- ("SDC_DEATH_MOUNTAIN_CRATER", "Death Mountain Crater (Spot17)", "Spot17"),
- ("SDC_GORON_CITY", "Goron City (Spot18)", "Spot18"),
- ("SDC_LON_LON_RANCH", "Lon Lon Ranch (Spot20)", "Spot20"),
- ("SDC_FIRE_TEMPLE", "Fire Temple (Hidan)", "Hidan"),
- ("SDC_DEKU_TREE", "Inside the Deku Tree (Ydan)", "Ydan"),
- ("SDC_DODONGOS_CAVERN", "Dodongo's Cavern (Ddan)", "Ddan"),
- ("SDC_JABU_JABU", "Inside Jabu Jabu's Belly (Bdan)", "Bdan"),
- ("SDC_FOREST_TEMPLE", "Forest Temple (Bmori1)", "Bmori1"),
- ("SDC_WATER_TEMPLE", "Water Temple (Mizusin)", "Mizusin"),
- ("SDC_SHADOW_TEMPLE_AND_WELL", "Shadow Temple (Hakadan)", "Hakadan"),
- ("SDC_SPIRIT_TEMPLE", "Spirit Temple (Jyasinzou)", "Jyasinzou"),
- ("SDC_INSIDE_GANONS_CASTLE", "Inside Ganon's Castle (Ganontika)", "Ganontika"),
- ("SDC_GERUDO_TRAINING_GROUND", "Gerudo Training Ground (Men)", "Men"),
- ("SDC_DEKU_TREE_BOSS", "Gohma's Lair (Ydan Boss)", "Ydan Boss"),
- ("SDC_WATER_TEMPLE_BOSS", "Morpha's Lair (Mizusin Bs)", "Mizusin Bs"),
- ("SDC_TEMPLE_OF_TIME", "Temple of Time (Tokinoma)", "Tokinoma"),
- ("SDC_GROTTOS", "Grottos (Kakusiana)", "Kakusiana"),
- ("SDC_CHAMBER_OF_THE_SAGES", "Chamber of the Sages (Kenjyanoma)", "Kenjyanoma"),
- ("SDC_GREAT_FAIRYS_FOUNTAIN", "Great Fairy Fountain", "Great Fairy Fountain"),
- ("SDC_SHOOTING_GALLERY", "Shooting Gallery (Syatekijyou)", "Syatekijyou"),
- ("SDC_CASTLE_COURTYARD_GUARDS", "Castle Hedge Maze (Day) (Hairal Niwa)", "Hairal Niwa"),
- ("SDC_OUTSIDE_GANONS_CASTLE", "Ganon's Castle Exterior (Ganon Tou)", "Ganon Tou"),
- ("SDC_ICE_CAVERN", "Ice Cavern (Ice Doukuto)", "Ice Doukuto"),
- (
- "SDC_GANONS_TOWER_COLLAPSE_EXTERIOR",
- "Ganondorf's Death Scene (Tower Escape Exterior) (Ganon Final)",
- "Ganon Final",
- ),
- ("SDC_FAIRYS_FOUNTAIN", "Fairy Fountain", "Fairy Fountain"),
- ("SDC_THIEVES_HIDEOUT", "Thieves' Hideout (Gerudoway)", "Gerudoway"),
- ("SDC_BOMBCHU_BOWLING_ALLEY", "Bombchu Bowling Alley (Bowling)", "Bowling"),
- ("SDC_ROYAL_FAMILYS_TOMB", "Royal Family's Tomb (Hakaana Ouke)", "Hakaana Ouke"),
- ("SDC_LAKESIDE_LABORATORY", "Lakeside Laboratory (Hylia Labo)", "Hylia Labo"),
- ("SDC_LON_LON_BUILDINGS", "Lon Lon Ranch House & Tower (Souko)", "Souko"),
- ("SDC_MARKET_GUARD_HOUSE", "Guard House (Miharigoya)", "Miharigoya"),
- ("SDC_POTION_SHOP_GRANNY", "Granny's Potion Shop (Mahouya)", "Mahouya"),
- ("SDC_CALM_WATER", "Calm Water", "Calm Water"),
- ("SDC_GRAVE_EXIT_LIGHT_SHINING", "Grave Exit Light Shining", "Grave Exit Light Shining"),
- ("SDC_BESITU", "Ganondorf Test Room (Besitu)", "Besitu"),
- ("SDC_FISHING_POND", "Fishing Pond (Turibori)", "Turibori"),
- ("SDC_GANONS_TOWER_COLLAPSE_INTERIOR", "Ganon's Tower (Collapsing) (Ganon Sonogo)", "Ganon Sonogo"),
- ("SDC_INSIDE_GANONS_CASTLE_COLLAPSE", "Inside Ganon's Castle (Collapsing) (Ganontika Sonogo)", "Ganontika Sonogo"),
-]
-
oot_world_defaults = {
"geometryMode": {
"zBuffer": True,
@@ -619,3 +546,58 @@
"cycleType": "G_CYC_2CYCLE",
},
}
+
+halfday_bits_day0_dawn = 1 << 9
+halfday_bits_day0_night = 1 << 8
+halfday_bits_day1_dawn = 1 << 7
+halfday_bits_day1_night = 1 << 6
+halfday_bits_day2_dawn = 1 << 5
+halfday_bits_day2_night = 1 << 4
+halfday_bits_day3_dawn = 1 << 3
+halfday_bits_day3_night = 1 << 2
+halfday_bits_day4_dawn = 1 << 1
+halfday_bits_day4_night = 1 << 0
+
+halfday_bits_values = [
+ halfday_bits_day0_dawn,
+ halfday_bits_day0_night,
+ halfday_bits_day1_dawn,
+ halfday_bits_day1_night,
+ halfday_bits_day2_dawn,
+ halfday_bits_day2_night,
+ halfday_bits_day3_dawn,
+ halfday_bits_day3_night,
+ halfday_bits_day4_dawn,
+ halfday_bits_day4_night,
+]
+
+halfday_bits_all_dawns = (
+ halfday_bits_day0_dawn
+ | halfday_bits_day1_dawn
+ | halfday_bits_day2_dawn
+ | halfday_bits_day3_dawn
+ | halfday_bits_day4_dawn
+)
+
+halfday_bits_all_nights = (
+ halfday_bits_day0_night
+ | halfday_bits_day1_night
+ | halfday_bits_day2_night
+ | halfday_bits_day3_night
+ | halfday_bits_day4_night
+)
+
+
+enum_to_halfday_bits = {
+ "0-Dawn": halfday_bits_day0_dawn,
+ "0-Night": halfday_bits_day0_night,
+ "1-Dawn": halfday_bits_day1_dawn,
+ "1-Night": halfday_bits_day1_night,
+ "2-Dawn": halfday_bits_day2_dawn,
+ "2-Night": halfday_bits_day2_night,
+ "3-Dawn": halfday_bits_day3_dawn,
+ "3-Night": halfday_bits_day3_night,
+ "4-Dawn": halfday_bits_day4_dawn,
+ "4-Night": halfday_bits_day4_night,
+}
+halfday_bits_to_enum = {val: key for key, val in enum_to_halfday_bits.items()}
diff --git a/fast64_internal/oot/cutscene/classes.py b/fast64_internal/z64/cutscene/classes.py
similarity index 63%
rename from fast64_internal/oot/cutscene/classes.py
rename to fast64_internal/z64/cutscene/classes.py
index 72739a8cd..549034672 100644
--- a/fast64_internal/oot/cutscene/classes.py
+++ b/fast64_internal/z64/cutscene/classes.py
@@ -3,7 +3,7 @@
from dataclasses import dataclass, field
from bpy.types import Object
from typing import Optional
-from ..oot_constants import ootData
+from ...game_data import game_data
from .motion.utility import getBlenderPosition, getBlenderRotation, getRotation, getInteger
@@ -13,6 +13,9 @@ def cs_import_float(v_str: str):
# NOTE: ``paramNumber`` is the expected number of parameters inside the parsed commands,
# this account for the unused parameters. Every classes are based on the commands arguments from ``z64cutscene_commands.h``
+#
+# some commands have a `size` attribute, it's purpose is for the exporter as
+# MM's camera command requires the size of the camera data in bytes
@dataclass
@@ -24,14 +27,17 @@ class CutsceneCmdBase:
startFrame: Optional[int] = None
endFrame: Optional[int] = None
+ # MM doesn't have startFrame and endFrame, instead it's just the framecount
+ duration: Optional[int] = None
+
def getEnumValue(self, enumKey: str, index: int, isSeqLegacy: bool = False):
- enum = ootData.enumData.enumByKey[enumKey]
- item = enum.itemById.get(self.params[index])
+ enum = game_data.z64.enums.enumByKey[enumKey]
+ item = enum.item_by_id.get(self.params[index])
if item is None:
setting = getInteger(self.params[index])
if isSeqLegacy:
setting -= 1
- item = enum.itemByIndex.get(setting)
+ item = enum.item_by_index.get(setting)
return item.key if item is not None else self.params[index]
@@ -55,6 +61,82 @@ def __post_init__(self):
self.pos = [getInteger(self.params[4]), getInteger(self.params[5]), getInteger(self.params[6])]
+# MM's new camera commands
+
+
+@dataclass
+class CutsceneCmdNewCamPoint(CutsceneCmdBase):
+ """This class contains a single Camera Point command data (the newer version)"""
+
+ interp_type: Optional[str] = None
+ weight: Optional[int] = None
+ pos: list[int] = field(default_factory=list)
+ relative_to: Optional[str] = None
+ paramNumber: int = 7
+ size: int = 0xC
+
+ def __post_init__(self):
+ if self.params is not None:
+ self.interp_type = self.params[0]
+ self.weight = getInteger(self.params[1])
+ self.duration = getInteger(self.params[2])
+ self.pos = [getInteger(self.params[3]), getInteger(self.params[4]), getInteger(self.params[5])]
+ self.relative_to = self.params[6]
+
+
+@dataclass
+class CutsceneCmdCamMisc(CutsceneCmdBase):
+ """This class contains the Camera Misc data"""
+
+ camRoll: Optional[int] = None
+ viewAngle: Optional[float] = None
+ paramNumber: int = 4
+ size: int = 0x8
+
+ def __post_init__(self):
+ if self.params is not None:
+ self.camRoll = getInteger(self.params[1])
+ self.viewAngle = getInteger(self.params[2])
+
+
+@dataclass
+class CutsceneSplinePoint:
+ # this is not a real command but it helps as each camera point is made of one at, one eye and one misc
+ at: Optional[CutsceneCmdNewCamPoint] = None
+ eye: Optional[CutsceneCmdNewCamPoint] = None
+ misc: Optional[CutsceneCmdCamMisc] = None
+
+
+@dataclass
+class CutsceneCmdCamSpline(CutsceneCmdBase):
+ """This class contains the Camera Spline data"""
+
+ num_entries: Optional[int] = None
+ entries: list[CutsceneSplinePoint] = field(default_factory=list)
+ paramNumber: int = 4
+ size: int = 0x8
+
+ def __post_init__(self):
+ if self.params is not None:
+ self.num_entries = getInteger(self.params[0])
+ self.duration = getInteger(self.params[3])
+
+
+@dataclass
+class CutsceneCmdCamSplineList(CutsceneCmdBase):
+ """This class contains the Camera Spline list data"""
+
+ num_bytes: Optional[int] = None
+ entries: list[CutsceneCmdCamSpline] = field(default_factory=list)
+ paramNumber: int = 1
+ listName: str = "camSplineListNew"
+ size: int = 0x8
+
+ def __post_init__(self):
+ if self.params is not None:
+ self.num_bytes = getInteger(self.params[0])
+
+
@dataclass
class CutsceneCmdActorCue(CutsceneCmdBase):
"""This class contains a single Actor Cue command data"""
@@ -101,7 +183,7 @@ def __post_init__(self):
self.commandType = self.commandType.removeprefix("0x")
self.commandType = "0x" + "0" * (4 - len(self.commandType)) + self.commandType
else:
- self.commandType = ootData.enumData.enumByKey["csCmd"].itemById[self.commandType].key
+ self.commandType = game_data.z64.enums.enumByKey["cs_cmd"].item_by_id[self.commandType].key
self.entryTotal = getInteger(self.params[1].strip())
@@ -195,14 +277,14 @@ def __post_init__(self):
class CutsceneCmdMisc(CutsceneCmdBase):
"""This class contains a single misc command entry"""
- type: Optional[str] = None # see ``CutsceneMiscType`` in decomp
+ type: Optional[str] = None # see `CutsceneMiscType` in decomp
paramNumber: int = 14
def __post_init__(self):
if self.params is not None:
self.startFrame = getInteger(self.params[1])
self.endFrame = getInteger(self.params[2])
- self.type = self.getEnumValue("csMiscType", 0)
+ self.type = self.getEnumValue("cs_misc_type", 0)
@dataclass
@@ -231,7 +313,21 @@ def __post_init__(self):
if self.params is not None:
self.startFrame = getInteger(self.params[1])
self.endFrame = getInteger(self.params[2])
- self.type = self.getEnumValue("csTransitionType", 0)
+ self.type = self.getEnumValue("cs_transition_type", 0)
+
+
+@dataclass
+class CutsceneCmdTransitionList(CutsceneCmdBase):
+ """This class contains Transition list command data"""
+
+ entryTotal: Optional[int] = None
+ entries: list[CutsceneCmdTransition] = field(default_factory=list)
+ paramNumber: int = 1
+ listName: str = "transitionListNew"
+
+ def __post_init__(self):
+ if self.params is not None:
+ self.entryTotal = getInteger(self.params[0])
@dataclass
@@ -250,9 +346,9 @@ def __post_init__(self):
self.startFrame = getInteger(self.params[1])
self.endFrame = getInteger(self.params[2])
self.textId = getInteger(self.params[0])
- self.type = self.getEnumValue("csTextType", 3)
- self.altTextId1 = (getInteger(self.params[4]),)
- self.altTextId2 = (getInteger(self.params[5]),)
+ self.type = self.getEnumValue("cs_text_type", 3)
+ self.altTextId1 = getInteger(self.params[4])
+ self.altTextId2 = getInteger(self.params[5])
@dataclass
@@ -281,16 +377,58 @@ def __post_init__(self):
if self.params is not None:
self.startFrame = getInteger(self.params[1])
self.endFrame = getInteger(self.params[2])
- self.ocarinaActionId = self.getEnumValue("ocarinaSongActionId", 0)
+ self.ocarinaActionId = self.getEnumValue("ocarina_song_action_id", 0)
self.messageId = getInteger(self.params[3])
+@dataclass
+class CutsceneCmdTextGeneric(CutsceneCmdBase):
+ """This class contains generic text command data"""
+
+ textId: Optional[int] = None
+ topOptionBranch: Optional[int] = None
+ bottomOptionBranch: Optional[int] = None
+ paramNumber: int = 5
+ id: str = "Generic"
+
+ def __post_init__(self):
+ if self.params is not None:
+ self.textId = getInteger(self.params[0])
+ self.startFrame = getInteger(self.params[1])
+ self.endFrame = getInteger(self.params[2])
+ self.topOptionBranch = getInteger(self.params[3])
+ self.bottomOptionBranch = getInteger(self.params[4])
+
+
+@dataclass
+class CutsceneCmdTextMask(CutsceneCmdBase):
+ """This class contains mask/remains text command data"""
+
+ defaultTextId: Optional[int] = None
+ alternativeTextId: Optional[int] = None
+ paramNumber: int = 4
+ id: str = "Mask"
+
+ def __post_init__(self):
+ if self.params is not None:
+ self.defaultTextId = getInteger(self.params[0])
+ self.startFrame = getInteger(self.params[1])
+ self.endFrame = getInteger(self.params[2])
+ self.alternativeTextId = getInteger(self.params[3])
+
+
@dataclass
class CutsceneCmdTextList(CutsceneCmdBase):
"""This class contains Text List command data"""
entryTotal: Optional[int] = None
- entries: list[CutsceneCmdText | CutsceneCmdTextNone | CutsceneCmdTextOcarinaAction] = field(default_factory=list)
+ entries: list[
+ CutsceneCmdText
+ | CutsceneCmdTextNone
+ | CutsceneCmdTextOcarinaAction
+ | CutsceneCmdTextGeneric
+ | CutsceneCmdTextMask
+ ] = field(default_factory=list)
paramNumber: int = 1
listName: str = "textList"
@@ -372,7 +510,7 @@ def __post_init__(self):
if self.params is not None:
self.startFrame = getInteger(self.params[1])
self.endFrame = getInteger(self.params[2])
- self.seqId = self.getEnumValue("seqId", 0, self.isLegacy)
+ self.seqId = self.getEnumValue("seq_id", 0, self.isLegacy)
@dataclass
@@ -402,7 +540,7 @@ def __post_init__(self):
if self.params is not None:
self.startFrame = getInteger(self.params[1])
self.endFrame = getInteger(self.params[2])
- self.seqPlayer = self.getEnumValue("csFadeOutSeqPlayer", 0)
+ self.seqPlayer = self.getEnumValue("cs_fade_out_seq_player", 0)
@dataclass
@@ -455,14 +593,170 @@ def __post_init__(self):
class CutsceneCmdDestination(CutsceneCmdBase):
"""This class contains Destination command data"""
- id: Optional[str] = None
+ type: Optional[str] = None
paramNumber: int = 3
listName: str = "destination"
def __post_init__(self):
if self.params is not None:
- self.id = self.getEnumValue("csDestination", 0)
+ self.type = self.getEnumValue("cs_destination" if game_data.z64.is_oot() else "destinationType", 0)
+ self.startFrame = getInteger(self.params[1])
+
+
+@dataclass
+class CutsceneCmdDestinationList(CutsceneCmdBase):
+ """This class contains Destination list command data"""
+
+ entryTotal: Optional[int] = None
+ entries: list[CutsceneCmdDestination] = field(default_factory=list)
+ paramNumber: int = 1
+ listName: str = "destinationListNew"
+
+ def __post_init__(self):
+ if self.params is not None:
+ self.entryTotal = getInteger(self.params[0])
+
+
+@dataclass
+class CutsceneCmdMotionBlur(CutsceneCmdBase):
+ """This class contains motion blur command data"""
+
+ type: Optional[str] = None
+ paramNumber: int = 3
+
+ def __post_init__(self):
+ if self.params is not None:
+ self.type = self.getEnumValue("cs_motion_blur_type", 0)
self.startFrame = getInteger(self.params[1])
+ self.endFrame = getInteger(self.params[2])
+
+
+@dataclass
+class CutsceneCmdMotionBlurList(CutsceneCmdBase):
+ """This class contains motion blur list command data"""
+
+ entryTotal: Optional[int] = None
+ entries: list[CutsceneCmdDestination] = field(default_factory=list)
+ paramNumber: int = 1
+ listName: str = "motionBlurList"
+
+ def __post_init__(self):
+ if self.params is not None:
+ self.entryTotal = getInteger(self.params[0])
+
+
+@dataclass
+class CutsceneCmdModifySeq(CutsceneCmdBase):
+ """This class contains modify seq command data"""
+
+ type: Optional[str] = None
+ paramNumber: int = 3
+
+ def __post_init__(self):
+ if self.params is not None:
+ self.type = self.getEnumValue("cs_modify_seq_type", 0)
+ self.startFrame = getInteger(self.params[1])
+ self.endFrame = getInteger(self.params[2])
+
+
+@dataclass
+class CutsceneCmdModifySeqList(CutsceneCmdBase):
+ """This class contains modify seq list command data"""
+
+ entryTotal: Optional[int] = None
+ entries: list[CutsceneCmdModifySeq] = field(default_factory=list)
+ paramNumber: int = 1
+ listName: str = "modifySeqList"
+
+ def __post_init__(self):
+ if self.params is not None:
+ self.entryTotal = getInteger(self.params[0])
+
+
+@dataclass
+class CutsceneCmdChooseCreditsScenes(CutsceneCmdBase):
+ """This class contains choose credits scenes command data"""
+
+ type: Optional[str] = None
+ paramNumber: int = 3
+
+ def __post_init__(self):
+ if self.params is not None:
+ self.type = self.getEnumValue("cs_credits_scene_type", 0)
+ self.startFrame = getInteger(self.params[1])
+ self.endFrame = getInteger(self.params[2])
+
+
+@dataclass
+class CutsceneCmdChooseCreditsScenesList(CutsceneCmdBase):
+ """This class contains choose credits scenes list command data"""
+
+ entryTotal: Optional[int] = None
+ entries: list[CutsceneCmdChooseCreditsScenes] = field(default_factory=list)
+ paramNumber: int = 1
+ listName: str = "creditsSceneList"
+
+ def __post_init__(self):
+ if self.params is not None:
+ self.entryTotal = getInteger(self.params[0])
+
+
+@dataclass
+class CutsceneCmdTransitionGeneral(CutsceneCmdBase):
+ """This class contains transition general command data"""
+
+ type: Optional[str] = None
+ rgb: list[int] = field(default_factory=list)
+ paramNumber: int = 6
+
+ def __post_init__(self):
+ if self.params is not None:
+ self.type = self.getEnumValue("cs_transition_general", 0)
+ self.startFrame = getInteger(self.params[1])
+ self.endFrame = getInteger(self.params[2])
+ self.rgb = [getInteger(self.params[3]), getInteger(self.params[4]), getInteger(self.params[5])]
+
+
+@dataclass
+class CutsceneCmdTransitionGeneralList(CutsceneCmdBase):
+ """This class contains transition general list command data"""
+
+ entryTotal: Optional[int] = None
+ entries: list[CutsceneCmdTransitionGeneral] = field(default_factory=list)
+ paramNumber: int = 1
+ listName: str = "transitionGeneralList"
+
+ def __post_init__(self):
+ if self.params is not None:
+ self.entryTotal = getInteger(self.params[0])
+
+
+@dataclass
+class CutsceneCmdGiveTatl(CutsceneCmdBase):
+ """This class contains give tatl command data"""
+
+ giveTatl: Optional[bool] = None
+ paramNumber: int = 3
+
+ def __post_init__(self):
+ if self.params is not None:
+ self.giveTatl = self.params[0] in {"true", "1"}
+ self.startFrame = getInteger(self.params[1])
+ self.endFrame = getInteger(self.params[2])
+
+
+@dataclass
+class CutsceneCmdGiveTatlList(CutsceneCmdBase):
+ """This class contains give tatl list command data"""
+
+ entryTotal: Optional[int] = None
+ entries: list[CutsceneCmdGiveTatl] = field(default_factory=list)
+ paramNumber: int = 1
+ listName: str = "giveTatlList"
+
+ def __post_init__(self):
+ if self.params is not None:
+ self.entryTotal = getInteger(self.params[0])
@dataclass
@@ -492,6 +786,16 @@ class Cutscene:
seqList: list[CutsceneCmdStartStopSeqList] = field(default_factory=list)
fadeSeqList: list[CutsceneCmdFadeSeqList] = field(default_factory=list)
+ # lists from the new cutscene system
+ camSplineListNew: list[CutsceneCmdCamSplineList] = field(default_factory=list)
+ transitionListNew: list[CutsceneCmdTransitionList] = field(default_factory=list)
+ destinationListNew: list[CutsceneCmdDestinationList] = field(default_factory=list)
+ motionBlurList: list[CutsceneCmdMotionBlurList] = field(default_factory=list)
+ modifySeqList: list[CutsceneCmdModifySeqList] = field(default_factory=list)
+ creditsSceneList: list[CutsceneCmdChooseCreditsScenesList] = field(default_factory=list)
+ transitionGeneralList: list[CutsceneCmdTransitionGeneralList] = field(default_factory=list)
+ giveTatlList: list[CutsceneCmdGiveTatlList] = field(default_factory=list)
+
class CutsceneObjectFactory:
"""This class contains functions to create new Blender objects"""
@@ -527,13 +831,15 @@ def getNewCutsceneObject(self, name: str, frameCount: int, parentObj: Object):
def getNewActorCueListObject(self, name: str, commandType: str, parentObj: Object):
newActorCueListObj = self.getNewEmptyObject(name, False, parentObj)
newActorCueListObj.ootEmptyType = f"CS {'Player' if 'Player' in name else 'Actor'} Cue List"
- cmdEnum = ootData.enumData.enumByKey["csCmd"]
+ cmdEnum = game_data.z64.enums.enumByKey["cs_cmd"]
if commandType == "Player":
commandType = "player_cue"
- index = cmdEnum.itemByKey[commandType].index if commandType in cmdEnum.itemByKey else int(commandType, base=16)
- item = cmdEnum.itemByIndex.get(index)
+ index = (
+ cmdEnum.item_by_key[commandType].index if commandType in cmdEnum.item_by_key else int(commandType, base=16)
+ )
+ item = cmdEnum.item_by_index.get(index)
if item is not None:
newActorCueListObj.ootCSMotionProperty.actorCueListProp.commandType = item.key
@@ -566,11 +872,11 @@ def getNewActorCueObject(
item = None
if isPlayer:
- playerEnum = ootData.enumData.enumByKey["csPlayerCueId"]
+ playerEnum = game_data.z64.enums.enumByKey["cs_player_cue_id"]
if isinstance(actionID, int):
- item = playerEnum.itemByIndex.get(actionID)
+ item = playerEnum.item_by_index.get(actionID)
else:
- item = playerEnum.itemByKey.get(actionID)
+ item = playerEnum.item_by_key.get(actionID)
if item is not None:
newActorCueObj.ootCSMotionProperty.actorCueProp.playerCueID = item.key
diff --git a/fast64_internal/oot/cutscene/constants.py b/fast64_internal/z64/cutscene/constants.py
similarity index 64%
rename from fast64_internal/oot/cutscene/constants.py
rename to fast64_internal/z64/cutscene/constants.py
index d5e155e5b..6bd7c63fe 100644
--- a/fast64_internal/oot/cutscene/constants.py
+++ b/fast64_internal/z64/cutscene/constants.py
@@ -1,4 +1,4 @@
-from ..oot_constants import ootData
+from ...game_data import game_data
from .classes import (
CutsceneCmdActorCueList,
CutsceneCmdActorCue,
@@ -27,39 +27,32 @@
CutsceneCmdRumbleController,
CutsceneCmdRumbleControllerList,
CutsceneCmdDestination,
+ CutsceneCmdCamSplineList,
+ CutsceneCmdTransitionList,
+ CutsceneCmdDestinationList,
+ CutsceneCmdMotionBlurList,
+ CutsceneCmdModifySeqList,
+ CutsceneCmdChooseCreditsScenesList,
+ CutsceneCmdTransitionGeneralList,
+ CutsceneCmdGiveTatlList,
+ CutsceneCmdCamSpline,
+ CutsceneCmdNewCamPoint,
+ CutsceneCmdCamMisc,
+ CutsceneCmdTextGeneric,
+ CutsceneCmdTextMask,
+ CutsceneCmdMotionBlur,
+ CutsceneCmdModifySeq,
+ CutsceneCmdChooseCreditsScenes,
+ CutsceneCmdTransitionGeneral,
+ CutsceneCmdGiveTatl,
)
-ootEnumCSListTypeListC = {
- "TextList": "CS_TEXT_LIST",
- "Transition": "CS_TRANSITION",
- "LightSettingsList": "CS_LIGHT_SETTING_LIST",
- "TimeList": "CS_TIME_LIST",
- "StartSeqList": "CS_START_SEQ_LIST",
- "StopSeqList": "CS_STOP_SEQ_LIST",
- "FadeOutSeqList": "CS_FADE_OUT_SEQ_LIST",
- "MiscList": "CS_MISC_LIST",
- "RumbleList": "CS_RUMBLE_CONTROLLER_LIST",
-}
-
ootEnumCSWriteType = [
("Custom", "Custom", "Provide the name of a cutscene header variable", "", 0),
("Object", "Object", "Reference to Blender object representing cutscene", "", 2),
]
-# order here sets order on the UI
-ootEnumCSListType = [
- ("TextList", "Text List", "Textbox", "ALIGN_BOTTOM", 0),
- ("MiscList", "Misc List", "Misc", "OPTIONS", 7),
- ("RumbleList", "Rumble List", "Rumble Controller", "OUTLINER_OB_FORCE_FIELD", 8),
- ("Transition", "Transition", "Transition", "COLORSET_10_VEC", 1),
- ("LightSettingsList", "Light Settings List", "Lighting", "LIGHT_SUN", 2),
- ("TimeList", "Time List", "Time", "TIME", 3),
- ("StartSeqList", "Start Seq List", "Play BGM", "PLAY", 4),
- ("StopSeqList", "Stop Seq List", "Stop BGM", "SNAP_FACE", 5),
- ("FadeOutSeqList", "Fade-Out Seq List", "Fade BGM", "IPO_EASE_IN_OUT", 6),
-]
-
csListTypeToIcon = {
"TextList": "ALIGN_BOTTOM",
"Transition": "COLORSET_10_VEC",
@@ -70,6 +63,12 @@
"FadeOutSeqList": "IPO_EASE_IN_OUT",
"MiscList": "OPTIONS",
"RumbleList": "OUTLINER_OB_FORCE_FIELD",
+ "MotionBlurList": "ONIONSKIN_ON",
+ "ModifySeqList": "IPO_CONSTANT",
+ "CreditsSceneList": "WORLD",
+ "TransitionGeneralList": "COLORSET_06_VEC",
+ "StartAmbienceList": "SNAP_FACE",
+ "FadeOutAmbienceList": "IPO_EASE_IN_OUT",
}
ootEnumCSTextboxType = [
@@ -78,7 +77,16 @@
("OcarinaAction", "Ocarina Action", "Learn Song"),
]
-ootEnumCSTextboxTypeIcons = ["FILE_TEXT", "HIDE_ON", "FILE_SOUND"]
+ootEnumCSTextboxTypeIcons = {
+ "Text": "FILE_TEXT",
+ "None": "HIDE_ON",
+ "OcarinaAction": "FILE_SOUND",
+ "Default": "FILE_TEXT",
+ "Type1": "ALIGN_LEFT",
+ "Type3": "ALIGN_CENTER",
+ "BossesRemains": "GHOST_ENABLED",
+ "AllNormalMasks": "RECOVER_LAST",
+}
ootCSSubPropToName = {
"startFrame": "Start Frame",
@@ -104,16 +112,34 @@
"rumbleSourceStrength": "Source Strength",
"rumbleDuration": "Duration",
"rumbleDecreaseRate": "Decrease Rate",
+ "rumble_type": "Rumble Type",
+ # Transition
+ "transition_type": "Transition Type",
+ # Motion Blur
+ "blur_type": "Motion Blur Type",
+ # Transition General
+ "trans_general_type": "Transition Type",
+ "trans_color": "Transition Color",
+ # Choose Credits Scene
+ "credits_scene_type": "Credits Scene Type",
+ # Modify Seq
+ "mod_seq_type": "Modify Seq Type",
# Lists
"TextList": "Text List",
"TimeList": "Time List",
"FadeOutSeqList": "Fade-Out Seq List",
- "Transition": "Transition",
+ "Transition": "Transition List",
"StartSeqList": "Start Seq List",
"MiscList": "Misc List",
"LightSettingsList": "Light Settings List",
"StopSeqList": "Stop Seq List",
"RumbleList": "Rumble List",
+ "MotionBlurList": "Motion Blur List",
+ "ModifySeqList": "Modify Seq List",
+ "CreditsSceneList": "Choose Credits Scene List",
+ "TransitionGeneralList": "Transition General List",
+ "StartAmbienceList": "Start Ambience List",
+ "FadeOutAmbienceList": "Fade-Out Ambience List",
}
ootEnumCSMotionCamMode = [
@@ -122,11 +148,7 @@
("eyeOrAT", "Eye/AT Point", "Single Eye/AT point (not recommended)"),
]
-ootEnumCSActorCueListCommandType = [
- item for item in ootData.enumData.ootEnumCsCmd if "actor_cue" in item[0] or "player_cue" in item[0]
-]
-ootEnumCSActorCueListCommandType.sort()
-ootEnumCSActorCueListCommandType.insert(0, ("Custom", "Custom", "Custom"))
+# Import/Export
ootCSLegacyToNewCmdNames = {
"CS_CAM_POS_LIST": "CS_CAM_EYE_SPLINE",
@@ -157,6 +179,8 @@
"CS_PLAY_BGM": "L_CS_START_SEQ",
"CS_STOP_BGM_LIST": "CS_STOP_SEQ_LIST",
"CS_STOP_BGM": "L_CS_STOP_SEQ",
+ "CS_BEGIN_CUTSCENE": "CS_HEADER",
+ "CS_END": "CS_END_OF_SCRIPT",
}
ootCSListCommands = [
@@ -180,6 +204,15 @@
"CS_PLAY_BGM_LIST",
"CS_STOP_BGM_LIST",
"CS_LIGHTING_LIST",
+ # from new system
+ "CS_CAM_SPLINE_LIST",
+ "CS_TRANSITION_LIST",
+ "CS_DESTINATION_LIST",
+ "CS_MOTION_BLUR_LIST",
+ "CS_MODIFY_SEQ_LIST",
+ "CS_CHOOSE_CREDITS_SCENES_LIST",
+ "CS_TRANSITION_GENERAL_LIST",
+ "CS_GIVE_TATL_LIST",
]
ootCSListEntryCommands = [
@@ -204,49 +237,46 @@
"L_CS_LIGHT_SETTING",
"L_CS_START_SEQ",
"L_CS_STOP_SEQ",
+ # from new system
+ "CS_CAM_POINT_NEW",
+ "CS_CAM_MISC",
+ "CS_CAM_END",
+ "CS_CAM_SPLINE", # technically a list but treating it as an entry
+ "CS_TEXT_DEFAULT",
+ "CS_TEXT_TYPE_1",
+ "CS_TEXT_TYPE_3",
+ "CS_TEXT_BOSSES_REMAINS",
+ "CS_TEXT_ALL_NORMAL_MASKS",
+ "CS_MOTION_BLUR",
+ "CS_MODIFY_SEQ",
+ "CS_CHOOSE_CREDITS_SCENES",
+ "CS_TRANSITION_GENERAL",
+ "CS_GIVE_TATL",
]
ootCSSingleCommands = [
"CS_HEADER",
"CS_END_OF_SCRIPT",
+ # note: `CutsceneImport.correct_command_lists()` can move these ones in `ootCSListEntryCommands`
"CS_TRANSITION",
"CS_DESTINATION",
]
+
ootCSListAndSingleCommands = ootCSSingleCommands + ootCSListCommands
ootCSListAndSingleCommands.remove("CS_HEADER")
ootCutsceneCommandsC = ootCSSingleCommands + ootCSListCommands + ootCSListEntryCommands
-cmdToClass = {
- "CS_CAM_POINT": CutsceneCmdCamPoint,
- "CS_MISC": CutsceneCmdMisc,
- "CS_LIGHT_SETTING": CutsceneCmdLightSetting,
- "CS_TIME": CutsceneCmdTime,
- "CS_FADE_OUT_SEQ": CutsceneCmdFadeSeq,
- "CS_RUMBLE_CONTROLLER": CutsceneCmdRumbleController,
- "CS_TEXT": CutsceneCmdText,
- "CS_TEXT_NONE": CutsceneCmdTextNone,
- "CS_TEXT_OCARINA_ACTION": CutsceneCmdTextOcarinaAction,
- "CS_START_SEQ": CutsceneCmdStartStopSeq,
- "CS_STOP_SEQ": CutsceneCmdStartStopSeq,
- "CS_ACTOR_CUE": CutsceneCmdActorCue,
- "CS_PLAYER_CUE": CutsceneCmdActorCue,
- "CS_CAM_EYE_SPLINE": CutsceneCmdCamEyeSpline,
- "CS_CAM_AT_SPLINE": CutsceneCmdCamATSpline,
- "CS_CAM_EYE_SPLINE_REL_TO_PLAYER": CutsceneCmdCamEyeSplineRelToPlayer,
- "CS_CAM_AT_SPLINE_REL_TO_PLAYER": CutsceneCmdCamATSplineRelToPlayer,
- "CS_CAM_EYE": CutsceneCmdCamEye,
- "CS_CAM_AT": CutsceneCmdCamAT,
- "CS_MISC_LIST": CutsceneCmdMiscList,
- "CS_TRANSITION": CutsceneCmdTransition,
- "CS_TEXT_LIST": CutsceneCmdTextList,
- "CS_LIGHT_SETTING_LIST": CutsceneCmdLightSettingList,
- "CS_TIME_LIST": CutsceneCmdTimeList,
- "CS_FADE_OUT_SEQ_LIST": CutsceneCmdFadeSeqList,
- "CS_RUMBLE_CONTROLLER_LIST": CutsceneCmdRumbleControllerList,
- "CS_START_SEQ_LIST": CutsceneCmdStartStopSeqList,
- "CS_STOP_SEQ_LIST": CutsceneCmdStartStopSeqList,
- "CS_ACTOR_CUE_LIST": CutsceneCmdActorCueList,
- "CS_PLAYER_CUE_LIST": CutsceneCmdActorCueList,
- "CS_DESTINATION": CutsceneCmdDestination,
-}
+custom_values = [
+ "csMiscType",
+ "csTextType",
+ "ocarinaAction",
+ "csSeqID",
+ "csSeqPlayer",
+ "rumble_type",
+ "transition_type",
+ "blur_type",
+ "trans_general_type",
+ "credits_scene_type",
+ "mod_seq_type",
+]
diff --git a/fast64_internal/oot/cutscene/exporter/__init__.py b/fast64_internal/z64/cutscene/exporter/__init__.py
similarity index 100%
rename from fast64_internal/oot/cutscene/exporter/__init__.py
rename to fast64_internal/z64/cutscene/exporter/__init__.py
diff --git a/fast64_internal/oot/cutscene/exporter/classes.py b/fast64_internal/z64/cutscene/exporter/classes.py
similarity index 92%
rename from fast64_internal/oot/cutscene/exporter/classes.py
rename to fast64_internal/z64/cutscene/exporter/classes.py
index 8b42ca5ef..d3da102fc 100644
--- a/fast64_internal/oot/cutscene/exporter/classes.py
+++ b/fast64_internal/z64/cutscene/exporter/classes.py
@@ -5,8 +5,7 @@
from typing import TYPE_CHECKING
from bpy.types import Object
from ....utility import PluginError, indent
-from ...oot_constants import ootData
-from ..constants import ootEnumCSListTypeListC
+from ....game_data import game_data
if TYPE_CHECKING:
from ..properties import OOTCutsceneProperty, OOTCSTextProperty
@@ -40,7 +39,7 @@ class CutsceneCmdToC:
"""This class contains functions to create the cutscene commands"""
def getEnumValue(self, enumKey: str, owner, propName: str):
- item = ootData.enumData.enumByKey[enumKey].itemByKey.get(getattr(owner, propName))
+ item = game_data.z64.enums.enumByKey[enumKey].item_by_key.get(getattr(owner, propName))
return item.id if item is not None else getattr(owner, f"{propName}Custom")
def getGenericListCmd(self, cmdName: str, entryTotal: int):
@@ -89,7 +88,7 @@ def getTextOcarinaActionCmd(self, ocarinaAction: CutsceneCmdTextOcarinaAction):
)
def getDestinationCmd(self, csProp: "OOTCutsceneProperty"):
- dest = self.getEnumValue("csDestination", csProp, "csDestination")
+ dest = self.getEnumValue("cs_destination", csProp, "csDestination")
return indent * 2 + f"CS_DESTINATION({dest}, {csProp.csDestinationStartFrame}, 0),\n"
def getActorCueListCmd(self, actorCueList: CutsceneCmdActorCueList, isPlayerActor: bool):
@@ -212,7 +211,7 @@ def getActorCueListData(self, isPlayer: bool):
if commandType == "Custom":
commandType = obj.ootCSMotionProperty.actorCueListProp.commandTypeCustom
elif self.useDecomp:
- commandType = ootData.enumData.enumByKey["csCmd"].itemByKey[commandType].id
+ commandType = game_data.z64.enums.enumByKey["cs_cmd"].item_by_key[commandType].id
# ignoring dummy cue
actorCueList = CutsceneCmdActorCueList(None, entryTotal=entryTotal - 1, commandType=commandType)
@@ -227,7 +226,7 @@ def getActorCueListData(self, isPlayer: bool):
if isPlayer:
cueID = childObj.ootCSMotionProperty.actorCueProp.playerCueID
if cueID != "Custom":
- actionID = ootData.enumData.enumByKey["csPlayerCueId"].itemByKey[cueID].id
+ actionID = game_data.z64.enums.enumByKey["cs_player_cue_id"].item_by_key[cueID].id
if actionID is None:
actionID = childObj.ootCSMotionProperty.actorCueProp.cueActionID
@@ -348,7 +347,7 @@ def getTextListData(self, textEntry: "OOTCSTextProperty"):
textEntry.startFrame,
textEntry.endFrame,
textEntry.textID,
- self.getEnumValue("csTextType", textEntry, "csTextType"),
+ self.getEnumValue("cs_text_type", textEntry, "csTextType"),
textEntry.topOptionTextID,
textEntry.bottomOptionTextID,
)
@@ -361,7 +360,7 @@ def getTextListData(self, textEntry: "OOTCSTextProperty"):
None,
textEntry.startFrame,
textEntry.endFrame,
- self.getEnumValue("ocarinaSongActionId", textEntry, "ocarinaAction"),
+ self.getEnumValue("ocarina_song_action_id", textEntry, "ocarinaAction"),
textEntry.ocarinaMessageId,
)
)
@@ -386,25 +385,25 @@ def getCutsceneData(self):
enumKey = "csFadeOutSeqPlayer" if entry.listType == "FadeOutSeqList" else "seqId"
propName = "csSeqPlayer" if entry.listType == "FadeOutSeqList" else "csSeqID"
subData += self.getGenericSeqCmd(
- ootEnumCSListTypeListC[entry.listType].removesuffix("_LIST"),
+ game_data.z64.cs_list_type_to_cmd[entry.listType].removesuffix("_LIST"),
self.getEnumValue(enumKey, elem, propName),
elem.startFrame,
elem.endFrame,
)
- case "Transition":
- subData += self.getTransitionCmd(
- CutsceneCmdTransition(
- None,
- entry.transitionStartFrame,
- entry.transitionEndFrame,
- self.getEnumValue("csTransitionType", entry, "transitionType"),
- )
- )
case _:
curList = getattr(entry, (entry.listType[0].lower() + entry.listType[1:]))
entryTotal = len(curList)
for elem in curList:
match entry.listType:
+ case "Transition":
+ subData += self.getTransitionCmd(
+ CutsceneCmdTransition(
+ None,
+ elem.startFrame,
+ elem.endFrame,
+ self.getEnumValue("cs_transition_type", elem, "transition_type"),
+ )
+ )
case "TextList":
subData += self.getTextListData(elem)
case "LightSettingsList":
@@ -423,7 +422,7 @@ def getCutsceneData(self):
None,
elem.startFrame,
elem.endFrame,
- self.getEnumValue("csMiscType", elem, "csMiscType"),
+ self.getEnumValue("cs_misc_type", elem, "csMiscType"),
)
)
case "RumbleList":
@@ -440,7 +439,7 @@ def getCutsceneData(self):
case _:
raise PluginError("ERROR: Unknown Cutscene List Type!")
if entry.listType != "Transition":
- listCmd = self.getGenericListCmd(ootEnumCSListTypeListC[entry.listType], entryTotal)
+ listCmd = self.getGenericListCmd(game_data.z64.cs_list_type_to_cmd[entry.listType], entryTotal)
self.entryTotal += 1
data += listCmd + subData
diff --git a/fast64_internal/oot/cutscene/exporter/functions.py b/fast64_internal/z64/cutscene/exporter/functions.py
similarity index 100%
rename from fast64_internal/oot/cutscene/exporter/functions.py
rename to fast64_internal/z64/cutscene/exporter/functions.py
diff --git a/fast64_internal/oot/cutscene/importer/__init__.py b/fast64_internal/z64/cutscene/importer/__init__.py
similarity index 100%
rename from fast64_internal/oot/cutscene/importer/__init__.py
rename to fast64_internal/z64/cutscene/importer/__init__.py
diff --git a/fast64_internal/z64/cutscene/importer/classes.py b/fast64_internal/z64/cutscene/importer/classes.py
new file mode 100644
index 000000000..7d841aee8
--- /dev/null
+++ b/fast64_internal/z64/cutscene/importer/classes.py
@@ -0,0 +1,878 @@
+import bpy
+import re
+
+from dataclasses import dataclass
+from typing import Optional, TYPE_CHECKING
+from bpy.types import Object, Armature
+from ....utility import PluginError
+from ...utility import is_oot_features
+from ..motion.utility import setupCutscene, getBlenderPosition, getInteger
+
+if TYPE_CHECKING:
+ from ..properties import OOTCSListProperty, OOTCutsceneProperty
+
+from ..constants import (
+ ootCSLegacyToNewCmdNames,
+ ootCSListCommands,
+ ootCutsceneCommandsC,
+ ootCSListEntryCommands,
+ ootCSSingleCommands,
+ ootCSListAndSingleCommands,
+ custom_values,
+)
+
+from ..classes import (
+ CutsceneObjectFactory,
+)
+
+# TODO: move these classes to a common place outside the exporter
+from ...exporter.utility import Utility
+from ...exporter.cutscene import Cutscene, CutsceneData
+from ...exporter.cutscene.actor_cue import CutsceneCmdActorCueList, CutsceneCmdActorCue
+from ...exporter.cutscene.text import (
+ CutsceneCmdTextList,
+ CutsceneCmdText,
+ CutsceneCmdTextNone,
+ CutsceneCmdTextOcarinaAction,
+)
+
+from ...exporter.cutscene.seq import (
+ CutsceneCmdStartStopSeqList,
+ CutsceneCmdFadeSeqList,
+ CutsceneCmdStartStopSeq,
+ CutsceneCmdFadeSeq,
+ CutsceneCmdModifySeq,
+ CutsceneCmdModifySeqList,
+ CutsceneCmdStartAmbience,
+ CutsceneCmdStartAmbienceList,
+ CutsceneCmdFadeOutAmbience,
+ CutsceneCmdFadeOutAmbienceList,
+)
+
+from ...exporter.cutscene.misc import (
+ CutsceneCmdLightSetting,
+ CutsceneCmdTime,
+ CutsceneCmdMisc,
+ CutsceneCmdRumbleController,
+ CutsceneCmdDestination,
+ CutsceneCmdMiscList,
+ CutsceneCmdRumbleControllerList,
+ CutsceneCmdTransition,
+ CutsceneCmdTransitionList,
+ CutsceneCmdLightSettingList,
+ CutsceneCmdTimeList,
+ CutsceneCmdMotionBlur,
+ CutsceneCmdMotionBlurList,
+ CutsceneCmdChooseCreditsScenes,
+ CutsceneCmdChooseCreditsScenesList,
+ CutsceneCmdTransitionGeneral,
+ CutsceneCmdTransitionGeneralList,
+ CutsceneCmdGiveTatl,
+)
+
+from ...exporter.cutscene.camera import (
+ CutsceneCmdCamPoint,
+ CutsceneCmdCamEyeSpline,
+ CutsceneCmdCamATSpline,
+ CutsceneCmdCamEyeSplineRelToPlayer,
+ CutsceneCmdCamATSplineRelToPlayer,
+ CutsceneCmdCamEye,
+ CutsceneCmdCamAT,
+ CutsceneCmdNewCamPoint,
+ CutsceneCmdCamMisc,
+ CutsceneSplinePoint,
+ CutsceneCmdCamSpline,
+ CutsceneCmdCamSplineList,
+)
+
+cmdToClass = {
+ "CS_CAM_POINT": CutsceneCmdCamPoint,
+ "CS_MISC": CutsceneCmdMisc,
+ "CS_LIGHT_SETTING": CutsceneCmdLightSetting,
+ "CS_TIME": CutsceneCmdTime,
+ "CS_FADE_OUT_SEQ": CutsceneCmdFadeSeq,
+ "CS_RUMBLE_CONTROLLER": CutsceneCmdRumbleController,
+ "CS_TEXT": CutsceneCmdText,
+ "CS_TEXT_NONE": CutsceneCmdTextNone,
+ "CS_TEXT_OCARINA_ACTION": CutsceneCmdTextOcarinaAction,
+ "CS_START_SEQ": CutsceneCmdStartStopSeq,
+ "CS_STOP_SEQ": CutsceneCmdStartStopSeq,
+ "CS_ACTOR_CUE": CutsceneCmdActorCue,
+ "CS_PLAYER_CUE": CutsceneCmdActorCue,
+ "CS_CAM_EYE_SPLINE": CutsceneCmdCamEyeSpline,
+ "CS_CAM_AT_SPLINE": CutsceneCmdCamATSpline,
+ "CS_CAM_EYE_SPLINE_REL_TO_PLAYER": CutsceneCmdCamEyeSplineRelToPlayer,
+ "CS_CAM_AT_SPLINE_REL_TO_PLAYER": CutsceneCmdCamATSplineRelToPlayer,
+ "CS_CAM_EYE": CutsceneCmdCamEye,
+ "CS_CAM_AT": CutsceneCmdCamAT,
+ "CS_MISC_LIST": CutsceneCmdMiscList,
+ "CS_TRANSITION": CutsceneCmdTransition,
+ "CS_TEXT_LIST": CutsceneCmdTextList,
+ "CS_LIGHT_SETTING_LIST": CutsceneCmdLightSettingList,
+ "CS_TIME_LIST": CutsceneCmdTimeList,
+ "CS_FADE_OUT_SEQ_LIST": CutsceneCmdFadeSeqList,
+ "CS_RUMBLE_CONTROLLER_LIST": CutsceneCmdRumbleControllerList,
+ "CS_START_SEQ_LIST": CutsceneCmdStartStopSeqList,
+ "CS_STOP_SEQ_LIST": CutsceneCmdStartStopSeqList,
+ "CS_ACTOR_CUE_LIST": CutsceneCmdActorCueList,
+ "CS_PLAYER_CUE_LIST": CutsceneCmdActorCueList,
+ "CS_DESTINATION": CutsceneCmdDestination,
+ # from new system
+ "CS_CAM_SPLINE_LIST": CutsceneCmdCamSplineList,
+ "CS_CAM_SPLINE": CutsceneCmdCamSpline,
+ "CS_CAM_POINT_NEW": CutsceneCmdNewCamPoint,
+ "CS_CAM_MISC": CutsceneCmdCamMisc,
+ "CS_TRANSITION_LIST": CutsceneCmdTransitionList,
+ "CS_TEXT_DEFAULT": CutsceneCmdText,
+ "CS_TEXT_TYPE_1": CutsceneCmdText,
+ "CS_TEXT_TYPE_3": CutsceneCmdText,
+ "CS_TEXT_BOSSES_REMAINS": CutsceneCmdText,
+ "CS_TEXT_ALL_NORMAL_MASKS": CutsceneCmdText,
+ "CS_MOTION_BLUR_LIST": CutsceneCmdMotionBlurList,
+ "CS_MOTION_BLUR": CutsceneCmdMotionBlur,
+ "CS_MODIFY_SEQ_LIST": CutsceneCmdModifySeqList,
+ "CS_MODIFY_SEQ": CutsceneCmdModifySeq,
+ "CS_CHOOSE_CREDITS_SCENES_LIST": CutsceneCmdChooseCreditsScenesList,
+ "CS_CHOOSE_CREDITS_SCENES": CutsceneCmdChooseCreditsScenes,
+ "CS_TRANSITION_GENERAL_LIST": CutsceneCmdTransitionGeneralList,
+ "CS_TRANSITION_GENERAL": CutsceneCmdTransitionGeneral,
+ "CS_GIVE_TATL": CutsceneCmdGiveTatl,
+}
+
+list_type_to_list_prop_member = {
+ "Text": "textList",
+ "Misc": "miscList",
+ "Transition": "transition_list",
+ "LightSettings": "lightSettingsList",
+ "Time": "timeList",
+ "Rumble": "rumbleList",
+ "MotionBlur": "motion_blur_list",
+ "TransitionGeneral": "trans_general_list",
+ "CreditsScene": "credits_scene_list",
+ "ModifySeq": "mod_seq_list",
+ "StartSeq": "seqList",
+ "StopSeq": "seqList",
+ "FadeOutSeq": "seqList",
+ "StartAmbience": "seqList",
+ "FadeOutAmbience": "seqList",
+}
+
+list_type_to_cs_data_member = {
+ "Text": "textList",
+ "Misc": "miscList",
+ "Transition": "transitionList",
+ "LightSettings": "lightSettingsList",
+ "Time": "timeList",
+ "Rumble": "rumbleList",
+ "MotionBlur": "motion_blur_list",
+ "TransitionGeneral": "transition_general_list",
+ "CreditsScene": "credits_scene_list",
+ "ModifySeq": "modify_seq_list",
+ "StartSeq": "seqList",
+ "StopSeq": "seqList",
+ "FadeOutSeq": "fadeSeqList",
+ "StartAmbience": "start_ambience_list",
+ "FadeOutAmbience": "fade_out_ambience_list",
+}
+
+
+@dataclass
+class ParsedCutscene:
+ """Local class used to order the parsed cutscene properly"""
+
+ csName: str
+ csData: list[str] # contains every command lists or standalone ones like ``CS_TRANSITION()``
+
+
+@dataclass
+class PropertyData:
+ listType: str
+ subPropsData: dict[str, str]
+ useEndFrame: bool
+
+
+@dataclass
+class CutsceneImport(CutsceneObjectFactory):
+ """This class contains functions to create the new cutscene Blender data"""
+
+ filePath: Optional[str] # used when importing from the panel
+ fileData: Optional[str] # used when importing the cutscenes when importing a scene
+ csName: Optional[str] # used when import a specific cutscene
+ new_cs_system: bool
+
+ def getCmdParams(self, data: str, cmdName: str, paramNumber: int):
+ """Returns the list of every parameter of the given command"""
+
+ # kinda hacky but better than duplicating the classes
+ if self.new_cs_system:
+ if cmdName in {"CS_START_SEQ", "CS_FADE_OUT_SEQ", "CS_LIGHT_SETTING"}:
+ paramNumber = 3
+ elif cmdName in {"CS_MISC", "CS_STOP_SEQ", "CS_TEXT_ALL_NORMAL_MASKS"}:
+ paramNumber = 4
+ elif cmdName in {"CS_TEXT_DEFAULT", "CS_TEXT_TYPE_1", "CS_TEXT_TYPE_3", "CS_TEXT_BOSSES_REMAINS"}:
+ paramNumber = 5
+ elif cmdName in {"CS_RUMBLE_CONTROLLER"}:
+ paramNumber = 6
+
+ parenthesis = "(" if not cmdName.endswith("(") else ""
+ data = data.strip().removeprefix(f"{cmdName}{parenthesis}").replace(" ", "").removesuffix(")")
+ if "CS_FLOAT" in data:
+ data = re.sub(r"CS_FLOAT\([a-fA-F0-9x]*,([0-9e+-.f]*)\)", r"\1", data, re.DOTALL)
+ data = re.sub(r"CS_FLOAT\([a-fA-F0-9x]*,([0-9e+-.f]*)", r"\1", data, re.DOTALL)
+ params = data.split(",")
+ validTimeCmd = cmdName == "CS_TIME" and len(params) == 6 and paramNumber == 5
+ if len(params) != paramNumber and not validTimeCmd:
+ raise PluginError(
+ f"ERROR: The number of expected parameters for `{cmdName}` "
+ + "and the number of found ones is not the same!"
+ )
+ return params
+
+ def getNewCutscene(self, csData: str, name: str, use_macros: bool, motion_only: bool):
+ params = self.getCmdParams(csData, "CS_HEADER", Cutscene.paramNumber)
+ new_cs = Cutscene.new(name, None, use_macros, motion_only)
+ new_cs.totalEntries = getInteger(params[0])
+ new_cs.frameCount = getInteger(params[1])
+ return new_cs
+
+ def correct_command_lists(self, command: str):
+ """If using the new cs system, moves standalone commands to the proper lists"""
+ if self.new_cs_system:
+ if command in ootCSSingleCommands:
+ ootCSSingleCommands.remove(command)
+ if command in ootCSListAndSingleCommands:
+ ootCSListAndSingleCommands.remove(command)
+ if command not in ootCSListEntryCommands:
+ ootCSListEntryCommands.append(command)
+
+ def getParsedCutscenes(self):
+ """Returns the parsed commands read from every cutscene we can find"""
+
+ fileData = ""
+
+ if self.fileData is not None:
+ fileData = self.fileData
+ elif self.filePath is not None:
+ with open(self.filePath, "r") as inputFile:
+ fileData = inputFile.read()
+ else:
+ raise PluginError("ERROR: File data can't be found!")
+
+ self.correct_command_lists("CS_TRANSITION")
+ self.correct_command_lists("CS_DESTINATION")
+
+ # replace old names
+ oldNames = list(ootCSLegacyToNewCmdNames.keys())
+ fileData = fileData.replace("CS_CMD_CONTINUE", "CS_CAM_CONTINUE")
+ fileData = fileData.replace("CS_CMD_STOP", "CS_CAM_STOP")
+ for oldName in oldNames:
+ fileData = fileData.replace(f"{oldName}(", f"{ootCSLegacyToNewCmdNames[oldName]}(")
+
+ # handle conflicts between the cs system of OoT and MM
+ if self.new_cs_system:
+ fileData = fileData.replace("CS_CAM_POINT", "CS_CAM_POINT_NEW")
+ fileData = fileData.replace("CS_RUMBLE", "CS_RUMBLE_CONTROLLER")
+
+ # make a list of existing cutscene names, to skip importing them if found
+ existingCutsceneNames = [
+ csObj.name.removeprefix("Cutscene.")
+ for csObj in bpy.data.objects
+ if csObj.type == "EMPTY" and csObj.ootEmptyType == "Cutscene"
+ ]
+
+ fileLines: list[str] = []
+ for line in fileData.split("\n"):
+ fileLines.append(line.strip())
+
+ # parse cutscenes
+ csData = []
+ cutsceneList: list[list[str]] = []
+ foundCutscene = False
+ for line in fileLines:
+ if not line.startswith("//") and not line.startswith("/*"):
+ if "CutsceneData " in line:
+ # split with "[" just in case the array has a set size
+ csName = line.split(" ")[1].split("[")[0]
+ if csName in existingCutsceneNames:
+ print(f"WARNING: Cutscene '{csName}' already exists in this blend's data.")
+ foundCutscene = True
+ print(f"INFO: Found cutscene '{csName}' in the file data.")
+
+ if foundCutscene:
+ sLine = line.strip()
+ csCmd = sLine.split("(")[0]
+ if "CutsceneData " not in line and "};" not in line and csCmd not in ootCutsceneCommandsC:
+ if len(csData) > 0:
+ csData[-1] += line
+
+ if len(csData) == 0 or sLine.startswith("CS_") and not sLine.startswith("CS_FLOAT"):
+ if self.csName is None or self.csName == csName:
+ csData.append(line)
+
+ if "};" in line:
+ foundCutscene = False
+ if len(csData) > 0:
+ cutsceneList.append(csData)
+ csData = []
+
+ if len(cutsceneList) == 0:
+ print("INFO: Found no cutscenes in this file!")
+ return None
+
+ # parse the commands from every cutscene we found
+ parsedCutscenes: list[ParsedCutscene] = []
+ for cutscene in cutsceneList:
+ cmdListFound = False
+ curCmdPrefix = None
+ parsedCS = []
+ parsedData = ""
+ csName = None
+
+ for line in cutscene:
+ curCmd = line.strip().split("(")[0]
+ index = cutscene.index(line) + 1
+ nextCmd = cutscene[index].strip().split("(")[0] if index < len(cutscene) else None
+ line = line.strip()
+ if "CutsceneData" in line:
+ csName = line.split(" ")[1][:-2]
+
+ # NOTE: ``CS_UNK_DATA()`` are commands that are completely useless, so we're ignoring those
+ if csName is not None and not "CS_UNK_DATA" in curCmd:
+ if curCmd in ootCutsceneCommandsC:
+ line = line.removesuffix(",") + "\n"
+
+ if curCmd in ootCSSingleCommands and curCmd != "CS_END_OF_SCRIPT":
+ parsedData += line
+
+ if not cmdListFound and curCmd in ootCSListCommands:
+ cmdListFound = True
+ parsedData = ""
+
+ # camera and lighting have "non-standard" list names
+ if curCmd.startswith("CS_CAM"):
+ curCmdPrefix = "CS_CAM"
+ elif curCmd.startswith("CS_LIGHT") or curCmd.startswith("L_CS_LIGHT"):
+ curCmdPrefix = "CS_LIGHT"
+ else:
+ curCmdPrefix = curCmd[:-5]
+
+ if curCmdPrefix is not None:
+ if curCmdPrefix in curCmd:
+ parsedData += line
+ elif not cmdListFound and curCmd in ootCSListEntryCommands:
+ print(f"{csName}, command:\n{line}")
+ raise PluginError(f"ERROR: Found a list entry outside a list inside ``{csName}``!")
+
+ if cmdListFound and nextCmd == "CS_END_OF_SCRIPT" or nextCmd in ootCSListAndSingleCommands:
+ cmdListFound = False
+ parsedCS.append(parsedData)
+ parsedData = ""
+ elif not "CutsceneData" in curCmd and not "};" in curCmd:
+ print(f"WARNING: Unknown command found: ``{curCmd}``")
+ cmdListFound = False
+
+ if csName is not None and len(parsedCS) > 0:
+ parsedCutscenes.append(ParsedCutscene(csName, parsedCS))
+ else:
+ raise PluginError("ERROR: Something wrong happened during the parsing of the cutscene.")
+
+ return parsedCutscenes
+
+ def getCutsceneList(self, use_macros: bool, motion_only: bool):
+ """Returns the list of cutscenes with the data processed"""
+
+ parsedCutscenes = self.getParsedCutscenes()
+
+ if parsedCutscenes is None:
+ # if it's none then there's no cutscene in the file
+ return None
+
+ if len(parsedCutscenes) == 0:
+ raise PluginError("ERROR: No cutscene was found.")
+
+ cutsceneList: list[Cutscene] = []
+
+ # for each cutscene from the list returned by getParsedCutscenes(),
+ # create classes containing the cutscene's informations
+ # that will be used later when creating Blender objects to complete the import
+ for parsedCS in parsedCutscenes:
+ cutscene = None
+
+ for data in parsedCS.csData:
+ cmdData = data.removesuffix("\n").split("\n")
+ cmdListData = cmdData.pop(0)
+ cmdListName = cmdListData.strip().split("(")[0]
+
+ if cmdListName == "CS_HEADER":
+ # create a new cutscene data
+ cutscene = self.getNewCutscene(data, parsedCS.csName, use_macros, motion_only)
+ elif cutscene is not None and data.startswith(f"{cmdListName}("):
+ # if we have a cutscene, create and add the commands data in it
+ isPlayer = cmdListData.startswith("CS_PLAYER_CUE_LIST(")
+ isStartSeq = cmdListData.startswith("CS_START_SEQ_LIST(")
+ isStopSeq = cmdListData.startswith("CS_STOP_SEQ_LIST(")
+
+ cs_list_raw_cmd = cmdToClass.get(cmdListName)
+ if cs_list_raw_cmd is not None:
+ cmdList = getattr(cutscene.data, "playerCueList" if isPlayer else cs_list_raw_cmd.listName)
+
+ paramNumber = cs_list_raw_cmd.paramNumber - 1 if isPlayer else cs_list_raw_cmd.paramNumber
+ params = self.getCmdParams(cmdListData, cmdListName, paramNumber)
+
+ if isStartSeq or isStopSeq:
+ new_cs_list_data = cs_list_raw_cmd.from_params(
+ params, type="start" if isStartSeq else "stop"
+ )
+ elif cmdListData.startswith("CS_ACTOR_CUE_LIST(") or isPlayer:
+ new_cs_list_data = cs_list_raw_cmd.from_params(params, isPlayer=isPlayer)
+ else:
+ new_cs_list_data = cs_list_raw_cmd.from_params(params)
+
+ # treating camera commands separately if using the new cs commands
+ # as it became more complex to parse since it's basically a list in a list in a list
+ if self.new_cs_system and "CAM" in cmdListName:
+ foundEndCmd = False
+ cur_point = 0
+ at_list: list[CutsceneCmdNewCamPoint] = []
+ eye_list: list[CutsceneCmdNewCamPoint] = []
+ misc_list: list[CutsceneCmdCamMisc] = []
+ cur_spline_entry: Optional[CutsceneCmdCamSpline] = None
+ for d in cmdData:
+ cmdEntryName = d.strip().split("(")[0]
+
+ if foundEndCmd:
+ raise ValueError("ERROR: More camera commands after last one!")
+
+ if "CS_CAM_END" in d:
+ foundEndCmd = True
+ continue
+
+ entryCmd = cmdToClass[cmdEntryName]
+ params = self.getCmdParams(d, cmdEntryName, entryCmd.paramNumber)
+ listEntry = entryCmd.from_params(params)
+
+ if cmdEntryName == "CS_CAM_SPLINE":
+ cur_spline_entry = listEntry
+ elif cur_spline_entry is not None:
+ cs_data_entry_base = cmdToClass[cmdEntryName]
+ sub_params = self.getCmdParams(d, cmdEntryName, cs_data_entry_base.paramNumber)
+
+ if cur_point < cur_spline_entry.num_entries:
+ at_list.append(cs_data_entry_base.from_params(sub_params))
+ cur_point += 1
+ elif cur_point < cur_spline_entry.num_entries * 2:
+ eye_list.append(cs_data_entry_base.from_params(sub_params))
+ cur_point += 1
+ elif cur_point < cur_spline_entry.num_entries * 3:
+ misc_list.append(cs_data_entry_base.from_params(sub_params))
+ cur_point += 1
+
+ if cur_point == cur_spline_entry.num_entries * 3:
+ assert len(at_list) == len(eye_list) == len(misc_list)
+
+ for i, (at, eye, misc) in enumerate(zip(at_list, eye_list, misc_list)):
+ if i < len(at_list) - 1:
+ # ignore the last points, see the exporter to know why
+ cur_spline_entry.entries.append(CutsceneSplinePoint(at, eye, misc))
+
+ new_cs_list_data.entries.append(cur_spline_entry)
+ cur_point = 0
+ at_list.clear()
+ eye_list.clear()
+ misc_list.clear()
+ cur_spline_entry = None
+ else:
+ raise ValueError("ERROR: Invalid number of entries.")
+ elif cmdListName != "CS_TRANSITION" and cmdListName != "CS_DESTINATION":
+ # this condition still works for both versions since the list name actually ends with "_LIST",
+ # same thing in the next if/else block when it adds the data to the `Cutscene` class
+
+ foundEndCmd = False
+ for d in cmdData:
+ cmdEntryName = d.strip().split("(")[0]
+ isLegacy = d.startswith("L_")
+ if isLegacy:
+ cmdEntryName = cmdEntryName.removeprefix("L_")
+ d = d.removeprefix("L_")
+
+ if "CAM" in cmdListName:
+ flag = d.removeprefix("CS_CAM_POINT(").split(",")[0]
+ if foundEndCmd:
+ raise PluginError("ERROR: More camera commands after last one!")
+ foundEndCmd = "CS_CAM_STOP" in flag or "-1" in flag
+
+ entryCmd = cmdToClass[cmdEntryName]
+ params = self.getCmdParams(d, cmdEntryName, entryCmd.paramNumber)
+
+ if "CS_LIGHT_SETTING(" in d:
+ listEntry = entryCmd.from_params(params, isLegacy)
+ elif isPlayer:
+ listEntry = entryCmd.from_params(params, True)
+ elif isStartSeq or isStopSeq:
+ listEntry = entryCmd.from_params(
+ params, "start" if isStartSeq else "stop", isLegacy
+ )
+ elif "CS_TEXT_" in cmdEntryName:
+ listEntry = entryCmd.from_params(params, cmdEntryName)
+ else:
+ listEntry = entryCmd.from_params(params)
+ new_cs_list_data.entries.append(listEntry)
+ if cmdListName == "CS_DESTINATION":
+ cutscene.data.destination = new_cs_list_data
+ else:
+ cmdList.append(new_cs_list_data)
+ else:
+ print(f"WARNING: `{cmdListName}` is not implemented yet!")
+
+ # after processing the commands we can add the cutscene to the cutscene list
+ if cutscene is not None:
+ cutsceneList.append(cutscene)
+ return cutsceneList
+
+ def setActorCueData(self, csObj: Object, actorCueList: list[CutsceneCmdActorCueList], cueName: str, csNbr: int):
+ """Creates the objects from the Actor Cue List data"""
+
+ cueObjList = []
+ cueEndFrames = []
+ for i, entry in enumerate(actorCueList, 1):
+ if len(entry.entries) == 0:
+ raise PluginError("ERROR: Actor Cue List does not have any Actor Cue!")
+
+ lastFrame = lastPos = None
+ actorCueListObj = self.getNewActorCueListObject(
+ f"CS_{csNbr:02}.{cueName} Cue List {i:02}", entry.commandType, csObj
+ )
+
+ for j, actorCue in enumerate(entry.entries, 1):
+ if lastFrame is not None and lastFrame != actorCue.startFrame:
+ raise PluginError("ERROR: Actor Cues are not temporally continuous!")
+
+ if lastPos is not None and lastPos != actorCue.startPos:
+ raise PluginError("ERROR: Actor Cues are not spatially continuous!")
+
+ cueObjList.append(
+ self.getNewActorCueObject(
+ f"CS_{csNbr:02}.{cueName} Cue {i}.{j:02}",
+ actorCue.startFrame,
+ actorCue.actionID,
+ actorCue.startPos,
+ actorCue.rot,
+ actorCueListObj,
+ )
+ )
+ lastFrame = actorCue.endFrame
+ lastPos = actorCue.endPos
+ cueEndFrames.append(lastFrame)
+
+ # we need a dummy actor cue to get the end position of the last real one
+ if lastFrame is not None:
+ cueObjList.append(
+ self.getNewActorCueObject(
+ f"CS_{csNbr:02}.{cueName} Cue {i}.999 (D)",
+ lastFrame,
+ "DUMMY",
+ lastPos,
+ actorCue.rot,
+ actorCueListObj,
+ )
+ )
+ cueEndFrames.append(lastFrame + 1)
+
+ # updating the end frames
+ if len(cueEndFrames) != len(cueObjList):
+ raise PluginError("ERROR: Lists lengths do not match!")
+
+ for obj, endFrame in zip(cueObjList, cueEndFrames):
+ # reading this value will trigger the "get" function
+ getEndFrame = obj.ootCSMotionProperty.actorCueProp.cueEndFrame
+
+ if endFrame != getEndFrame and obj.ootEmptyType != "CS Dummy Cue":
+ print(f"WARNING: `{obj.name}`'s end frame do not match the one from the script!")
+
+ def validateCameraData(self, cutscene: Cutscene):
+ """Safety checks to make sure the camera data is correct"""
+
+ camLists: list[tuple[str, list, list]] = [
+ ("Eye and AT Spline", cutscene.data.camEyeSplineList, cutscene.data.camATSplineList),
+ (
+ "Eye and AT Spline Rel to Player",
+ cutscene.data.camEyeSplineRelPlayerList,
+ cutscene.data.camATSplineRelPlayerList,
+ ),
+ ("Eye and AT", cutscene.data.camEyeList, cutscene.data.camATList),
+ ]
+
+ for camType, eyeList, atList in camLists:
+ for eyeListEntry, atListEntry in zip(eyeList, atList):
+ eyeTotal = len(eyeListEntry.entries)
+ atTotal = len(atListEntry.entries)
+
+ # Eye -> bone's head, AT -> bone's tail, that's why both lists requires the same length
+ if eyeTotal != atTotal:
+ raise PluginError(f"ERROR: Found {eyeTotal} Eye lists but {atTotal} AT lists in {camType}!")
+
+ if eyeTotal < 4:
+ raise PluginError(f"ERROR: Only {eyeTotal} cam point in this command!")
+
+ if eyeTotal > 4:
+ # NOTE: There is a bug in the game where when incrementing to the next set of key points,
+ # the key point which checked for whether it's the last point or not is the last point
+ # of the next set, not the last point of the old set. This means we need to remove
+ # the extra point at the end that will only tell the game that this camera shot stops.
+ del eyeListEntry.entries[-1]
+ del atListEntry.entries[-1]
+
+ def setBoneData(
+ self, cameraShotObj: Object, boneData: list[tuple[CutsceneCmdCamPoint, CutsceneCmdCamPoint]], csNbr: int
+ ):
+ """Creates the bones from the Camera Point data"""
+
+ scale = bpy.context.scene.ootBlenderScale
+ for i, (eyePoint, atPoint) in enumerate(boneData, 1):
+ # we need the edit mode to be able to change the bone's location
+ bpy.ops.object.mode_set(mode="EDIT")
+ armatureData: Armature = cameraShotObj.data
+ boneName = f"CS_{csNbr:02}.Camera Point {i:02}"
+ newEditBone = armatureData.edit_bones.new(boneName)
+ newEditBone.head = getBlenderPosition(eyePoint.pos, scale)
+ newEditBone.tail = getBlenderPosition(atPoint.pos, scale)
+ bpy.ops.object.mode_set(mode="OBJECT")
+ newBone = armatureData.bones[boneName]
+
+ if eyePoint.frame != 0:
+ print("WARNING: Frames must be 0!")
+
+ # using the "AT" (look-at) data since this is what determines where the camera is looking
+ # the "Eye" only sets the location of the camera
+ newBone.ootCamShotPointProp.shotPointFrame = atPoint.frame
+ newBone.ootCamShotPointProp.shotPointViewAngle = atPoint.viewAngle
+ newBone.ootCamShotPointProp.shotPointRoll = atPoint.camRoll
+
+ def setCameraShotData(
+ self, csObj: Object, eyePoints: list, atPoints: list, camMode: str, startIndex: int, csNbr: int
+ ):
+ """Creates the armatures from the Camera Shot data"""
+
+ endIndex = 0
+
+ # this is required to be able to change the object mode
+ if bpy.context.mode != "OBJECT":
+ bpy.ops.object.mode_set(mode="OBJECT")
+
+ for i, (camEyeSpline, camATSpline) in enumerate(zip(eyePoints, atPoints), startIndex):
+ cameraShotObj = self.getNewArmatureObject(f"CS_{csNbr:02}.Camera Shot {i:02}", True, csObj)
+
+ if camEyeSpline.endFrame < camEyeSpline.startFrame + 2 or camATSpline.endFrame < camATSpline.startFrame + 2:
+ print("WARNING: Non-standard end frame")
+
+ cameraShotObj.data.ootCamShotProp.shotStartFrame = camEyeSpline.startFrame
+ cameraShotObj.data.ootCamShotProp.shotCamMode = camMode
+ boneData = [(eyePoint, atPoint) for eyePoint, atPoint in zip(camEyeSpline.entries, camATSpline.entries)]
+ self.setBoneData(cameraShotObj, boneData, csNbr)
+ endIndex = i
+
+ return endIndex + 1
+
+ def create_camera_shots(self, cs_obj: Object, spline_list: list[CutsceneCmdCamSplineList], cs_nbr: int):
+ scale = bpy.context.scene.ootBlenderScale
+ i = 0
+
+ # this is required to be able to change the object mode
+ if bpy.context.mode != "OBJECT":
+ bpy.ops.object.mode_set(mode="OBJECT")
+
+ for splines in spline_list:
+ start_index = i
+
+ for i, spline in enumerate(splines.entries, start_index):
+ new_shot = self.getNewArmatureObject(f"CS_{cs_nbr:02}.Camera Shot {i:02}", True, cs_obj)
+ shot_props = new_shot.data.ootCamShotProp
+ armature_data: Armature = new_shot.data
+
+ shot_props.shot_duration = spline.duration
+ # TODO: move this to the bones?
+ first_entry = spline.entries[0]
+ self.setPropOrCustom(
+ shot_props,
+ "shot_interp_type",
+ Utility.enum_id_to_key(first_entry.at.interp_type, "cs_spline_interp_type"),
+ )
+ self.setPropOrCustom(
+ shot_props,
+ "shot_spline_rel_to",
+ Utility.enum_id_to_key(first_entry.at.relative_to, "cs_spline_rel"),
+ )
+
+ for j, point in enumerate(spline.entries):
+ bpy.ops.object.mode_set(mode="EDIT")
+ boneName = f"CS_{cs_nbr:02}.Camera Point {j:02}"
+ new_edit_bone = armature_data.edit_bones.new(boneName)
+ new_edit_bone.head = getBlenderPosition(point.eye.pos, scale)
+ new_edit_bone.tail = getBlenderPosition(point.at.pos, scale)
+ bpy.ops.object.mode_set(mode="OBJECT")
+ new_bone = armature_data.bones[boneName]
+
+ # using the "AT" (look-at) data since this is what determines where the camera is looking
+ # the "Eye" only sets the location of the camera
+ new_bone.ootCamShotPointProp.shot_point_duration = point.at.duration
+ new_bone.ootCamShotPointProp.shotPointViewAngle = point.misc.viewAngle
+ new_bone.ootCamShotPointProp.shotPointRoll = point.misc.camRoll
+
+ def setPropOrCustom(self, prop, propName: str, value):
+ try:
+ setattr(prop, propName, value)
+ except TypeError:
+ setattr(prop, propName, "Custom")
+ setattr(prop, f"{propName}_custom" if "_" in propName else f"{propName}Custom", value)
+
+ def setSubPropertyData(self, subPropsData: dict[str, str], newSubElem, entry):
+ for key, value in subPropsData.items():
+ if value is not None:
+ if key in custom_values:
+ valueToSet = getattr(entry, value)
+ self.setPropOrCustom(newSubElem, key, valueToSet)
+ else:
+ setattr(newSubElem, key, getattr(entry, value))
+
+ def setPropertyData(self, csProp: "OOTCutsceneProperty", cutscene: Cutscene, propDataList: list[PropertyData]):
+ for data in propDataList:
+ listName = list_type_to_list_prop_member[data.listType]
+ dataList = getattr(cutscene.data, list_type_to_cs_data_member[data.listType])
+ for list in dataList:
+ newElem: "OOTCSListProperty" = csProp.csLists.add()
+ newElem.listType = f"{data.listType}List" if data.listType != "Transition" else data.listType
+
+ list.entries.sort(key=lambda elem: elem.startFrame)
+ for entry in list.entries:
+ name = listName
+ if data.listType in {"StartSeq", "StopSeq", "StartAmbience", "FadeOutSeq"}:
+ name = "seqList"
+
+ newSubElem = getattr(newElem, name).add()
+ newSubElem.startFrame = entry.startFrame
+
+ if data.useEndFrame:
+ newSubElem.endFrame = entry.endFrame
+
+ if data.listType == "Text":
+ self.setPropOrCustom(newSubElem, "textboxType", entry.id)
+ match entry.id:
+ case "Text":
+ newSubElem.textID = f"0x{entry.textId:04X}"
+ self.setPropOrCustom(newSubElem, "csTextType", entry.type)
+ case "None":
+ pass
+ case "OcarinaAction":
+ newSubElem.ocarinaMessageId = f"0x{entry.messageId:04X}"
+ self.setPropOrCustom(newSubElem, "ocarinaAction", entry.ocarinaActionId)
+ case _:
+ raise PluginError("ERROR: Unknown text type!")
+ self.setSubPropertyData(data.subPropsData, newSubElem, entry)
+
+ def setCutsceneData(self, csNumber, use_macros: bool, motion_only: bool):
+ """Creates the cutscene empty objects from the file data"""
+
+ cutsceneList = self.getCutsceneList(use_macros, motion_only)
+
+ if cutsceneList is None:
+ # if it's none then there's no cutscene in the file
+ return csNumber
+
+ if len(cutsceneList) == 0:
+ raise PluginError("ERROR: No cutscene was found.")
+
+ for i, cutscene in enumerate(cutsceneList, csNumber):
+ print(f'Found Cutscene "{cutscene.name}"! Importing...')
+ if is_oot_features():
+ self.validateCameraData(cutscene)
+ csName = f"Cutscene.{cutscene.name}"
+ csObj = self.getNewCutsceneObject(csName, cutscene.frameCount, None)
+ csProp = csObj.ootCutsceneProperty
+ csNumber = i
+
+ self.setActorCueData(csObj, cutscene.data.actorCueList, "Actor", i)
+ self.setActorCueData(csObj, cutscene.data.playerCueList, "Player", i)
+
+ if is_oot_features():
+ if len(cutscene.data.camEyeSplineList) > 0:
+ lastIndex = self.setCameraShotData(
+ csObj, cutscene.data.camEyeSplineList, cutscene.data.camATSplineList, "splineEyeOrAT", 1, i
+ )
+
+ if len(cutscene.data.camEyeSplineRelPlayerList) > 0:
+ lastIndex = self.setCameraShotData(
+ csObj,
+ cutscene.data.camEyeSplineRelPlayerList,
+ cutscene.data.camATSplineRelPlayerList,
+ "splineEyeOrATRelPlayer",
+ lastIndex,
+ i,
+ )
+
+ if len(cutscene.data.camEyeList) > 0:
+ lastIndex = self.setCameraShotData(
+ csObj, cutscene.data.camEyeList, cutscene.data.camATList, "eyeOrAT", lastIndex, i
+ )
+ else:
+ if len(cutscene.data.camSplineList) > 0:
+ self.create_camera_shots(csObj, cutscene.data.camSplineList, i)
+
+ if cutscene.data.destination is not None:
+ csProp.csUseDestination = True
+ csProp.csDestinationStartFrame = cutscene.data.destination.startFrame
+ self.setPropOrCustom(csProp, "csDestination", cutscene.data.destination.id)
+
+ if not is_oot_features() and cutscene.data.give_tatl is not None:
+ csProp.csUseDestination = True
+ csProp.csDestinationStartFrame = cutscene.data.destination.startFrame
+ self.setPropOrCustom(csProp, "cs_give_tatl", cutscene.data.give_tatl.giveTatl)
+
+ propDataList = [
+ PropertyData("Text", {"textboxType": "id"}, True),
+ PropertyData("Misc", {"csMiscType": "type"}, True),
+ PropertyData("Transition", {"transition_type": "type"}, True),
+ PropertyData("LightSettings", {"lightSettingsIndex": "lightSetting"}, False),
+ PropertyData("Time", {"hour": "hour", "minute": "minute"}, False),
+ PropertyData("StartSeq", {"csSeqID": "seqId"}, True),
+ PropertyData("StopSeq", {"csSeqID": "seqId"}, True),
+ PropertyData("StartAmbience", {"csSeqID": "seqId"}, True),
+ PropertyData("FadeOutSeq", {"csSeqPlayer": "seqPlayer"}, True),
+ PropertyData(
+ "Rumble",
+ {
+ "rumbleSourceStrength": "sourceStrength",
+ "rumbleDuration": "duration",
+ "rumbleDecreaseRate": "decreaseRate",
+ "rumble_type": "type",
+ },
+ False,
+ ),
+ ]
+
+ if not is_oot_features():
+ propDataList.extend(
+ [
+ PropertyData("MotionBlur", {"blur_type": "type"}, True),
+ PropertyData(
+ "TransitionGeneral",
+ {
+ "trans_general_type": "type",
+ "trans_color": "rgb",
+ },
+ True,
+ ),
+ PropertyData("CreditsScene", {"credits_scene_type": "type"}, False),
+ PropertyData("ModifySeq", {"mod_seq_type": "type"}, False),
+ PropertyData("FadeOutAmbience", {}, True),
+ ]
+ )
+
+ self.setPropertyData(csProp, cutscene, propDataList)
+
+ # Init camera + preview objects and setup the scene
+ setupCutscene(csObj)
+ bpy.ops.object.select_all(action="DESELECT")
+ print("Success!")
+
+ # ``csNumber`` makes sure there's no duplicates
+ return csNumber + 1
diff --git a/fast64_internal/z64/cutscene/importer/functions.py b/fast64_internal/z64/cutscene/importer/functions.py
new file mode 100644
index 000000000..4a9ccfab4
--- /dev/null
+++ b/fast64_internal/z64/cutscene/importer/functions.py
@@ -0,0 +1,16 @@
+import bpy
+
+from typing import Optional
+from .classes import CutsceneImport
+from ...utility import is_oot_features
+
+
+def importCutsceneData(filePath: Optional[str], sceneData: Optional[str], csName: Optional[str] = None):
+ """Initialises and imports the cutscene data from either a file or the scene data"""
+ # NOTE: ``sceneData`` is the data read when importing a scene
+ csMotionImport = CutsceneImport(filePath, sceneData, csName, not is_oot_features())
+ return csMotionImport.setCutsceneData(
+ bpy.context.scene.ootCSNumber,
+ bpy.context.scene.fast64.oot.hackerFeaturesEnabled or bpy.context.scene.fast64.oot.useDecompFeatures,
+ bpy.context.scene.fast64.oot.exportMotionOnly,
+ )
diff --git a/fast64_internal/oot/cutscene/motion/operators.py b/fast64_internal/z64/cutscene/motion/operators.py
similarity index 96%
rename from fast64_internal/oot/cutscene/motion/operators.py
rename to fast64_internal/z64/cutscene/motion/operators.py
index 8eff5c5aa..ec1643e58 100644
--- a/fast64_internal/oot/cutscene/motion/operators.py
+++ b/fast64_internal/z64/cutscene/motion/operators.py
@@ -1,14 +1,13 @@
import bpy
-from bpy.types import Object, Operator, Context, Armature
+from bpy.types import Object, Operator, Armature, Context
from bpy.utils import register_class, unregister_class
from bpy.props import StringProperty, EnumProperty, BoolProperty
import mathutils
from dataclasses import dataclass
from ....utility import PluginError
-from ...oot_constants import ootData
+from ....game_data import game_data
from ..classes import CutsceneObjectFactory
-from ..constants import ootEnumCSActorCueListCommandType
from ..preview import initFirstFrame, setupCompositorNodes
from .utility import (
setupActorCuePreview,
@@ -26,9 +25,10 @@ def createNewActorCueList(csObj: Object, isPlayer: bool):
"""Creates a new Actor or Player Cue List and adds one basic cue and the dummy one"""
objFactory = CutsceneObjectFactory()
playerOrActor = "Player" if isPlayer else "Actor"
- newActorCueListObj = objFactory.getNewActorCueListObject(f"New {playerOrActor} Cue List", "actor_cue_0_0", None)
index, csPrefix = getNameInformations(csObj, f"{playerOrActor} Cue List", None)
- newActorCueListObj.name = f"{csPrefix}.{playerOrActor} Cue List {index:02}"
+ newActorCueListObj = objFactory.getNewActorCueListObject(
+ f"{csPrefix}.{playerOrActor} Cue List {index:02}", "actor_cue_0_0", None
+ )
# add a basic actor cue and the dummy one
for i in range(2):
@@ -418,7 +418,7 @@ class OOT_SearchActorCueCmdTypeEnumOperator(Operator):
bl_property = "commandType"
bl_options = {"REGISTER", "UNDO"}
- commandType: EnumProperty(items=ootEnumCSActorCueListCommandType, default="actor_cue_0_0")
+ commandType: EnumProperty(items=lambda self, context: game_data.z64.get_enum("actor_cue_list_cmd_type"), default=1)
objName: StringProperty()
def execute(self, context):
@@ -440,7 +440,7 @@ class OOT_SearchPlayerCueIdEnumOperator(Operator):
bl_property = "playerCueID"
bl_options = {"REGISTER", "UNDO"}
- playerCueID: EnumProperty(items=ootData.enumData.ootEnumCsPlayerCueId, default="cueid_none")
+ playerCueID: EnumProperty(items=lambda self, context: game_data.z64.get_enum("playerCueID"), default=1)
objName: StringProperty()
def execute(self, context):
diff --git a/fast64_internal/oot/cutscene/motion/panels.py b/fast64_internal/z64/cutscene/motion/panels.py
similarity index 95%
rename from fast64_internal/oot/cutscene/motion/panels.py
rename to fast64_internal/z64/cutscene/motion/panels.py
index a2018fed4..68de34d40 100644
--- a/fast64_internal/oot/cutscene/motion/panels.py
+++ b/fast64_internal/z64/cutscene/motion/panels.py
@@ -1,9 +1,9 @@
from bpy.utils import register_class, unregister_class
-from ....panels import OOT_Panel
+from ....panels import Z64_Panel
from .properties import CutsceneCmdCameraShotProperty, CutsceneCmdCameraShotPointProperty
-class OOT_CSMotionCameraShotPanel(OOT_Panel):
+class OOT_CSMotionCameraShotPanel(Z64_Panel):
bl_label = "Cutscene Motion Camera Shot Controls"
bl_idname = "OOT_PT_camera_shot_panel"
bl_space_type = "PROPERTIES"
diff --git a/fast64_internal/oot/cutscene/motion/preview.py b/fast64_internal/z64/cutscene/motion/preview.py
similarity index 100%
rename from fast64_internal/oot/cutscene/motion/preview.py
rename to fast64_internal/z64/cutscene/motion/preview.py
diff --git a/fast64_internal/oot/cutscene/motion/properties.py b/fast64_internal/z64/cutscene/motion/properties.py
similarity index 77%
rename from fast64_internal/oot/cutscene/motion/properties.py
rename to fast64_internal/z64/cutscene/motion/properties.py
index 7251b3c03..245658d41 100644
--- a/fast64_internal/oot/cutscene/motion/properties.py
+++ b/fast64_internal/z64/cutscene/motion/properties.py
@@ -3,10 +3,11 @@
from bpy.types import PropertyGroup, Object, UILayout, Armature, Bone, Scene, EditBone
from bpy.props import IntProperty, StringProperty, PointerProperty, EnumProperty, FloatProperty
from bpy.utils import register_class, unregister_class
-from ...oot_upgrade import upgradeCutsceneMotion
-from ...oot_utility import getEnumName
-from ...oot_constants import ootData
-from ..constants import ootEnumCSMotionCamMode, ootEnumCSActorCueListCommandType
+from ....utility import prop_split
+from ...upgrade import upgradeCutsceneMotion
+from ...utility import getEnumName, is_oot_features
+from ....game_data import game_data
+from ..constants import ootEnumCSMotionCamMode
from .operators import (
CutsceneCmdAddActorCue,
@@ -33,7 +34,9 @@ def getNextCuesStartFrame(self):
class CutsceneCmdActorCueListProperty(PropertyGroup):
commandType: EnumProperty(
- items=ootEnumCSActorCueListCommandType, name="CS Actor Cue Command Type", default="actor_cue_0_0"
+ items=lambda self, context: game_data.z64.get_enum("actor_cue_list_cmd_type"),
+ name="CS Actor Cue Command Type",
+ default=1,
)
commandTypeCustom: StringProperty(name="CS Actor Cue Command Type Custom")
actorCueListToPreview: PointerProperty(
@@ -58,7 +61,7 @@ def draw_props(self, layout: UILayout, isPreview: bool, labelPrefix: str, objNam
OOT_SearchActorCueCmdTypeEnumOperator.bl_idname, icon="VIEWZOOM", text="Command Type:"
)
searchOp.objName = objName
- searchBox.label(text=getEnumName(ootEnumCSActorCueListCommandType, self.commandType))
+ searchBox.label(text=getEnumName(game_data.z64.get_enum("actor_cue_list_cmd_type"), self.commandType))
if self.commandType == "Custom":
split = box.split(factor=0.5)
@@ -87,7 +90,7 @@ class CutsceneCmdActorCueProperty(PropertyGroup):
get=lambda self: getNextCuesStartFrame(self),
)
- playerCueID: EnumProperty(items=ootData.enumData.ootEnumCsPlayerCueId, default="cueid_none")
+ playerCueID: EnumProperty(items=lambda self, context: game_data.z64.get_enum("playerCueID"), default=1)
cueActionID: StringProperty(
name="Action ID", default="0x0001", description="Actor action. Meaning is unique for each different actor."
)
@@ -117,7 +120,7 @@ def draw_props(self, layout: UILayout, labelPrefix: str, isDummy: bool, objName:
split = box.split(factor=0.5)
searchOp = split.operator(OOT_SearchPlayerCueIdEnumOperator.bl_idname, icon="VIEWZOOM", text=label)
searchOp.objName = objName
- split.label(text=getEnumName(ootData.enumData.ootEnumCsPlayerCueId, self.playerCueID))
+ split.label(text=getEnumName(game_data.z64.get_enum("playerCueID"), self.playerCueID))
if not isPlayer or self.playerCueID == "Custom":
split = box.split(factor=0.5)
@@ -147,23 +150,51 @@ class CutsceneCmdCameraShotProperty(PropertyGroup):
default="splineEyeOrAT",
)
+ shot_duration: IntProperty(min=0, default=120)
+ shot_spline_rel_to: EnumProperty(items=lambda self, context: game_data.z64.get_enum("spline_rel_to"), default=1)
+ shot_spline_rel_to_custom: StringProperty(default="CS_CAM_REL_CUSTOM")
+
+ shot_interp_type: EnumProperty(
+ items=lambda self, context: game_data.z64.get_enum("spline_interp_type"),
+ name="Interpolation Type",
+ description="values 1-3 only uses a single point from the cmd, values 4-5 uses multiple points from the cmd, value 6 only uses a single point from the cmd",
+ default=5,
+ )
+ shot_interp_type_custom: StringProperty(default="CS_CAM_INTERP_CUSTOM")
+
def getEndFrame(self, camShotObj: Object = None):
if camShotObj is None:
camShotObj = bpy.context.view_layer.objects.active
if camShotObj.type == "ARMATURE":
+ shot_frame = self.shotStartFrame if game_data.z64.is_oot() else self.shot_duration
boneFrameList: list[int] = [bone.ootCamShotPointProp.shotPointFrame for bone in camShotObj.data.bones]
# "fake" eye end frame
- return self.shotStartFrame + max(2, sum(frame for frame in boneFrameList)) + 1
+ return shot_frame + max(2, sum(frame for frame in boneFrameList)) + 1
return -1
def draw_props(self, layout: UILayout, label: str):
+ # update manually since we don't use shared enum mode
+ game_data.z64.update(bpy.context, None)
+
box = layout.box()
box.label(text=label)
- split = box.split(factor=0.5)
- split.prop(self, "shotStartFrame")
- split.prop(self, "shotEndFrame")
- box.row().prop(self, "shotCamMode", expand=True)
+ if game_data.z64.is_oot():
+ split = box.split(factor=0.5)
+ split.prop(self, "shotStartFrame")
+ split.prop(self, "shotEndFrame")
+ box.row().prop(self, "shotCamMode", expand=True)
+ else:
+ layout_shot = box.column()
+ prop_split(layout_shot, self, "shot_duration", "Duration")
+ prop_split(layout_shot, self, "shot_spline_rel_to", "Camera Relative To")
+ if self.shot_spline_rel_to == "Custom":
+ prop_split(layout_shot, self, "shot_spline_rel_to_custom", "")
+
+ prop_split(layout_shot, self, "shot_interp_type", "Camera Interpolation Type")
+ if self.shot_interp_type == "Custom":
+ prop_split(layout_shot, self, "shot_interp_type_custom", "")
+ prop_split(layout_shot, self, "shotEndFrame", "End Frame")
box.operator(CutsceneCmdAddBone.bl_idname)
@@ -197,6 +228,8 @@ class CutsceneCmdCameraShotPointProperty(PropertyGroup):
set=lambda self, value: self.setValue(value, "roll"),
)
+ shot_point_duration: IntProperty(min=0, default=30, name="Duration")
+
# internal usage only
frame: IntProperty(default=30, min=0)
viewAngle: FloatProperty(default=60.0, min=0.01, max=179.99)
@@ -227,7 +260,8 @@ def draw_props(self, layout: UILayout):
box = layout.box()
box.label(text="Bone / Key point:")
row = box.row()
- for propName in ["shotPointFrame", "shotPointViewAngle", "shotPointRoll"]:
+ first_prop = "shotPointFrame" if is_oot_features() else "shot_point_duration"
+ for propName in [first_prop, "shotPointViewAngle", "shotPointRoll"]:
row.prop(self, propName)
row = box.row()
diff --git a/fast64_internal/oot/cutscene/motion/utility.py b/fast64_internal/z64/cutscene/motion/utility.py
similarity index 96%
rename from fast64_internal/oot/cutscene/motion/utility.py
rename to fast64_internal/z64/cutscene/motion/utility.py
index d78fa8b74..41a28cdc7 100644
--- a/fast64_internal/oot/cutscene/motion/utility.py
+++ b/fast64_internal/z64/cutscene/motion/utility.py
@@ -4,7 +4,7 @@
from bpy.types import Object, Bone, Context, EditBone, Armature
from mathutils import Vector
from ....utility import yUpToZUp
-from ...oot_utility import ootParseRotation
+from ...utility import ootParseRotation, twos_complement
class BoneData:
@@ -84,14 +84,17 @@ def getBlenderPosition(pos: list[int], scale: int):
return [float(pos[0]) / scale, -float(pos[2]) / scale, float(pos[1]) / scale]
-def getInteger(number: str):
+def getInteger(number: str, is_mm: bool = False):
"""Returns an int number (handles properly negative hex numbers)"""
if number.startswith("0x"):
- number = number.removeprefix("0x")
+ if is_mm:
+ return twos_complement(number, 16)
+ else:
+ number = number.removeprefix("0x")
- # ``"0" * (8 - len(number)`` adds the missing zeroes (if necessary) to have a 8 digit hex number
- return unpack("!i", bytes.fromhex("0" * (8 - len(number)) + number))[0]
+ # ``"0" * (8 - len(number)`` adds the missing zeroes (if necessary) to have a 8 digit hex number
+ return unpack("!i", bytes.fromhex("0" * (8 - len(number)) + number))[0]
else:
return int(number)
diff --git a/fast64_internal/oot/cutscene/operators.py b/fast64_internal/z64/cutscene/operators.py
similarity index 97%
rename from fast64_internal/oot/cutscene/operators.py
rename to fast64_internal/z64/cutscene/operators.py
index d36ed2c82..f48e961a5 100644
--- a/fast64_internal/oot/cutscene/operators.py
+++ b/fast64_internal/z64/cutscene/operators.py
@@ -9,8 +9,8 @@
from bpy.utils import register_class, unregister_class
from ...utility import CData, PluginError, writeCData, raisePluginError
from ..collection_utility import getCollection
-from ..oot_constants import ootData
-from .constants import ootEnumCSTextboxType, ootEnumCSListType
+from ...game_data import game_data
+from .constants import ootEnumCSTextboxType
from .importer import importCutsceneData
from .exporter import getNewCutsceneExport
from ..exporter.cutscene import Cutscene
@@ -144,7 +144,7 @@ class OOTCSListAdd(Operator):
bl_options = {"REGISTER", "UNDO"}
collectionType: StringProperty()
- listType: EnumProperty(items=ootEnumCSListType)
+ listType: EnumProperty(items=lambda self, context: game_data.z64.get_enum("cs_list_type"))
objName: StringProperty()
def execute(self, context):
@@ -279,7 +279,7 @@ class OOT_SearchCSDestinationEnumOperator(Operator):
bl_property = "csDestination"
bl_options = {"REGISTER", "UNDO"}
- csDestination: EnumProperty(items=ootData.enumData.ootEnumCsDestination, default="cutscene_map_ganon_horse")
+ csDestination: EnumProperty(items=lambda self, context: game_data.z64.get_enum("csDestination"), default=1)
objName: StringProperty()
def execute(self, context):
@@ -301,7 +301,7 @@ class OOT_SearchCSSeqOperator(Operator):
bl_property = "seqId"
bl_options = {"REGISTER", "UNDO"}
- seqId: EnumProperty(items=ootData.enumData.ootEnumSeqId, default="general_sfx")
+ seqId: EnumProperty(items=lambda self, context: game_data.z64.get_enum("seqId"), default=1)
itemIndex: IntProperty()
listType: StringProperty()
diff --git a/fast64_internal/oot/cutscene/panels.py b/fast64_internal/z64/cutscene/panels.py
similarity index 77%
rename from fast64_internal/oot/cutscene/panels.py
rename to fast64_internal/z64/cutscene/panels.py
index 5cf84a908..522e737d3 100644
--- a/fast64_internal/oot/cutscene/panels.py
+++ b/fast64_internal/z64/cutscene/panels.py
@@ -2,24 +2,24 @@
from bpy.types import Scene
from bpy.props import BoolProperty
from ...utility import prop_split
-from ...panels import OOT_Panel
+from ...panels import Z64_Panel
+from ..utility import is_oot_features
from .operators import OOT_ExportCutscene, OOT_ExportAllCutscenes, OOT_ImportCutscene
-class OoT_PreviewSettingsPanel(OOT_Panel):
- bl_idname = "OOT_PT_preview_settings"
- bl_label = "OOT CS Preview Settings"
+class OoT_PreviewSettingsPanel(Z64_Panel):
+ bl_idname = "Z64_PT_preview_settings"
+ bl_label = "CS Preview Settings"
def draw(self, context):
context.scene.ootPreviewSettingsProperty.draw_props(self.layout)
-class OOT_CutscenePanel(OOT_Panel):
- bl_idname = "OOT_PT_export_cutscene"
- bl_label = "OOT Cutscene Exporter"
+class OOT_CutscenePanel(Z64_Panel):
+ bl_idname = "Z64_PT_export_cutscene"
+ bl_label = "Cutscenes"
bl_space_type = "VIEW_3D"
bl_region_type = "UI"
- bl_category = "OOT"
def draw(self, context):
layout = self.layout
@@ -27,6 +27,10 @@ def draw(self, context):
exportBox = layout.box()
exportBox.label(text="Cutscene Exporter")
+ exportBox.enabled = is_oot_features()
+ if not exportBox.enabled:
+ layout.label(text="Export not implemented yet.")
+
prop_split(exportBox, context.scene, "ootCutsceneExportPath", "Export To")
activeObj = context.view_layer.objects.active
@@ -48,6 +52,11 @@ def draw(self, context):
importBox = layout.box()
importBox.label(text="Cutscene Importer")
+
+ importBox.enabled = is_oot_features()
+ if not importBox.enabled:
+ layout.label(text="Import not implemented yet.")
+
prop_split(importBox, context.scene, "ootCSImportName", "Import")
prop_split(importBox, context.scene, "ootCutsceneImportPath", "From")
diff --git a/fast64_internal/oot/cutscene/preview.py b/fast64_internal/z64/cutscene/preview.py
similarity index 71%
rename from fast64_internal/oot/cutscene/preview.py
rename to fast64_internal/z64/cutscene/preview.py
index b3a4f8c40..ea9efbcd1 100644
--- a/fast64_internal/oot/cutscene/preview.py
+++ b/fast64_internal/z64/cutscene/preview.py
@@ -5,6 +5,7 @@
from bpy.types import Scene, Object, Node
from bpy.app.handlers import persistent
from ...utility import gammaInverse, hexOrDecInt
+from ..utility import is_oot_features
from .motion.utility import getCutsceneCamera
if TYPE_CHECKING:
@@ -66,6 +67,7 @@ def setupCompositorNodes():
# get the existing nodes
nodeTree = bpy.context.scene.node_tree
nodeRenderLayer = nodeComposite = nodeRGBTrans = nodeAlphaOver = nodeRGBMisc = nodeMixRGBMisc = None
+ node_motion_blur = None
for node in nodeTree.nodes.values():
if node.type == "R_LAYERS":
nodeRenderLayer = node
@@ -79,27 +81,34 @@ def setupCompositorNodes():
nodeAlphaOver = node
if node.label == "CSMisc_MixRGB":
nodeMixRGBMisc = node
+ if node.label == "CSMotionBlur":
+ node_motion_blur = node
# create or set the data of each nodes
- nodeRenderLayer = getNode(nodeRenderLayer, "CompositorNodeRLayers", "CSPreview_RenderLayer", (-500, 0))
+ nodeRenderLayer = getNode(nodeRenderLayer, "CompositorNodeRLayers", "CSPreview_RenderLayer", (-300, 0))
nodeRGBMisc = getNode(nodeRGBMisc, "CompositorNodeRGB", "CSMisc_RGB", (-200, -200))
nodeRGBTrans = getNode(nodeRGBTrans, "CompositorNodeRGB", "CSTrans_RGB", (0, -200))
nodeMixRGBMisc = getNode(nodeMixRGBMisc, "CompositorNodeMixRGB", "CSMisc_MixRGB", (0, 0))
nodeAlphaOver = getNode(nodeAlphaOver, "CompositorNodeAlphaOver", "CSPreview_AlphaOver", (200, 0))
- nodeComposite = getNode(nodeComposite, "CompositorNodeComposite", "CSPreview_Composite", (400, 0))
+ node_motion_blur = getNode(node_motion_blur, "CompositorNodeDBlur", "CSMotionBlur", (400, 0))
+ nodeComposite = getNode(nodeComposite, "CompositorNodeComposite", "CSPreview_Composite", (600, 0))
# link the nodes together
nodeTree.links.new(nodeMixRGBMisc.inputs[1], nodeRenderLayer.outputs[0])
nodeTree.links.new(nodeMixRGBMisc.inputs[2], nodeRGBMisc.outputs[0])
nodeTree.links.new(nodeAlphaOver.inputs[1], nodeMixRGBMisc.outputs[0])
nodeTree.links.new(nodeAlphaOver.inputs[2], nodeRGBTrans.outputs[0])
- nodeTree.links.new(nodeComposite.inputs[0], nodeAlphaOver.outputs[0])
+ nodeTree.links.new(node_motion_blur.inputs[0], nodeAlphaOver.outputs[0])
+ nodeTree.links.new(nodeComposite.inputs[0], node_motion_blur.outputs[0])
# misc settings
nodeMixRGBMisc.use_alpha = True
nodeMixRGBMisc.blend_type = "COLOR"
bpy.context.scene.ootPreviewSettingsProperty.ootCSPreviewNodesReady = True
+ # blur settings
+ node_motion_blur.iterations = 1
+
def initFirstFrame(csObj: Object, useNodeFeatures: bool, defaultCam: Object):
# set default values for frame 0
@@ -107,6 +116,7 @@ def initFirstFrame(csObj: Object, useNodeFeatures: bool, defaultCam: Object):
color = [0.0, 0.0, 0.0, 0.0]
bpy.context.scene.node_tree.nodes["CSTrans_RGB"].outputs[0].default_value = color
bpy.context.scene.node_tree.nodes["CSMisc_RGB"].outputs[0].default_value = color
+ bpy.context.scene.node_tree.nodes["CSMotionBlur"].zoom = 0.0
csObj.ootCutsceneProperty.preview.trigger = False
csObj.ootCutsceneProperty.preview.isFixedCamSet = False
if defaultCam is not None:
@@ -123,6 +133,8 @@ def processCurrentFrame(csObj: Object, curFrame: float, useNodeFeatures: bool, c
initFirstFrame(csObj, useNodeFeatures, cameraObjects[1])
if useNodeFeatures:
+ previewProp = csObj.ootCutsceneProperty.preview
+
for transitionCmd in previewProp.transitionList:
startFrame = transitionCmd.startFrame
endFrame = transitionCmd.endFrame
@@ -155,13 +167,18 @@ def processCurrentFrame(csObj: Object, curFrame: float, useNodeFeatures: bool, c
else:
alpha = (1.0 - lerp) * linear255
- if "half" in transitionCmd.type:
+ if transitionCmd.type == "gray_to_black":
+ color[0] = color[1] = color[2] = (1.0 - lerp) * linear160
+ alpha = 1.0
+ elif transitionCmd.type == "black_to_gray":
+ color[0] = color[1] = color[2] = lerp * linear160
+ alpha = 1.0
+ elif "half" in transitionCmd.type:
if "_in_" in transitionCmd.type:
alpha = linear255 - ((1.0 - lerp) * linear155)
else:
alpha = linear255 - (linear155 * lerp)
-
- if "gray_" in transitionCmd.type or previewProp.trigger:
+ elif "gray_" in transitionCmd.type or previewProp.trigger:
color[0] = color[1] = color[2] = linear160 * alpha
elif "red_" in transitionCmd.type:
color[0] = linear255 * alpha
@@ -173,6 +190,59 @@ def processCurrentFrame(csObj: Object, curFrame: float, useNodeFeatures: bool, c
color[3] = alpha
bpy.context.scene.node_tree.nodes["CSTrans_RGB"].outputs[0].default_value = color
+ for trans_general_cmd in previewProp.transition_general_list:
+ startFrame = trans_general_cmd.startFrame
+ endFrame = trans_general_cmd.endFrame
+ frameCur = curFrame
+
+ if is_oot_features():
+ bpy.context.scene.node_tree.nodes["CSTrans_RGB"].outputs[0].default_value = (0.0, 0.0, 0.0, 0.0)
+ break
+
+ if trans_general_cmd.type == "Unknown":
+ print("ERROR: Unknown command!")
+
+ if frameCur >= startFrame and endFrame >= frameCur:
+ color = [0.0, 0.0, 0.0, 0.0]
+ lerp = getLerp(endFrame, startFrame, frameCur)
+ linear255 = getColor(255.0)
+
+ if trans_general_cmd.type in {"trans_general_fill_in", "trans_general_fill_out"}:
+ if trans_general_cmd.type == "trans_general_fill_in":
+ alpha = linear255 * lerp
+ else:
+ alpha = (1.0 - lerp) * linear255
+
+ color[0] = trans_general_cmd.color[0] * alpha
+ color[1] = trans_general_cmd.color[1] * alpha
+ color[2] = trans_general_cmd.color[2] * alpha
+ color[3] = alpha
+ bpy.context.scene.node_tree.nodes["CSTrans_RGB"].outputs[0].default_value = color
+
+ for blur_cmd in previewProp.motion_blur_list:
+ startFrame = blur_cmd.startFrame
+ endFrame = blur_cmd.endFrame
+ frameCur = curFrame
+
+ if is_oot_features():
+ bpy.context.scene.node_tree.nodes["CSTrans_RGB"].outputs[0].default_value = (0.0, 0.0, 0.0, 0.0)
+ break
+
+ if blur_cmd.type == "Unknown":
+ print("ERROR: Unknown command!")
+
+ if frameCur >= startFrame and frameCur <= endFrame:
+ if blur_cmd.type == "motion_blur_enable":
+ bpy.context.scene.node_tree.nodes["CSMotionBlur"].zoom = 0.180
+ previewProp.blur_reinit = False
+ elif blur_cmd.type == "motion_blur_disable":
+ lerp = getLerp(endFrame, startFrame, frameCur)
+
+ if lerp >= 0.9:
+ bpy.context.scene.node_tree.nodes["CSMotionBlur"].zoom = 0.0
+ else:
+ bpy.context.scene.node_tree.nodes["CSMotionBlur"].zoom = (1.0 - lerp) * 0.18
+
for miscCmd in previewProp.miscList:
startFrame = miscCmd.startFrame
endFrame = miscCmd.endFrame
@@ -260,18 +330,34 @@ def cutscenePreviewFrameHandler(scene: Scene):
# set preview properties
previewProp.miscList.clear()
previewProp.transitionList.clear()
+ previewProp.motion_blur_list.clear()
+ previewProp.transition_general_list.clear()
for item in csObj.ootCutsceneProperty.csLists:
if item.listType == "Transition":
- newProp = previewProp.transitionList.add()
- newProp.startFrame = item.transitionStartFrame
- newProp.endFrame = item.transitionEndFrame
- newProp.type = item.transitionType
+ for trans_entry in item.transition_list:
+ newProp = previewProp.transitionList.add()
+ newProp.startFrame = trans_entry.startFrame
+ newProp.endFrame = trans_entry.endFrame
+ newProp.type = trans_entry.transition_type
elif item.listType == "MiscList":
for miscEntry in item.miscList:
newProp = previewProp.miscList.add()
newProp.startFrame = miscEntry.startFrame
newProp.endFrame = miscEntry.endFrame
newProp.type = miscEntry.csMiscType
+ elif item.listType == "MotionBlurList":
+ for blur_entry in item.motion_blur_list:
+ newProp = previewProp.motion_blur_list.add()
+ newProp.startFrame = blur_entry.startFrame
+ newProp.endFrame = blur_entry.endFrame
+ newProp.type = blur_entry.blur_type
+ elif item.listType == "TransitionGeneralList":
+ for trans_general_entry in item.trans_general_list:
+ newProp = previewProp.transition_general_list.add()
+ newProp.startFrame = trans_general_entry.startFrame
+ newProp.endFrame = trans_general_entry.endFrame
+ newProp.type = trans_general_entry.trans_general_type
+ newProp.color = trans_general_entry.trans_color
# execute the main preview logic
curFrame = bpy.context.scene.frame_current
diff --git a/fast64_internal/oot/cutscene/properties.py b/fast64_internal/z64/cutscene/properties.py
similarity index 59%
rename from fast64_internal/oot/cutscene/properties.py
rename to fast64_internal/z64/cutscene/properties.py
index 9511dd32a..81c2fc019 100644
--- a/fast64_internal/oot/cutscene/properties.py
+++ b/fast64_internal/z64/cutscene/properties.py
@@ -1,11 +1,21 @@
+import bpy
+
from bpy.types import PropertyGroup, Object, UILayout, Scene, Context
-from bpy.props import StringProperty, EnumProperty, IntProperty, BoolProperty, CollectionProperty, PointerProperty
+from bpy.props import (
+ StringProperty,
+ EnumProperty,
+ IntProperty,
+ BoolProperty,
+ CollectionProperty,
+ PointerProperty,
+ FloatVectorProperty,
+)
from bpy.utils import register_class, unregister_class
from ...utility import PluginError, prop_split
from ..collection_utility import OOTCollectionAdd, drawCollectionOps
-from ..oot_utility import getEnumName
-from ..oot_constants import ootData
-from ..oot_upgrade import upgradeCutsceneSubProps, upgradeCSListProps, upgradeCutsceneProperty
+from ..utility import getEnumName, is_oot_features
+from ...game_data import game_data
+from ..upgrade import upgradeCutsceneSubProps, upgradeCSListProps, upgradeCutsceneProperty
from .operators import OOTCSTextAdd, OOT_SearchCSDestinationEnumOperator, OOTCSListAdd, OOT_SearchCSSeqOperator
from .motion.preview import previewFrameHandler
from .motion.utility import getCutsceneCamera
@@ -19,10 +29,10 @@
from .constants import (
ootEnumCSTextboxType,
- ootEnumCSListType,
ootEnumCSTextboxTypeIcons,
ootCSSubPropToName,
csListTypeToIcon,
+ custom_values,
)
@@ -67,6 +77,9 @@ def draw_props(
drawCollectionOps(box, cmdIndex, collectionType + "." + self.attrName, listIndex, objName)
for p in self.subprops:
+ if game_data.z64.is_oot() and p == "rumble_type" and not bpy.context.scene.fast64.oot.mm_features:
+ continue
+
if self.filterProp(p, listProp):
name = self.filterName(p, listProp)
displayName = ootCSSubPropToName[name]
@@ -82,18 +95,11 @@ def draw_props(
seqOp.itemIndex = cmdIndex
seqOp.listType = listProp.listType
- customValues = [
- "csMiscType",
- "csTextType",
- "ocarinaAction",
- "csSeqID",
- "csSeqPlayer",
- ]
value = getattr(self, p)
- if name in customValues and value == "Custom":
- prop_split(box, self, f"{name}Custom", f"{displayName} Custom")
+ if name in custom_values and value == "Custom":
+ prop_split(box, self, f"{name}_custom" if "_" in p else f"{name}Custom", f"{displayName} Custom")
- if name == "csTextType" and value != "choice":
+ if is_oot_features() and name == "csTextType" and value != "choice":
break
@@ -114,13 +120,17 @@ class OOTCSTextProperty(OOTCutsceneCommon, PropertyGroup):
# subprops
textID: StringProperty(name="", default="0x0000")
ocarinaAction: EnumProperty(
- name="Ocarina Action", items=ootData.enumData.ootEnumOcarinaSongActionId, default="teach_minuet"
+ name="Ocarina Action",
+ items=lambda self, context: game_data.z64.get_enum("ocarinaAction"),
+ default=1,
)
ocarinaActionCustom: StringProperty(default="OCARINA_ACTION_CUSTOM")
- topOptionTextID: StringProperty(name="", default="0x0000")
- bottomOptionTextID: StringProperty(name="", default="0x0000")
+ topOptionTextID: StringProperty(name="", default="0xFFFF")
+ bottomOptionTextID: StringProperty(name="", default="0xFFFF")
ocarinaMessageId: StringProperty(name="", default="0x0000")
- csTextType: EnumProperty(name="Text Type", items=ootData.enumData.ootEnumCsTextType, default="normal")
+ csTextType: EnumProperty(
+ name="Text Type", items=lambda self, context: game_data.z64.get_enum("csTextType"), default=1
+ )
csTextTypeCustom: StringProperty(default="CS_TEXT_CUSTOM")
def getName(self):
@@ -153,15 +163,22 @@ class OOTCSTimeProperty(OOTCutsceneCommon, PropertyGroup):
class OOTCSSeqProperty(OOTCutsceneCommon, PropertyGroup):
attrName = "seqList"
subprops = ["csSeqID", "startFrame", "endFrame"]
- csSeqID: EnumProperty(name="Seq ID", items=ootData.enumData.ootEnumSeqId, default="general_sfx")
+ csSeqID: EnumProperty(name="Seq ID", items=lambda self, context: game_data.z64.get_enum("seqId"), default=1)
csSeqIDCustom: StringProperty(default="NA_BGM_CUSTOM")
csSeqPlayer: EnumProperty(
- name="Seq Player", items=ootData.enumData.ootEnumCsFadeOutSeqPlayer, default="fade_out_fanfare"
+ name="Seq Player", items=lambda self, context: game_data.z64.get_enum("csSeqPlayer"), default=1
)
csSeqPlayerCustom: StringProperty(default="CS_FADE_OUT_CUSTOM")
def filterProp(self, name, listProp):
- return name != "endFrame" or listProp.listType == "FadeOutSeqList"
+ if game_data.z64.is_mm():
+ types = {"FadeOutSeqList", "StopSeqList", "StartAmbienceList", "FadeOutAmbienceList"}
+ else:
+ types = {"FadeOutSeqList"}
+ if "Ambience" in listProp.listType and listProp.listType in types:
+ return name != "csSeqID"
+ else:
+ return name != "endFrame" or listProp.listType in types
def filterName(self, name, listProp):
if name == "csSeqID" and listProp.listType == "FadeOutSeqList":
@@ -172,40 +189,98 @@ def filterName(self, name, listProp):
class OOTCSMiscProperty(OOTCutsceneCommon, PropertyGroup):
attrName = "miscList"
subprops = ["csMiscType", "startFrame", "endFrame"]
- csMiscType: EnumProperty(name="Type", items=ootData.enumData.ootEnumCsMiscType, default="rain")
+ csMiscType: EnumProperty(name="Type", items=lambda self, context: game_data.z64.get_enum("csMiscType"), default=1)
csMiscTypeCustom: StringProperty(default="CS_MISC_CUSTOM")
class OOTCSRumbleProperty(OOTCutsceneCommon, PropertyGroup):
attrName = "rumbleList"
- subprops = ["startFrame", "rumbleSourceStrength", "rumbleDuration", "rumbleDecreaseRate"]
+ subprops = ["rumble_type", "startFrame", "rumbleSourceStrength", "rumbleDuration", "rumbleDecreaseRate"]
# those variables are unsigned chars in decomp
# see https://github.com/zeldaret/oot/blob/542012efa68d110d6b631f9d149f6e5f4e68cc8e/src/code/z_rumble.c#L58-L77
rumbleSourceStrength: IntProperty(name="", default=0, min=0, max=255)
rumbleDuration: IntProperty(name="", default=0, min=0, max=255)
rumbleDecreaseRate: IntProperty(name="", default=0, min=0, max=255)
+ rumble_type: EnumProperty(name="", items=game_data.z64.enums.enum_cs_rumble_type, default=1)
+ rumble_type_custom: StringProperty()
+
+
+class OOTCSTransitionProperty(OOTCutsceneCommon, PropertyGroup):
+ attrName = "transition_list"
+ subprops = ["transition_type", "startFrame", "endFrame"]
+
+ transition_type: EnumProperty(
+ name="", items=lambda self, context: game_data.z64.get_enum("transitionType"), default=1
+ )
+ transition_type_custom: StringProperty("CS_TRANS_CUSTOM")
+
+
+class OOTCSMotionBlurProperty(OOTCutsceneCommon, PropertyGroup):
+ attrName = "motion_blur_list"
+ subprops = ["blur_type", "startFrame", "endFrame"]
+
+ blur_type: EnumProperty(name="", items=lambda self, context: game_data.z64.get_enum("blur_type"), default=1)
+ blur_type_custom: StringProperty("CS_TRANS_CUSTOM")
+
+
+class OOTCSTransitionGeneralProperty(OOTCutsceneCommon, PropertyGroup):
+ attrName = "trans_general_list"
+ subprops = ["trans_general_type", "startFrame", "endFrame", "trans_color"]
+
+ trans_general_type: EnumProperty(
+ name="", items=lambda self, context: game_data.z64.get_enum("trans_general"), default=1
+ )
+ trans_general_type_custom: StringProperty("CS_TRANS_GENERAL_CUSTOM")
+
+ trans_color: FloatVectorProperty(
+ name="Color",
+ subtype="COLOR",
+ size=4,
+ min=0,
+ max=1,
+ default=(1, 1, 1, 1),
+ )
+
+
+class OOTCSChooseCreditsSceneProperty(OOTCutsceneCommon, PropertyGroup):
+ attrName = "credits_scene_list"
+ subprops = ["credits_scene_type", "startFrame"]
+
+ credits_scene_type: EnumProperty(
+ name="", items=lambda self, context: game_data.z64.get_enum("credits_scene_type"), default=1
+ )
+ credits_scene_type_custom: StringProperty("CS_CREDITS_CUSTOM")
+
+
+class OOTCSModifySeqProperty(OOTCutsceneCommon, PropertyGroup):
+ attrName = "mod_seq_list"
+ subprops = ["mod_seq_type", "startFrame"]
+
+ mod_seq_type: EnumProperty(name="", items=lambda self, context: game_data.z64.get_enum("mod_seq_type"), default=1)
+ mod_seq_type_custom: StringProperty("CS_MOD_SEQ_CUSTOM")
class OOTCSListProperty(PropertyGroup):
expandTab: BoolProperty(default=True)
- listType: EnumProperty(items=ootEnumCSListType)
+ listType: EnumProperty(items=lambda self, context: game_data.z64.get_enum("cs_list_type"))
textList: CollectionProperty(type=OOTCSTextProperty)
lightSettingsList: CollectionProperty(type=OOTCSLightSettingsProperty)
timeList: CollectionProperty(type=OOTCSTimeProperty)
seqList: CollectionProperty(type=OOTCSSeqProperty)
miscList: CollectionProperty(type=OOTCSMiscProperty)
rumbleList: CollectionProperty(type=OOTCSRumbleProperty)
-
- transitionType: EnumProperty(items=ootData.enumData.ootEnumCsTransitionType, default="gray_fill_in")
- transitionTypeCustom: StringProperty(default="CS_TRANS_CUSTOM")
- transitionStartFrame: IntProperty(name="", default=0, min=0)
- transitionEndFrame: IntProperty(name="", default=1, min=0)
+ transition_list: CollectionProperty(type=OOTCSTransitionProperty)
+ motion_blur_list: CollectionProperty(type=OOTCSMotionBlurProperty)
+ trans_general_list: CollectionProperty(type=OOTCSTransitionGeneralProperty)
+ credits_scene_list: CollectionProperty(type=OOTCSChooseCreditsSceneProperty)
+ mod_seq_list: CollectionProperty(type=OOTCSModifySeqProperty)
def draw_props(self, layout: UILayout, listIndex: int, objName: str, collectionType: str):
box = layout.box().column()
- enumName = getEnumName(ootEnumCSListType, self.listType)
+ list_type_enum = game_data.z64.get_enum("cs_list_type")
+ enumName = getEnumName(list_type_enum, self.listType)
# Draw current command tab
box.prop(
@@ -224,23 +299,31 @@ def draw_props(self, layout: UILayout, listIndex: int, objName: str, collectionT
if self.listType == "TextList":
attrName = "textList"
elif self.listType == "Transition":
- prop_split(box, self, "transitionType", "Transition Type")
- if self.transitionType == "Custom":
- prop_split(box, self, "transitionTypeCustom", "Transition Type Custom")
-
- prop_split(box, self, "transitionStartFrame", "Start Frame")
- prop_split(box, self, "transitionEndFrame", "End Frame")
- return
+ attrName = "transition_list"
elif self.listType == "LightSettingsList":
attrName = "lightSettingsList"
elif self.listType == "TimeList":
attrName = "timeList"
- elif self.listType in ["StartSeqList", "StopSeqList", "FadeOutSeqList"]:
+ elif self.listType in [
+ "StartSeqList",
+ "StopSeqList",
+ "FadeOutSeqList",
+ "StartAmbienceList",
+ "FadeOutAmbienceList",
+ ]:
attrName = "seqList"
elif self.listType == "MiscList":
attrName = "miscList"
elif self.listType == "RumbleList":
attrName = "rumbleList"
+ elif self.listType == "MotionBlurList":
+ attrName = "motion_blur_list"
+ elif self.listType == "TransitionGeneralList":
+ attrName = "trans_general_list"
+ elif self.listType == "CreditsSceneList":
+ attrName = "credits_scene_list"
+ elif self.listType == "ModifySeqList":
+ attrName = "mod_seq_list"
else:
raise PluginError("Internal error: invalid listType " + self.listType)
@@ -255,7 +338,7 @@ def draw_props(self, layout: UILayout, listIndex: int, objName: str, collectionT
addOp = row.operator(
OOTCSTextAdd.bl_idname,
text="Add " + ootEnumCSTextboxType[l][1],
- icon=ootEnumCSTextboxTypeIcons[l],
+ icon=ootEnumCSTextboxTypeIcons[ootEnumCSTextboxType[l][0]],
)
addOp.collectionType = collectionType + ".textList"
@@ -264,7 +347,7 @@ def draw_props(self, layout: UILayout, listIndex: int, objName: str, collectionT
addOp.objName = objName
else:
addOp = box.operator(
- OOTCollectionAdd.bl_idname, text="Add item to " + getEnumName(ootEnumCSListType, self.listType)
+ OOTCollectionAdd.bl_idname, text="Add item to " + getEnumName(list_type_enum, self.listType)
)
addOp.option = len(data)
addOp.collectionType = collectionType + "." + attrName
@@ -274,11 +357,11 @@ def draw_props(self, layout: UILayout, listIndex: int, objName: str, collectionT
for i, p in enumerate(data):
# ``p`` type:
# OOTCSTextProperty | OOTCSLightSettingsProperty | OOTCSTimeProperty |
- # OOTCSSeqProperty | OOTCSMiscProperty | OOTCSRumbleProperty
+ # OOTCSSeqProperty | OOTCSMiscProperty | OOTCSRumbleProperty | OOTCSTransitionProperty
p.draw_props(box, self, listIndex, i, objName, collectionType, enumName.removesuffix(" List"))
if len(data) == 0:
- box.label(text="No items in " + getEnumName(ootEnumCSListType, self.listType))
+ box.label(text="No items in " + getEnumName(list_type_enum, self.listType))
class OOTCutsceneCommandBase:
@@ -294,14 +377,33 @@ class OOTCutsceneMiscProperty(OOTCutsceneCommandBase, PropertyGroup):
type: StringProperty(default="Unknown")
+class OOTCutsceneMotionBlurPreviewProperty(OOTCutsceneCommandBase, PropertyGroup):
+ type: StringProperty(default="Unknown")
+
+
+class OOTCutsceneTransitionGeneralPreviewProperty(OOTCutsceneCommandBase, PropertyGroup):
+ type: StringProperty(default="Unknown")
+ color: FloatVectorProperty(
+ name="Color",
+ subtype="COLOR",
+ size=4,
+ min=0,
+ max=1,
+ default=(1, 1, 1, 1),
+ )
+
+
class OOTCutscenePreviewProperty(PropertyGroup):
transitionList: CollectionProperty(type=OOTCutsceneTransitionProperty)
miscList: CollectionProperty(type=OOTCutsceneMiscProperty)
+ motion_blur_list: CollectionProperty(type=OOTCutsceneMotionBlurPreviewProperty)
+ transition_general_list: CollectionProperty(type=OOTCutsceneTransitionGeneralPreviewProperty)
trigger: BoolProperty(default=False) # for ``CS_TRANS_TRIGGER_INSTANCE``
isFixedCamSet: BoolProperty(default=False)
prevFrame: IntProperty(default=-1)
nextFrame: IntProperty(default=1)
+ blur_reinit: BoolProperty(default=True)
class OOTCutscenePreviewSettingsProperty(PropertyGroup):
@@ -360,13 +462,28 @@ class OOTCutsceneProperty(PropertyGroup):
csEndFrame: IntProperty(name="End Frame", min=0, default=100)
csUseDestination: BoolProperty(name="Cutscene Destination (Scene Change)")
csDestination: EnumProperty(
- name="Destination", items=ootData.enumData.ootEnumCsDestination, default="cutscene_map_ganon_horse"
+ name="Destination", items=lambda self, context: game_data.z64.get_enum("csDestination"), default=1
)
csDestinationCustom: StringProperty(default="CS_DEST_CUSTOM")
csDestinationStartFrame: IntProperty(name="Start Frame", min=0, default=99)
+ cs_give_tatl: BoolProperty(name="Give Tatl")
+ cs_give_tatl_start_frame: IntProperty(name="Start Frame", min=0, default=99)
csLists: CollectionProperty(type=OOTCSListProperty, name="Cutscene Lists")
- menuTab: EnumProperty(items=ootEnumCSListType)
+ next_entrance: StringProperty(default="0xFFFF")
+ play_on_spawn: IntProperty(min=0, default=0)
+ spawn_flag_type: EnumProperty(
+ items=[
+ ("Custom", "Custom", "Custom"),
+ ("CS_SPAWN_FLAG_NONE", "None", "None"),
+ ("CS_SPAWN_FLAG_ALWAYS", "Always", "Always"),
+ ("CS_SPAWN_FLAG_ONCE", "Flag", "Flag"),
+ ],
+ default=2,
+ )
+ spawn_flag: StringProperty(default="0x00")
+ spawn_flags_custom: StringProperty(default="CS_SPAWN_FLAG_CUSTOM")
+ menuTab: EnumProperty(items=lambda self, context: game_data.z64.get_enum("cs_list_type"))
preview: PointerProperty(type=OOTCutscenePreviewProperty)
@staticmethod
@@ -386,7 +503,24 @@ def upgrade_object(obj):
for csListSubProp in getattr(csListProp, listName):
upgradeCutsceneSubProps(csListSubProp)
+ if csListProp.listType == "Transition":
+ new_entry = csListProp.transition_list.add()
+
+ if "transitionType" in csListProp:
+ new_entry.transition_type = csListProp.transitionType
+ del csListProp["transitionType"]
+ if "transitionTypeCustom" in csListProp:
+ new_entry.transition_type_custom = csListProp.transitionTypeCustom
+ del csListProp["transitionTypeCustom"]
+ if "transitionStartFrame" in csListProp:
+ new_entry.startFrame = csListProp.transitionStartFrame
+ del csListProp["transitionStartFrame"]
+ if "transitionEndFrame" in csListProp:
+ new_entry.endFrame = csListProp.transitionEndFrame
+ del csListProp["transitionEndFrame"]
+
def draw_props(self, layout: UILayout, obj: Object):
+ layout = layout.column()
split = layout.split(factor=0.5)
split.operator(CutsceneCmdCreateCameraShot.bl_idname, icon="VIEW_CAMERA")
split.operator(CutsceneCmdPlayPreview.bl_idname, icon="RESTRICT_VIEW_OFF")
@@ -399,6 +533,17 @@ def draw_props(self, layout: UILayout, obj: Object):
split.label(text="Cutscene End Frame")
split.prop(self, "csEndFrame")
+ if game_data.z64.is_mm() or not is_oot_features():
+ prop_split(layout, self, "next_entrance", "Next Entrance")
+ prop_split(layout, self, "play_on_spawn", "Spawn")
+ prop_split(layout, self, "spawn_flag_type", "Spawn Flags")
+
+ if self.spawn_flag_type == "Custom":
+ prop_split(layout, self, "spawn_flags_custom", "")
+
+ if self.spawn_flag_type == "CS_SPAWN_FLAG_ONCE":
+ prop_split(layout, self, "spawn_flag", "")
+
commandsBox = layout.box()
commandsBox.box().label(text="Cutscene Commands")
@@ -407,13 +552,25 @@ def draw_props(self, layout: UILayout, obj: Object):
if self.csUseDestination:
b.prop(self, "csDestinationStartFrame")
- searchBox = b.box()
- boxRow = searchBox.row()
- searchOp = boxRow.operator(OOT_SearchCSDestinationEnumOperator.bl_idname, icon="VIEWZOOM", text="")
- searchOp.objName = obj.name
- boxRow.label(text=getEnumName(ootData.enumData.ootEnumCsDestination, self.csDestination))
+ if game_data.z64.is_oot():
+ searchBox = b.box()
+ boxRow = searchBox.row()
+ searchOp = boxRow.operator(OOT_SearchCSDestinationEnumOperator.bl_idname, icon="VIEWZOOM", text="")
+ searchOp.objName = obj.name
+ boxRow.label(text=getEnumName(game_data.z64.get_enum("csDestination"), self.csDestination))
+ layout_custom = searchBox
+ else:
+ layout_custom = b
+ prop_split(b, self, "csDestination", "Cutscene Destination Type")
+
if self.csDestination == "Custom":
- prop_split(searchBox.column(), self, "csDestinationCustom", "Cutscene Destination Custom")
+ prop_split(layout_custom.column(), self, "csDestinationCustom", "Cutscene Destination Custom")
+
+ if not is_oot_features():
+ b = commandsBox.box()
+ b.prop(self, "cs_give_tatl")
+ if self.cs_give_tatl:
+ b.prop(self, "cs_give_tatl_start_frame")
commandsBox.column_flow(columns=3, align=True).prop(self, "menuTab", expand=True)
label = f"Add New {ootCSSubPropToName[self.menuTab]}"
@@ -435,9 +592,16 @@ def draw_props(self, layout: UILayout, obj: Object):
OOTCSSeqProperty,
OOTCSMiscProperty,
OOTCSRumbleProperty,
+ OOTCSTransitionProperty,
+ OOTCSMotionBlurProperty,
+ OOTCSTransitionGeneralProperty,
+ OOTCSChooseCreditsSceneProperty,
+ OOTCSModifySeqProperty,
OOTCSListProperty,
OOTCutsceneTransitionProperty,
OOTCutsceneMiscProperty,
+ OOTCutsceneMotionBlurPreviewProperty,
+ OOTCutsceneTransitionGeneralPreviewProperty,
OOTCutscenePreviewProperty,
OOTCutscenePreviewSettingsProperty,
OOTCutsceneProperty,
@@ -445,6 +609,8 @@ def draw_props(self, layout: UILayout, obj: Object):
def cutscene_props_register():
+ game_data.z64.update(None, "OOT", True)
+
for cls in classes:
register_class(cls)
diff --git a/fast64_internal/oot/cutscene_docs.md b/fast64_internal/z64/cutscene_docs.md
similarity index 100%
rename from fast64_internal/oot/cutscene_docs.md
rename to fast64_internal/z64/cutscene_docs.md
diff --git a/fast64_internal/oot/exporter/__init__.py b/fast64_internal/z64/exporter/__init__.py
similarity index 95%
rename from fast64_internal/oot/exporter/__init__.py
rename to fast64_internal/z64/exporter/__init__.py
index e85ef12fc..e9ed576c6 100644
--- a/fast64_internal/oot/exporter/__init__.py
+++ b/fast64_internal/z64/exporter/__init__.py
@@ -1,11 +1,12 @@
import bpy
import os
+from ...game_data import game_data
from mathutils import Matrix
from bpy.types import Object
from ...f3d.f3d_gbi import DLFormat, TextureExportSettings
-from ..oot_model_classes import OOTModel
-from ..oot_f3d_writer import writeTextureArraysNew, writeTextureArraysExisting1D
+from ..model_classes import OOTModel
+from ..f3d_writer import writeTextureArraysNew, writeTextureArraysExisting1D
from .scene import Scene
from .decomp_edit import Files
@@ -19,7 +20,7 @@
writeFile,
)
-from ..oot_utility import (
+from ..utility import (
ExportInfo,
OOTObjectCategorizer,
ootDuplicateHierarchy,
@@ -93,6 +94,8 @@ def export(originalSceneObj: Object, transform: Matrix, exportInfo: ExportInfo):
# circular import fixes
from .decomp_edit.config import Config
+ game_data.z64.update(bpy.context, None)
+
checkObjectReference(originalSceneObj, "Scene object")
scene = SceneExport.create_scene(originalSceneObj, transform, exportInfo)
diff --git a/fast64_internal/oot/exporter/actor.py b/fast64_internal/z64/exporter/actor.py
similarity index 100%
rename from fast64_internal/oot/exporter/actor.py
rename to fast64_internal/z64/exporter/actor.py
diff --git a/fast64_internal/oot/exporter/collision/__init__.py b/fast64_internal/z64/exporter/collision/__init__.py
similarity index 98%
rename from fast64_internal/oot/exporter/collision/__init__.py
rename to fast64_internal/z64/exporter/collision/__init__.py
index bc39bc2c4..35fb7f476 100644
--- a/fast64_internal/oot/exporter/collision/__init__.py
+++ b/fast64_internal/z64/exporter/collision/__init__.py
@@ -6,7 +6,7 @@
from bpy.ops import object
from typing import Optional
from ....utility import PluginError, CData, indent
-from ...oot_utility import convertIntTo2sComplement
+from ...utility import convertIntTo2sComplement
from ..utility import Utility
from .polygons import CollisionPoly, CollisionPolygons
from .surface import SurfaceType, SurfaceTypes
@@ -162,7 +162,7 @@ def getCollisionData(dataHolder: Optional[Object], transform: Matrix, useMacros:
Utility.getPropValue(colProp, "floorSetting"),
colProp.decreaseHeight,
colProp.eponaBlock,
- Utility.getPropValue(colProp, "sound"),
+ Utility.getPropValue(colProp, "sound", enum_key="surface_material"),
Utility.getPropValue(colProp, "terrain"),
colProp.lightingSetting,
int(colProp.echo, base=16),
diff --git a/fast64_internal/oot/exporter/collision/camera.py b/fast64_internal/z64/exporter/collision/camera.py
similarity index 76%
rename from fast64_internal/oot/exporter/collision/camera.py
rename to fast64_internal/z64/exporter/collision/camera.py
index 012defcee..aec40e985 100644
--- a/fast64_internal/oot/exporter/collision/camera.py
+++ b/fast64_internal/z64/exporter/collision/camera.py
@@ -3,8 +3,10 @@
from dataclasses import dataclass, field
from mathutils import Quaternion, Matrix
from bpy.types import Object
+from typing import Optional
from ....utility import PluginError, CData, indent
-from ...oot_utility import getObjectList
+from ....game_data import game_data
+from ...utility import getObjectList
from ...collision.constants import decomp_compat_map_CameraSType
from ...collision.properties import OOTCameraPositionProperty
from ..utility import Utility
@@ -102,6 +104,51 @@ def getCrawlspacePosList(dataHolder: Object, transform: Matrix):
)
return crawlspacePosList
+ @staticmethod
+ def get_camera_info(
+ data_holder: Object, cam_obj: Object, transform: Matrix, cam_pos_data: Optional[dict[int, CameraData]]
+ ):
+ camProp: OOTCameraPositionProperty = cam_obj.ootCameraPositionProperty
+ cam_setting = camProp.camSType
+
+ if cam_pos_data is None:
+ cam_pos_data = {}
+
+ if cam_setting == "Custom":
+ setting = camProp.camSTypeCustom
+ else:
+ setting = (
+ decomp_compat_map_CameraSType.get(cam_setting, cam_setting) if game_data.z64.is_oot() else cam_setting
+ )
+
+ if camProp.hasPositionData or camProp.is_actor_cs_cam:
+ if camProp.index in cam_pos_data:
+ raise PluginError(f"ERROR: Repeated camera position index: {camProp.index} for {cam_obj.name}")
+
+ # Camera faces opposite direction
+ pos, rot, _, _ = Utility.getConvertedTransformWithOrientation(
+ transform, data_holder, cam_obj, Quaternion((0, 1, 0), math.radians(180.0))
+ )
+
+ fov = math.degrees(cam_obj.data.angle)
+ cam_pos_data[camProp.index] = CameraData(
+ pos,
+ rot,
+ -1
+ if camProp.use_setting_default_fov
+ else round(fov * 100 if fov > 3.6 else fov), # see CAM_DATA_SCALED() macro
+ cam_obj.ootCameraPositionProperty.bgImageOverrideIndex,
+ )
+
+ return CameraInfo(
+ setting,
+ 3
+ if camProp.hasPositionData or camProp.is_actor_cs_cam
+ else 0, # cameras are using 3 entries in the data array
+ cam_pos_data[camProp.index] if camProp.hasPositionData or camProp.is_actor_cs_cam else None,
+ camProp.index,
+ )
+
@staticmethod
def getBgCamInfoList(dataHolder: Object, transform: Matrix):
"""Returns a list of camera informations from camera objects"""
@@ -113,37 +160,15 @@ def getBgCamInfoList(dataHolder: Object, transform: Matrix):
for camObj in camObjList:
camProp: OOTCameraPositionProperty = camObj.ootCameraPositionProperty
- if camProp.camSType == "Custom":
- setting = camProp.camSTypeCustom
- else:
- setting = decomp_compat_map_CameraSType.get(camProp.camSType, camProp.camSType)
-
- if camProp.hasPositionData:
- if camProp.index in camPosData:
- raise PluginError(f"ERROR: Repeated camera position index: {camProp.index} for {camObj.name}")
-
- # Camera faces opposite direction
- pos, rot, _, _ = Utility.getConvertedTransformWithOrientation(
- transform, dataHolder, camObj, Quaternion((0, 1, 0), math.radians(180.0))
- )
-
- fov = math.degrees(camObj.data.angle)
- camPosData[camProp.index] = CameraData(
- pos,
- rot,
- round(fov * 100 if fov > 3.6 else fov), # see CAM_DATA_SCALED() macro
- camObj.ootCameraPositionProperty.bgImageOverrideIndex,
- )
+ # ignore non-scene cameras
+ if camProp.is_actor_cs_cam:
+ continue
if camProp.index in camInfoData:
raise PluginError(f"ERROR: Repeated camera entry: {camProp.index} for {camObj.name}")
- camInfoData[camProp.index] = CameraInfo(
- setting,
- 3 if camProp.hasPositionData else 0, # cameras are using 3 entries in the data array
- camPosData[camProp.index] if camProp.hasPositionData else None,
- camProp.index,
- )
+ camInfoData[camProp.index] = BgCamInformations.get_camera_info(dataHolder, camObj, transform, camPosData)
+
return list(camInfoData.values())
@staticmethod
diff --git a/fast64_internal/oot/exporter/collision/polygons.py b/fast64_internal/z64/exporter/collision/polygons.py
similarity index 100%
rename from fast64_internal/oot/exporter/collision/polygons.py
rename to fast64_internal/z64/exporter/collision/polygons.py
diff --git a/fast64_internal/oot/exporter/collision/surface.py b/fast64_internal/z64/exporter/collision/surface.py
similarity index 100%
rename from fast64_internal/oot/exporter/collision/surface.py
rename to fast64_internal/z64/exporter/collision/surface.py
diff --git a/fast64_internal/oot/exporter/collision/vertex.py b/fast64_internal/z64/exporter/collision/vertex.py
similarity index 100%
rename from fast64_internal/oot/exporter/collision/vertex.py
rename to fast64_internal/z64/exporter/collision/vertex.py
diff --git a/fast64_internal/oot/exporter/collision/waterbox.py b/fast64_internal/z64/exporter/collision/waterbox.py
similarity index 99%
rename from fast64_internal/oot/exporter/collision/waterbox.py
rename to fast64_internal/z64/exporter/collision/waterbox.py
index 15d158239..1267fecf0 100644
--- a/fast64_internal/oot/exporter/collision/waterbox.py
+++ b/fast64_internal/z64/exporter/collision/waterbox.py
@@ -1,7 +1,7 @@
from dataclasses import dataclass
from mathutils import Matrix
from bpy.types import Object
-from ...oot_utility import getObjectList
+from ...utility import getObjectList
from ....utility import CData, checkIdentityRotation, indent
from ..utility import Utility
diff --git a/fast64_internal/z64/exporter/cutscene/__init__.py b/fast64_internal/z64/exporter/cutscene/__init__.py
new file mode 100644
index 000000000..a101e400e
--- /dev/null
+++ b/fast64_internal/z64/exporter/cutscene/__init__.py
@@ -0,0 +1,229 @@
+import bpy
+
+from dataclasses import dataclass, field
+from typing import Optional, TYPE_CHECKING
+from bpy.types import Object
+from ....game_data import game_data
+from ....utility import PluginError, CData, indent
+from ...utility import getCustomProperty, is_oot_features
+from ...scene.properties import Z64_SceneHeaderProperty
+from .data import CutsceneData
+
+if TYPE_CHECKING:
+ from ...cutscene.properties import OOTCutsceneProperty
+
+
+# NOTE: ``paramNumber`` is the expected number of parameters inside the parsed commands,
+# this account for the unused parameters. Every classes are based on the commands arguments from ``z64cutscene_commands.h``
+
+# NOTE: ``params`` is the list of parsed parameters, it can't be ``None`` if we're importing a scene,
+# when it's ``None`` it will get the data from the cutscene objects
+
+
+@dataclass
+class Cutscene:
+ """This class defines a cutscene, including its data and its informations"""
+
+ name: str
+ data: CutsceneData
+ totalEntries: int
+ frameCount: int
+ useMacros: bool
+ motionOnly: bool
+ next_entrance: Optional[str]
+ spawn: Optional[int]
+ spawn_flags: Optional[str]
+
+ paramNumber: int = field(init=False, default=2)
+
+ @staticmethod
+ def new(name: Optional[str], csObj: Optional[Object], useMacros: bool, motionOnly: bool):
+ # when csObj is None it means we're in import context
+ if csObj is not None:
+ if name is None:
+ name = csObj.name.removeprefix("Cutscene.").replace(".", "_")
+
+ cs_prop: "OOTCutsceneProperty" = csObj.ootCutsceneProperty
+ if cs_prop.spawn_flag_type == "Custom":
+ spawn_flag: str = cs_prop.spawn_flags_custom
+ elif cs_prop.spawn_flag_type == "CS_SPAWN_FLAG_ONCE":
+ spawn_flag: str = f"CS_SPAWN_FLAG_ONCE({cs_prop.spawn_flag})"
+ else:
+ spawn_flag: str = cs_prop.spawn_flag_type
+
+ data = CutsceneData.new(csObj, useMacros, motionOnly)
+ return Cutscene(
+ name,
+ data,
+ data.totalEntries,
+ data.frameCount,
+ useMacros,
+ motionOnly,
+ cs_prop.next_entrance if game_data.z64.is_mm() or not is_oot_features() else None,
+ cs_prop.play_on_spawn if game_data.z64.is_mm() or not is_oot_features() else None,
+ spawn_flag if game_data.z64.is_mm() or not is_oot_features() else None,
+ )
+ else:
+ # if importing, return a blank cutscene that we will update later
+ return Cutscene(
+ name, CutsceneData.new(None, useMacros, motionOnly), 0, 0, useMacros, motionOnly, None, None, None
+ )
+
+ def get_entry(self):
+ return "{ " + f"{self.name}, {self.next_entrance}, {self.spawn}, {self.spawn_flags}" + " }"
+
+ def getC(self):
+ """Returns the cutscene data"""
+
+ if self.data is not None:
+ csData = CData()
+ declarationBase = f"CutsceneData {self.name}[]"
+
+ # this list's order defines the order of the commands in the cutscene array
+ dataListNames = []
+
+ if not self.motionOnly:
+ dataListNames.extend(
+ [
+ "textList",
+ "miscList",
+ "rumbleList",
+ "transitionList",
+ "lightSettingsList",
+ "timeList",
+ "seqList",
+ "fadeSeqList",
+ "motion_blur_list",
+ "credits_scene_list",
+ "transition_general_list",
+ "modify_seq_list",
+ "start_ambience_list",
+ "fade_out_ambience_list",
+ ]
+ )
+
+ if is_oot_features():
+ dataListNames.extend(
+ [
+ "camEyeSplineList",
+ "camATSplineList",
+ "camEyeSplineRelPlayerList",
+ "camATSplineRelPlayerList",
+ "camEyeList",
+ "camATList",
+ ]
+ )
+ else:
+ dataListNames.extend(
+ [
+ "camSplineList",
+ ]
+ )
+
+ dataListNames.extend(
+ [
+ "playerCueList",
+ "actorCueList",
+ ]
+ )
+
+ if self.data.motionFrameCount > self.frameCount:
+ self.frameCount += self.data.motionFrameCount - self.frameCount
+
+ # .h
+ csData.header = f"extern {declarationBase};\n"
+
+ # .c
+ if game_data.z64.is_mm():
+ cs_header = "CS_BEGIN_CUTSCENE"
+ cs_end = "CS_END"
+ else:
+ cs_header = "CS_HEADER"
+ cs_end = "CS_END_OF_SCRIPT"
+
+ command_data = ""
+ for curList in dataListNames:
+ for entry in getattr(self.data, curList):
+ if len(entry.entries) > 0:
+ command_data += entry.getCmd()
+
+ csData.source = (
+ declarationBase
+ + " = {\n"
+ + (indent + f"{cs_header}({self.totalEntries}, {self.frameCount}),\n")
+ + (self.data.destination.getCmd() if self.data.destination is not None else "")
+ + (self.data.give_tatl.getCmd() if self.data.give_tatl is not None else "")
+ + command_data
+ + (indent + f"{cs_end}(),\n")
+ + "};\n\n"
+ )
+
+ return csData
+ else:
+ raise PluginError("ERROR: CutsceneData not initialised!")
+
+
+@dataclass
+class SceneCutscene:
+ """This class hosts cutscene data"""
+
+ name: str
+ entries: list[Cutscene]
+
+ @staticmethod
+ def new(name: str, props: Z64_SceneHeaderProperty, headerIndex: int, useMacros: bool):
+ csObj: Object = props.csWriteObject
+ cutsceneObjects: list[Object] = [csObj for csObj in props.extraCutscenes]
+ entries: list[Cutscene] = []
+
+ if headerIndex > 0 and len(cutsceneObjects) > 0:
+ raise PluginError("ERROR: Extra cutscenes can only belong to the main header!")
+
+ cutsceneObjects.insert(0, csObj)
+ for csObj in cutsceneObjects:
+ if csObj is not None:
+ if csObj.ootEmptyType != "Cutscene":
+ raise PluginError(
+ "ERROR: Object selected as cutscene is wrong type, must be empty with Cutscene type"
+ )
+ elif csObj.parent is not None:
+ raise PluginError("ERROR: Cutscene empty object should not be parented to anything")
+
+ writeType = props.csWriteType
+ csWriteCustom = None
+ if writeType == "Custom":
+ csWriteCustom = getCustomProperty(props, "csWriteCustom")
+
+ if props.writeCutscene:
+ # if csWriteCustom is None then the name will auto-set from the csObj passed in the class
+ entries.append(
+ Cutscene.new(csWriteCustom, csObj, useMacros, bpy.context.scene.fast64.oot.exportMotionOnly)
+ )
+ return SceneCutscene(name, entries)
+
+ def to_c(self):
+ """Returns the cutscene script entry list (for MM)"""
+
+ data = CData()
+ array_name = f"CutsceneScriptEntry {self.name}[]"
+
+ # .h
+ data.header = f"extern {array_name};"
+
+ # .c
+ data.source = (
+ array_name + " = {\n" + indent + f",\n{indent}".join(cs.get_entry() for cs in self.entries) + "\n};\n\n"
+ )
+
+ return data
+
+ def getCmd(self):
+ """Returns the cutscene data scene command"""
+ if len(self.entries) == 0:
+ raise PluginError("ERROR: Cutscene entry list is empty!")
+
+ if is_oot_features():
+ # entry No. 0 is always self.csObj
+ return indent + f"SCENE_CMD_CUTSCENE_DATA({self.entries[0].name}),\n"
+ else:
+ return indent + f"SCENE_CMD_CUTSCENE_SCRIPT_LIST({len(self.entries)}, {self.name}),\n"
diff --git a/fast64_internal/oot/exporter/cutscene/actor_cue.py b/fast64_internal/z64/exporter/cutscene/actor_cue.py
similarity index 90%
rename from fast64_internal/oot/exporter/cutscene/actor_cue.py
rename to fast64_internal/z64/exporter/cutscene/actor_cue.py
index 08f484340..7cb837e78 100644
--- a/fast64_internal/oot/exporter/cutscene/actor_cue.py
+++ b/fast64_internal/z64/exporter/cutscene/actor_cue.py
@@ -1,6 +1,6 @@
from dataclasses import dataclass, field
from ....utility import PluginError, indent
-from ...oot_constants import ootData
+from ....game_data import game_data
from ...cutscene.motion.utility import getRotation, getInteger
from .common import CutsceneCmdBase
@@ -18,14 +18,15 @@ class CutsceneCmdActorCue(CutsceneCmdBase):
paramNumber: int = field(init=False, default=15)
@staticmethod
- def from_params(params: list[str]):
+ def from_params(params: list[str], is_player: bool = False):
return CutsceneCmdActorCue(
getInteger(params[1]),
getInteger(params[2]),
- getInteger(params[0]),
+ CutsceneCmdBase.getEnumValue("cs_player_cue_id", params[0]) if is_player else getInteger(params[0]),
[getRotation(params[3]), getRotation(params[4]), getRotation(params[5])],
[getInteger(params[6]), getInteger(params[7]), getInteger(params[8])],
[getInteger(params[9]), getInteger(params[10]), getInteger(params[11])],
+ is_player,
)
def getCmd(self):
@@ -74,7 +75,7 @@ def from_params(params: list[str], isPlayer: bool):
commandType = commandType.removeprefix("0x")
commandType = "0x" + "0" * (4 - len(commandType)) + commandType
else:
- commandType = ootData.enumData.enumByKey["csCmd"].itemById[commandType].key
+ commandType = game_data.z64.enums.enumByKey["cs_cmd"].item_by_id[commandType].key
entryTotal = getInteger(params[1].strip())
return CutsceneCmdActorCueList(None, None, isPlayer, commandType, entryTotal)
diff --git a/fast64_internal/oot/exporter/cutscene/camera.py b/fast64_internal/z64/exporter/cutscene/camera.py
similarity index 58%
rename from fast64_internal/oot/exporter/cutscene/camera.py
rename to fast64_internal/z64/exporter/cutscene/camera.py
index 105998ee0..4b9a7d7f3 100644
--- a/fast64_internal/oot/exporter/cutscene/camera.py
+++ b/fast64_internal/z64/exporter/cutscene/camera.py
@@ -1,6 +1,7 @@
import struct
from dataclasses import dataclass, field
+from typing import Optional
from ....utility import PluginError, indent
from ...cutscene.motion.utility import getInteger
from .common import CutsceneCmdBase
@@ -158,3 +159,138 @@ def from_params(params: list[str]):
def getCmd(self):
return self.getCamListCmd("CS_CAM_AT", self.startFrame, self.endFrame)
+
+
+# MM's new camera commands
+
+
+@dataclass
+class CutsceneCmdNewCamPoint:
+ """This class contains a single Camera Point command data (the newer version)"""
+
+ interp_type: str
+ weight: int
+ duration: int
+ pos: list[int]
+ relative_to: str
+
+ paramNumber: int = field(init=False, default=7)
+ size: int = field(init=False, default=0xC)
+
+ @staticmethod
+ def from_params(params: list[str]):
+ return CutsceneCmdNewCamPoint(
+ params[0],
+ getInteger(params[1]),
+ getInteger(params[2]),
+ [getInteger(params[3], True), getInteger(params[4], True), getInteger(params[5], True)],
+ params[6],
+ )
+
+ def to_c(self):
+ return (
+ indent * 4
+ + "CS_CAM_POINT("
+ + f"{self.interp_type}, "
+ + f"{self.weight}, "
+ + f"{self.duration}, "
+ + f"{self.pos[0]}, {self.pos[1]}, {self.pos[2]}, "
+ + f"{self.relative_to}"
+ + "),\n"
+ )
+
+
+@dataclass
+class CutsceneCmdCamMisc:
+ """This class contains the Camera Misc data"""
+
+ camRoll: int
+ viewAngle: float
+
+ paramNumber: int = field(init=False, default=4)
+ size: int = field(init=False, default=0x8)
+
+ @staticmethod
+ def from_params(params: list[str]):
+ return CutsceneCmdCamMisc(getInteger(params[1]), getInteger(params[2]))
+
+ def to_c(self):
+ return indent * 4 + f"CS_CAM_MISC(0, {self.camRoll}, {self.viewAngle}, 0),\n"
+
+
+@dataclass
+class CutsceneSplinePoint:
+ # this is not a real command but it helps as each camera point is made of one at, one eye and one misc
+ at: CutsceneCmdNewCamPoint
+ eye: CutsceneCmdNewCamPoint
+ misc: CutsceneCmdCamMisc
+ size = 0x20
+
+
+@dataclass
+class CutsceneCmdCamSpline:
+ """This class contains the Camera Spline data"""
+
+ num_entries: int
+ duration: int
+ entries: list[CutsceneSplinePoint] = field(init=False, default_factory=list)
+
+ paramNumber: int = field(init=False, default=4)
+ size: int = field(init=False, default=0x8)
+
+ @staticmethod
+ def from_params(params: list[str]):
+ return CutsceneCmdCamSpline(getInteger(params[0]), getInteger(params[3]))
+
+ def to_c(self):
+ at_list: list[str] = []
+ eye_list: list[str] = []
+ misc_list: list[str] = []
+
+ for entry in self.entries:
+ at_list.append(entry.at.to_c())
+ eye_list.append(entry.eye.to_c())
+ misc_list.append(entry.misc.to_c())
+
+ return (
+ (indent * 3 + f"CS_CAM_SPLINE({len(self.entries)}, 0, 0, {self.duration}),\n")
+ + (indent * 4 + "// At Camera Points\n")
+ + "".join(at for at in at_list)
+ + (indent * 4 + "// Eye Camera Points\n")
+ + "".join(eye for eye in eye_list)
+ + (indent * 4 + "// FoV/Roll Settings\n")
+ + "".join(misc for misc in misc_list)
+ )
+
+
+@dataclass
+class CutsceneCmdCamSplineList:
+ """This class contains the Camera Spline list data"""
+
+ num_bytes: int
+ entries: list[CutsceneCmdCamSpline] = field(init=False, default_factory=list)
+
+ paramNumber: int = 1
+ listName: str = "camSplineList"
+ size: int = 0x8
+
+ @staticmethod
+ def from_params(params: list[str]):
+ return CutsceneCmdCamSplineList(getInteger(params[0]))
+
+ def getCmd(self):
+ data = ""
+ num_bytes = 0
+
+ for entry in self.entries:
+ data += entry.to_c()
+ num_bytes += entry.size
+
+ for item in entry.entries:
+ num_bytes += item.size
+
+ return (
+ (indent * 2 + f"CS_CAM_SPLINE_LIST({num_bytes}),\n")
+ + "".join(entry.to_c() for entry in self.entries)
+ + (indent * 2 + "CS_CAM_END(),\n")
+ )
diff --git a/fast64_internal/oot/exporter/cutscene/common.py b/fast64_internal/z64/exporter/cutscene/common.py
similarity index 81%
rename from fast64_internal/oot/exporter/cutscene/common.py
rename to fast64_internal/z64/exporter/cutscene/common.py
index ab91fb283..3fa7513c8 100644
--- a/fast64_internal/oot/exporter/cutscene/common.py
+++ b/fast64_internal/z64/exporter/cutscene/common.py
@@ -1,7 +1,8 @@
from dataclasses import dataclass
from typing import Optional
from ....utility import PluginError, indent
-from ...oot_constants import ootData
+from ...utility import is_oot_features
+from ....game_data import game_data
from ...cutscene.motion.utility import getInteger
@@ -20,13 +21,13 @@ def validateFrames(self, checkEndFrame: bool = True):
@staticmethod
def getEnumValue(enumKey: str, value: str, isSeqLegacy: bool = False):
- enum = ootData.enumData.enumByKey[enumKey]
- item = enum.itemById.get(value)
+ enum = game_data.z64.enums.enumByKey[enumKey]
+ item = enum.item_by_id.get(value)
if item is None:
setting = getInteger(value)
if isSeqLegacy:
setting -= 1
- item = enum.itemByIndex.get(setting)
+ item = enum.item_by_index.get(setting)
return item.key if item is not None else value
def getGenericListCmd(self, cmdName: str, entryTotal: int):
@@ -42,4 +43,5 @@ def getGenericSeqCmd(self, cmdName: str, type: str, startFrame: int, endFrame: i
self.validateFrames()
if type is None:
raise PluginError("ERROR: Seq type is None!")
- return indent * 3 + f"{cmdName}({type}, {startFrame}, {endFrame}" + ", 0" * 8 + "),\n"
+ pad_amount = 8 if is_oot_features() else 1 if cmdName == "CS_STOP_SEQ" else 0
+ return indent * 3 + f"{cmdName}({type}, {startFrame}, {endFrame}" + ", 0" * pad_amount + "),\n"
diff --git a/fast64_internal/z64/exporter/cutscene/data.py b/fast64_internal/z64/exporter/cutscene/data.py
new file mode 100644
index 000000000..5f5b2b0e5
--- /dev/null
+++ b/fast64_internal/z64/exporter/cutscene/data.py
@@ -0,0 +1,637 @@
+import bpy
+import math
+
+from copy import copy
+from typing import Optional, TYPE_CHECKING
+from bpy.types import Object, Bone
+from ....utility import PluginError, exportColor
+from ....game_data import game_data
+from ...utility import is_oot_features
+from .actor_cue import CutsceneCmdActorCueList, CutsceneCmdActorCue
+from .text import CutsceneCmdTextList, CutsceneCmdText, CutsceneCmdTextNone, CutsceneCmdTextOcarinaAction
+
+from .seq import (
+ CutsceneCmdStartStopSeqList,
+ CutsceneCmdFadeSeqList,
+ CutsceneCmdStartStopSeq,
+ CutsceneCmdFadeSeq,
+ CutsceneCmdModifySeq,
+ CutsceneCmdModifySeqList,
+ CutsceneCmdStartAmbience,
+ CutsceneCmdStartAmbienceList,
+ CutsceneCmdFadeOutAmbience,
+ CutsceneCmdFadeOutAmbienceList,
+)
+
+from .misc import (
+ CutsceneCmdLightSetting,
+ CutsceneCmdTime,
+ CutsceneCmdMisc,
+ CutsceneCmdRumbleController,
+ CutsceneCmdDestination,
+ CutsceneCmdMiscList,
+ CutsceneCmdRumbleControllerList,
+ CutsceneCmdTransition,
+ CutsceneCmdTransitionList,
+ CutsceneCmdLightSettingList,
+ CutsceneCmdTimeList,
+ CutsceneCmdMotionBlur,
+ CutsceneCmdMotionBlurList,
+ CutsceneCmdChooseCreditsScenes,
+ CutsceneCmdChooseCreditsScenesList,
+ CutsceneCmdTransitionGeneral,
+ CutsceneCmdTransitionGeneralList,
+ CutsceneCmdGiveTatl,
+)
+
+from .camera import (
+ CutsceneCmdCamPoint,
+ CutsceneCmdCamEyeSpline,
+ CutsceneCmdCamATSpline,
+ CutsceneCmdCamEyeSplineRelToPlayer,
+ CutsceneCmdCamATSplineRelToPlayer,
+ CutsceneCmdCamEye,
+ CutsceneCmdCamAT,
+ CutsceneCmdNewCamPoint,
+ CutsceneCmdCamMisc,
+ CutsceneSplinePoint,
+ CutsceneCmdCamSpline,
+ CutsceneCmdCamSplineList,
+)
+
+if TYPE_CHECKING:
+ from ...cutscene.properties import OOTCutsceneProperty, OOTCSTextProperty
+
+
+cmdToClass = {
+ "TextList": CutsceneCmdTextList,
+ "LightSettingsList": CutsceneCmdLightSettingList,
+ "TimeList": CutsceneCmdTimeList,
+ "MiscList": CutsceneCmdMiscList,
+ "RumbleList": CutsceneCmdRumbleControllerList,
+ "StartSeqList": CutsceneCmdStartStopSeqList,
+ "StopSeqList": CutsceneCmdStartStopSeqList,
+ "FadeOutSeqList": CutsceneCmdFadeSeqList,
+ "StartSeq": CutsceneCmdStartStopSeq,
+ "StopSeq": CutsceneCmdStartStopSeq,
+ "FadeOutSeq": CutsceneCmdFadeSeq,
+ "Transition": CutsceneCmdTransitionList,
+ "MotionBlurList": CutsceneCmdMotionBlurList,
+ "CreditsSceneList": CutsceneCmdChooseCreditsScenesList,
+ "TransitionGeneralList": CutsceneCmdTransitionGeneralList,
+ "ModifySeqList": CutsceneCmdModifySeqList,
+ "StartAmbience": CutsceneCmdStartAmbience,
+ "FadeOutAmbience": CutsceneCmdFadeOutAmbience,
+ "StartAmbienceList": CutsceneCmdStartAmbienceList,
+ "FadeOutAmbienceList": CutsceneCmdFadeOutAmbienceList,
+}
+
+# to CutsceneData list
+cmdToList = {
+ "TextList": "textList",
+ "LightSettingsList": "lightSettingsList",
+ "TimeList": "timeList",
+ "MiscList": "miscList",
+ "RumbleList": "rumbleList",
+ "Transition": "transitionList",
+ "MotionBlurList": "motion_blur_list",
+ "CreditsSceneList": "credits_scene_list",
+ "TransitionGeneralList": "transition_general_list",
+ "ModifySeqList": "modify_seq_list",
+ "StartAmbienceList": "start_ambience_list",
+ "FadeOutAmbienceList": "fade_out_ambience_list",
+}
+
+
+class CutsceneData:
+ """This class defines the command data inside a cutscene"""
+
+ def __init__(self, useMacros: bool, motionOnly: bool):
+ self.useMacros = useMacros
+ self.motionOnly = motionOnly
+
+ self.totalEntries: int = 0
+ self.frameCount: int = 0
+ self.motionFrameCount: int = 0
+ self.camEndFrame: int = 0
+ self.destination: Optional[CutsceneCmdDestination] = None
+ self.give_tatl: Optional[CutsceneCmdGiveTatl] = None
+ self.actorCueList: list[CutsceneCmdActorCueList] = []
+ self.playerCueList: list[CutsceneCmdActorCueList] = []
+ self.camEyeSplineList: list[CutsceneCmdCamEyeSpline] = []
+ self.camATSplineList: list[CutsceneCmdCamATSpline] = []
+ self.camEyeSplineRelPlayerList: list[CutsceneCmdCamEyeSplineRelToPlayer] = []
+ self.camATSplineRelPlayerList: list[CutsceneCmdCamATSplineRelToPlayer] = []
+ self.camEyeList: list[CutsceneCmdCamEye] = []
+ self.camATList: list[CutsceneCmdCamAT] = []
+ self.textList: list[CutsceneCmdTextList] = []
+ self.miscList: list[CutsceneCmdMiscList] = []
+ self.rumbleList: list[CutsceneCmdRumbleControllerList] = []
+ self.transitionList: list[CutsceneCmdTransitionList] = []
+ self.lightSettingsList: list[CutsceneCmdLightSettingList] = []
+ self.timeList: list[CutsceneCmdTimeList] = []
+ self.seqList: list[CutsceneCmdStartStopSeqList] = []
+ self.fadeSeqList: list[CutsceneCmdFadeSeqList] = []
+
+ # lists from the new cutscene system
+ self.camSplineList: list[CutsceneCmdCamSplineList] = []
+ self.motion_blur_list: list[CutsceneCmdMotionBlurList] = []
+ self.modify_seq_list: list[CutsceneCmdModifySeqList] = []
+ self.credits_scene_list: list[CutsceneCmdChooseCreditsScenesList] = []
+ self.transition_general_list: list[CutsceneCmdTransitionGeneralList] = []
+ self.start_ambience_list: list[CutsceneCmdStartAmbienceList] = []
+ self.fade_out_ambience_list: list[CutsceneCmdFadeOutAmbienceList] = []
+
+ @staticmethod
+ def new(csObj: Optional[Object], useMacros: bool, motionOnly: bool):
+ newCutsceneData = CutsceneData(useMacros, motionOnly)
+
+ if csObj is not None:
+ # when csObj is None it means we're in import context
+ csProp: "OOTCutsceneProperty" = csObj.ootCutsceneProperty
+ csObjects = {
+ "CS Actor Cue List": [],
+ "CS Player Cue List": [],
+ "camShot": [],
+ }
+
+ for obj in csObj.children_recursive:
+ if obj.type == "EMPTY" and obj.ootEmptyType in csObjects.keys():
+ csObjects[obj.ootEmptyType].append(obj)
+ elif obj.type == "ARMATURE":
+ csObjects["camShot"].append(obj)
+ csObjects["camShot"].sort(key=lambda obj: obj.name)
+
+ newCutsceneData.setCutsceneData(csObjects, csProp)
+
+ return newCutsceneData
+
+ def getOoTRotation(self, obj: Object):
+ """Returns the converted Blender rotation"""
+
+ def conv(r):
+ r /= 2.0 * math.pi
+ r -= math.floor(r)
+ r = round(r * 0x10000)
+
+ if r >= 0x8000:
+ r += 0xFFFF0000
+
+ assert r >= 0 and r <= 0xFFFFFFFF and (r <= 0x7FFF or r >= 0xFFFF8000)
+
+ return hex(r & 0xFFFF)
+
+ rotXYZ = [conv(obj.rotation_euler[0]), conv(obj.rotation_euler[2]), conv(obj.rotation_euler[1])]
+ return [f"DEG_TO_BINANG({(int(rot, base=16) * (180 / 0x8000)):.3f})" for rot in rotXYZ]
+
+ def getOoTPosition(self, pos):
+ """Returns the converted Blender position"""
+
+ scale = bpy.context.scene.ootBlenderScale
+
+ x = round(pos[0] * scale)
+ y = round(pos[2] * scale)
+ z = round(-pos[1] * scale)
+
+ if any(v < -0x8000 or v >= 0x8000 for v in (x, y, z)):
+ raise RuntimeError(f"Position(s) too large, out of range: {x}, {y}, {z}")
+
+ return [x, y, z]
+
+ def getEnumValueFromProp(self, enumKey: str, owner, propName: str, custom_suffix: str = "Custom"):
+ game_data.z64.update(bpy.context, None)
+ item = game_data.z64.enums.enumByKey[enumKey].item_by_key.get(getattr(owner, propName))
+ return item.id if item is not None else getattr(owner, f"{propName}{custom_suffix}")
+
+ def setActorCueListData(self, csObjects: dict[str, list[Object]], isPlayer: bool):
+ """Returns the Actor Cue List commands from the corresponding objects"""
+
+ playerOrActor = f"{'Player' if isPlayer else 'Actor'}"
+ actorCueListObjects = csObjects[f"CS {playerOrActor} Cue List"]
+ actorCueListObjects.sort(key=lambda o: o.ootCSMotionProperty.actorCueProp.cueStartFrame)
+
+ self.totalEntries += len(actorCueListObjects)
+ for obj in actorCueListObjects:
+ entryTotal = len(obj.children)
+
+ if entryTotal == 0:
+ raise PluginError("ERROR: The Actor Cue List does not contain any child Actor Cue objects")
+
+ if obj.children[-1].ootEmptyType != "CS Dummy Cue":
+ # we need an extra point that won't be exported to get the real last cue's
+ # end frame and end position
+ raise PluginError("ERROR: The Actor Cue List is missing the extra dummy point!")
+
+ commandType = obj.ootCSMotionProperty.actorCueListProp.commandType
+
+ if commandType == "Custom":
+ commandType = obj.ootCSMotionProperty.actorCueListProp.commandTypeCustom
+ elif self.useMacros:
+ commandType = game_data.z64.enums.enumByKey["cs_cmd"].item_by_key[commandType].id
+
+ # ignoring dummy cue
+ newActorCueList = CutsceneCmdActorCueList(None, None, isPlayer, commandType, entryTotal - 1)
+
+ for i, childObj in enumerate(obj.children, 1):
+ startFrame = childObj.ootCSMotionProperty.actorCueProp.cueStartFrame
+ if i < len(obj.children) and childObj.ootEmptyType != "CS Dummy Cue":
+ endFrame = obj.children[i].ootCSMotionProperty.actorCueProp.cueStartFrame
+ actionID = None
+
+ if isPlayer:
+ cueID = childObj.ootCSMotionProperty.actorCueProp.playerCueID
+ if cueID != "Custom":
+ actionID = game_data.z64.enums.enumByKey["cs_player_cue_id"].item_by_key[cueID].id
+
+ if actionID is None:
+ actionID = childObj.ootCSMotionProperty.actorCueProp.cueActionID
+
+ new_entry = CutsceneCmdActorCue(
+ startFrame,
+ endFrame,
+ actionID,
+ self.getOoTRotation(childObj),
+ self.getOoTPosition(childObj.location),
+ self.getOoTPosition(obj.children[i].location),
+ isPlayer,
+ )
+ newActorCueList.entries.append(new_entry)
+
+ self.actorCueList.append(newActorCueList)
+
+ def getCameraShotPointData(self, bones: list[Bone], useAT: bool):
+ """Returns the Camera Point data from the bone data"""
+
+ shotPoints: list[CutsceneCmdCamPoint] = []
+
+ if len(bones) < 4:
+ raise RuntimeError("Camera Armature needs at least 4 bones!")
+
+ for bone in bones:
+ if bone.parent is not None:
+ raise RuntimeError("Camera Armature bones are not allowed to have parent bones!")
+
+ shotPoints.append(
+ CutsceneCmdCamPoint(
+ None,
+ None,
+ ("CS_CAM_CONTINUE" if self.useMacros else "0"),
+ bone.ootCamShotPointProp.shotPointRoll if useAT else 0,
+ bone.ootCamShotPointProp.shotPointFrame,
+ bone.ootCamShotPointProp.shotPointViewAngle,
+ self.getOoTPosition(bone.head if not useAT else bone.tail),
+ )
+ )
+
+ # NOTE: because of the game's bug explained in the importer we need to add an extra dummy point when exporting
+ shotPoints.append(
+ CutsceneCmdCamPoint(None, None, "CS_CAM_STOP" if self.useMacros else "-1", 0, 0, 0.0, [0, 0, 0])
+ )
+ return shotPoints
+
+ def getCamCmdFunc(self, camMode: str, useAT: bool):
+ """Returns the camera get function depending on the camera mode"""
+
+ camCmdFuncMap = {
+ "splineEyeOrAT": self.getCamATSplineCmd if useAT else self.getCamEyeSplineCmd,
+ "splineEyeOrATRelPlayer": self.getCamATSplineRelToPlayerCmd
+ if useAT
+ else self.getCamEyeSplineRelToPlayerCmd,
+ "eyeOrAT": self.getCamATCmd if useAT else self.getCamEyeCmd,
+ }
+
+ return camCmdFuncMap[camMode]
+
+ def getCamClassOrList(self, isClass: bool, camMode: str, useAT: bool):
+ """Returns the camera dataclass depending on the camera mode"""
+
+ # TODO: improve this
+ if isClass:
+ camCmdClassMap = {
+ "splineEyeOrAT": CutsceneCmdCamATSpline if useAT else CutsceneCmdCamEyeSpline,
+ "splineEyeOrATRelPlayer": CutsceneCmdCamATSplineRelToPlayer
+ if useAT
+ else CutsceneCmdCamEyeSplineRelToPlayer,
+ "eyeOrAT": CutsceneCmdCamAT if useAT else CutsceneCmdCamEye,
+ }
+ else:
+ camCmdClassMap = {
+ "splineEyeOrAT": "camATSplineList" if useAT else "camEyeSplineList",
+ "splineEyeOrATRelPlayer": "camATSplineRelPlayerList" if useAT else "camEyeSplineRelPlayerList",
+ "eyeOrAT": "camATList" if useAT else "camEyeList",
+ }
+
+ return camCmdClassMap[camMode]
+
+ def getNewCamData(self, shotObj: Object, useAT: bool):
+ """Returns the Camera Shot data from the corresponding Armatures"""
+
+ entries = self.getCameraShotPointData(shotObj.data.bones, useAT)
+ startFrame = shotObj.data.ootCamShotProp.shotStartFrame
+
+ # "fake" end frame
+ endFrame = startFrame + max(2, sum(point.frame for point in entries)) + (entries[-2].frame if useAT else 1)
+
+ if not useAT:
+ for pointData in entries:
+ pointData.frame = 0
+ self.camEndFrame = endFrame
+
+ return self.getCamClassOrList(True, shotObj.data.ootCamShotProp.shotCamMode, useAT)(
+ startFrame, endFrame, entries
+ )
+
+ def setCameraShotData(self, csObjects: dict[str, list[Object]]):
+ shotObjects = csObjects["camShot"]
+
+ if len(shotObjects) > 0:
+ if is_oot_features():
+ motionFrameCount = -1
+ for shotObj in shotObjects:
+ camMode = shotObj.data.ootCamShotProp.shotCamMode
+
+ eyeCamList = getattr(self, self.getCamClassOrList(False, camMode, False))
+ eyeCamList.append(self.getNewCamData(shotObj, False))
+
+ atCamList = getattr(self, self.getCamClassOrList(False, camMode, True))
+ atCamList.append(self.getNewCamData(shotObj, True))
+
+ motionFrameCount = max(motionFrameCount, self.camEndFrame + 1)
+ self.motionFrameCount += motionFrameCount
+ self.totalEntries += len(shotObjects) * 2
+ else:
+ new_spline_list = CutsceneCmdCamSplineList(0) # bytes are computed when getting the c data
+ i = 0
+
+ for shot_obj in shotObjects:
+ shot_prop = shot_obj.data.ootCamShotProp
+ new_spline = CutsceneCmdCamSpline(0, shot_prop.shot_duration)
+ at_total = 0
+ eye_total = 0
+
+ for i, bone in enumerate(shot_obj.data.bones):
+ bone_prop = bone.ootCamShotPointProp
+
+ if i > 0 and i < len(shot_obj.data.bones) - 1:
+ at_total += bone_prop.shot_point_duration
+ eye_total += bone_prop.shot_point_duration
+
+ # MM TODO: move shot_interp_type and shot_spline_rel_to per bone?
+ # add weight property
+ new_spline.entries.append(
+ CutsceneSplinePoint(
+ # At
+ CutsceneCmdNewCamPoint(
+ self.getEnumValueFromProp(
+ "cs_spline_interp_type", shot_prop, "shot_interp_type", "_custom"
+ ),
+ 0x64,
+ bone_prop.shot_point_duration,
+ self.getOoTPosition(bone.tail),
+ self.getEnumValueFromProp(
+ "cs_spline_rel", shot_prop, "shot_spline_rel_to", "_custom"
+ ),
+ ),
+ # Eye
+ CutsceneCmdNewCamPoint(
+ self.getEnumValueFromProp(
+ "cs_spline_interp_type", shot_prop, "shot_interp_type", "_custom"
+ ),
+ 0x64,
+ bone_prop.shot_point_duration,
+ self.getOoTPosition(bone.head),
+ self.getEnumValueFromProp(
+ "cs_spline_rel", shot_prop, "shot_spline_rel_to", "_custom"
+ ),
+ ),
+ # Misc
+ CutsceneCmdCamMisc(bone_prop.shotPointRoll, bone_prop.shotPointViewAngle),
+ )
+ )
+
+ # I don't understand how this camera system works, as far as I can tell it's
+ # the same as OoT? It's clear that the first and last points are ignored and by
+ # looking at the vanilla scenes I came to the conclusion that the last point of
+ # each point type (at and eye) have a duration that is the sum of all the previous
+ # point's durations except the very first one. By looking at the code I'm thinking
+ # this might work exactly like OoT where you need an extra point because of some bug
+ # (see the readme for explanations), idk if this is necessary ¯\_(ツ)_/¯
+ #
+ # this block copies the last entry's informations to the extra one,
+ # except for the duration which is computed in the loop above
+ last_entry = new_spline.entries[-1]
+ # last_entry.at.duration = at_total
+ # last_entry.eye.duration = eye_total
+ new_spline.entries.append(
+ CutsceneSplinePoint(
+ # At
+ CutsceneCmdNewCamPoint(
+ last_entry.at.interp_type,
+ last_entry.at.weight,
+ at_total,
+ last_entry.at.pos,
+ last_entry.at.relative_to,
+ ),
+ # Eye
+ CutsceneCmdNewCamPoint(
+ last_entry.eye.interp_type,
+ last_entry.eye.weight,
+ eye_total,
+ last_entry.eye.pos,
+ last_entry.eye.relative_to,
+ ),
+ # Misc
+ copy(last_entry.misc),
+ )
+ )
+ i += 1 # accounting for the extra point
+
+ new_spline.num_entries = i
+ new_spline_list.entries.append(new_spline)
+
+ self.camSplineList.append(new_spline_list)
+ self.totalEntries += 1
+
+ def getNewTextCmd(self, textEntry: "OOTCSTextProperty"):
+ match textEntry.textboxType:
+ case "Text":
+ return CutsceneCmdText(
+ textEntry.startFrame,
+ textEntry.endFrame,
+ textEntry.textID,
+ self.getEnumValueFromProp("cs_text_type", textEntry, "csTextType"),
+ textEntry.topOptionTextID,
+ textEntry.bottomOptionTextID,
+ )
+ case "None":
+ return CutsceneCmdTextNone(textEntry.startFrame, textEntry.endFrame)
+ case "OcarinaAction":
+ return CutsceneCmdTextOcarinaAction(
+ textEntry.startFrame,
+ textEntry.endFrame,
+ self.getEnumValueFromProp("ocarina_song_action_id", textEntry, "ocarinaAction"),
+ textEntry.ocarinaMessageId,
+ )
+ raise PluginError("ERROR: Unknown text type!")
+
+ def setCutsceneData(self, csObjects: dict[str, list[Object]], csProp: "OOTCutsceneProperty"):
+ self.setActorCueListData(csObjects, True)
+ self.setActorCueListData(csObjects, False)
+ self.setCameraShotData(csObjects)
+
+ # don't process the cutscene empty if we don't want its data
+ if self.motionOnly:
+ return
+
+ if csProp.csUseDestination:
+ self.destination = CutsceneCmdDestination(
+ csProp.csDestinationStartFrame,
+ csProp.csDestinationStartFrame + 1,
+ self.getEnumValueFromProp("cs_destination", csProp, "csDestination"),
+ )
+ self.totalEntries += 1
+
+ if not is_oot_features() and csProp.cs_give_tatl:
+ self.give_tatl = CutsceneCmdGiveTatl(
+ csProp.cs_give_tatl_start_frame,
+ csProp.cs_give_tatl_start_frame + 1,
+ "true" if csProp.cs_give_tatl else "false",
+ )
+ self.totalEntries += 1
+
+ self.frameCount = csProp.csEndFrame
+ self.totalEntries += len(csProp.csLists)
+
+ prop_map = {
+ "Transition": "transition_list",
+ "MotionBlurList": "motion_blur_list",
+ "CreditsSceneList": "credits_scene_list",
+ "TransitionGeneralList": "trans_general_list",
+ "ModifySeqList": "mod_seq_list",
+ }
+
+ for entry in csProp.csLists:
+ match entry.listType:
+ case "StartSeqList" | "StopSeqList" | "FadeOutSeqList" | "StartAmbienceList" | "FadeOutAmbienceList":
+ isFadeOutSeq = entry.listType == "FadeOutSeqList"
+ is_start_ambience = entry.listType == "StartAmbienceList"
+ is_fade_out_ambience = entry.listType == "FadeOutAmbienceList"
+ cmdList = cmdToClass[entry.listType](None, None)
+ cmdList.entryTotal = len(entry.seqList)
+
+ if not isFadeOutSeq and not is_start_ambience and not is_fade_out_ambience:
+ cmdList.type = "start" if entry.listType == "StartSeqList" else "stop"
+
+ for elem in entry.seqList:
+ data = cmdToClass[entry.listType.removesuffix("List")](elem.startFrame, elem.endFrame)
+
+ if not is_start_ambience and not is_fade_out_ambience:
+ if isFadeOutSeq:
+ data.seqPlayer = self.getEnumValueFromProp(
+ "cs_fade_out_seq_player", elem, "csSeqPlayer"
+ )
+ else:
+ data.type = cmdList.type
+ data.seqId = self.getEnumValueFromProp("seq_id", elem, "csSeqID")
+
+ cmdList.entries.append(data)
+
+ if is_start_ambience:
+ self.start_ambience_list.append(cmdList)
+ elif is_fade_out_ambience:
+ self.fade_out_ambience_list.append(cmdList)
+ elif isFadeOutSeq:
+ self.fadeSeqList.append(cmdList)
+ else:
+ self.seqList.append(cmdList)
+ case _:
+ if entry.listType in prop_map.keys():
+ prop_name = prop_map[entry.listType]
+ cmdList = cmdToClass[entry.listType](None, None)
+ else:
+ prop_name = entry.listType[0].lower() + entry.listType[1:]
+ cmdList = cmdToClass[entry.listType](None, None)
+ curList = getattr(entry, prop_name)
+ cmdList.entryTotal = len(curList)
+ for elem in curList:
+ match entry.listType:
+ case "ModifySeqList":
+ cmdList.entries.append(
+ CutsceneCmdModifySeq(
+ elem.startFrame,
+ elem.startFrame + 1,
+ self.getEnumValueFromProp("cs_modify_seq_type", elem, "mod_seq_type"),
+ )
+ )
+ case "CreditsSceneList":
+ cmdList.entries.append(
+ CutsceneCmdChooseCreditsScenes(
+ elem.startFrame,
+ elem.startFrame + 1,
+ self.getEnumValueFromProp("cs_credits_scene_type", elem, "credits_scene_type"),
+ )
+ )
+ case "TransitionGeneralList":
+ cmdList.entries.append(
+ CutsceneCmdTransitionGeneral(
+ elem.startFrame,
+ elem.endFrame,
+ self.getEnumValueFromProp("cs_transition_general", elem, "trans_general_type"),
+ exportColor(elem.trans_color[0:3]),
+ )
+ )
+ case "MotionBlurList":
+ cmdList.entries.append(
+ CutsceneCmdMotionBlur(
+ elem.startFrame,
+ elem.endFrame,
+ self.getEnumValueFromProp("cs_motion_blur_type", elem, "blur_type"),
+ )
+ )
+ case "Transition":
+ cmdList.entries.append(
+ CutsceneCmdTransition(
+ elem.startFrame,
+ elem.endFrame,
+ self.getEnumValueFromProp("cs_transition_type", elem, "transition_type"),
+ )
+ )
+ case "TextList":
+ cmdList.entries.append(self.getNewTextCmd(elem))
+ case "LightSettingsList":
+ cmdList.entries.append(
+ CutsceneCmdLightSetting(
+ elem.startFrame, elem.endFrame, False, elem.lightSettingsIndex
+ )
+ )
+ case "TimeList":
+ cmdList.entries.append(
+ CutsceneCmdTime(elem.startFrame, elem.endFrame, elem.hour, elem.minute)
+ )
+ case "MiscList":
+ cmdList.entries.append(
+ CutsceneCmdMisc(
+ elem.startFrame,
+ elem.endFrame,
+ self.getEnumValueFromProp("cs_misc_type", elem, "csMiscType"),
+ )
+ )
+ case "RumbleList":
+ if not is_oot_features():
+ rumble_type = self.getEnumValueFromProp("cs_rumble_type", elem, "rumble_type")
+ else:
+ rumble_type = None
+
+ cmdList.entries.append(
+ CutsceneCmdRumbleController(
+ elem.startFrame,
+ elem.startFrame + 1,
+ elem.rumbleSourceStrength,
+ elem.rumbleDuration,
+ elem.rumbleDecreaseRate,
+ rumble_type,
+ )
+ )
+ case _:
+ raise PluginError("ERROR: Unknown Cutscene List Type!")
+ getattr(self, cmdToList[entry.listType]).append(cmdList)
diff --git a/fast64_internal/z64/exporter/cutscene/misc.py b/fast64_internal/z64/exporter/cutscene/misc.py
new file mode 100644
index 000000000..bfa0e4194
--- /dev/null
+++ b/fast64_internal/z64/exporter/cutscene/misc.py
@@ -0,0 +1,424 @@
+from dataclasses import dataclass, field
+from typing import Optional
+from ....game_data import game_data
+from ....utility import PluginError, indent
+from ...utility import is_oot_features, parseColor
+from ...cutscene.motion.utility import getInteger
+from .common import CutsceneCmdBase
+
+
+@dataclass
+class CutsceneCmdMisc(CutsceneCmdBase):
+ """This class contains a single misc command entry"""
+
+ type: str # see ``CutsceneMiscType`` in decomp
+ paramNumber: int = field(init=False, default=14)
+
+ @staticmethod
+ def from_params(params: list[str]):
+ return CutsceneCmdMisc(
+ getInteger(params[1]), getInteger(params[2]), CutsceneCmdBase.getEnumValue("cs_misc_type", params[0])
+ )
+
+ def getCmd(self):
+ self.validateFrames()
+ pad_amount = 11 if is_oot_features() else 1
+ return indent * 3 + (f"CS_MISC({self.type}, {self.startFrame}, {self.endFrame}" + ", 0" * pad_amount + "),\n")
+
+
+@dataclass
+class CutsceneCmdLightSetting(CutsceneCmdBase):
+ """This class contains Light Setting command data"""
+
+ isLegacy: bool
+ lightSetting: int
+ paramNumber: int = field(init=False, default=11)
+
+ @staticmethod
+ def from_params(params: list[str], isLegacy: bool):
+ lightSetting = getInteger(params[0])
+ return CutsceneCmdLightSetting(
+ getInteger(params[1]), getInteger(params[2]), isLegacy, lightSetting - 1 if isLegacy else lightSetting
+ )
+
+ def getCmd(self):
+ self.validateFrames(False)
+ pad_amount = 12 if is_oot_features() else 1
+ return indent * 3 + (f"CS_LIGHT_SETTING({self.lightSetting}, {self.startFrame}" + ", 0" * pad_amount + "),\n")
+
+
+@dataclass
+class CutsceneCmdTime(CutsceneCmdBase):
+ """This class contains Time Ocarina Action command data"""
+
+ hour: int
+ minute: int
+ paramNumber: int = field(init=False, default=5)
+
+ @staticmethod
+ def from_params(params: list[str]):
+ return CutsceneCmdTime(
+ getInteger(params[1]),
+ getInteger(params[2]),
+ getInteger(params[3]),
+ getInteger(params[4]),
+ )
+
+ def getCmd(self):
+ self.validateFrames(False)
+ return indent * 3 + f"CS_TIME(0, {self.startFrame}, 0, {self.hour}, {self.minute}),\n"
+
+
+@dataclass
+class CutsceneCmdRumbleController(CutsceneCmdBase):
+ """This class contains Rumble Controller command data"""
+
+ sourceStrength: int
+ duration: int
+ decreaseRate: int
+ type: Optional[str]
+ paramNumber: int = field(init=False, default=8)
+
+ @staticmethod
+ def from_params(params: list[str]):
+ return CutsceneCmdRumbleController(
+ getInteger(params[1]),
+ getInteger(params[2]),
+ getInteger(params[3]),
+ getInteger(params[4]),
+ getInteger(params[5]),
+ params[0] if not is_oot_features() else None,
+ )
+
+ def getCmd(self):
+ self.validateFrames(False)
+ if is_oot_features():
+ return indent * 3 + (
+ f"CS_RUMBLE_CONTROLLER("
+ + f"0, {self.startFrame}, 0, {self.sourceStrength}, {self.duration}, {self.decreaseRate}, 0, 0),\n"
+ )
+ else:
+ return indent * 3 + (
+ f"CS_RUMBLE("
+ + f"{self.type}, {self.startFrame}, {self.endFrame}, "
+ + f"{self.sourceStrength}, {self.duration}, {self.decreaseRate}),\n"
+ )
+
+
+@dataclass
+class CutsceneCmdMiscList(CutsceneCmdBase):
+ """This class contains Misc command data"""
+
+ entryTotal: int = field(init=False, default=0)
+ entries: list[CutsceneCmdMisc] = field(init=False, default_factory=list)
+ paramNumber: int = field(init=False, default=1)
+ listName: str = field(init=False, default="miscList")
+
+ @staticmethod
+ def from_params(params: list[str]):
+ new = CutsceneCmdMiscList(None, None)
+ new.entryTotal = getInteger(params[0])
+ return new
+
+ def getCmd(self):
+ if len(self.entries) == 0:
+ raise PluginError("ERROR: Entry list is empty!")
+ return self.getGenericListCmd("CS_MISC_LIST", self.entryTotal) + "".join(
+ entry.getCmd() for entry in self.entries
+ )
+
+
+@dataclass
+class CutsceneCmdLightSettingList(CutsceneCmdBase):
+ """This class contains Light Setting List command data"""
+
+ entryTotal: int = field(init=False, default=0)
+ entries: list[CutsceneCmdLightSetting] = field(init=False, default_factory=list)
+ paramNumber: int = field(init=False, default=1)
+ listName: str = field(init=False, default="lightSettingsList")
+
+ @staticmethod
+ def from_params(params: list[str]):
+ new = CutsceneCmdLightSettingList(None, None)
+ new.entryTotal = getInteger(params[0])
+ return new
+
+ def getCmd(self):
+ if len(self.entries) == 0:
+ raise PluginError("ERROR: Entry list is empty!")
+ return self.getGenericListCmd("CS_LIGHT_SETTING_LIST", self.entryTotal) + "".join(
+ entry.getCmd() for entry in self.entries
+ )
+
+
+@dataclass
+class CutsceneCmdTimeList(CutsceneCmdBase):
+ """This class contains Time List command data"""
+
+ entryTotal: int = field(init=False, default=0)
+ entries: list[CutsceneCmdTime] = field(init=False, default_factory=list)
+ paramNumber: int = field(init=False, default=1)
+ listName: str = field(init=False, default="timeList")
+
+ @staticmethod
+ def from_params(params: list[str]):
+ new = CutsceneCmdTimeList(None, None)
+ new.entryTotal = getInteger(params[0])
+ return new
+
+ def getCmd(self):
+ if len(self.entries) == 0:
+ raise PluginError("ERROR: Entry list is empty!")
+ return self.getGenericListCmd("CS_TIME_LIST", self.entryTotal) + "".join(
+ entry.getCmd() for entry in self.entries
+ )
+
+
+@dataclass
+class CutsceneCmdRumbleControllerList(CutsceneCmdBase):
+ """This class contains Rumble Controller List command data"""
+
+ entryTotal: int = field(init=False, default=0)
+ entries: list[CutsceneCmdRumbleController] = field(init=False, default_factory=list)
+ paramNumber: int = field(init=False, default=1)
+ listName: str = field(init=False, default="rumbleList")
+
+ @staticmethod
+ def from_params(params: list[str]):
+ new = CutsceneCmdRumbleControllerList(None, None)
+ new.entryTotal = getInteger(params[0])
+ return new
+
+ def getCmd(self):
+ if len(self.entries) == 0:
+ raise PluginError("ERROR: Entry list is empty!")
+ suffix = "_CONTROLLER" if is_oot_features() else ""
+ return self.getGenericListCmd(f"CS_RUMBLE{suffix}_LIST", self.entryTotal) + "".join(
+ entry.getCmd() for entry in self.entries
+ )
+
+
+@dataclass
+class CutsceneCmdDestination(CutsceneCmdBase):
+ """This class contains Destination command data"""
+
+ id: str
+ paramNumber: int = field(init=False, default=3)
+ listName: str = field(init=False, default="destination")
+
+ @staticmethod
+ def from_params(params: list[str]):
+ return CutsceneCmdDestination(
+ getInteger(params[1]), getInteger(params[2]), CutsceneCmdBase.getEnumValue("cs_destination", params[0])
+ )
+
+ def getCmd(self):
+ self.validateFrames(False)
+ if is_oot_features():
+ return indent * 2 + f"CS_DESTINATION({self.id}, {self.startFrame}, {self.endFrame}),\n"
+ else:
+ return (
+ indent * 2
+ + f"CS_DESTINATION_LIST(1),\n"
+ + indent * 3
+ + f"CS_DESTINATION({self.id}, {self.startFrame}, {self.endFrame}),\n"
+ )
+
+
+@dataclass
+class CutsceneCmdTransition(CutsceneCmdBase):
+ """This class contains Transition command data"""
+
+ type: str
+ paramNumber: int = field(init=False, default=3)
+
+ @staticmethod
+ def from_params(params: list[str]):
+ return CutsceneCmdTransition(
+ getInteger(params[1]), getInteger(params[2]), CutsceneCmdBase.getEnumValue("cs_transition_type", params[0])
+ )
+
+ def to_c(self):
+ self.validateFrames()
+ return indent * 3 + f"CS_TRANSITION({self.type}, {self.startFrame}, {self.endFrame}),\n"
+
+
+@dataclass
+class CutsceneCmdTransitionList(CutsceneCmdBase):
+ """This class contains Transition list command data"""
+
+ entryTotal: int = field(init=False, default=0)
+ entries: list[CutsceneCmdTransition] = field(default_factory=list)
+ paramNumber: int = field(init=False, default=1)
+ listName: str = field(init=False, default="transitionList")
+
+ @staticmethod
+ def from_params(params: list[str]):
+ new = CutsceneCmdTransitionList(None, None)
+ new.entryTotal = getInteger(params[0])
+ return new
+
+ def getCmd(self):
+ if game_data.z64.is_oot():
+ return "".join(entry.to_c() for entry in self.entries)
+ else:
+ return (
+ indent * 2
+ + f"CS_TRANSITION_LIST({len(self.entries)}),\n"
+ + "".join(entry.to_c() for entry in self.entries)
+ )
+
+
+@dataclass
+class CutsceneCmdMotionBlur(CutsceneCmdBase):
+ """This class contains motion blur command data"""
+
+ type: str
+ paramNumber: int = field(init=False, default=3)
+
+ @staticmethod
+ def from_params(params: list[str]):
+ return CutsceneCmdMotionBlur(
+ getInteger(params[1]),
+ getInteger(params[2]),
+ CutsceneCmdBase.getEnumValue("cs_motion_blur_type", params[0]),
+ )
+
+ def to_c(self):
+ return indent * 3 + f"CS_MOTION_BLUR({self.type}, {self.startFrame}, {self.endFrame}),\n"
+
+
+@dataclass
+class CutsceneCmdMotionBlurList(CutsceneCmdBase):
+ """This class contains motion blur list command data"""
+
+ entryTotal: int = field(init=False, default=0)
+ entries: list[CutsceneCmdMotionBlur] = field(default_factory=list)
+ paramNumber: int = field(init=False, default=1)
+ listName: str = field(init=False, default="motion_blur_list")
+
+ @staticmethod
+ def from_params(params: list[str]):
+ new = CutsceneCmdMotionBlurList(None, None)
+ new.entryTotal = getInteger(params[0])
+ return new
+
+ def getCmd(self):
+ return (
+ indent * 2
+ + f"CS_MOTION_BLUR_LIST({len(self.entries)}),\n"
+ + "".join(entry.to_c() for entry in self.entries)
+ )
+
+
+@dataclass
+class CutsceneCmdChooseCreditsScenes(CutsceneCmdBase):
+ """This class contains choose credits scenes command data"""
+
+ type: str
+ paramNumber: int = field(init=False, default=3)
+
+ @staticmethod
+ def from_params(params: list[str]):
+ return CutsceneCmdChooseCreditsScenes(
+ getInteger(params[1]),
+ getInteger(params[2]),
+ CutsceneCmdBase.getEnumValue("cs_credits_scene_type", params[0]),
+ )
+
+ def to_c(self):
+ return indent * 3 + f"CS_CHOOSE_CREDITS_SCENES({self.type}, {self.startFrame}, {self.endFrame}),\n"
+
+
+@dataclass
+class CutsceneCmdChooseCreditsScenesList(CutsceneCmdBase):
+ """This class contains choose credits scenes list command data"""
+
+ entryTotal: int = field(init=False, default=0)
+ entries: list[CutsceneCmdChooseCreditsScenes] = field(default_factory=list)
+ paramNumber: int = field(init=False, default=1)
+ listName: str = field(init=False, default="credits_scene_list")
+
+ @staticmethod
+ def from_params(params: list[str]):
+ new = CutsceneCmdChooseCreditsScenesList(None, None)
+ new.entryTotal = getInteger(params[0])
+ return new
+
+ def getCmd(self):
+ return (
+ indent * 2
+ + f"CS_CHOOSE_CREDITS_SCENES_LIST({len(self.entries)}),\n"
+ + "".join(entry.to_c() for entry in self.entries)
+ )
+
+
+@dataclass
+class CutsceneCmdTransitionGeneral(CutsceneCmdBase):
+ """This class contains transition general command data"""
+
+ type: str
+ rgb: list[int]
+ paramNumber: int = field(init=False, default=6)
+
+ @staticmethod
+ def from_params(params: list[str]):
+ rgb = list(parseColor((getInteger(params[3]), getInteger(params[4]), getInteger(params[5]))))
+ rgb.append(1)
+ return CutsceneCmdTransitionGeneral(
+ getInteger(params[1]),
+ getInteger(params[2]),
+ CutsceneCmdBase.getEnumValue("cs_transition_general", params[0]),
+ rgb,
+ )
+
+ def to_c(self):
+ color = ", ".join(f"{c}" for c in self.rgb)
+ return indent * 3 + f"CS_TRANSITION_GENERAL({self.type}, {self.startFrame}, {self.endFrame}, {color}),\n"
+
+
+@dataclass
+class CutsceneCmdTransitionGeneralList(CutsceneCmdBase):
+ """This class contains transition general list command data"""
+
+ entryTotal: int = field(init=False, default=0)
+ entries: list[CutsceneCmdTransitionGeneral] = field(default_factory=list)
+ paramNumber: int = field(init=False, default=1)
+ listName: str = field(init=False, default="transition_general_list")
+
+ @staticmethod
+ def from_params(params: list[str]):
+ new = CutsceneCmdTransitionGeneralList(None, None)
+ new.entryTotal = getInteger(params[0])
+ return new
+
+ def getCmd(self):
+ return (
+ indent * 2
+ + f"CS_TRANSITION_GENERAL_LIST({len(self.entries)}),\n"
+ + "".join(entry.to_c() for entry in self.entries)
+ )
+
+
+@dataclass
+class CutsceneCmdGiveTatl(CutsceneCmdBase):
+ """This class contains give tatl command data"""
+
+ giveTatl: bool
+ paramNumber: int = field(init=False, default=3)
+
+ @staticmethod
+ def from_params(params: list[str]):
+ return CutsceneCmdGiveTatl(
+ getInteger(params[1]),
+ getInteger(params[2]),
+ params[0] in {"true", "1"},
+ )
+
+ def getCmd(self):
+ return (
+ indent * 2
+ + f"CS_GIVE_TATL_LIST(1),\n"
+ + indent * 3
+ + f"CS_GIVE_TATL({self.giveTatl}, {self.startFrame}, {self.endFrame}),\n"
+ )
diff --git a/fast64_internal/z64/exporter/cutscene/seq.py b/fast64_internal/z64/exporter/cutscene/seq.py
new file mode 100644
index 000000000..0d58d8347
--- /dev/null
+++ b/fast64_internal/z64/exporter/cutscene/seq.py
@@ -0,0 +1,207 @@
+from dataclasses import dataclass, field
+from typing import Optional
+from ....utility import PluginError, indent
+from ...cutscene.motion.utility import getInteger
+from .common import CutsceneCmdBase
+
+
+@dataclass
+class CutsceneCmdStartStopSeq(CutsceneCmdBase):
+ """This class contains Start/Stop Seq command data"""
+
+ isLegacy: bool = field(init=False, default=False)
+ seqId: Optional[str] = field(init=False, default=None)
+ paramNumber: int = field(init=False, default=11)
+ type: Optional[str] = field(init=False, default=None) # "start" or "stop"
+
+ @staticmethod
+ def from_params(params: list[str], type: str, is_legacy: bool):
+ new = CutsceneCmdStartStopSeq(getInteger(params[1]), getInteger(params[2]))
+ new.isLegacy = is_legacy
+ new.seqId = CutsceneCmdBase.getEnumValue("seq_id", params[0], new.isLegacy)
+ new.type = type
+ return new
+
+ def getCmd(self):
+ self.validateFrames()
+ if self.type is None:
+ raise PluginError("ERROR: Type is None!")
+ return self.getGenericSeqCmd(f"CS_{self.type.upper()}_SEQ", self.seqId, self.startFrame, self.endFrame)
+
+
+@dataclass
+class CutsceneCmdFadeSeq(CutsceneCmdBase):
+ """This class contains Fade Seq command data"""
+
+ seqPlayer: str = field(init=False, default=str())
+ paramNumber: int = field(init=False, default=11)
+
+ @staticmethod
+ def from_params(params: list[str]):
+ new = CutsceneCmdFadeSeq(getInteger(params[1]), getInteger(params[2]))
+ new.seqPlayer = CutsceneCmdBase.getEnumValue("cs_fade_out_seq_player", params[0])
+ return new
+
+ def getCmd(self):
+ self.validateFrames()
+ return self.getGenericSeqCmd("CS_FADE_OUT_SEQ", self.seqPlayer, self.startFrame, self.endFrame)
+
+
+@dataclass
+class CutsceneCmdStartStopSeqList(CutsceneCmdBase):
+ """This class contains Start/Stop Seq List command data"""
+
+ entryTotal: int = field(init=False, default=0)
+ type: str = field(init=False, default=str()) # "start" or "stop"
+ entries: list[CutsceneCmdStartStopSeq] = field(init=False, default_factory=list)
+ paramNumber: int = field(init=False, default=1)
+ listName: str = field(init=False, default="seqList")
+
+ @staticmethod
+ def from_params(params: list[str], type: str):
+ new = CutsceneCmdStartStopSeqList(None, None)
+ new.entryTotal = getInteger(params[0])
+ new.type = type
+ return new
+
+ def getCmd(self):
+ if len(self.entries) == 0:
+ raise PluginError("ERROR: Entry list is empty!")
+ return self.getGenericListCmd(f"CS_{self.type.upper()}_SEQ_LIST", self.entryTotal) + "".join(
+ entry.getCmd() for entry in self.entries
+ )
+
+
+@dataclass
+class CutsceneCmdFadeSeqList(CutsceneCmdBase):
+ """This class contains Fade Seq List command data"""
+
+ entryTotal: int = field(init=False, default=0)
+ entries: list[CutsceneCmdFadeSeq] = field(init=False, default_factory=list)
+ paramNumber: int = field(init=False, default=1)
+ listName: str = field(init=False, default="fadeSeqList")
+
+ @staticmethod
+ def from_params(params: list[str]):
+ new = CutsceneCmdFadeSeqList(None, None)
+ new.entryTotal = getInteger(params[0])
+ return new
+
+ def getCmd(self):
+ if len(self.entries) == 0:
+ raise PluginError("ERROR: Entry list is empty!")
+ return self.getGenericListCmd("CS_FADE_OUT_SEQ_LIST", self.entryTotal) + "".join(
+ entry.getCmd() for entry in self.entries
+ )
+
+
+@dataclass
+class CutsceneCmdModifySeq(CutsceneCmdBase):
+ """This class contains modify seq command data"""
+
+ type: str
+ paramNumber: int = field(init=False, default=3)
+
+ @staticmethod
+ def from_params(params: list[str]):
+ return CutsceneCmdModifySeq(
+ getInteger(params[1]), getInteger(params[2]), CutsceneCmdBase.getEnumValue("cs_modify_seq_type", params[0])
+ )
+
+ def to_c(self):
+ return indent * 3 + f"CS_MODIFY_SEQ({self.type}, {self.startFrame}, {self.endFrame}),\n"
+
+
+@dataclass
+class CutsceneCmdModifySeqList(CutsceneCmdBase):
+ """This class contains modify seq list command data"""
+
+ entryTotal: int = field(init=False, default=0)
+ entries: list[CutsceneCmdModifySeq] = field(default_factory=list)
+ paramNumber: int = field(init=False, default=1)
+ listName: str = field(init=False, default="modify_seq_list")
+
+ @staticmethod
+ def from_params(params: list[str]):
+ new = CutsceneCmdModifySeqList(None, None)
+ new.entryTotal = getInteger(params[0])
+ return new
+
+ def getCmd(self):
+ return (
+ indent * 2 + f"CS_MODIFY_SEQ_LIST({len(self.entries)}),\n" + "".join(entry.to_c() for entry in self.entries)
+ )
+
+
+@dataclass
+class CutsceneCmdStartAmbience(CutsceneCmdBase):
+ """This class contains modify seq command data"""
+
+ paramNumber: int = field(init=False, default=3)
+
+ @staticmethod
+ def from_params(params: list[str]):
+ return CutsceneCmdStartAmbience(getInteger(params[1]), getInteger(params[2]))
+
+ def to_c(self):
+ return indent * 3 + f"CS_START_AMBIENCE(0, {self.startFrame}, {self.endFrame}),\n"
+
+
+@dataclass
+class CutsceneCmdStartAmbienceList(CutsceneCmdBase):
+ """This class contains modify seq list command data"""
+
+ entryTotal: int = field(init=False, default=0)
+ entries: list[CutsceneCmdStartAmbience] = field(default_factory=list)
+ paramNumber: int = field(init=False, default=1)
+ listName: str = field(init=False, default="start_ambience_list")
+
+ @staticmethod
+ def from_params(params: list[str]):
+ new = CutsceneCmdStartAmbienceList(None, None)
+ new.entryTotal = getInteger(params[0])
+ return new
+
+ def getCmd(self):
+ return (
+ indent * 2
+ + f"CS_START_AMBIENCE_LIST({len(self.entries)}),\n"
+ + "".join(entry.to_c() for entry in self.entries)
+ )
+
+
+@dataclass
+class CutsceneCmdFadeOutAmbience(CutsceneCmdBase):
+ """This class contains modify seq command data"""
+
+ paramNumber: int = field(init=False, default=3)
+
+ @staticmethod
+ def from_params(params: list[str]):
+ return CutsceneCmdFadeOutAmbience(getInteger(params[1]), getInteger(params[2]))
+
+ def to_c(self):
+ return indent * 3 + f"CS_FADE_OUT_AMBIENCE(0, {self.startFrame}, {self.endFrame}),\n"
+
+
+@dataclass
+class CutsceneCmdFadeOutAmbienceList(CutsceneCmdBase):
+ """This class contains modify seq list command data"""
+
+ entryTotal: int = field(init=False, default=0)
+ entries: list[CutsceneCmdFadeOutAmbience] = field(default_factory=list)
+ paramNumber: int = field(init=False, default=1)
+ listName: str = field(init=False, default="fade_out_ambience_list")
+
+ @staticmethod
+ def from_params(params: list[str]):
+ new = CutsceneCmdFadeOutAmbienceList(None, None)
+ new.entryTotal = getInteger(params[0])
+ return new
+
+ def getCmd(self):
+ return (
+ indent * 2
+ + f"CS_FADE_OUT_AMBIENCE_LIST({len(self.entries)}),\n"
+ + "".join(entry.to_c() for entry in self.entries)
+ )
diff --git a/fast64_internal/oot/exporter/cutscene/text.py b/fast64_internal/z64/exporter/cutscene/text.py
similarity index 50%
rename from fast64_internal/oot/exporter/cutscene/text.py
rename to fast64_internal/z64/exporter/cutscene/text.py
index a19efb96e..16e907594 100644
--- a/fast64_internal/oot/exporter/cutscene/text.py
+++ b/fast64_internal/z64/exporter/cutscene/text.py
@@ -1,15 +1,27 @@
from dataclasses import dataclass, field
+from typing import Optional
from ....utility import PluginError, indent
+from ...utility import is_oot_features
from ...cutscene.motion.utility import getInteger
from .common import CutsceneCmdBase
+mm_cmd_name_to_type = {
+ "CS_TEXT_NONE": "CS_TEXT_TYPE_NONE",
+ "CS_TEXT_DEFAULT": "CS_TEXT_TYPE_DEFAULT",
+ "CS_TEXT_TYPE_1": "CS_TEXT_TYPE_1",
+ "CS_TEXT_OCARINA_ACTION": "CS_TEXT_OCARINA_ACTION",
+ "CS_TEXT_TYPE_3": "CS_TEXT_TYPE_3",
+ "CS_TEXT_BOSSES_REMAINS": "CS_TEXT_TYPE_BOSSES_REMAINS",
+ "CS_TEXT_ALL_NORMAL_MASKS": "CS_TEXT_TYPE_ALL_NORMAL_MASKS",
+}
+
@dataclass
class CutsceneCmdText(CutsceneCmdBase):
"""This class contains Text command data"""
textId: int
- type: str
+ type: Optional[str]
altTextId1: int
altTextId2: int
@@ -17,23 +29,47 @@ class CutsceneCmdText(CutsceneCmdBase):
id: str = field(init=False, default="Text")
@staticmethod
- def from_params(params: list[str]):
- return CutsceneCmdText(
- getInteger(params[1]),
- getInteger(params[2]),
- getInteger(params[0]),
- CutsceneCmdBase.getEnumValue("csTextType", params[3]),
- getInteger(params[4]),
- getInteger(params[5]),
- )
+ def from_params(params: list[str], cmd_name: str):
+ if is_oot_features():
+ return CutsceneCmdText(
+ getInteger(params[1]),
+ getInteger(params[2]),
+ getInteger(params[0]),
+ CutsceneCmdBase.getEnumValue("cs_text_type", params[3]),
+ getInteger(params[4]),
+ getInteger(params[5]),
+ )
+ else:
+ return CutsceneCmdText(
+ getInteger(params[1]),
+ getInteger(params[2]),
+ getInteger(params[0]),
+ CutsceneCmdBase.getEnumValue("cs_text_type", mm_cmd_name_to_type[cmd_name]),
+ getInteger(params[3]),
+ getInteger(params[4]),
+ )
def getCmd(self):
self.validateFrames()
- return indent * 3 + (
- f"CS_TEXT("
- + f"{self.textId}, {self.startFrame}, {self.endFrame}, {self.type}, {self.altTextId1}, {self.altTextId2}"
- + "),\n"
- )
+ if is_oot_features():
+ command = (
+ f"CS_TEXT("
+ + f"{self.textId}, {self.startFrame}, {self.endFrame}, {self.type}, {self.altTextId1}, {self.altTextId2}"
+ + "),\n"
+ )
+ else:
+ command = f"{self.type}("
+ if self.type not in {"CS_TEXT_TYPE_1", "CS_TEXT_TYPE_3"}:
+ command = command.replace("_TYPE", "")
+ match self.type:
+ case "CS_TEXT_TYPE_DEFAULT" | "CS_TEXT_TYPE_1" | "CS_TEXT_TYPE_3":
+ command += (
+ f"{self.textId}, {self.startFrame}, {self.endFrame}, {self.altTextId1}, {self.altTextId2}"
+ )
+ case "CS_TEXT_TYPE_BOSSES_REMAINS" | "CS_TEXT_TYPE_ALL_NORMAL_MASKS":
+ command += f"{self.textId}, {self.startFrame}, {self.endFrame}, {self.altTextId1}"
+ command += "),\n"
+ return indent * 3 + command
@dataclass
@@ -44,7 +80,7 @@ class CutsceneCmdTextNone(CutsceneCmdBase):
id: str = field(init=False, default="None")
@staticmethod
- def from_params(params: list[str]):
+ def from_params(params: list[str], cmd_name: str):
return CutsceneCmdTextNone(getInteger(params[0]), getInteger(params[1]))
def getCmd(self):
@@ -63,11 +99,11 @@ class CutsceneCmdTextOcarinaAction(CutsceneCmdBase):
id: str = field(init=False, default="OcarinaAction")
@staticmethod
- def from_params(params: list[str]):
+ def from_params(params: list[str], cmd_name: str):
return CutsceneCmdTextOcarinaAction(
getInteger(params[1]),
getInteger(params[2]),
- CutsceneCmdBase.getEnumValue("ocarinaSongActionId", params[0]),
+ CutsceneCmdBase.getEnumValue("ocarina_song_action_id", params[0]),
getInteger(params[3]),
)
@@ -93,7 +129,7 @@ class CutsceneCmdTextList(CutsceneCmdBase):
@staticmethod
def from_params(params: list[str]):
- new = CutsceneCmdTextList()
+ new = CutsceneCmdTextList(None, None)
new.entryTotal = getInteger(params[0])
return new
diff --git a/fast64_internal/oot/exporter/decomp_edit/__init__.py b/fast64_internal/z64/exporter/decomp_edit/__init__.py
similarity index 96%
rename from fast64_internal/oot/exporter/decomp_edit/__init__.py
rename to fast64_internal/z64/exporter/decomp_edit/__init__.py
index 95af7f77f..15792dd5a 100644
--- a/fast64_internal/oot/exporter/decomp_edit/__init__.py
+++ b/fast64_internal/z64/exporter/decomp_edit/__init__.py
@@ -2,7 +2,7 @@
import re
import shutil
-from ...oot_utility import ExportInfo, RemoveInfo, getSceneDirFromLevelName
+from ...utility import ExportInfo, RemoveInfo, getSceneDirFromLevelName
from ..scene import Scene
from ..file import SceneFile
from .scene_table import SceneTableUtility
diff --git a/fast64_internal/oot/exporter/decomp_edit/config.py b/fast64_internal/z64/exporter/decomp_edit/config.py
similarity index 95%
rename from fast64_internal/oot/exporter/decomp_edit/config.py
rename to fast64_internal/z64/exporter/decomp_edit/config.py
index a11eec82e..7c1069c63 100644
--- a/fast64_internal/oot/exporter/decomp_edit/config.py
+++ b/fast64_internal/z64/exporter/decomp_edit/config.py
@@ -2,7 +2,7 @@
import re
from ....utility import PluginError, readFile, writeFile
-from ...scene.properties import OOTBootupSceneOptions
+from ...scene.properties import OOT_BootupSceneOptions
class Config:
@@ -64,8 +64,8 @@ def writeBootupSettings(
writeFile(configPath, data)
@staticmethod
- def setBootupScene(configPath: str, entranceIndex: str, options: "OOTBootupSceneOptions"):
- # ``options`` argument type: OOTBootupSceneOptions
+ def setBootupScene(configPath: str, entranceIndex: str, options: "OOT_BootupSceneOptions"):
+ # ``options`` argument type: OOT_BootupSceneOptions
linkAge = "LINK_AGE_CHILD"
timeOfDay = "NEXT_TIME_NONE"
cutsceneIndex = "0xFFEF"
@@ -108,7 +108,7 @@ def clearBootupScene(configPath: str):
)
@staticmethod
- def getParamsFromOptions(options: "OOTBootupSceneOptions") -> tuple[str, str]:
+ def getParamsFromOptions(options: "OOT_BootupSceneOptions") -> tuple[str, str]:
timeOfDay = (
"NEXT_TIME_DAY"
if options.headerOption == "Child Day" or options.headerOption == "Adult Day"
diff --git a/fast64_internal/oot/exporter/decomp_edit/scene_table.py b/fast64_internal/z64/exporter/decomp_edit/scene_table.py
similarity index 83%
rename from fast64_internal/oot/exporter/decomp_edit/scene_table.py
rename to fast64_internal/z64/exporter/decomp_edit/scene_table.py
index ccdf43da0..3b85e5a94 100644
--- a/fast64_internal/oot/exporter/decomp_edit/scene_table.py
+++ b/fast64_internal/z64/exporter/decomp_edit/scene_table.py
@@ -4,7 +4,9 @@
from dataclasses import dataclass, field
from typing import Optional
from ....utility import PluginError, writeFile
-from ...oot_constants import ootEnumSceneID, ootSceneNameToID
+from ....game_data import game_data
+from ...utility import is_oot_features
+from ...constants import ootEnumSceneID, ootSceneNameToID, mm_enum_scene_id, mm_scene_name_to_id
ADDED_SCENES_COMMENT = "// Added scenes"
@@ -13,8 +15,13 @@ def get_original_index(enum_value: str) -> Optional[int]:
"""
Returns the original index of a specific scene
"""
+ if game_data.z64.is_oot():
+ enum_scene_id = ootEnumSceneID
+ else:
+ enum_scene_id = mm_enum_scene_id
+
for index, scene_enum in enumerate(
- [elem[0] for elem in ootEnumSceneID[1:]]
+ [elem[0] for elem in enum_scene_id[1:]]
): # ignore first value in array ('Custom')
if scene_enum == enum_value:
return index
@@ -22,7 +29,10 @@ def get_original_index(enum_value: str) -> Optional[int]:
def get_scene_enum_from_name(scene_name: str):
- return ootSceneNameToID.get(scene_name, f"SCENE_{scene_name.upper()}")
+ if game_data.z64.is_oot():
+ return ootSceneNameToID.get(scene_name, f"SCENE_{scene_name.upper()}")
+ else:
+ return mm_scene_name_to_id.get(scene_name, f"SCENE_{scene_name.upper()}")
@dataclass
@@ -46,10 +56,22 @@ def from_line(original_line: str):
index = original_line.index(macro_start) + len(macro_start)
parsed = original_line[index:].removesuffix(")")
- params = parsed.split(", ")
- assert len(params) == 6
+ if is_oot_features():
+ params = parsed.split(", ")
+ assert len(params) == 6
+ return SceneTableEntry(*params)
+ else:
+ split = parsed.split(", ")
+ params = []
+ for i, elem in enumerate(split):
+ if i == 5:
+ break
+ else:
+ params.append(elem)
+ params.append(", ".join(split[i:]))
+ assert len(params) == 6
+ return SceneTableEntry(params[0], params[2], params[1], params[3], params[4], params[5])
- return SceneTableEntry(*params)
else:
raise PluginError("ERROR: This line is not a scene table entry!")
@@ -66,11 +88,18 @@ def from_scene(scene_name: str, draw_config: str, title_card_name: str):
def to_c(self, index: int):
"""Returns the entry as C code"""
- return (
- f"/* 0x{index:02X} */ "
- f"DEFINE_SCENE({self.spec_name}, {self.title_card_name}, {self.enum_value}, "
- f"{self.draw_config}, {self.unk1}, {self.unk2})"
- )
+ if is_oot_features():
+ return (
+ f"/* 0x{index:02X} */ "
+ f"DEFINE_SCENE({self.spec_name}, {self.title_card_name}, {self.enum_value}, "
+ f"{self.draw_config}, {self.unk1}, {self.unk2})"
+ )
+ else:
+ return (
+ f"/* 0x{index:02X} */ "
+ f"DEFINE_SCENE({self.spec_name}, {self.enum_value}, {self.title_card_name}, "
+ f"{self.draw_config}, {self.unk1}, {self.unk2})"
+ )
@dataclass
@@ -82,9 +111,14 @@ class SceneTableSection:
def to_c(self, index: int):
directive = f"{self.directive}\n" if self.directive else ""
- terminator = "\n#endif" if self.directive and self.directive.startswith("#if") else ""
+ terminator = "\n#endif" if self.directive is not None and self.directive.startswith("#if") else ""
entry_string = "\n".join(entry.to_c(index + i) for i, entry in enumerate(self.entries))
- return f"{directive}{entry_string}{terminator}\n\n"
+ section_c = f"{directive}{entry_string}{terminator}\n"
+
+ if game_data.z64.is_oot():
+ section_c += "\n"
+
+ return section_c
@dataclass
@@ -137,7 +171,7 @@ def new(export_path: str):
if current_section: # handles non-directive section preceding directive section
sections.append(current_section)
current_section = SceneTableSection(line)
- else:
+ elif "DEFINE_SCENE_UNSET" not in line:
if not current_section:
current_section = SceneTableSection(None)
current_section.entries.append(SceneTableEntry.from_line(line))
@@ -251,12 +285,15 @@ class SceneTableUtility:
@staticmethod
def get_draw_config(scene_name: str):
"""Read draw config from scene table"""
+ if game_data.z64.is_oot():
+ scene_name = f"{scene_name}_scene"
+
scene_table = SceneTable.new(
os.path.join(bpy.path.abspath(bpy.context.scene.ootDecompPath), "include/tables/scene_table.h")
)
spec_dict = {entry.spec_name: entry for entry in scene_table.get_entries_flattened()}
- entry = spec_dict.get(f"{scene_name}_scene")
+ entry = spec_dict.get(f"{scene_name}")
if entry is not None:
return entry.draw_config
diff --git a/fast64_internal/oot/exporter/decomp_edit/spec.py b/fast64_internal/z64/exporter/decomp_edit/spec.py
similarity index 99%
rename from fast64_internal/oot/exporter/decomp_edit/spec.py
rename to fast64_internal/z64/exporter/decomp_edit/spec.py
index f6b122980..fa28c35a9 100644
--- a/fast64_internal/oot/exporter/decomp_edit/spec.py
+++ b/fast64_internal/z64/exporter/decomp_edit/spec.py
@@ -6,7 +6,7 @@
from pathlib import Path
from typing import Optional
from ....utility import PluginError, writeFile, indent
-from ...oot_utility import ExportInfo, getSceneDirFromLevelName
+from ...utility import ExportInfo, getSceneDirFromLevelName
from ..scene import Scene
from ..file import SceneFile
diff --git a/fast64_internal/oot/exporter/file.py b/fast64_internal/z64/exporter/file.py
similarity index 100%
rename from fast64_internal/oot/exporter/file.py
rename to fast64_internal/z64/exporter/file.py
diff --git a/fast64_internal/oot/exporter/room/__init__.py b/fast64_internal/z64/exporter/room/__init__.py
similarity index 88%
rename from fast64_internal/oot/exporter/room/__init__.py
rename to fast64_internal/z64/exporter/room/__init__.py
index 3fe3d6c2e..340a02240 100644
--- a/fast64_internal/oot/exporter/room/__init__.py
+++ b/fast64_internal/z64/exporter/room/__init__.py
@@ -3,10 +3,11 @@
from mathutils import Matrix
from bpy.types import Object
from ....utility import PluginError, CData, indent
+from ....game_data import game_data
from ....f3d.f3d_gbi import ScrollMethod, TextureExportSettings
-from ...room.properties import OOTRoomHeaderProperty
-from ...oot_object import addMissingObjectsToAllRoomHeaders
-from ...oot_model_classes import OOTModel, OOTGfxFormatter
+from ...room.properties import Z64_RoomHeaderProperty
+from ...object import addMissingObjectsToAllRoomHeaders
+from ...model_classes import OOTModel, OOTGfxFormatter
from ..file import RoomFile
from ..utility import Utility, altHeaderList
from .header import RoomAlternateHeader, RoomHeader
@@ -57,19 +58,20 @@ def new(
)
hasAlternateHeaders = False
- for i, header in enumerate(altHeaderList, 1):
- altP: OOTRoomHeaderProperty = getattr(altProp, f"{header}Header")
- if not altP.usePreviousHeader:
- hasAlternateHeaders = True
- newRoomHeader = RoomHeader.new(
- f"{name}_header{i:02}",
- altP,
- sceneObj,
- roomObj,
- transform,
- i,
- )
- setattr(altHeader, header, newRoomHeader)
+ if game_data.z64.is_oot():
+ for i, header in enumerate(altHeaderList, 1):
+ altP: Z64_RoomHeaderProperty = getattr(altProp, f"{header}Header")
+ if not altP.usePreviousHeader:
+ hasAlternateHeaders = True
+ newRoomHeader = RoomHeader.new(
+ f"{name}_header{i:02}",
+ altP,
+ sceneObj,
+ roomObj,
+ transform,
+ i,
+ )
+ setattr(altHeader, header, newRoomHeader)
altHeader.cutscenes = [
RoomHeader.new(
@@ -80,7 +82,7 @@ def new(
transform,
i,
)
- for i, csHeader in enumerate(altProp.cutsceneHeaders, 4)
+ for i, csHeader in enumerate(altProp.cutsceneHeaders, game_data.z64.cs_index_start)
]
hasAlternateHeaders = True if len(altHeader.cutscenes) > 0 else hasAlternateHeaders
@@ -169,7 +171,8 @@ def getRoomMainC(self):
+ "\n};\n\n"
)
- roomHeaders.insert(0, (self.mainHeader, "Child Day (Default)"))
+ header_name = "Child Day (Default)" if game_data.z64.is_oot() else "Default"
+ roomHeaders.insert(0, (self.mainHeader, header_name))
for i, (curHeader, headerDesc) in enumerate(roomHeaders):
if curHeader is not None:
roomC.source += "/**\n * " + f"Header {headerDesc}\n" + "*/\n"
diff --git a/fast64_internal/oot/exporter/room/header.py b/fast64_internal/z64/exporter/room/header.py
similarity index 60%
rename from fast64_internal/oot/exporter/room/header.py
rename to fast64_internal/z64/exporter/room/header.py
index 5de9b738f..83b01387e 100644
--- a/fast64_internal/oot/exporter/room/header.py
+++ b/fast64_internal/z64/exporter/room/header.py
@@ -4,11 +4,12 @@
from typing import Optional
from mathutils import Matrix
from bpy.types import Object
-from ....utility import CData, indent
-from ...oot_utility import getObjectList
-from ...oot_constants import ootData
-from ...room.properties import OOTRoomHeaderProperty
-from ...actor.properties import OOTActorProperty
+from ....utility import PluginError, CData, indent
+from ....game_data import game_data
+from ...utility import getObjectList, is_oot_features, getEvalParams
+from ...constants import halfday_bits_all_dawns, halfday_bits_all_nights, enum_to_halfday_bits
+from ...room.properties import Z64_RoomHeaderProperty
+from ...actor.properties import Z64_ActorProperty
from ..utility import Utility
from ..actor import Actor
@@ -26,9 +27,15 @@ class RoomInfos:
roomBehavior: str
playerIdleType: str
- disableWarpSongs: bool
showInvisActors: bool
+ # OoT
+ disableWarpSongs: bool
+
+ # MM
+ enable_pos_lights: bool
+ enable_storm: bool
+
### Skybox And Time ###
disableSky: bool
@@ -45,14 +52,27 @@ class RoomInfos:
strength: int
@staticmethod
- def new(props: Optional[OOTRoomHeaderProperty]):
+ def new(props: Optional[Z64_RoomHeaderProperty]):
+ disableWarpSongs = False
+ enable_pos_lights = False
+ enable_storm = False
+
+ if game_data.z64.is_oot():
+ disableWarpSongs = props.disableWarpSongs
+
+ if not is_oot_features():
+ enable_pos_lights = props.enable_pos_lights
+ enable_storm = props.enable_storm
+
return RoomInfos(
props.roomIndex,
props.roomShape,
- Utility.getPropValue(props, "roomBehaviour"),
- Utility.getPropValue(props, "linkIdleMode"),
- props.disableWarpSongs,
+ Utility.getPropValue(props, "roomBehaviour", "roomBehaviourCustom"),
+ Utility.getPropValue(props, "linkIdleMode", "linkIdleModeCustom"),
props.showInvisibleActors,
+ disableWarpSongs,
+ enable_pos_lights,
+ enable_storm,
props.disableSkybox,
props.disableSunMoon,
0xFF if props.leaveTimeUnchanged else props.timeHours,
@@ -67,11 +87,17 @@ def new(props: Optional[OOTRoomHeaderProperty]):
def getCmds(self):
"""Returns the echo settings, room behavior, skybox disables and time settings room commands"""
showInvisActors = "true" if self.showInvisActors else "false"
- disableWarpSongs = "true" if self.disableWarpSongs else "false"
disableSkybox = "true" if self.disableSky else "false"
disableSunMoon = "true" if self.disableSunMoon else "false"
+ disableWarpSongs = "true" if self.disableWarpSongs else "false"
+
+ if is_oot_features():
+ roomBehaviorArgs = f"{self.roomBehavior}, {self.playerIdleType}, {showInvisActors}, {disableWarpSongs}"
+ else:
+ enable_pos_lights = "true" if self.enable_pos_lights else "false"
+ enable_storm = "true" if self.enable_storm else "false"
+ roomBehaviorArgs = f"{self.roomBehavior}, {self.playerIdleType}, {showInvisActors}, {disableWarpSongs}, {enable_pos_lights}, {enable_storm}"
- roomBehaviorArgs = f"{self.roomBehavior}, {self.playerIdleType}, {showInvisActors}, {disableWarpSongs}"
cmdList = [
f"SCENE_CMD_ECHO_SETTINGS({self.echo})",
f"SCENE_CMD_ROOM_BEHAVIOR({roomBehaviorArgs})",
@@ -93,13 +119,13 @@ class RoomObjects:
objectList: list[str]
@staticmethod
- def new(name: str, props: Optional[OOTRoomHeaderProperty]):
+ def new(name: str, props: Optional[Z64_RoomHeaderProperty]):
objectList: list[str] = []
for objProp in props.objectList:
if objProp.objectKey == "Custom":
objectList.append(objProp.objectIDCustom)
else:
- objectList.append(ootData.objectData.objectsByKey[objProp.objectKey].id)
+ objectList.append(game_data.z64.objects.objects_by_key[objProp.objectKey].id)
return RoomObjects(name, objectList)
def getDefineName(self):
@@ -140,15 +166,21 @@ class RoomActors:
actorList: list[Actor]
@staticmethod
- def get_rotation_values(actorProp: OOTActorProperty, blender_rot_values: list[int]):
+ def get_rotation_values(actorProp: Z64_ActorProperty, blender_rot_values: list[int]):
# Figure out which rotation to export, Blender's or the override
custom = (
- "_custom" if not bpy.context.scene.fast64.oot.use_new_actor_panel or actorProp.actor_id == "Custom" else ""
+ "_custom"
+ if not bpy.context.scene.fast64.oot.can_use_new_actor_panel() or actorProp.actor_id == "Custom"
+ else ""
)
rot_values = [getattr(actorProp, f"rot_{rot}{custom}") for rot in ["x", "y", "z"]]
- export_rot_values = [f"DEG_TO_BINANG({(rot * (180 / 0x8000)):.3f})" for rot in blender_rot_values]
- if not bpy.context.scene.fast64.oot.use_new_actor_panel or actorProp.actor_id == "Custom":
+ if game_data.z64.is_oot():
+ export_rot_values = [f"DEG_TO_BINANG({(rot * (180 / 0x8000)):.3f})" for rot in blender_rot_values]
+ else:
+ export_rot_values = [f"{round(rot * (180 / 0x8000))}" for rot in blender_rot_values]
+
+ if not bpy.context.scene.fast64.oot.can_use_new_actor_panel() or actorProp.actor_id == "Custom":
export_rot_values = rot_values if actorProp.rot_override else export_rot_values
else:
for i, rot in enumerate(["X", "Y", "Z"]):
@@ -167,13 +199,16 @@ def new(
headerIndex: int,
room_index: int,
):
+ game_data.z64.update(bpy.context, None)
actorList: list[Actor] = []
actorObjList = getObjectList(sceneObj.children, "EMPTY", "Actor", parentObj=roomObj, room_index=room_index)
for obj in actorObjList:
- actorProp: OOTActorProperty = obj.ootActorProperty
+ actorProp: Z64_ActorProperty = obj.ootActorProperty
if not Utility.isCurrentHeaderValid(actorProp.headerSettings, headerIndex):
continue
+ actor_id: str = actorProp.actor_id
+
# The Actor list is filled with ``("None", f"{i} (Deleted from the XML)", "None")`` for
# the total number of actors defined in the XML. If the user deletes one, this will prevent
# any data loss as Blender saves the index of the element in the Actor list used for the EnumProperty
@@ -188,22 +223,67 @@ def new(
else:
actor.id = actorProp.actor_id
- actor.rot = ", ".join(RoomActors.get_rotation_values(actorProp, rot))
+ rotation = RoomActors.get_rotation_values(actorProp, rot)
+
+ if is_oot_features():
+ actor.rot = ", ".join(rotation)
+ else:
+ halfday_bits = 0
+
+ if actorProp.halfday_all:
+ halfday_bits |= halfday_bits_all_dawns | halfday_bits_all_nights
+ else:
+ if actorProp.halfday_all_dawns:
+ halfday_bits |= halfday_bits_all_dawns
+
+ if actorProp.halfday_all_nights:
+ halfday_bits |= halfday_bits_all_nights
+
+ # if the value is 0 it means it's not all nor all dawns nor all nights
+ if halfday_bits == 0:
+ for item in actorProp.halfday_bits:
+ custom_value = 0
+
+ try:
+ if item.value == "Custom":
+ custom_value = int(getEvalParams(item.value_custom), base=0)
+ except:
+ raise PluginError(
+ f"ERROR: custom spawn schedule values don't support non-evaluable characters! ({repr(obj.name)})"
+ )
+
+ halfday_bits |= enum_to_halfday_bits.get(item.value, custom_value)
+
+ # rot.x stores a part of the halfday bits value
+ # rot.y stores a cutscene index
+ # rot.z stores the other part of the halfday bits
+ cs_index = actorProp.actor_cs_index & 0x7F
+ spawn_flags = [
+ f"0x{(halfday_bits >> 7) & 0x07:02X}",
+ "CS_ID_GLOBAL_END" if cs_index == 0x7F else cs_index,
+ f"0x{halfday_bits & 0x7F:02X}",
+ ]
+ spawn_rot = [f"SPAWN_ROT_FLAGS({r}" for r in rotation]
+ actor.rot = ", ".join(f"{rot}, {flag})" for rot, flag in zip(spawn_rot, spawn_flags))
actor.name = (
- ootData.actorData.actorsByID[actorProp.actor_id].name.replace(
- f" - {actorProp.actor_id.removeprefix('ACTOR_')}", ""
- )
- if actorProp.actor_id != "Custom"
+ game_data.z64.actors.actorsByID[actor_id].name.replace(f" - {actor_id.removeprefix('ACTOR_')}", "")
+ if actor_id != "Custom"
else "Custom Actor"
)
actor.pos = pos
- actor.params = (
- actorProp.params
- if bpy.context.scene.fast64.oot.use_new_actor_panel and actorProp.actor_id != "Custom"
- else actorProp.params_custom
- )
+
+ # force custom params for MM (temp solution until the xml is documented properly)
+ if (
+ game_data.z64.is_oot()
+ and bpy.context.scene.fast64.oot.can_use_new_actor_panel()
+ and actorProp.actor_id != "Custom"
+ ):
+ actor.params = actorProp.params
+ else:
+ actor.params = actorProp.params_custom
+
actorList.append(actor)
return RoomActors(name, actorList)
@@ -248,7 +328,7 @@ class RoomHeader:
@staticmethod
def new(
name: str,
- props: Optional[OOTRoomHeaderProperty],
+ props: Optional[Z64_RoomHeaderProperty],
sceneObj: Optional[Object],
roomObj: Optional[Object],
transform: Matrix,
diff --git a/fast64_internal/oot/exporter/room/shape.py b/fast64_internal/z64/exporter/room/shape.py
similarity index 99%
rename from fast64_internal/oot/exporter/room/shape.py
rename to fast64_internal/z64/exporter/room/shape.py
index 96278f917..04ae3c82d 100644
--- a/fast64_internal/oot/exporter/room/shape.py
+++ b/fast64_internal/z64/exporter/room/shape.py
@@ -7,14 +7,14 @@
from ....utility import PluginError, CData, toAlnum, indent
from ....f3d.f3d_gbi import SPDisplayList, SPEndDisplayList, GfxListTag, GfxList, DLFormat
from ....f3d.f3d_writer import TriangleConverterInfo, saveStaticModel, getInfoDict
-from ...room.properties import OOTRoomHeaderProperty, OOTBGProperty
-from ...oot_model_classes import OOTModel
+from ...room.properties import Z64_RoomHeaderProperty, Z64_BGProperty
+from ...model_classes import OOTModel
from ..utility import Utility
from bpy.types import Object
from mathutils import Matrix, Vector
from ....f3d.occlusion_planes.exporter import addOcclusionQuads, OcclusionPlaneCandidatesList
-from ...oot_utility import (
+from ...utility import (
CullGroup,
checkUniformScale,
ootConvertTranslation,
@@ -210,7 +210,7 @@ def get_height(self) -> int:
return self.image.size[1] if self.image else 0
@staticmethod
- def new(name: str, prop: OOTBGProperty):
+ def new(name: str, prop: Z64_BGProperty):
if prop.image is None:
raise PluginError(
'A room is has room shape "Image" but does not have an image set in one of its BG images.'
@@ -520,7 +520,7 @@ def create_shape(
sceneObj: Object,
roomObj: Object,
saveTexturesAsPNG: bool,
- props: OOTRoomHeaderProperty,
+ props: Z64_RoomHeaderProperty,
):
name = f"{room_name}_shapeHeader"
dl_name = f"{room_name}_shapeDListsEntry"
diff --git a/fast64_internal/oot/exporter/scene/__init__.py b/fast64_internal/z64/exporter/scene/__init__.py
similarity index 79%
rename from fast64_internal/oot/exporter/scene/__init__.py
rename to fast64_internal/z64/exporter/scene/__init__.py
index af0c0ca4c..fd5acd741 100644
--- a/fast64_internal/oot/exporter/scene/__init__.py
+++ b/fast64_internal/z64/exporter/scene/__init__.py
@@ -5,9 +5,11 @@
from bpy.types import Object
from typing import Optional
from ....utility import PluginError, CData, indent
+from ....game_data import game_data
from ....f3d.f3d_gbi import TextureExportSettings, ScrollMethod
-from ...scene.properties import OOTSceneHeaderProperty
-from ...oot_model_classes import OOTModel, OOTGfxFormatter
+from ...scene.properties import Z64_SceneHeaderProperty
+from ...model_classes import OOTModel, OOTGfxFormatter
+from ...utility import is_oot_features
from ..file import SceneFile
from ..utility import Utility, altHeaderList
from ..collision import CollisionHeader
@@ -30,9 +32,6 @@ class Scene:
@staticmethod
def new(name: str, sceneObj: Object, transform: Matrix, useMacros: bool, saveTexturesAsPNG: bool, model: OOTModel):
i = 0
- rooms = RoomEntries.new(
- f"{name}_roomList", name.removesuffix("_scene"), model, sceneObj, transform, saveTexturesAsPNG
- )
colHeader = CollisionHeader.new(
f"{name}_collisionHeader",
@@ -53,20 +52,23 @@ def new(name: str, sceneObj: Object, transform: Matrix, useMacros: bool, saveTex
altHeader = SceneAlternateHeader(f"{name}_alternateHeaders")
altProp = sceneObj.ootAlternateSceneHeaders
- for i, header in enumerate(altHeaderList, 1):
- altP: OOTSceneHeaderProperty = getattr(altProp, f"{header}Header")
- if altP.usePreviousHeader:
- continue
- try:
- setattr(
- altHeader, header, SceneHeader.new(f"{name}_header{i:02}", altP, sceneObj, transform, i, useMacros)
- )
- hasAlternateHeaders = True
- except Exception as exc:
- raise PluginError(f"In alternate scene header {header}: {exc}") from exc
+ if game_data.z64.is_oot():
+ for i, header in enumerate(altHeaderList, 1):
+ altP: Z64_SceneHeaderProperty = getattr(altProp, f"{header}Header")
+ if altP.usePreviousHeader:
+ continue
+ try:
+ setattr(
+ altHeader,
+ header,
+ SceneHeader.new(f"{name}_header{i:02}", altP, sceneObj, transform, i, useMacros),
+ )
+ hasAlternateHeaders = True
+ except Exception as exc:
+ raise PluginError(f"In alternate scene header {header}: {exc}") from exc
altHeader.cutscenes = []
- for i, csHeader in enumerate(altProp.cutsceneHeaders, 4):
+ for i, csHeader in enumerate(altProp.cutsceneHeaders, game_data.z64.cs_index_start):
try:
altHeader.cutscenes.append(
SceneHeader.new(f"{name}_header{i:02}", csHeader, sceneObj, transform, i, useMacros)
@@ -74,6 +76,11 @@ def new(name: str, sceneObj: Object, transform: Matrix, useMacros: bool, saveTex
except Exception as exc:
raise PluginError(f"In alternate, cutscene header {i}: {exc}") from exc
+ # process room after scene because of actor cutscenes requiring to be processed before actors
+ rooms = RoomEntries.new(
+ f"{name}_roomList", name.removesuffix("_scene"), model, sceneObj, transform, saveTexturesAsPNG
+ )
+
hasAlternateHeaders = True if len(altHeader.cutscenes) > 0 else hasAlternateHeaders
altHeader = altHeader if hasAlternateHeaders else None
return Scene(name, model, mainHeader, altHeader, rooms, colHeader, hasAlternateHeaders)
@@ -134,6 +141,9 @@ def getCmdList(self, curHeader: SceneHeader, hasAltHeaders: bool):
+ curHeader.entranceActors.getCmd()
+ (curHeader.exits.getCmd() if len(curHeader.exits.exitList) > 0 else "")
+ (curHeader.cutscene.getCmd() if len(curHeader.cutscene.entries) > 0 else "")
+ + (curHeader.map_data.get_cmds() if curHeader.map_data is not None and curHeader.map_data.is_used() else "")
+ + (curHeader.anim_mat.get_cmd() if curHeader.anim_mat is not None and curHeader.anim_mat.is_used() else "")
+ + (curHeader.actor_cs.get_cmds() if curHeader.actor_cs is not None and curHeader.actor_cs.is_used() else "")
+ Utility.getEndCmd()
+ "};\n\n"
)
@@ -148,21 +158,27 @@ def getSceneMainC(self):
altHeaderPtrs = None
if self.hasAlternateHeaders:
- headers = [
- (self.altHeader.childNight, "Child Night"),
- (self.altHeader.adultDay, "Adult Day"),
- (self.altHeader.adultNight, "Adult Night"),
- ]
+ if game_data.z64.is_oot():
+ headers = [
+ (self.altHeader.childNight, "Child Night"),
+ (self.altHeader.adultDay, "Adult Day"),
+ (self.altHeader.adultNight, "Adult Night"),
+ ]
for i, csHeader in enumerate(self.altHeader.cutscenes):
headers.append((csHeader, f"Cutscene No. {i + 1}"))
altHeaderPtrs = "\n".join(
- indent + curHeader.name + "," if curHeader is not None else indent + "NULL," if i < 4 else ""
+ indent + curHeader.name + ","
+ if curHeader is not None
+ else indent + "NULL,"
+ if i < game_data.z64.cs_index_start
+ else ""
for i, (curHeader, _) in enumerate(headers, 1)
)
- headers.insert(0, (self.mainHeader, "Child Day (Default)"))
+ header_name = "Child Day (Default)" if is_oot_features() else "Default"
+ headers.insert(0, (self.mainHeader, header_name))
for i, (curHeader, headerDesc) in enumerate(headers):
if curHeader is not None:
sceneC.source += "/**\n * " + f"Header {headerDesc}\n" + "*/\n"
@@ -222,7 +238,15 @@ def getNewSceneFile(self, path: str, isSingleFile: bool, textureExportSettings:
sceneCutsceneData = self.getSceneCutscenesC()
sceneTexturesData = self.getSceneTexturesC(textureExportSettings)
- if bpy.context.scene.fast64.oot.is_globalh_present():
+ if game_data.z64.is_mm():
+ # temp solution until mm headers are split (or figure out which ones are required)
+ includes = [
+ '#include "ultra64.h"',
+ '#include "macros.h"',
+ '#include "z64.h"',
+ '#include "command_macros_base.h"',
+ ]
+ elif bpy.context.scene.fast64.oot.is_globalh_present():
includes = [
'#include "ultra64.h"',
'#include "macros.h"',
diff --git a/fast64_internal/z64/exporter/scene/actor_cutscene.py b/fast64_internal/z64/exporter/scene/actor_cutscene.py
new file mode 100644
index 000000000..75f810de4
--- /dev/null
+++ b/fast64_internal/z64/exporter/scene/actor_cutscene.py
@@ -0,0 +1,239 @@
+from dataclasses import dataclass
+from bpy.types import Object
+from mathutils import Matrix
+from typing import Optional
+from ....utility import CData, PluginError, indent
+from ...utility import getObjectList, is_oot_features
+from ...actor_cutscene.properties import Z64_ActorCutscene
+from ...actor.properties import Z64_ActorProperty
+from ..utility import Utility
+from ..collision.camera import BgCamInformations, CameraInfo
+
+
+class ActorCutscene:
+ def __init__(
+ self, scene_obj: Object, transform: Matrix, props: Z64_ActorCutscene, name: str, index: int, obj: Object
+ ):
+ self.name = name
+ self.priority: int = props.priority
+ self.length: int = props.length
+ self.index = index
+ self.cam_info: Optional[CameraInfo] = None
+
+ if props.cs_cam_id == "Custom":
+ self.cs_cam_id: str = props.cs_cam_id_custom
+ elif props.cs_cam_id == "Camera":
+ if props.cs_cam_obj is None:
+ raise PluginError("ERROR: The Actor Cutscene Camera object is unset!")
+
+ cam_props = props.cs_cam_obj.ootCameraPositionProperty
+ self.cs_cam_id: str = f"{cam_props.index}"
+
+ # since it's literally the same thing we can just use `BgCamInformations`
+ self.cam_info = BgCamInformations.get_camera_info(scene_obj, props.cs_cam_obj, transform, None)
+ self.cam_info.arrayIndex = cam_props.index * 3
+ else:
+ self.cs_cam_id: str = props.cs_cam_id
+
+ self.script_index: int = props.script_index
+ self.additional_cs_id: int = props.additional_cs_id
+ self.end_sfx: str = Utility.getPropValue(props, "end_sfx", "end_sfx_custom")
+ self.custom_value: str = props.custom_value
+ self.hud_visibility: str = Utility.getPropValue(props, "hud_visibility", "hud_visibility_custom")
+ self.end_cam: str = Utility.getPropValue(props, "end_cam", "end_cam_custom")
+ self.letterbox_size: int = props.letterbox_size
+
+ if self.script_index != -1 and self.cs_cam_id not in {"CS_CAM_ID_NONE", "-1"}:
+ print(
+ "WARNING: this actor cutscene entry won't use the camera cutscene since the script takes the priority."
+ )
+
+ def cutscene_entry_to_c(self):
+ values = [
+ self.priority,
+ self.length,
+ self.cs_cam_id,
+ self.script_index,
+ self.additional_cs_id,
+ self.end_sfx,
+ self.custom_value,
+ self.hud_visibility,
+ self.end_cam,
+ self.letterbox_size,
+ ]
+
+ return "{ " + ", ".join(f"{value}" for value in values) + " }"
+
+
+@dataclass
+class SceneActorCutscene:
+ """This class hosts actor cutscene data"""
+
+ name: str
+ cam_info_array_name: str
+ cam_data_array_name: str
+ header_index: int
+ entries: list[ActorCutscene]
+
+ @staticmethod
+ def get_entries(name: str, scene_obj: Object, transform: Matrix, header_index: int, empty_type: str, start: int):
+ entries: list[ActorCutscene] = []
+ obj_list = getObjectList(scene_obj.children_recursive, "EMPTY", empty_type)
+ processed = []
+ i = 0
+
+ for obj in obj_list:
+ actor_prop: Z64_ActorProperty = obj.ootActorProperty
+
+ if empty_type == "Actor":
+ header_settings = actor_prop.headerSettings
+ else:
+ header_settings = obj.z64_actor_cs_property.header_settings
+
+ for i, item in enumerate(obj.z64_actor_cs_property.entries, start):
+ if Utility.isCurrentHeaderValid(header_settings, header_index) and not actor_prop.use_global_actor_cs:
+ index = actor_prop.actor_cs_index if actor_prop.use_global_actor_cs else i
+ new_entry = ActorCutscene(scene_obj, transform, item, name, index, obj)
+
+ if new_entry.cs_cam_id not in {"Custom", "Camera", "CS_CAM_ID_NONE"}:
+ if new_entry.cs_cam_id not in processed:
+ entries.append(new_entry)
+ processed.append(new_entry.cs_cam_id)
+ else:
+ raise PluginError(f"ERROR: reapeated actor cutscene camera id {repr(new_entry.cs_cam_id)}")
+
+ start += i
+
+ # validate actor cs index
+ if empty_type == "Actor":
+ for obj in obj_list:
+ index = actor_prop.actor_cs_index
+
+ if index > i and index < 127:
+ raise PluginError(f"ERROR: actor cutscene index out of bounds! ({index} for a total of {i + 1})")
+
+ return entries
+
+ @staticmethod
+ def new(name: str, scene_obj: Object, transform: Matrix, header_index: int):
+ entries = SceneActorCutscene.get_entries(name, scene_obj, transform, header_index, "Actor Cutscene", 0)
+ entries.extend(SceneActorCutscene.get_entries(name, scene_obj, transform, header_index, "Actor", len(entries)))
+
+ # validate camera indices
+ known_camera_indices: list[int] = []
+ for entry in entries:
+ if entry.cam_info is not None:
+ if entry.cam_info.camIndex not in known_camera_indices:
+ known_camera_indices.append(entry.cam_info.camIndex)
+ else:
+ raise PluginError(f"ERROR: reapeated actor cutscene camera index! ({entry.cam_info.camIndex})")
+
+ return SceneActorCutscene(name, f"{name}CameraInfo", f"{name}CameraData", header_index, entries)
+
+ def is_used(self):
+ return not is_oot_features() and len(self.entries) > 0
+
+ def is_cs_cam_used(self):
+ for entry in self.entries:
+ if entry.cam_info is not None:
+ return True
+ return False
+
+ def get_cs_cam_list_length(self):
+ length = 0
+ for entry in self.entries:
+ if entry.cam_info is not None:
+ length += 1
+ return length
+
+ def get_cmds(self):
+ """Returns the actor cutscene commands"""
+
+ commands = [
+ f"SCENE_CMD_ACTOR_CUTSCENE_LIST({len(self.entries)}, {self.name}List)",
+ ]
+
+ if self.is_cs_cam_used():
+ commands.append(
+ f"SCENE_CMD_ACTOR_CUTSCENE_CAM_LIST({self.get_cs_cam_list_length()}, {self.cam_info_array_name})"
+ )
+
+ return indent + f",\n{indent}".join(commands) + ",\n"
+
+ def cam_data_to_c(self):
+ """Returns the camera data positions array"""
+
+ data = CData()
+ array_name = f"Vec3s {self.cam_data_array_name}[]"
+
+ # .h
+ data.header = f"extern {array_name};\n"
+
+ # .c
+
+ # sort the camera list as it might not end up in the right order
+ camera_list: list[CameraInfo] = []
+ for i, entry in enumerate(self.entries):
+ if entry.cam_info is not None:
+ camera_list.append(entry.cam_info)
+ camera_list.sort(key=lambda item: item.camIndex)
+
+ data.source = array_name + " = {\n"
+
+ for item in camera_list:
+ data.source += item.data.getEntryC() + "\n"
+
+ data.source = data.source[:-1] # remove extra newline
+ data.source += "};\n\n"
+
+ return data
+
+ def cam_info_to_c(self):
+ """Returns the array containing the informations of each cameras"""
+
+ data = CData()
+ array_name = f"ActorCsCamInfo {self.cam_info_array_name}[]"
+
+ # .h
+ data.header = f"extern {array_name};\n"
+
+ # .c
+
+ # sort the camera list as it might not end up in the right order
+ camera_list: list[CameraInfo] = []
+ for i, entry in enumerate(self.entries):
+ if entry.cam_info is not None:
+ camera_list.append(entry.cam_info)
+ camera_list.sort(key=lambda item: item.camIndex)
+
+ data.source = (
+ (array_name + " = {\n")
+ + "".join(item.getInfoEntryC(self.cam_data_array_name) for item in camera_list)
+ + "};\n\n"
+ )
+
+ return data
+
+ def to_c(self):
+ data = CData()
+ array_name = f"CutsceneEntry {self.name}List[]"
+
+ if self.is_cs_cam_used():
+ data.append(self.cam_data_to_c())
+ data.append(self.cam_info_to_c())
+
+ # .h
+ data.header += f"extern {array_name};\n"
+
+ # .c
+ data.source += (
+ (array_name + " = {\n")
+ + (
+ ",\n".join(
+ indent + f"/* {i:02} */ " + entry.cutscene_entry_to_c() for i, entry in enumerate(self.entries)
+ )
+ )
+ + "\n};\n\n"
+ )
+
+ return data
diff --git a/fast64_internal/oot/exporter/scene/actors.py b/fast64_internal/z64/exporter/scene/actors.py
similarity index 78%
rename from fast64_internal/oot/exporter/scene/actors.py
rename to fast64_internal/z64/exporter/scene/actors.py
index 7f4424b7e..54d5ce968 100644
--- a/fast64_internal/oot/exporter/scene/actors.py
+++ b/fast64_internal/z64/exporter/scene/actors.py
@@ -5,9 +5,9 @@
from mathutils import Matrix
from bpy.types import Object
from ....utility import PluginError, CData, indent
-from ...oot_utility import getObjectList
-from ...oot_constants import ootData
-from ...actor.properties import OOTActorProperty
+from ....game_data import game_data
+from ...utility import getObjectList, is_oot_features
+from ...actor.properties import Z64_ActorProperty
from ..utility import Utility
from ..actor import Actor
@@ -62,8 +62,9 @@ def new(name: str, sceneObj: Object, transform: Matrix, headerIndex: int):
entries: list[TransitionActor] = []
for obj in actorObjList:
transActorProp = obj.ootTransitionActorProperty
- actorProp: OOTActorProperty = transActorProp.actor
- if Utility.isCurrentHeaderValid(actorProp.headerSettings, headerIndex) and actorProp.actor_id != "None":
+ actorProp: Z64_ActorProperty = transActorProp.actor
+ actor_id: str = actorProp.actor_id
+ if Utility.isCurrentHeaderValid(actorProp.headerSettings, headerIndex) and actor_id != "None":
pos, rot, _, _ = Utility.getConvertedTransform(transform, sceneObj, obj, True)
transActor = TransitionActor()
@@ -77,24 +78,30 @@ def new(name: str, sceneObj: Object, transform: Matrix, headerIndex: int):
front = (fromIndex, Utility.getPropValue(transActorProp, "cameraTransitionFront"))
back = (toIndex, Utility.getPropValue(transActorProp, "cameraTransitionBack"))
- if actorProp.actor_id == "Custom":
+ if actor_id == "Custom":
transActor.id = actorProp.actor_id_custom
else:
- transActor.id = actorProp.actor_id
+ transActor.id = actor_id
transActor.name = (
- ootData.actorData.actorsByID[actorProp.actor_id].name.replace(
- f" - {actorProp.actor_id.removeprefix('ACTOR_')}", ""
- )
- if actorProp.actor_id != "Custom"
+ game_data.z64.actors.actorsByID[actor_id].name.replace(f" - {actor_id.removeprefix('ACTOR_')}", "")
+ if actor_id != "Custom"
else "Custom Actor"
)
transActor.pos = pos
- transActor.rot = f"DEG_TO_BINANG({(rot[1] * (180 / 0x8000)):.3f})" # TODO: Correct axis?
+
+ rot_deg = rot[1] * (180 / 0x8000)
+ if is_oot_features():
+ transActor.rot = f"DEG_TO_BINANG({rot_deg:.3f})" # TODO: Correct axis?
+ else:
+ transActor.rot = f"((0x{round(rot_deg):04X} & 0x1FF) << 7) | ({actorProp.actor_cs_index} & 0x7F)"
+
transActor.params = (
actorProp.params
- if bpy.context.scene.fast64.oot.use_new_actor_panel and actorProp.actor_id != "Custom"
+ if game_data.z64.is_oot()
+ and bpy.context.scene.fast64.oot.can_use_new_actor_panel()
+ and actorProp.actor_id != "Custom"
else actorProp.params_custom
)
transActor.roomFrom, transActor.cameraFront = front
@@ -150,27 +157,37 @@ def new(name: str, sceneObj: Object, transform: Matrix, headerIndex: int):
actorObjList = getObjectList(sceneObj.children_recursive, "EMPTY", "Entrance")
for obj in actorObjList:
entranceProp = obj.ootEntranceProperty
- actorProp: OOTActorProperty = entranceProp.actor
- if Utility.isCurrentHeaderValid(actorProp.headerSettings, headerIndex) and actorProp.actor_id != "None":
+ actorProp: Z64_ActorProperty = entranceProp.actor
+ actor_id: str = actorProp.actor_id
+ if Utility.isCurrentHeaderValid(actorProp.headerSettings, headerIndex) and actor_id != "None":
pos, rot, _, _ = Utility.getConvertedTransform(transform, sceneObj, obj, True)
entranceActor = EntranceActor()
entranceActor.name = (
- ootData.actorData.actorsByID[actorProp.actor_id].name.replace(
- f" - {actorProp.actor_id.removeprefix('ACTOR_')}", ""
- )
- if actorProp.actor_id != "Custom"
+ game_data.z64.actors.actorsByID[actor_id].name.replace(f" - {actor_id.removeprefix('ACTOR_')}", "")
+ if actor_id != "Custom"
else "Custom Actor"
)
entranceActor.id = "ACTOR_PLAYER" if not entranceProp.customActor else actorProp.actor_id_custom
entranceActor.pos = pos
- entranceActor.rot = ", ".join(f"DEG_TO_BINANG({(r * (180 / 0x8000)):.3f})" for r in rot)
- entranceActor.params = (
- actorProp.params
- if bpy.context.scene.fast64.oot.use_new_actor_panel and not entranceProp.customActor
- else actorProp.params_custom
- )
+
+ if is_oot_features():
+ entranceActor.rot = ", ".join(f"DEG_TO_BINANG({(r * (180 / 0x8000)):.3f})" for r in rot)
+ else:
+ # MM seems to use degrees directly
+ entranceActor.rot = ", ".join(f"SPAWN_ROT_FLAGS({round(r * (180 / 0x8000))}, 0x00)" for r in rot)
+
+ # force custom params for MM (temp solution until the xml is documented properly)
+ if (
+ game_data.z64.is_oot()
+ and bpy.context.scene.fast64.oot.can_use_new_actor_panel()
+ and not entranceProp.customActor
+ ):
+ entranceActor.params = actorProp.params
+ else:
+ entranceActor.params = actorProp.params_custom
+
if entranceProp.tiedRoom is not None:
entranceActor.roomIndex = entranceProp.tiedRoom.ootRoomHeader.roomIndex
else:
@@ -227,7 +244,7 @@ def getC(self):
"""Returns the spawn array"""
spawnList = CData()
- listName = f"Spawn {self.name}"
+ listName = f"Spawn {self.name}" if game_data.z64.is_oot() else f"EntranceEntry {self.name}"
# .h
spawnList.header = f"extern {listName}[];\n"
diff --git a/fast64_internal/z64/exporter/scene/animated_mats.py b/fast64_internal/z64/exporter/scene/animated_mats.py
new file mode 100644
index 000000000..f8839a30b
--- /dev/null
+++ b/fast64_internal/z64/exporter/scene/animated_mats.py
@@ -0,0 +1,302 @@
+from dataclasses import dataclass
+from bpy.types import Object
+from ....utility import CData, PluginError, exportColor, scaleToU8, indent
+from ...utility import getObjectList, is_oot_features
+from ...animated_mats.properties import (
+ Z64_AnimatedMatColorParams,
+ Z64_AnimatedMatTexScrollParams,
+ Z64_AnimatedMatTexCycleParams,
+ Z64_AnimatedMaterial,
+)
+
+
+class AnimatedMatColorParams:
+ def __init__(
+ self,
+ props: Z64_AnimatedMatColorParams,
+ segment_num: int,
+ type_num: int,
+ base_name: str,
+ header_index: int,
+ index: int,
+ ):
+ # the code adds back 7 when processing animated materials
+ self.segment_num = segment_num - 7
+ self.type_num = type_num
+ self.base_name = base_name
+ self.header_index = header_index
+ self.header_suffix = f"_{index:02}"
+ self.name = f"{self.base_name}ColorParams{self.header_suffix}"
+ self.frame_length = props.frame_count
+ self.prim_colors: list[tuple[int, int, int, int]] = []
+ self.env_colors: list[tuple[int, int, int]] = []
+ self.frames: list[int] = []
+
+ for keyframe in props.keyframes:
+ prim = exportColor(keyframe.prim_color[0:3]) + [scaleToU8(keyframe.prim_color[3])]
+ self.prim_colors.append((prim[0], prim[1], prim[2], prim[3], keyframe.prim_lod_frac))
+ self.env_colors.append(tuple(exportColor(keyframe.env_color[0:3]) + [scaleToU8(keyframe.env_color[3])]))
+ self.frames.append(keyframe.frame_num)
+
+ if keyframe.frame_num > self.frame_length:
+ raise PluginError("ERROR: the frame number cannot be higher than the total frame count!")
+
+ self.frame_count = len(self.frames)
+ assert len(self.frames) == len(self.prim_colors) == len(self.env_colors)
+
+ def to_c(self):
+ data = CData()
+ prim_array_name = f"{self.base_name}ColorPrimColor{self.header_suffix}"
+ env_array_name = f"{self.base_name}ColorEnvColor{self.header_suffix}"
+ frames_array_name = f"{self.base_name}ColorKeyFrames{self.header_suffix}"
+ params_name = f"AnimatedMatColorParams {self.name}"
+
+ # .h
+ data.header = (
+ f"extern F3DPrimColor {prim_array_name}[];\n"
+ + f"extern F3DEnvColor {env_array_name}[];\n"
+ + f"extern u16 {frames_array_name}[];\n"
+ + f"extern {params_name};\n"
+ )
+
+ # .c
+ data.source = (
+ (
+ (f"F3DPrimColor {prim_array_name}[]" + " = {\n" + indent)
+ + f",\n{indent}".join(
+ "{ " + f"{entry[0]}, {entry[1]}, {entry[2]}, {entry[3]}, {entry[4]}" + " }"
+ for entry in self.prim_colors
+ )
+ + "\n};\n\n"
+ )
+ + (
+ (f"F3DEnvColor {env_array_name}[]" + " = {\n" + indent)
+ + f",\n{indent}".join(
+ "{ " + f"{entry[0]}, {entry[1]}, {entry[2]}, {entry[3]}" + " }" for entry in self.env_colors
+ )
+ + "\n};\n\n"
+ )
+ + (
+ (f"u16 {frames_array_name}[]" + " = {\n" + indent)
+ + f",\n{indent}".join(f"{entry}" for entry in self.frames)
+ + "\n};\n\n"
+ )
+ + (
+ (params_name + " = {\n")
+ + (indent + f"{self.frame_length},\n")
+ + (indent + f"{self.frame_count},\n")
+ + (indent + f"{prim_array_name},\n")
+ + (indent + f"{env_array_name},\n")
+ + (indent + f"{frames_array_name},\n")
+ + "};\n\n"
+ )
+ )
+
+ return data
+
+
+class AnimatedMatTexScrollParams:
+ def __init__(
+ self,
+ props: Z64_AnimatedMatTexScrollParams,
+ segment_num: int,
+ type_num: int,
+ base_name: str,
+ header_index: int,
+ index: int,
+ ):
+ # the code adds back 7 when processing animated materials
+ self.segment_num = segment_num - 7
+ self.type_num = type_num
+ self.base_name = base_name
+ self.header_index = header_index
+ self.header_suffix = f"_{index:02}"
+ self.name = f"{self.base_name}TexScrollParams{self.header_suffix}"
+ self.entries: list[str] = []
+
+ for item in props.entries:
+ self.entries.append("{ " + f"{item.step_x}, {item.step_y}, {item.width}, {item.height}" + " }")
+
+ def to_c(self):
+ data = CData()
+ params_name = f"AnimatedMatTexScrollParams {self.name}[]"
+
+ # .h
+ data.header = f"extern {params_name};\n"
+
+ # .c
+ data.source = (
+ f"{params_name}" + " = {\n" + indent + f",\n{indent}".join(entry for entry in self.entries) + "\n};\n\n"
+ )
+
+ return data
+
+
+class AnimatedMatTexCycleParams:
+ def __init__(
+ self,
+ props: Z64_AnimatedMatTexCycleParams,
+ segment_num: int,
+ type_num: int,
+ base_name: str,
+ header_index: int,
+ index: int,
+ ):
+ # the code adds back 7 when processing animated materials
+ self.segment_num = segment_num - 7
+ self.type_num = type_num
+ self.base_name = base_name
+ self.header_index = header_index
+ self.header_suffix = f"_{index:02}"
+ self.name = f"{self.base_name}TexCycleParams{self.header_suffix}"
+ self.textures: list[str] = []
+ self.frames: list[int] = []
+
+ for keyframe in props.keyframes:
+ self.textures.append(keyframe.texture)
+ self.frames.append(keyframe.frame_num)
+
+ def to_c(self):
+ data = CData()
+ texture_array_name = f"{self.base_name}CycleTextures{self.header_suffix}"
+ frame_array_name = f"{self.base_name}CycleKeyFrames{self.header_suffix}"
+ params_name = f"AnimatedMatTexCycleParams {self.name}"
+
+ # .h
+ data.header = (
+ f"extern TexturePtr {texture_array_name}[];\n"
+ + f"extern u8 {frame_array_name}[];\n"
+ + f"extern {params_name};\n"
+ )
+
+ # .c
+ data.source = (
+ (
+ (f"TexturePtr {texture_array_name}[]" + " = {\n")
+ + indent
+ + f",\n{indent}".join(texture for texture in self.textures)
+ + "\n};\n\n"
+ )
+ + (
+ (f"u8 {frame_array_name}[]" + " = {\n")
+ + indent
+ + ", ".join(f"{frame}" for frame in self.frames)
+ + "\n};\n\n"
+ )
+ + (
+ (params_name + " = {\n")
+ + indent
+ + f"{len(self.frames)}, {texture_array_name}, {frame_array_name},"
+ + "\n};\n\n"
+ )
+ )
+
+ return data
+
+
+class AnimatedMaterial:
+ def __init__(self, props: Z64_AnimatedMaterial, base_name: str, scene_header_index: int):
+ self.name = base_name
+ self.scene_header_index = scene_header_index
+ self.header_index = props.header_index
+ self.entries: list[AnimatedMatColorParams | AnimatedMatTexScrollParams | AnimatedMatTexCycleParams] = []
+
+ type_list_map: dict[
+ str, tuple[AnimatedMatColorParams | AnimatedMatTexScrollParams | AnimatedMatTexCycleParams, str, int]
+ ] = {
+ "tex_scroll": (AnimatedMatTexScrollParams, "tex_scroll_params", 0),
+ "two_tex_scroll": (AnimatedMatTexScrollParams, "tex_scroll_params", 1),
+ "color": (AnimatedMatColorParams, "color_params", 2),
+ "color_lerp": (AnimatedMatColorParams, "color_params", 3),
+ "color_nonlinear_interp": (AnimatedMatColorParams, "color_params", 4),
+ "tex_cycle": (AnimatedMatTexCycleParams, "tex_cycle_params", 5),
+ }
+
+ for i, item in enumerate(props.entries):
+ if item.type != "Custom":
+ class_def, prop_name, type_num = type_list_map[item.type]
+ # example: `self.tex_scroll_entries.append(AnimatedMatTexScrollParams(item.tex_scroll_params, base_name, header_index))`
+ self.entries.append(
+ class_def(getattr(item, prop_name), item.segment_num, type_num, base_name, self.header_index, i)
+ )
+
+ # the last entry's segment need to be negative
+ if len(self.entries) > 0 and self.entries[-1].segment_num > 0:
+ self.entries[-1].segment_num = -self.entries[-1].segment_num
+
+ def to_c(self):
+ data = CData()
+
+ for entry in self.entries:
+ if entry.header_index == -1 or entry.header_index == self.scene_header_index:
+ data.append(entry.to_c())
+
+ if len(self.entries) > 0:
+ array_name = f"AnimatedMaterial {self.name}[]"
+
+ # .h
+ data.header += f"extern {array_name};"
+
+ # .c
+ data.source += (
+ (array_name + " = {\n" + indent)
+ + f",\n{indent}".join(
+ "{ "
+ + f"{entry.segment_num} /* {abs(entry.segment_num) + 7} */, "
+ + f"{entry.type_num}, "
+ + f"{'&' if entry.type_num in {2, 3, 4, 5} else ''}{entry.name}"
+ + " }"
+ for entry in self.entries
+ )
+ + "\n};\n\n"
+ )
+ else:
+ raise PluginError("ERROR: Trying to export animated materials with empty entries!")
+
+ return data
+
+
+@dataclass
+class SceneAnimatedMaterial:
+ """This class hosts exit data"""
+
+ name: str
+ header_index: int
+ entries: list[AnimatedMaterial]
+
+ @staticmethod
+ def new(name: str, scene_obj: Object, header_index: int):
+ obj_list = getObjectList(scene_obj.children_recursive, "EMPTY", "Animated Materials")
+ entries: list[AnimatedMaterial] = []
+
+ for obj in obj_list:
+ if obj.z64_anim_mats_property.mode == "Scene":
+ entries.extend(
+ [AnimatedMaterial(item, name, header_index) for item in obj.z64_anim_mats_property.items]
+ )
+
+ last_index = -1
+ for entry in entries:
+ if entry.header_index >= 0:
+ if entry.header_index > last_index:
+ last_index = entry.header_index
+ else:
+ raise PluginError("ERROR: Animated Materials header indices are not consecutives!")
+
+ return SceneAnimatedMaterial(name, header_index, entries)
+
+ def is_used(self):
+ return not is_oot_features() and len(self.entries) > 0
+
+ def get_cmd(self):
+ """Returns the sound settings, misc settings, special files and skybox settings scene commands"""
+
+ return indent + f"SCENE_CMD_ANIMATED_MATERIAL_LIST({self.name}),\n"
+
+ def to_c(self):
+ data = CData()
+
+ for entry in self.entries:
+ data.append(entry.to_c())
+
+ return data
diff --git a/fast64_internal/oot/exporter/scene/general.py b/fast64_internal/z64/exporter/scene/general.py
similarity index 56%
rename from fast64_internal/oot/exporter/scene/general.py
rename to fast64_internal/z64/exporter/scene/general.py
index 493589e6c..98d424e96 100644
--- a/fast64_internal/oot/exporter/scene/general.py
+++ b/fast64_internal/z64/exporter/scene/general.py
@@ -1,7 +1,15 @@
from dataclasses import dataclass
+from mathutils import Matrix
from bpy.types import Object
from ....utility import PluginError, CData, exportColor, ootGetBaseOrCustomLight, indent
-from ...scene.properties import OOTSceneHeaderProperty, OOTLightProperty
+from ....game_data import game_data
+from ...utility import is_oot_features, getObjectList, getEvalParams
+from ...scene.properties import (
+ Z64_SceneHeaderProperty,
+ Z64_LightProperty,
+ Z64_MapDataChestProperty,
+ Z64_MapDataRoomProperty,
+)
from ..utility import Utility
@@ -89,9 +97,9 @@ class SceneLighting:
settings: list[EnvLightSettings]
@staticmethod
- def new(name: str, props: OOTSceneHeaderProperty):
+ def new(name: str, props: Z64_SceneHeaderProperty):
envLightMode = Utility.getPropValue(props, "skyboxLighting")
- lightList: dict[str, OOTLightProperty] = {}
+ lightList: dict[str, Z64_LightProperty] = {}
settings: list[EnvLightSettings] = []
if envLightMode == "LIGHT_MODE_TIME":
@@ -164,13 +172,14 @@ class SceneInfos:
# Skybox
skyboxID: str
skyboxConfig: str
+ skybox_texture_id: str # MM
# Sound
sequenceID: str
ambienceID: str
specID: str
- ### Camera And World Map ###
+ ### Camera And World Map (OoT) ###
# World Map
worldMapLocation: str
@@ -178,39 +187,58 @@ class SceneInfos:
# Camera
sceneCamType: str
+ ### REGION VISITED (MM) ###
+
+ set_region_visited: bool
+
@staticmethod
- def new(props: OOTSceneHeaderProperty, sceneObj: Object):
+ def new(props: Z64_SceneHeaderProperty, sceneObj: Object):
+ if is_oot_features():
+ skybox_texture_id = ""
+ else:
+ skybox_texture_id = Utility.getPropValue(props, "skybox_texture_id")
+
return SceneInfos(
- Utility.getPropValue(props, "globalObject"),
- Utility.getPropValue(props, "naviCup"),
- Utility.getPropValue(props.sceneTableEntry, "drawConfig"),
+ Utility.getPropValue(props, "globalObject", "globalObjectCustom", enum_key="global_object"),
+ Utility.getPropValue(props, "naviCup") if game_data.z64.is_oot() else "NAVI_QUEST_HINTS_NONE",
+ Utility.getPropValue(props.sceneTableEntry, "drawConfig", enum_key="draw_config"),
props.appendNullEntrance,
sceneObj.fast64.oot.scene.write_dummy_room_list,
Utility.getPropValue(props, "title_card_name"),
Utility.getPropValue(props, "skyboxID"),
Utility.getPropValue(props, "skyboxCloudiness"),
- Utility.getPropValue(props, "musicSeq"),
+ skybox_texture_id,
+ Utility.getPropValue(props, "musicSeq", enum_key="seq_id"),
Utility.getPropValue(props, "nightSeq"),
Utility.getPropValue(props, "audioSessionPreset"),
- Utility.getPropValue(props, "mapLocation"),
- Utility.getPropValue(props, "cameraMode"),
+ Utility.getPropValue(props, "mapLocation") if is_oot_features() else "",
+ Utility.getPropValue(props, "cameraMode") if is_oot_features() else "",
+ props.set_region_visited if not is_oot_features() else False,
)
def getCmds(self, lights: SceneLighting):
"""Returns the sound settings, misc settings, special files and skybox settings scene commands"""
+ commands = [
+ f"SCENE_CMD_SOUND_SETTINGS({self.specID}, {self.ambienceID}, {self.sequenceID})",
+ f"SCENE_CMD_SPECIAL_FILES({self.naviHintType}, {self.keepObjectID})",
+ ]
- return (
- indent
- + f",\n{indent}".join(
+ if is_oot_features():
+ commands.extend(
[
- f"SCENE_CMD_SOUND_SETTINGS({self.specID}, {self.ambienceID}, {self.sequenceID})",
f"SCENE_CMD_MISC_SETTINGS({self.sceneCamType}, {self.worldMapLocation})",
- f"SCENE_CMD_SPECIAL_FILES({self.naviHintType}, {self.keepObjectID})",
f"SCENE_CMD_SKYBOX_SETTINGS({self.skyboxID}, {self.skyboxConfig}, {lights.envLightMode})",
]
)
- + ",\n"
- )
+ else:
+ commands.append(
+ f"SCENE_CMD_SKYBOX_SETTINGS({self.skybox_texture_id}, {self.skyboxID}, {self.skyboxConfig}, {lights.envLightMode})"
+ )
+
+ if self.set_region_visited:
+ commands.append("SCENE_CMD_SET_REGION_VISITED()")
+
+ return indent + f",\n{indent}".join(commands) + ",\n"
@dataclass
@@ -221,7 +249,7 @@ class SceneExits(Utility):
exitList: list[tuple[int, str]]
@staticmethod
- def new(name: str, props: OOTSceneHeaderProperty):
+ def new(name: str, props: Z64_SceneHeaderProperty):
# TODO: proper implementation of exits
exitList: list[tuple[int, str]] = []
@@ -254,3 +282,116 @@ def getC(self):
)
return exitListC
+
+
+class MapDataChest:
+ def __init__(self, chest_prop: Z64_MapDataChestProperty, scene_obj: Object, transform: Matrix):
+ if chest_prop.chest_obj is None:
+ raise PluginError("ERROR: The chest empty object is unset.")
+
+ pos, _, _, _ = Utility.getConvertedTransform(transform, scene_obj, chest_prop.chest_obj, True)
+
+ self.room_idx = self.get_room_index(chest_prop, scene_obj)
+ self.chest_flag = int(getEvalParams(chest_prop.chest_obj.ootActorProperty.params_custom), base=0) & 0x1F
+ self.pos = pos
+
+ def get_room_index(self, chest_prop: Z64_MapDataChestProperty, scene_obj: Object) -> int:
+ room_obj_list = getObjectList(scene_obj.children_recursive, "EMPTY", "Room")
+
+ for room_obj in room_obj_list:
+ if chest_prop.chest_obj in room_obj.children_recursive:
+ return room_obj.ootRoomHeader.roomIndex
+
+ raise PluginError(f"ERROR: Can't find the room associated with '{chest_prop.chest_obj.name}'")
+
+ def to_c(self):
+ return "{ " + f"{self.room_idx}, {self.chest_flag}, {self.pos[0]}, {self.pos[1]}, {self.pos[2]}" + " }"
+
+
+class MapDataRoom:
+ def __init__(self, room_prop: Z64_MapDataRoomProperty):
+ self.map_id: str = room_prop.map_id
+ self.center_x: int = room_prop.center_x
+ self.floor_y: int = room_prop.floor_y
+ self.center_z: int = room_prop.center_z
+ self.flags: str = room_prop.flags
+
+ def to_c(self):
+ return "{ " + f"{self.map_id}, {self.center_x}, {self.floor_y}, {self.center_z}, {self.flags}" + " }"
+
+
+@dataclass
+class SceneMapData:
+ """This class hosts exit data"""
+
+ name: str
+ map_scale: int
+ room_list: list[MapDataRoom]
+ chest_list: list[MapDataChest]
+
+ @staticmethod
+ def new(name: str, props: Z64_SceneHeaderProperty, scene_obj: Object, transform: Matrix):
+ return SceneMapData(
+ name,
+ props.minimap_scale,
+ [MapDataRoom(room_prop) for room_prop in props.minimap_room_list],
+ [MapDataChest(chest_prop, scene_obj, transform) for chest_prop in props.minimap_chest_list],
+ )
+
+ def is_used(self):
+ return not is_oot_features() and (len(self.room_list) > 0 or len(self.chest_list) > 0)
+
+ def get_cmds(self):
+ """Returns the sound settings, misc settings, special files and skybox settings scene commands"""
+ commands = []
+
+ if len(self.room_list) > 0:
+ commands.append(f"SCENE_CMD_MAP_DATA(&{self.name})")
+
+ if len(self.chest_list) > 0:
+ commands.append(f"SCENE_CMD_MAP_DATA_CHESTS({len(self.chest_list)}, {self.name}Chest)")
+
+ if len(commands) > 0:
+ return indent + f",\n{indent}".join(commands) + ",\n"
+
+ return ""
+
+ def to_c(self):
+ map_data_c = CData()
+ scene_list_name = f"MapDataScene {self.name}"
+ room_list_name = f"MapDataRoom {self.name}Room[{len(self.room_list)}]"
+ chest_list_name = f"MapDataChest {self.name}Chest[{len(self.chest_list)}]"
+ map_data_header = []
+ map_data_source = ""
+
+ if len(self.room_list) > 0:
+ map_data_header.extend([f"extern {scene_list_name};\n", f"extern {room_list_name};\n"])
+ map_data_source += (
+ # MapDataRoom
+ (room_list_name + " = {\n")
+ + "\n".join(indent + f"{room.to_c()}," for room in self.room_list)
+ + "\n};\n\n"
+ # MapDataScene
+ + (scene_list_name + " = {\n")
+ + (indent + f"{self.name}Room, {self.map_scale}")
+ + "\n};\n\n"
+ )
+
+ if len(self.chest_list) > 0:
+ map_data_header.append(f"extern {chest_list_name};\n")
+ map_data_source += (
+ # MapDataChest
+ (chest_list_name + " = {\n")
+ + "\n".join(indent + f"{chest.to_c()}," for chest in self.chest_list)
+ + "\n};\n\n"
+ )
+
+ # .h
+ if len(map_data_header) > 0:
+ map_data_c.header = "".join(map_data_header)
+
+ # .c
+ if len(map_data_source) > 0:
+ map_data_c.source = map_data_source
+
+ return map_data_c
diff --git a/fast64_internal/oot/exporter/scene/header.py b/fast64_internal/z64/exporter/scene/header.py
similarity index 58%
rename from fast64_internal/oot/exporter/scene/header.py
rename to fast64_internal/z64/exporter/scene/header.py
index 72bc8d32f..accd94302 100644
--- a/fast64_internal/oot/exporter/scene/header.py
+++ b/fast64_internal/z64/exporter/scene/header.py
@@ -3,11 +3,14 @@
from mathutils import Matrix
from bpy.types import Object
from ....utility import CData
-from ...scene.properties import OOTSceneHeaderProperty
+from ...utility import is_oot_features
+from ...scene.properties import Z64_SceneHeaderProperty
from ..cutscene import SceneCutscene
-from .general import SceneLighting, SceneInfos, SceneExits
+from .general import SceneLighting, SceneInfos, SceneExits, SceneMapData
from .actors import SceneTransitionActors, SceneEntranceActors, SceneSpawns
from .pathways import ScenePathways
+from .animated_mats import SceneAnimatedMaterial
+from .actor_cutscene import SceneActorCutscene
@dataclass
@@ -24,21 +27,38 @@ class SceneHeader:
spawns: Optional[SceneSpawns]
path: Optional[ScenePathways]
+ # MM
+ map_data: Optional[SceneMapData]
+ anim_mat: Optional[SceneAnimatedMaterial]
+ actor_cs: Optional[SceneActorCutscene]
+
@staticmethod
def new(
- name: str, props: OOTSceneHeaderProperty, sceneObj: Object, transform: Matrix, headerIndex: int, useMacros: bool
+ name: str,
+ props: Z64_SceneHeaderProperty,
+ sceneObj: Object,
+ transform: Matrix,
+ headerIndex: int,
+ useMacros: bool,
):
entranceActors = SceneEntranceActors.new(f"{name}_playerEntryList", sceneObj, transform, headerIndex)
return SceneHeader(
name,
SceneInfos.new(props, sceneObj),
SceneLighting.new(f"{name}_lightSettings", props),
- SceneCutscene.new(props, headerIndex, useMacros),
+ SceneCutscene.new(f"{name}_cutsceneScriptEntryList", props, headerIndex, useMacros),
SceneExits.new(f"{name}_exitList", props),
SceneTransitionActors.new(f"{name}_transitionActors", sceneObj, transform, headerIndex),
entranceActors,
SceneSpawns(f"{name}_entranceList", entranceActors.entries),
ScenePathways.new(f"{name}_pathway", sceneObj, transform, headerIndex),
+ SceneMapData.new(f"{name}_mapData", props, sceneObj, transform) if not is_oot_features() else None,
+ SceneAnimatedMaterial.new(f"{name}_AnimatedMaterial", sceneObj, headerIndex)
+ if not is_oot_features()
+ else None,
+ SceneActorCutscene.new(f"{name}_ActorCutscene", sceneObj, transform, headerIndex)
+ if not is_oot_features()
+ else None,
)
def getC(self):
@@ -46,16 +66,20 @@ def getC(self):
headerData = CData()
+ # Write the cutscene script entry list, if used
+ if not is_oot_features() and len(self.cutscene.entries) > 0:
+ headerData.append(self.cutscene.to_c())
+
# Write the spawn position list data and the entrance list
if len(self.entranceActors.entries) > 0:
headerData.append(self.entranceActors.getC())
headerData.append(self.spawns.getC())
- # Write the transition actor list data
+ # Write the transition actor list data, if used
if len(self.transitionActors.entries) > 0:
headerData.append(self.transitionActors.getC())
- # Write the exit list
+ # Write the exit list, if used
if len(self.exits.exitList) > 0:
headerData.append(self.exits.getC())
@@ -63,10 +87,22 @@ def getC(self):
if len(self.lighting.settings) > 0:
headerData.append(self.lighting.getC())
+ # Write the map data, if used
+ if self.map_data is not None and self.map_data.is_used():
+ headerData.append(self.map_data.to_c())
+
# Write the path data, if used
if len(self.path.pathList) > 0:
headerData.append(self.path.getC())
+ # Write the animated material list, if used
+ if self.anim_mat is not None and self.anim_mat.is_used():
+ headerData.append(self.anim_mat.to_c())
+
+ # Write the actor cutscene list, if used
+ if self.actor_cs is not None and self.actor_cs.is_used():
+ headerData.append(self.actor_cs.to_c())
+
return headerData
diff --git a/fast64_internal/oot/exporter/scene/pathways.py b/fast64_internal/z64/exporter/scene/pathways.py
similarity index 72%
rename from fast64_internal/oot/exporter/scene/pathways.py
rename to fast64_internal/z64/exporter/scene/pathways.py
index 331af8dbe..ca3777fce 100644
--- a/fast64_internal/oot/exporter/scene/pathways.py
+++ b/fast64_internal/z64/exporter/scene/pathways.py
@@ -1,17 +1,21 @@
from dataclasses import dataclass, field
+from typing import Optional
from mathutils import Matrix
from bpy.types import Object
+from ....game_data import game_data
from ....utility import PluginError, CData, indent
-from ...oot_utility import getObjectList
+from ...utility import getObjectList
from ..utility import Utility
@dataclass
-class Path:
+class ScenePath:
"""This class defines a pathway"""
name: str
- points: list[tuple[int, int, int]] = field(default_factory=list)
+ additional_path_index: Optional[int]
+ custom_value: Optional[int]
+ points: list[tuple[int, int, int]]
def getC(self):
"""Returns the pathway position array"""
@@ -40,21 +44,23 @@ class ScenePathways:
"""This class hosts pathways array data"""
name: str
- pathList: list[Path]
+ pathList: list[ScenePath]
@staticmethod
def new(name: str, sceneObj: Object, transform: Matrix, headerIndex: int):
- pathFromIndex: dict[int, Path] = {}
+ pathFromIndex: dict[int, ScenePath] = {}
pathObjList = getObjectList(sceneObj.children_recursive, "CURVE", splineType="Path")
for obj in pathObjList:
relativeTransform = transform @ sceneObj.matrix_world.inverted() @ obj.matrix_world
pathProps = obj.ootSplineProperty
- isHeaderValid = Utility.isCurrentHeaderValid(pathProps.headerSettings, headerIndex)
+ isHeaderValid = Utility.isCurrentHeaderValid(obj.ootSplineProperty.headerSettings, headerIndex)
if isHeaderValid and Utility.validateCurveData(obj):
if pathProps.index not in pathFromIndex:
- pathFromIndex[pathProps.index] = Path(
+ pathFromIndex[pathProps.index] = ScenePath(
f"{name}List{pathProps.index:02}",
+ pathProps.opt_path_index if game_data.z64.is_mm() else None,
+ pathProps.custom_value if game_data.z64.is_mm() else None,
[relativeTransform @ point.co.xyz for point in obj.data.splines[0].points],
)
else:
@@ -85,7 +91,14 @@ def getC(self):
pathListData.source = listName + " = {\n"
for path in self.pathList:
- pathListData.source += indent + "{ " + f"ARRAY_COUNTU({path.name}), {path.name}" + " },\n"
+ pathListData.source += (
+ (indent + "{ ")
+ + f"ARRAY_COUNTU({path.name}), "
+ + (f"{path.additional_path_index}, " if game_data.z64.is_mm() else "")
+ + (f"{path.custom_value}, " if game_data.z64.is_mm() else "")
+ + f"{path.name}"
+ + " },\n"
+ )
pathData.append(path.getC())
pathListData.source += "};\n\n"
diff --git a/fast64_internal/oot/exporter/scene/rooms.py b/fast64_internal/z64/exporter/scene/rooms.py
similarity index 97%
rename from fast64_internal/oot/exporter/scene/rooms.py
rename to fast64_internal/z64/exporter/scene/rooms.py
index 6948ef6bb..9efb03b81 100644
--- a/fast64_internal/oot/exporter/scene/rooms.py
+++ b/fast64_internal/z64/exporter/scene/rooms.py
@@ -2,8 +2,8 @@
from mathutils import Matrix
from bpy.types import Object
from ....utility import PluginError, CData, indent
-from ...oot_utility import getObjectList
-from ...oot_model_classes import OOTModel
+from ...utility import getObjectList
+from ...model_classes import OOTModel
from ..room import Room
diff --git a/fast64_internal/oot/exporter/utility.py b/fast64_internal/z64/exporter/utility.py
similarity index 59%
rename from fast64_internal/oot/exporter/utility.py
rename to fast64_internal/z64/exporter/utility.py
index 16a76590f..ca9d4f9a5 100644
--- a/fast64_internal/oot/exporter/utility.py
+++ b/fast64_internal/z64/exporter/utility.py
@@ -1,9 +1,11 @@
+from typing import Optional
from math import radians
from mathutils import Quaternion, Matrix
from bpy.types import Object
from ...utility import PluginError, indent
-from ..oot_utility import ootConvertTranslation, ootConvertRotation
-from ..actor.properties import OOTActorHeaderProperty
+from ...game_data import game_data
+from ..utility import ootConvertTranslation, ootConvertRotation
+from ..actor.properties import Z64_ActorHeaderProperty
altHeaderList = ["childNight", "adultDay", "adultNight"]
@@ -34,31 +36,51 @@ def roundPosition(position) -> tuple[int, int, int]:
return (round(position[0]), round(position[1]), round(position[2]))
@staticmethod
- def isCurrentHeaderValid(headerSettings: OOTActorHeaderProperty, headerIndex: int):
+ def isCurrentHeaderValid(headerSettings: Z64_ActorHeaderProperty, headerIndex: int):
"""Checks if the an alternate header can be used"""
- preset = headerSettings.sceneSetupPreset
+ if game_data.z64.is_oot():
+ preset = headerSettings.sceneSetupPreset
- if preset == "All Scene Setups" or (preset == "All Non-Cutscene Scene Setups" and headerIndex < 4):
- return True
+ if preset == "All Scene Setups" or (preset == "All Non-Cutscene Scene Setups" and headerIndex < 4):
+ return True
- if preset == "Custom":
- for i, header in enumerate(["childDay"] + altHeaderList):
- if getattr(headerSettings, f"{header}Header") and i == headerIndex:
- return True
+ if preset == "Custom":
+ for i, header in enumerate(["childDay"] + altHeaderList):
+ if getattr(headerSettings, f"{header}Header") and i == headerIndex:
+ return True
+ else:
+ if headerSettings.include_in_all_setups or headerSettings.childDayHeader and headerIndex == 0:
+ return True
- for csHeader in headerSettings.cutsceneHeaders:
- if csHeader.headerIndex == headerIndex:
- return True
+ for csHeader in headerSettings.cutsceneHeaders:
+ if csHeader.headerIndex == headerIndex:
+ return True
return False
@staticmethod
- def getPropValue(data, propName: str):
+ def getPropValue(data, prop_name: str, custom_name: Optional[str] = None, enum_key: Optional[str] = None):
"""Returns a property's value based on if the value is 'Custom'"""
- value = getattr(data, propName)
- return value if value != "Custom" else getattr(data, f"{propName}Custom")
+ value = getattr(data, prop_name)
+
+ if value != "Custom":
+ if enum_key is not None:
+ return game_data.z64.enums.enumByKey[enum_key].item_by_key[value].id
+ else:
+ return value
+ elif custom_name is not None:
+ if enum_key is not None:
+ return game_data.z64.enums.enumByKey[enum_key].item_by_key[getattr(data, custom_name)].id
+ else:
+ return getattr(data, custom_name)
+
+ return getattr(data, f"{prop_name}Custom")
+
+ @staticmethod
+ def enum_id_to_key(enum_value_id: str, enum_key: str):
+ return game_data.z64.enums.enumByKey[enum_key].item_by_id[enum_value_id].key
@staticmethod
def getConvertedTransformWithOrientation(
diff --git a/fast64_internal/oot/f3d/operators.py b/fast64_internal/z64/f3d/operators.py
similarity index 95%
rename from fast64_internal/oot/f3d/operators.py
rename to fast64_internal/z64/f3d/operators.py
index 8c009f0c3..bd867b8a5 100644
--- a/fast64_internal/oot/f3d/operators.py
+++ b/fast64_internal/z64/f3d/operators.py
@@ -11,14 +11,14 @@
from ...f3d.f3d_parser import importMeshC, getImportData
from ...f3d.f3d_gbi import DLFormat, F3D, TextureExportSettings, ScrollMethod, get_F3D_GBI
from ...f3d.f3d_writer import TriangleConverterInfo, removeDL, saveStaticModel, getInfoDict
-from ..oot_utility import ootGetObjectPath, getOOTScale
-from ..oot_model_classes import OOTF3DContext, ootGetIncludedAssetData
-from ..oot_texture_array import ootReadTextureArrays
-from ..oot_model_classes import OOTModel, OOTGfxFormatter
-from ..oot_f3d_writer import ootReadActorScale, writeTextureArraysNew, writeTextureArraysExisting
+from ..utility import ootGetObjectPath, getOOTScale
+from ..model_classes import OOTF3DContext, ootGetIncludedAssetData
+from ..texture_array import ootReadTextureArrays
+from ..model_classes import OOTModel, OOTGfxFormatter
+from ..f3d_writer import ootReadActorScale, writeTextureArraysNew, writeTextureArraysExisting
from .properties import OOTDLImportSettings, OOTDLExportSettings
-from ..oot_utility import (
+from ..utility import (
OOTObjectCategorizer,
ootDuplicateHierarchy,
ootCleanupScene,
diff --git a/fast64_internal/oot/f3d/panels.py b/fast64_internal/z64/f3d/panels.py
similarity index 88%
rename from fast64_internal/oot/f3d/panels.py
rename to fast64_internal/z64/f3d/panels.py
index 30b0bff20..641358f55 100644
--- a/fast64_internal/oot/f3d/panels.py
+++ b/fast64_internal/z64/f3d/panels.py
@@ -1,7 +1,7 @@
import bpy
from bpy.types import Panel, Mesh, Armature
from bpy.utils import register_class, unregister_class
-from ...panels import OOT_Panel
+from ...panels import Z64_Panel
from ...utility import prop_split
from .operators import OOT_ImportDL, OOT_ExportDL
from .properties import (
@@ -22,13 +22,13 @@ class OOT_DisplayListPanel(Panel):
@classmethod
def poll(cls, context):
- return context.scene.gameEditorMode == "OOT" and (
+ return context.scene.gameEditorMode in {"OOT", "MM"} and (
context.object is not None and isinstance(context.object.data, Mesh)
)
def draw(self, context):
box = self.layout.box().column()
- box.box().label(text="OOT DL Inspector")
+ box.box().label(text="DL Inspector")
obj = context.object
# prop_split(box, obj, "ootDrawLayer", "Draw Layer")
@@ -49,8 +49,8 @@ def draw(self, context):
class OOT_MaterialPanel(Panel):
- bl_label = "OOT Material"
- bl_idname = "MATERIAL_PT_OOT_Material_Inspector"
+ bl_label = "Material"
+ bl_idname = "MATERIAL_PT_Z64_Material_Inspector"
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "material"
@@ -58,7 +58,7 @@ class OOT_MaterialPanel(Panel):
@classmethod
def poll(cls, context):
- return context.material is not None and context.scene.gameEditorMode == "OOT"
+ return context.material is not None and context.scene.gameEditorMode in {"OOT", "MM"}
def draw(self, context):
layout = self.layout
@@ -82,8 +82,8 @@ def draw(self, context):
class OOT_DrawLayersPanel(Panel):
- bl_label = "OOT Draw Layers"
- bl_idname = "WORLD_PT_OOT_Draw_Layers_Panel"
+ bl_label = "Draw Layers"
+ bl_idname = "WORLD_PT_Z64_Draw_Layers_Panel"
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "world"
@@ -91,7 +91,7 @@ class OOT_DrawLayersPanel(Panel):
@classmethod
def poll(cls, context):
- return context.scene.gameEditorMode == "OOT"
+ return context.scene.gameEditorMode in {"OOT", "MM"}
def draw(self, context):
world = context.scene.world
@@ -101,9 +101,9 @@ def draw(self, context):
ootDefaultRenderModeProp.draw_props(self.layout)
-class OOT_ExportDLPanel(OOT_Panel):
- bl_idname = "OOT_PT_export_dl"
- bl_label = "OOT DL Exporter"
+class OOT_ExportDLPanel(Z64_Panel):
+ bl_idname = "Z64_PT_export_dl"
+ bl_label = "Display Lists"
# called every frame
def draw(self, context):
diff --git a/fast64_internal/oot/f3d/properties.py b/fast64_internal/z64/f3d/properties.py
similarity index 98%
rename from fast64_internal/oot/f3d/properties.py
rename to fast64_internal/z64/f3d/properties.py
index f2f1ca742..2007536c1 100644
--- a/fast64_internal/oot/f3d/properties.py
+++ b/fast64_internal/z64/f3d/properties.py
@@ -141,7 +141,7 @@ def draw_props(self, layout: UILayout, mat: Object, drawLayer: str):
return
suffix = "(" + drawLayerSuffix[drawLayer] + ")"
- layout.box().column().label(text="OOT Dynamic Material Properties " + suffix)
+ layout.box().column().label(text="Dynamic Material Properties " + suffix)
layout.label(text="See gSPSegment calls in z_scene_table.c.")
layout.label(text="Based off draw config index in gSceneTable.")
dynMatLayerProp: OOTDynamicMaterialDrawLayerProperty = getattr(self, drawLayer.lower())
@@ -178,8 +178,6 @@ def draw_props(self, layout: UILayout):
oot_dl_writer_classes = (
- OOTDLExportSettings,
- OOTDLImportSettings,
OOTDynamicMaterialDrawLayerProperty,
OOTDynamicMaterialProperty,
OOTDefaultRenderModesProperty,
diff --git a/fast64_internal/oot/oot_f3d_writer.py b/fast64_internal/z64/f3d_writer.py
similarity index 97%
rename from fast64_internal/oot/oot_f3d_writer.py
rename to fast64_internal/z64/f3d_writer.py
index baaffe893..2073c800c 100644
--- a/fast64_internal/oot/oot_f3d_writer.py
+++ b/fast64_internal/z64/f3d_writer.py
@@ -7,8 +7,8 @@
from ..utility import CData, getGroupIndexFromname, readFile, writeFile
from ..f3d.flipbook import flipbook_to_c, flipbook_2d_to_c, flipbook_data_to_c
from ..f3d.f3d_material import createF3DMat, F3DMaterial_UpdateLock, update_preset_manual
-from .oot_utility import replaceMatchContent, getOOTScale
-from .oot_texture_array import TextureFlipbook
+from .utility import replaceMatchContent, getOOTScale
+from .texture_array import TextureFlipbook
from ..f3d.f3d_writer import (
checkForF3dMaterialInFaces,
@@ -17,7 +17,7 @@
saveMeshByFaces,
)
-from .oot_model_classes import (
+from .model_classes import (
OOTTriangleConverterInfo,
OOTModel,
ootGetActorData,
@@ -350,7 +350,10 @@ def ootReadActorScale(basePath: str, overlayName: str, isLink: bool) -> Optional
scale = actorScaleMatch.group(1).strip()
if scale[-1] == "f":
scale = scale[:-1]
- return getOOTScale(1 / float(scale))
+ try:
+ return getOOTScale(1 / float(scale))
+ except:
+ print(f"WARNING: the scale value read is not a float ({repr(scale)})")
print("WARNING: auto-detection failed, defaulting to this panel's actor scale property value")
return None
diff --git a/fast64_internal/oot/file_settings.py b/fast64_internal/z64/file_settings.py
similarity index 63%
rename from fast64_internal/oot/file_settings.py
rename to fast64_internal/z64/file_settings.py
index 67206b597..774367a80 100644
--- a/fast64_internal/oot/file_settings.py
+++ b/fast64_internal/z64/file_settings.py
@@ -3,25 +3,31 @@
from bpy.types import Scene
from ..utility import prop_split
from ..render_settings import on_update_render_settings
-from ..panels import OOT_Panel
+from ..panels import Z64_Panel
-class OOT_FileSettingsPanel(OOT_Panel):
- bl_idname = "OOT_PT_file_settings"
- bl_label = "OOT File Settings"
+class OOT_FileSettingsPanel(Z64_Panel):
+ bl_idname = "Z64_PT_file_settings"
+ bl_label = "Workspace Settings"
bl_options = set() # default to being open
# called every frame
def draw(self, context):
col = self.layout.column()
col.scale_y = 1.1 # extra padding, makes it easier to see these main settings
- prop_split(col, context.scene, "ootBlenderScale", "OOT Scene Scale")
+ prop_split(col, context.scene, "ootBlenderScale", "Scene Scale")
prop_split(col, context.scene, "ootDecompPath", "Decomp Path")
- prop_split(col, context.scene.fast64.oot, "oot_version", "OoT Version")
- if context.scene.fast64.oot.oot_version == "Custom":
- prop_split(col, context.scene.fast64.oot, "oot_version_custom", "Custom Version")
+ if context.scene.gameEditorMode == "OOT":
+ version = "oot_version"
+ else:
+ version = "mm_version"
+
+ prop_split(col, context.scene.fast64.oot, version, "Version")
+
+ if getattr(context.scene.fast64.oot, version) == "Custom":
+ prop_split(col, context.scene.fast64.oot, "version_custom", "Custom Version")
col.prop(context.scene.fast64.oot, "headerTabAffectsVisibility")
col.prop(context.scene.fast64.oot, "hackerFeaturesEnabled")
@@ -29,7 +35,10 @@ def draw(self, context):
if not context.scene.fast64.oot.hackerFeaturesEnabled:
col.prop(context.scene.fast64.oot, "useDecompFeatures")
col.prop(context.scene.fast64.oot, "exportMotionOnly")
- col.prop(context.scene.fast64.oot, "use_new_actor_panel")
+
+ if context.scene.gameEditorMode == "OOT":
+ col.prop(context.scene.fast64.oot, "use_new_actor_panel")
+ col.prop(context.scene.fast64.oot, "mm_features")
oot_classes = (OOT_FileSettingsPanel,)
diff --git a/fast64_internal/oot/importer/__init__.py b/fast64_internal/z64/importer/__init__.py
similarity index 100%
rename from fast64_internal/oot/importer/__init__.py
rename to fast64_internal/z64/importer/__init__.py
diff --git a/fast64_internal/oot/importer/actor.py b/fast64_internal/z64/importer/actor.py
similarity index 63%
rename from fast64_internal/oot/importer/actor.py
rename to fast64_internal/z64/importer/actor.py
index 266122215..3c2746034 100644
--- a/fast64_internal/oot/importer/actor.py
+++ b/fast64_internal/z64/importer/actor.py
@@ -1,10 +1,18 @@
import re
import bpy
+from bpy.types import Object
from ...utility import parentObject, hexOrDecInt
-from ..scene.properties import OOTSceneHeaderProperty
-from ..oot_utility import setCustomProperty, getEvalParams
-from ..oot_constants import ootEnumCamTransition, ootData
+from ...game_data import game_data
+from ..scene.properties import Z64_SceneHeaderProperty
+from ..utility import setCustomProperty, getEvalParams, is_oot_features
+from ..constants import (
+ ootEnumCamTransition,
+ halfday_bits_all_dawns,
+ halfday_bits_all_nights,
+ halfday_bits_to_enum,
+ halfday_bits_values,
+)
from .classes import SharedSceneData
from .constants import actorsWithRotAsParam
@@ -35,6 +43,13 @@ def parseTransActorList(
position = tuple([hexOrDecInt(value) for value in params[5:8]])
rot_y = getEvalParams(params[8]) if "DEG_TO_BINANG" in params[8] else params[8]
+ actor_cs_index = 0x7F
+
+ if game_data.z64.is_mm():
+ rotY_int = int(rot_y, base=0)
+ rot_y = f"0x{(rotY_int >> 7) & 0x1FF:04X}"
+ actor_cs_index = rotY_int & 0x7F
+
rotation = tuple([0, hexOrDecInt(rot_y), 0])
roomIndexFront = hexOrDecInt(params[0])
@@ -53,12 +68,14 @@ def parseTransActorList(
position,
rotation,
actorParam,
+ actor_cs_index,
)
if not sharedSceneData.addHeaderIfItemExists(actorHash, "Transition Actor", headerIndex):
actorObj = createEmptyWithTransform(position, [0, 0, 0] if actorID in actorsWithRotAsParam else rotation)
actorObj.ootEmptyType = "Transition Actor"
actorObj.name = "Transition " + getDisplayNameFromActorID(params[4])
transActorProp = actorObj.ootTransitionActorProperty
+ actorProp = transActorProp.actor
sharedSceneData.transDict[actorHash] = actorObj
@@ -73,11 +90,19 @@ def parseTransActorList(
transActorProp.isRoomTransition = False
parentObject(toRoom, actorObj)
+ if game_data.z64.is_mm():
+ actorProp.actor_cs_index = actor_cs_index
+
setCustomProperty(transActorProp, "cameraTransitionFront", camFront, ootEnumCamTransition)
setCustomProperty(transActorProp, "cameraTransitionBack", camBack, ootEnumCamTransition)
- actorProp = transActorProp.actor
- setCustomProperty(actorProp, "actor_id", actorID, ootData.actorData.ootEnumActorID)
+ setCustomProperty(
+ actorProp,
+ "actor_id",
+ actorID,
+ game_data.z64.actors.ootEnumActorID,
+ "actorIDCustom",
+ )
if actorProp.actor_id != "Custom":
actorProp.params = actorParam
else:
@@ -87,7 +112,7 @@ def parseTransActorList(
def parseEntranceList(
- sceneHeader: OOTSceneHeaderProperty, roomObjs: list[bpy.types.Object], sceneData: str, entranceListName: str
+ sceneHeader: Z64_SceneHeaderProperty, roomObjs: list[bpy.types.Object], sceneData: str, entranceListName: str
):
entranceList = getDataMatch(sceneData, entranceListName, ["EntranceEntry", "Spawn"], "entrance List")
@@ -107,28 +132,49 @@ def parseEntranceList(
return entrances
-def parseActorInfo(actorMatch: re.Match, nestedBrackets: bool) -> tuple[str, list[int], list[int], str]:
+def parseActorInfo(
+ actorMatch: re.Match, nestedBrackets: bool
+) -> tuple[str, str, list[int], tuple[int], tuple[int], str]:
+ spawn_flags = [0x0000] * 3
+ actor_id_flags = 0x0000
+
if nestedBrackets:
actorID = actorMatch.group(1).strip()
position = tuple(
[hexOrDecInt(value.strip()) for value in actorMatch.group(2).split(",") if value.strip() != ""]
)
- rotation = tuple(
- [
- hexOrDecInt(getEvalParams(value.strip()))
- for value in actorMatch.group(3).split(",")
- if value.strip() != ""
- ]
- )
+
+ if game_data.z64.is_oot():
+ spawn_rotation = tuple(
+ [
+ hexOrDecInt(getEvalParams(value.strip()))
+ for value in actorMatch.group(3).split(",")
+ if value.strip() != ""
+ ]
+ )
+ else:
+ if "|" in actorID:
+ split = actorID.split(" | ")
+ actorID = split[0]
+ actor_id_flags = split[1]
+
+ spawn_rotation = []
+ spawn_flags = []
+ for value in actorMatch.group(3).replace(" ", "").replace("\n", "").split("SPAWN_ROT_FLAGS"):
+ if value != "":
+ rot, flags = value.removeprefix("(").removesuffix(",").removesuffix(")").split(",")
+ spawn_rotation.append(hexOrDecInt(getEvalParams(rot)))
+ spawn_flags.append(hexOrDecInt(getEvalParams(flags)))
+
actorParam = actorMatch.group(4).strip()
else:
params = [getEvalParams(value.strip()) for value in actorMatch.group(1).split(",")]
actorID = params[0]
position = tuple([hexOrDecInt(value) for value in params[1:4]])
- rotation = tuple([hexOrDecInt(value) for value in params[4:7]])
+ spawn_rotation = tuple([hexOrDecInt(value) for value in params[4:7]])
actorParam = params[7]
- return actorID, position, rotation, actorParam
+ return actorID, actor_id_flags, position, tuple(spawn_rotation), tuple(spawn_flags), actorParam
def parseSpawnList(
@@ -144,7 +190,9 @@ def parseSpawnList(
index = 0
regex, nestedBrackets = getActorRegex(spawnList)
for spawnMatch in re.finditer(regex, spawnList, flags=re.DOTALL):
- actorID, position, rotation, actorParam = parseActorInfo(spawnMatch, nestedBrackets)
+ actorID, actor_id_flags, position, rotation, spawn_flags, actorParam = parseActorInfo(
+ spawnMatch, nestedBrackets
+ )
spawnIndex, roomIndex = [value for value in entranceList if value[0] == index][0]
actorHash = (actorID, position, rotation, actorParam, spawnIndex, roomIndex)
@@ -157,12 +205,18 @@ def parseSpawnList(
spawnProp.spawnIndex = spawnIndex
spawnProp.customActor = actorID != "ACTOR_PLAYER"
actorProp = spawnProp.actor
- setCustomProperty(actorProp, "actor_id", actorID, ootData.actorData.ootEnumActorID)
+ setCustomProperty(
+ actorProp,
+ "actor_id",
+ actorID,
+ game_data.z64.actors.ootEnumActorID,
+ "actor_id_custom",
+ )
if actorProp.actor_id != "Custom":
actorProp.params = actorParam
else:
actorProp.params_custom = actorParam
- handleActorWithRotAsParam(actorProp, actorID, rotation)
+ handleActorWithRotAsParam(actorProp, actorID, rotation if game_data.z64.is_oot() else spawn_flags)
unsetAllHeadersExceptSpecified(actorProp.headerSettings, headerIndex)
sharedSceneData.entranceDict[actorHash] = spawnObj
@@ -191,21 +245,44 @@ def parseActorList(
actorHash = parseActorInfo(actorMatch, nestedBrackets) + (roomObj.ootRoomHeader.roomIndex,)
if not sharedSceneData.addHeaderIfItemExists(actorHash, "Actor", headerIndex):
- actorID, position, rotation, actorParam, roomIndex = actorHash
+ actorID, actor_id_flags, position, rotation, spawn_flags, actorParam, roomIndex = actorHash
actorObj = createEmptyWithTransform(position, [0, 0, 0] if actorID in actorsWithRotAsParam else rotation)
actorObj.ootEmptyType = "Actor"
actorObj.name = getDisplayNameFromActorID(actorID)
actorProp = actorObj.ootActorProperty
-
- setCustomProperty(actorProp, "actor_id", actorID, ootData.actorData.ootEnumActorID)
- if actorProp.actor_id != "Custom":
- actorProp.params = actorParam
- else:
- actorProp.params_custom = actorParam
+ setCustomProperty(
+ actorProp,
+ "actor_id",
+ actorID,
+ game_data.z64.actors.ootEnumActorID,
+ "actor_id_custom",
+ )
+ actorProp.actorParam = actorParam
handleActorWithRotAsParam(actorProp, actorID, rotation)
unsetAllHeadersExceptSpecified(actorProp.headerSettings, headerIndex)
+ if not is_oot_features():
+ if sharedSceneData.includeActorCs:
+ actorProp.actor_cs_index = spawn_flags[1]
+ actorProp.use_global_actor_cs = actorProp.actor_cs_index < 127
+
+ # see `Actor_SpawnEntry()`
+ halfday_bits = ((spawn_flags[0] & 7) << 7) | (spawn_flags[2] & 0x7F)
+ actorProp.halfday_all = halfday_bits == halfday_bits_all_dawns | halfday_bits_all_nights
+
+ if not actorProp.halfday_all:
+ actorProp.halfday_all_dawns = halfday_bits == halfday_bits_all_dawns
+ actorProp.halfday_all_nights = halfday_bits == halfday_bits_all_nights
+
+ if not actorProp.halfday_all and not actorProp.halfday_all_dawns and not actorProp.halfday_all_nights:
+ for bits in halfday_bits_values:
+ value = halfday_bits & bits
+
+ if value > 0:
+ new_entry = actorProp.halfday_bits.add()
+ new_entry.value = halfday_bits_to_enum[value]
+
sharedSceneData.actorDict[actorHash] = actorObj
parentObject(roomObj, actorObj)
diff --git a/fast64_internal/oot/importer/classes.py b/fast64_internal/z64/importer/classes.py
similarity index 87%
rename from fast64_internal/oot/importer/classes.py
rename to fast64_internal/z64/importer/classes.py
index 87f19d28c..0949caab1 100644
--- a/fast64_internal/oot/importer/classes.py
+++ b/fast64_internal/z64/importer/classes.py
@@ -1,5 +1,6 @@
from ...utility import PluginError
-from ..oot_utility import getHeaderSettings
+from ...game_data import game_data
+from ..utility import getHeaderSettings
from .constants import headerNames
@@ -16,6 +17,8 @@ def __init__(
includePaths: bool,
includeWaterBoxes: bool,
includeCutscenes: bool,
+ includeAnimatedMats: bool,
+ includeActorCs: bool,
):
self.actorDict = {} # actor hash : blender object
self.entranceDict = {} # actor hash : blender object
@@ -32,6 +35,8 @@ def __init__(
self.includePaths = includePaths
self.includeWaterBoxes = includeWaterBoxes
self.includeCutscenes = includeCutscenes
+ self.includeAnimatedMats = includeAnimatedMats
+ self.includeActorCs = includeActorCs
def addHeaderIfItemExists(self, hash, itemType: str, headerIndex: int):
if itemType == "Actor":
@@ -51,7 +56,7 @@ def addHeaderIfItemExists(self, hash, itemType: str, headerIndex: int):
actorObj = dictToAdd[hash]
headerSettings = getHeaderSettings(actorObj)
- if headerIndex < 4:
+ if headerIndex < game_data.z64.cs_index_start:
setattr(headerSettings, headerNames[headerIndex], True)
else:
cutsceneHeaders = headerSettings.cutsceneHeaders
diff --git a/fast64_internal/oot/importer/constants.py b/fast64_internal/z64/importer/constants.py
similarity index 100%
rename from fast64_internal/oot/importer/constants.py
rename to fast64_internal/z64/importer/constants.py
diff --git a/fast64_internal/oot/importer/room_header.py b/fast64_internal/z64/importer/room_header.py
similarity index 78%
rename from fast64_internal/oot/importer/room_header.py
rename to fast64_internal/z64/importer/room_header.py
index 36fa422d6..61aaba050 100644
--- a/fast64_internal/oot/importer/room_header.py
+++ b/fast64_internal/z64/importer/room_header.py
@@ -2,10 +2,10 @@
import re
from ...utility import hexOrDecInt
-from ..oot_utility import setCustomProperty
-from ..oot_model_classes import OOTF3DContext
-from ..room.properties import OOTRoomHeaderProperty
-from ..oot_constants import ootData, ootEnumLinkIdle, ootEnumRoomBehaviour
+from ...game_data import game_data
+from ..utility import setCustomProperty
+from ..model_classes import OOTF3DContext
+from ..room.properties import Z64_RoomHeaderProperty
from .utility import getDataMatch, stripName
from .classes import SharedSceneData
from .constants import headerNames
@@ -13,16 +13,16 @@
from .room_shape import parseMeshHeader
-def parseObjectList(roomHeader: OOTRoomHeaderProperty, sceneData: str, objectListName: str):
+def parseObjectList(roomHeader: Z64_RoomHeaderProperty, sceneData: str, objectListName: str):
objectData = getDataMatch(sceneData, objectListName, "s16", "object list")
objects = [value.strip() for value in objectData.split(",") if value.strip() != ""]
for object in objects:
objectProp = roomHeader.objectList.add()
- objByID = ootData.objectData.objectsByID.get(object)
+ objByID = game_data.z64.objects.objects_by_id.get(object)
if objByID is not None:
- objectProp.objectKey = objByID.key
+ setattr(objectProp, "objectKey", objByID.key)
else:
objectProp.objectIDCustom = object
@@ -49,14 +49,14 @@ def parseRoomCommands(
if headerIndex == 0:
roomHeader = roomObj.ootRoomHeader
- elif headerIndex < 4:
+ elif game_data.z64.is_oot() and headerIndex < game_data.z64.cs_index_start:
roomHeader = getattr(roomObj.ootAlternateRoomHeaders, headerNames[headerIndex])
roomHeader.usePreviousHeader = False
else:
cutsceneHeaders = roomObj.ootAlternateRoomHeaders.cutsceneHeaders
- while len(cutsceneHeaders) < headerIndex - 3:
+ while len(cutsceneHeaders) < headerIndex - (game_data.z64.cs_index_start - 1):
cutsceneHeaders.add()
- roomHeader = cutsceneHeaders[headerIndex - 4]
+ roomHeader = cutsceneHeaders[headerIndex - game_data.z64.cs_index_start]
commands = getDataMatch(sceneData, roomCommandsName, ["SceneCmd", "SCmdBase"], "scene commands")
for commandMatch in re.finditer(rf"(SCENE\_CMD\_[a-zA-Z0-9\_]*)\s*\((.*?)\)\s*,", commands, flags=re.DOTALL):
@@ -68,10 +68,27 @@ def parseRoomCommands(
elif command == "SCENE_CMD_ECHO_SETTINGS":
roomHeader.echo = args[0]
elif command == "SCENE_CMD_ROOM_BEHAVIOR":
- setCustomProperty(roomHeader, "roomBehaviour", args[0], ootEnumRoomBehaviour)
- setCustomProperty(roomHeader, "linkIdleMode", args[1], ootEnumLinkIdle)
+ setCustomProperty(
+ roomHeader,
+ "roomBehaviour",
+ args[0],
+ game_data.z64.ootEnumRoomBehaviour,
+ "roomBehaviourCustom",
+ )
+ setCustomProperty(
+ roomHeader,
+ "linkIdleMode",
+ args[1],
+ game_data.z64.ootEnumLinkIdle,
+ "linkIdleModeCustom",
+ )
roomHeader.showInvisibleActors = args[2] == "true" or args[2] == "1"
- roomHeader.disableWarpSongs = args[3] == "true" or args[3] == "1"
+
+ if game_data.z64.is_oot():
+ roomHeader.disableWarpSongs = args[3] == "true" or args[3] == "1"
+ else:
+ roomHeader.enable_pos_lights = args[4] == "true" or args[4] == "1"
+ roomHeader.enable_storm = args[5] == "true" or args[5] == "1"
elif command == "SCENE_CMD_SKYBOX_DISABLES":
roomHeader.disableSkybox = args[0] == "true" or args[0] == "1"
roomHeader.disableSunMoon = args[1] == "true" or args[1] == "1"
diff --git a/fast64_internal/oot/importer/room_shape.py b/fast64_internal/z64/importer/room_shape.py
similarity index 93%
rename from fast64_internal/oot/importer/room_shape.py
rename to fast64_internal/z64/importer/room_shape.py
index bff6d7c42..c0b35ccc7 100644
--- a/fast64_internal/oot/importer/room_shape.py
+++ b/fast64_internal/z64/importer/room_shape.py
@@ -5,9 +5,9 @@
from ...utility import parentObject, hexOrDecInt, yUpToZUp
from ...f3d.f3d_parser import importMeshC
-from ..oot_model_classes import OOTF3DContext
-from ..room.properties import OOTRoomHeaderProperty
-from ..oot_constants import ootEnumRoomShapeType
+from ..model_classes import OOTF3DContext
+from ..room.properties import Z64_RoomHeaderProperty
+from ..constants import ootEnumRoomShapeType
from .classes import SharedSceneData
from .utility import getDataMatch, stripName
@@ -45,7 +45,7 @@ def parseMeshHeader(
parseBGImageList(roomHeader, sceneData, bgListName, sharedSceneData)
-def parseBGImage(roomHeader: OOTRoomHeaderProperty, params: list[str], sharedSceneData: SharedSceneData):
+def parseBGImage(roomHeader: Z64_RoomHeaderProperty, params: list[str], sharedSceneData: SharedSceneData):
bgImage = roomHeader.bgImageList.add()
bgImage.otherModeFlags = params[10]
bgName = f"{params[3]}.jpg"
@@ -54,7 +54,7 @@ def parseBGImage(roomHeader: OOTRoomHeaderProperty, params: list[str], sharedSce
def parseBGImageList(
- roomHeader: OOTRoomHeaderProperty, sceneData: str, bgListName: str, sharedSceneData: SharedSceneData
+ roomHeader: Z64_RoomHeaderProperty, sceneData: str, bgListName: str, sharedSceneData: SharedSceneData
):
bgData = getDataMatch(sceneData, bgListName, "", "bg list")
bgList = [value.replace("{", "").strip() for value in bgData.split("},") if value.strip() != ""]
diff --git a/fast64_internal/oot/importer/scene.py b/fast64_internal/z64/importer/scene.py
similarity index 75%
rename from fast64_internal/oot/importer/scene.py
rename to fast64_internal/z64/importer/scene.py
index 38541609a..2dd73efd3 100644
--- a/fast64_internal/oot/importer/scene.py
+++ b/fast64_internal/z64/importer/scene.py
@@ -3,19 +3,19 @@
import bpy
import mathutils
+from ...game_data import game_data
from ...utility import readFile, hexOrDecInt
from ...f3d.f3d_parser import parseMatrices
from ...f3d.f3d_gbi import get_F3D_GBI
from ...f3d.flipbook import TextureFlipbook
-from ..oot_model_classes import OOTF3DContext
+from ..model_classes import OOTF3DContext
from ..exporter.decomp_edit.scene_table import SceneTableUtility
-from ..scene.properties import OOTImportSceneSettingsProperty
-from ..oot_constants import ootEnumDrawConfig
+from ..scene.properties import Z64_ImportSceneSettingsProperty
from .scene_header import parseSceneCommands
from .classes import SharedSceneData
from ..cutscene.importer import importCutsceneData
-from ..oot_utility import (
+from ..utility import (
getSceneDirFromLevelName,
setCustomProperty,
sceneNameFromID,
@@ -25,9 +25,22 @@
def parseDrawConfig(drawConfigName: str, sceneData: str, drawConfigData: str, f3dContext: OOTF3DContext):
- drawFunctionName = "Scene_DrawConfig" + "".join(
- [value.strip().lower().capitalize() for value in drawConfigName.replace("SDC_", "").split("_")]
- )
+ if game_data.z64.is_oot():
+ drawFunctionName = "Scene_DrawConfig" + "".join(
+ [value.strip().lower().capitalize() for value in drawConfigName.replace("SDC_", "").split("_")]
+ )
+ else:
+ mm_draw_config_to_func_name = {
+ "SCENE_DRAW_CFG_DEFAULT": "Scene_DrawConfigDefault",
+ "SCENE_DRAW_CFG_MAT_ANIM": "Scene_DrawConfigMatAnim",
+ "SCENE_DRAW_CFG_NOTHING": "Scene_DrawConfigDoNothing",
+ "SCENE_DRAW_CFG_UNUSED_3": "Scene_DrawConfig3",
+ "SCENE_DRAW_CFG_UNUSED_4": "Scene_DrawConfig4",
+ "SCENE_DRAW_CFG_UNUSED_5": "Scene_DrawConfig5",
+ "SCENE_DRAW_CFG_GREAT_BAY_TEMPLE": "Scene_DrawConfigGreatBayTemple",
+ "SCENE_DRAW_CFG_MAT_ANIM_MANUAL_STEP": "Scene_DrawConfigMatAnimManualStep",
+ }
+ drawFunctionName = mm_draw_config_to_func_name[drawConfigName]
# get draw function
match = re.search(rf"void\s*{re.escape(drawFunctionName)}(.*?)CLOSE\_DISPS", drawConfigData, flags=re.DOTALL)
@@ -75,10 +88,11 @@ def parseDrawConfig(drawConfigName: str, sceneData: str, drawConfigData: str, f3
def parseScene(
- settings: OOTImportSceneSettingsProperty,
+ settings: Z64_ImportSceneSettingsProperty,
option: str,
):
sceneName = settings.name
+
if settings.isCustomDest:
importPath = bpy.path.abspath(settings.destPath)
subfolder = None
@@ -90,6 +104,7 @@ def parseScene(
subfolder = None
importPath = bpy.path.abspath(bpy.context.scene.ootDecompPath)
+
importSubdir = ""
if settings.isCustomDest is not None:
importSubdir = subfolder
@@ -104,18 +119,12 @@ def parseScene(
False,
True,
)
- filePath = os.path.join(sceneFolderPath, f"{sceneName}_scene.c")
+ if game_data.z64.is_oot():
+ filePath = os.path.join(sceneFolderPath, f"{sceneName}_scene.c")
+ else:
+ filePath = os.path.join(sceneFolderPath, f"{sceneName}.c")
sceneData = readFile(filePath)
- # roomData = ""
- # sceneFolderFiles = [f for f in listdir(sceneFolderPath) if isfile(join(sceneFolderPath, f))]
- # for sceneFile in sceneFolderFiles:
- # if re.search(rf"{sceneName}_room_[0-9]+\.c", sceneFile):
- # roomPath = os.path.join(sceneFolderPath, sceneFile)
- # roomData += readFile(roomPath)
-
- # sceneData += roomData
-
if bpy.context.mode != "OBJECT":
bpy.context.mode = "OBJECT"
@@ -129,16 +138,22 @@ def parseScene(
if not settings.isCustomDest:
drawConfigName = SceneTableUtility.get_draw_config(sceneName)
- drawConfigData = readFile(os.path.join(importPath, "src/code/z_scene_table.c"))
+ filename = "z_scene_table" if game_data.z64.is_oot() else "z_scene_proc"
+ drawConfigData = readFile(os.path.join(importPath, f"src/code/{filename}.c"))
parseDrawConfig(drawConfigName, sceneData, drawConfigData, f3dContext)
bpy.context.space_data.overlay.show_relationship_lines = False
bpy.context.space_data.overlay.show_curve_normals = True
bpy.context.space_data.overlay.normals_length = 2
- sceneCommandsName = f"{sceneName}_sceneCommands"
+ if game_data.z64.is_oot():
+ sceneCommandsName = f"{sceneName}_sceneCommands"
+ else:
+ sceneCommandsName = f"{sceneName}Commands"
+
if sceneCommandsName not in sceneData:
sceneCommandsName = f"{sceneName}_scene_header00" # fast64 naming
+
sharedSceneData = SharedSceneData(
sceneFolderPath,
settings.includeMesh,
@@ -150,6 +165,8 @@ def parseScene(
settings.includePaths,
settings.includeWaterBoxes,
settings.includeCutscenes,
+ settings.includeAnimatedMats,
+ settings.includeActorCs,
)
if settings.includeCutscenes:
@@ -163,7 +180,8 @@ def parseScene(
sceneObj.ootSceneHeader.sceneTableEntry,
"drawConfig",
SceneTableUtility.get_draw_config(sceneName),
- ootEnumDrawConfig,
+ game_data.z64.get_enum("drawConfig"),
+ "drawConfigCustom",
)
if bpy.context.scene.fast64.oot.headerTabAffectsVisibility:
diff --git a/fast64_internal/oot/importer/scene_collision.py b/fast64_internal/z64/importer/scene_collision.py
similarity index 86%
rename from fast64_internal/oot/importer/scene_collision.py
rename to fast64_internal/z64/importer/scene_collision.py
index 2cf26200e..f3bdb7358 100644
--- a/fast64_internal/oot/importer/scene_collision.py
+++ b/fast64_internal/z64/importer/scene_collision.py
@@ -5,21 +5,17 @@
from random import random
from collections import OrderedDict
+from ...game_data import game_data
from ...utility import PluginError, parentObject, hexOrDecInt, yUpToZUp
from ..collision.properties import OOTMaterialCollisionProperty
-from ..oot_f3d_writer import getColliderMat
-from ..oot_utility import setCustomProperty, ootParseRotation
+from ..f3d_writer import getColliderMat
+from ..utility import setCustomProperty, ootParseRotation
from .utility import getDataMatch, getBits, checkBit, createCurveFromPoints, stripName
from .classes import SharedSceneData
from ..collision.constants import (
- ootEnumFloorSetting,
ootEnumWallSetting,
- ootEnumFloorProperty,
- ootEnumCollisionTerrain,
- ootEnumCollisionSound,
- ootEnumCameraSType,
- ootEnumCameraCrawlspaceSType,
+ enum_camera_crawlspace_stype,
)
@@ -40,13 +36,14 @@ def parseCrawlSpaceData(
crawlProp = curveObj.ootSplineProperty
crawlProp.splineType = "Crawlspace"
crawlProp.index = orderIndex
- setCustomProperty(crawlProp, "camSType", "CAM_SET_CRAWLSPACE", ootEnumCameraCrawlspaceSType)
+ setCustomProperty(crawlProp, "camSType", "CAM_SET_CRAWLSPACE", enum_camera_crawlspace_stype)
return curveObj
-def parseCamDataList(sceneObj: bpy.types.Object, camDataListName: str, sceneData: str):
- camMatchData = getDataMatch(sceneData, camDataListName, ["CamData", "BgCamInfo"], "camera data list")
+def parseCamDataList(sceneObj: bpy.types.Object, camDataListName: str, sceneData: str, is_actor_cs: bool = False):
+ type_names = "ActorCsCamInfo" if is_actor_cs else ["CamData", "BgCamInfo"]
+ camMatchData = getDataMatch(sceneData, camDataListName, type_names, "camera data list")
camDataList = [value.replace("{", "").strip() for value in camMatchData.split("},") if value.strip() != ""]
# orderIndex used for naming cameras in alphabetical order
@@ -55,28 +52,39 @@ def parseCamDataList(sceneObj: bpy.types.Object, camDataListName: str, sceneData
setting, count, posDataName = [value.strip() for value in camEntry.split(",")]
index = None
- objName = f"{sceneObj.name}_camPos_{format(orderIndex, '03')}"
+ objName = (
+ f"{sceneObj.name}_ActorCsCam_{format(orderIndex, '03')}"
+ if is_actor_cs
+ else f"{sceneObj.name}_camPos_{format(orderIndex, '03')}"
+ )
if posDataName != "NULL" and posDataName != "0":
index = hexOrDecInt(posDataName[posDataName.index("[") + 1 : -1])
posDataName = posDataName[1 : posDataName.index("[")] # remove '&' and '[n]'
- if setting == "CAM_SET_CRAWLSPACE" or setting == "0x001E":
+ if not is_actor_cs and setting == "CAM_SET_CRAWLSPACE" or setting == "0x001E":
obj = parseCrawlSpaceData(setting, sceneData, posDataName, index, hexOrDecInt(count), objName, orderIndex)
else:
- obj = parseCamPosData(setting, sceneData, posDataName, index, objName, orderIndex)
+ obj = parseCamPosData(setting, sceneData, posDataName, index, objName, orderIndex, is_actor_cs)
parentObject(sceneObj, obj)
orderIndex += 1
-def parseCamPosData(setting: str, sceneData: str, posDataName: str, index: int, objName: str, orderIndex: str):
+def parseCamPosData(
+ setting: str, sceneData: str, posDataName: str, index: int, objName: str, orderIndex: str, is_actor_cs: bool
+):
camera = bpy.data.cameras.new("Camera")
camObj = bpy.data.objects.new(objName, camera)
bpy.context.scene.collection.objects.link(camObj)
camProp = camObj.ootCameraPositionProperty
- setCustomProperty(camProp, "camSType", setting, ootEnumCameraSType)
- camProp.hasPositionData = posDataName != "NULL" and posDataName != "0"
+ setCustomProperty(camProp, "camSType", setting, game_data.z64.get_enum("camSType"), "camSTypeCustom")
+
+ if is_actor_cs:
+ camProp.is_actor_cs_cam = camProp.hasPositionData = True
+ else:
+ camProp.hasPositionData = posDataName != "NULL" and posDataName != "0"
+
camProp.index = orderIndex
# name is important for alphabetical ordering
@@ -114,8 +122,11 @@ def parseCamPosData(setting: str, sceneData: str, posDataName: str, index: int,
fovValue = hexOrDecInt(fov)
fovValue = int.from_bytes(fovValue.to_bytes(2, "big", signed=fovValue < 0x8000), "big", signed=True)
+ camProp.use_setting_default_fov = fovValue == -1
if fovValue > 360:
fovValue *= 0.01 # see CAM_DATA_SCALED() macro
+ elif camProp.use_setting_default_fov:
+ fovValue = 60 # TODO: set the fov value depending on each camera setting
camObj.data.angle = math.radians(fovValue)
return camObj
@@ -176,9 +187,21 @@ def parseSurfaceParams(
collision.eponaBlock = checkBit(params[0], 31)
collision.decreaseHeight = checkBit(params[0], 30)
- setCustomProperty(collision, "floorSetting", str(getBits(params[0], 26, 4)), ootEnumFloorSetting)
+ setCustomProperty(
+ collision,
+ "floorSetting",
+ str(getBits(params[0], 26, 4)),
+ game_data.z64.get_enum("floorSetting"),
+ "floorSettingCustom",
+ )
setCustomProperty(collision, "wallSetting", str(getBits(params[0], 21, 5)), ootEnumWallSetting)
- setCustomProperty(collision, "floorProperty", str(getBits(params[0], 13, 8)), ootEnumFloorProperty)
+ setCustomProperty(
+ collision,
+ "floorProperty",
+ str(getBits(params[0], 13, 8)),
+ game_data.z64.get_enum("floorProperty"),
+ "floorPropertyCustom",
+ )
collision.exitID = getBits(params[0], 8, 5)
collision.cameraID = getBits(params[0], 0, 8)
collision.isWallDamage = checkBit(params[1], 27)
@@ -197,8 +220,20 @@ def parseSurfaceParams(
collision.hookshotable = checkBit(params[1], 17)
collision.echo = str(getBits(params[1], 11, 6))
collision.lightingSetting = getBits(params[1], 6, 5)
- setCustomProperty(collision, "terrain", str(getBits(params[1], 4, 2)), ootEnumCollisionTerrain)
- setCustomProperty(collision, "sound", str(getBits(params[1], 0, 4)), ootEnumCollisionSound)
+ setCustomProperty(
+ collision,
+ "terrain",
+ str(getBits(params[1], 4, 2)),
+ game_data.z64.enum_floor_effect,
+ "terrainCustom",
+ )
+ setCustomProperty(
+ collision,
+ "sound",
+ str(getBits(params[1], 0, 4)),
+ game_data.z64.get_enum("sound"),
+ "soundCustom",
+ )
collision.ignoreCameraCollision = ignoreCamera
collision.ignoreActorCollision = ignoreActor
diff --git a/fast64_internal/z64/importer/scene_header.py b/fast64_internal/z64/importer/scene_header.py
new file mode 100644
index 000000000..f8443c9cb
--- /dev/null
+++ b/fast64_internal/z64/importer/scene_header.py
@@ -0,0 +1,706 @@
+import math
+import os
+import re
+import bpy
+import mathutils
+
+from bpy.types import Object
+from ...utility import PluginError, readFile, parentObject, hexOrDecInt
+from ...game_data import game_data
+from ...f3d.f3d_parser import parseMatrices
+from ..model_classes import OOTF3DContext
+from ..scene.properties import Z64_SceneHeaderProperty, Z64_LightProperty
+from ..animated_mats.properties import enum_anim_mat_type
+from ..actor_cutscene.properties import enum_cs_cam_id, enum_end_cam, enum_end_sfx, enum_hud_visibility
+from ..utility import (
+ getEvalParams,
+ setCustomProperty,
+ getObjectList,
+ getEnumIndex,
+ get_new_empty_object,
+ twos_complement,
+ parseColor,
+)
+from .constants import headerNames
+from .utility import getDataMatch, stripName
+from .classes import SharedSceneData
+from .room_header import parseRoomCommands
+from .actor import parseTransActorList, parseSpawnList, parseEntranceList
+from .scene_collision import parseCollisionHeader, parseCamDataList
+from .scene_pathways import parsePathList
+
+from ..constants import (
+ ootEnumAudioSessionPreset,
+ ootEnumCameraMode,
+ ootEnumMapLocation,
+ ootEnumNaviHints,
+ ootEnumSkyboxLighting,
+)
+
+
+def parseDirection(index: int, values: tuple[str, str, str]) -> tuple[float, float, float] | int:
+ values = [hexOrDecInt(value) for value in values]
+
+ if tuple(values) == (0, 0, 0):
+ return "Zero"
+ elif index == 0 and tuple(values) == (0x49, 0x49, 0x49):
+ return "Default"
+ elif index == 1 and tuple(values) == (0xB7, 0xB7, 0xB7):
+ return "Default"
+ else:
+ direction = mathutils.Vector(
+ [int.from_bytes(value.to_bytes(1, "big", signed=value < 127), "big", signed=True) / 127 for value in values]
+ )
+
+ return (
+ mathutils.Euler((0, 0, math.pi)).to_quaternion()
+ @ (mathutils.Euler((math.pi / 2, 0, 0)).to_quaternion() @ direction).rotation_difference(
+ mathutils.Vector((0, 0, 1))
+ )
+ ).to_euler()
+
+
+def parseLight(
+ lightHeader: Z64_LightProperty, index: int, rotation: mathutils.Euler, color: mathutils.Vector
+) -> bpy.types.Object | None:
+ setattr(lightHeader, f"useCustomDiffuse{index}", rotation != "Zero" and rotation != "Default")
+
+ if rotation == "Zero" or rotation == "Default":
+ setattr(lightHeader, f"zeroDiffuse{index}", rotation == "Zero")
+ setattr(lightHeader, f"diffuse{index}", color + (1,))
+ return None
+ else:
+ light = bpy.data.lights.new("Light", "SUN")
+ lightObj = bpy.data.objects.new("Light", light)
+ bpy.context.scene.collection.objects.link(lightObj)
+ setattr(lightHeader, f"diffuse{index}Custom", lightObj.data)
+ lightObj.rotation_euler = rotation
+ lightObj.data.color = color
+ lightObj.data.type = "SUN"
+ return lightObj
+
+
+def parseLightList(
+ sceneObj: bpy.types.Object,
+ sceneHeader: Z64_SceneHeaderProperty,
+ sceneData: str,
+ lightListName: str,
+ headerIndex: int,
+):
+ lightData = getDataMatch(sceneData, lightListName, ["LightSettings", "EnvLightSettings"], "light list")
+
+ # I currently don't understand the light list format in respect to this lighting flag.
+ # So we'll set it to custom instead.
+ if sceneHeader.skyboxLighting != "Custom":
+ sceneHeader.skyboxLightingCustom = sceneHeader.skyboxLighting
+ sceneHeader.skyboxLighting = "Custom"
+ sceneHeader.lightList.clear()
+
+ # convert string to ZAPD format if using new Fast64 output
+ if "// Ambient Color" in sceneData:
+ i = 0
+ lightData = lightData.replace("{", "").replace("}", "").replace("\n", "").replace(" ", "").replace(",,", ",")
+ data = "{ "
+ for part in lightData.split(","):
+ if i < 20:
+ if i == 18:
+ part = getEvalParams(part)
+ data += part + ", "
+ if i == 19:
+ data = data[:-2]
+ else:
+ data += "},\n{ " + part + ", "
+ i = 0
+ i += 1
+ lightData = data[:-4]
+
+ lightList = [
+ value.replace("{", "").replace("\n", "").replace(" ", "")
+ for value in lightData.split("},")
+ if value.strip() != ""
+ ]
+
+ index = 0
+ for lightEntry in lightList:
+ lightParams = [value.strip() for value in lightEntry.split(",")]
+
+ ambientColor = parseColor(lightParams[0:3])
+ diffuseDir0 = parseDirection(0, lightParams[3:6])
+ diffuseColor0 = parseColor(lightParams[6:9])
+ diffuseDir1 = parseDirection(1, lightParams[9:12])
+ diffuseColor1 = parseColor(lightParams[12:15])
+ fogColor = parseColor(lightParams[15:18])
+
+ blendFogShort = hexOrDecInt(lightParams[18])
+ fogNear = blendFogShort & ((1 << 10) - 1)
+ transitionSpeed = blendFogShort >> 10
+ z_far = hexOrDecInt(lightParams[19])
+
+ lightHeader = sceneHeader.lightList.add()
+ lightHeader.ambient = ambientColor + (1,)
+
+ lightObj0 = parseLight(lightHeader, 0, diffuseDir0, diffuseColor0)
+ lightObj1 = parseLight(lightHeader, 1, diffuseDir1, diffuseColor1)
+
+ if lightObj0 is not None:
+ parentObject(sceneObj, lightObj0)
+ lightObj0.location = [4 + headerIndex * 2, 0, -index * 2]
+ if lightObj1 is not None:
+ parentObject(sceneObj, lightObj1)
+ lightObj1.location = [4 + headerIndex * 2, 2, -index * 2]
+
+ lightHeader.fogColor = fogColor + (1,)
+ lightHeader.fogNear = fogNear
+ lightHeader.z_far = z_far
+ lightHeader.transitionSpeed = transitionSpeed
+
+ index += 1
+
+
+def parseExitList(sceneHeader: Z64_SceneHeaderProperty, sceneData: str, exitListName: str):
+ exitData = getDataMatch(sceneData, exitListName, "u16", "exit list")
+
+ # see also start position list
+ exitList = [value.strip() for value in exitData.split(",") if value.strip() != ""]
+ for exit in exitList:
+ exitProp = sceneHeader.exitList.add()
+ exitProp.exitIndex = "Custom"
+ exitProp.exitIndexCustom = exit
+
+
+def parseRoomList(
+ sceneObj: bpy.types.Object,
+ sceneData: str,
+ roomListName: str,
+ f3dContext: OOTF3DContext,
+ sharedSceneData: SharedSceneData,
+ headerIndex: int,
+):
+ roomList = getDataMatch(sceneData, roomListName, "RomFile", "room list")
+ index = 0
+ roomObjs = []
+
+ # Assumption that alternate scene headers all use the same room list.
+ for roomMatch in re.finditer(
+ rf"\{{([\(\)\sA-Za-z0-9\_]*),([\(\)\sA-Za-z0-9\_]*)\}}\s*,", roomList, flags=re.DOTALL
+ ):
+ roomName = roomMatch.group(1).strip().replace("SegmentRomStart", "")
+ if "(u32)" in roomName:
+ roomName = roomName[5:].strip()[1:] # includes leading underscore
+ elif "(uintptr_t)" in roomName:
+ roomName = roomName[11:].strip()[1:]
+ else:
+ roomName = roomName[1:]
+
+ roomPath = os.path.join(sharedSceneData.scenePath, f"{roomName}.c")
+ roomData = readFile(roomPath)
+ parseMatrices(roomData, f3dContext, 1 / bpy.context.scene.ootBlenderScale)
+
+ roomCommandsName = f"{roomName}Commands"
+ if roomCommandsName not in roomData:
+ roomCommandsName = f"{roomName}_header00" # fast64 naming
+
+ # Assumption that any shared textures are stored after the CollisionHeader.
+ # This is done to avoid including large collision data in regex searches.
+ try:
+ collisionHeaderIndex = sceneData.index("CollisionHeader ")
+ except:
+ collisionHeaderIndex = 0
+ sharedRoomData = sceneData[collisionHeaderIndex:]
+ roomObj = parseRoomCommands(
+ roomName,
+ None,
+ sharedRoomData + roomData,
+ roomCommandsName,
+ index,
+ f3dContext,
+ sharedSceneData,
+ headerIndex,
+ )
+ parentObject(sceneObj, roomObj)
+ index += 1
+ roomObjs.append(roomObj)
+
+ return roomObjs
+
+
+def parseAlternateSceneHeaders(
+ sceneObj: bpy.types.Object,
+ roomObjs: list[bpy.types.Object],
+ sceneData: str,
+ altHeadersListName: str,
+ f3dContext: OOTF3DContext,
+ sharedSceneData: SharedSceneData,
+):
+ altHeadersData = getDataMatch(sceneData, altHeadersListName, ["SceneCmd*", "SCmdBase*"], "alternate header list")
+ altHeadersList = [value.strip() for value in altHeadersData.split(",") if value.strip() != ""]
+
+ for i in range(len(altHeadersList)):
+ if not (altHeadersList[i] == "NULL" or altHeadersList[i] == "0"):
+ parseSceneCommands(
+ sceneObj.name, sceneObj, roomObjs, altHeadersList[i], sceneData, f3dContext, i + 1, sharedSceneData
+ )
+
+
+def parse_mm_map_data(scene_header, scene_data: str, list_name: str):
+ data_match = getDataMatch(scene_data, list_name, ["MapDataScene", "MinimapList"], "minimap scene", False)
+ scene_map_data = data_match.strip().split(", ")
+ scene_header.minimap_scale = int(scene_map_data[1], base=0)
+
+ data_match = getDataMatch(scene_data, scene_map_data[0], ["MapDataRoom", "MinimapEntry"], "minimap room")
+ room_map_data = data_match.strip().split("\n")
+ for data in room_map_data:
+ map_data = data.strip().removeprefix("{ ").removesuffix(" },").split(", ")
+ new_prop = scene_header.minimap_room_list.add()
+ new_prop.map_id = map_data[0]
+ new_prop.center_x = twos_complement(map_data[1], 16)
+ new_prop.floor_y = twos_complement(map_data[2], 16)
+ new_prop.center_z = twos_complement(map_data[3], 16)
+ new_prop.flags = map_data[4]
+
+
+def parse_mm_map_data_chest(
+ room_obj_list: list[Object], scene_header, scene_data: str, chest_count: int, list_name: str
+):
+ data_match = getDataMatch(scene_data, list_name, ["MapDataChest", "MinimapChest"], "minimap chest")
+ chest_map_data = data_match.strip().split("\n")
+
+ if len(chest_map_data) != chest_count:
+ print(
+ f"WARNING: chest count ({chest_count}) is different than parsed chest data length ({len(chest_map_data)})"
+ )
+
+ if room_obj_list is None or len(room_obj_list) == 0:
+ raise PluginError("ERROR: The room list doesn't exist or is empty!")
+
+ for data in chest_map_data:
+ map_data = data.strip().removeprefix("{ ").removesuffix(" },").split(", ")
+ chest_room_id, chest_flag, chest_pos_x, chest_pos_y, chest_pos_z = map_data
+
+ # fetch room
+ chest_room = None
+ for room in room_obj_list:
+ if getattr(room.ootRoomHeader, "roomIndex") == int(chest_room_id, base=0):
+ chest_room = room
+ break
+
+ # fetch chest actor (based on the chest flag and room index, maybe we should check for the coordinates too?)
+ chest_actor_obj = None
+ if chest_room is not None:
+ for child_obj in chest_room.children_recursive:
+ if child_obj.type == "EMPTY" and child_obj.ootEmptyType == "Actor":
+ actor_id: str = getattr(child_obj.ootActorProperty, "actor_id")
+ actor_params = int(getEvalParams(child_obj.ootActorProperty.params_custom), base=0)
+
+ if actor_id in {"ACTOR_EN_BOX"}:
+ actor_chest_flag = actor_params & 0x1F
+ if actor_chest_flag == int(chest_flag, base=0):
+ chest_actor_obj = child_obj
+ break
+ else:
+ raise PluginError("ERROR: Chest's Room not found!")
+
+ if chest_actor_obj is not None:
+ new_prop = scene_header.minimap_chest_list.add()
+ new_prop.chest_obj = chest_actor_obj
+ else:
+ raise PluginError("ERROR: Chest Object not found!")
+
+
+animated_material_first_list_name = ""
+
+
+def parse_animated_material(scene_obj: Object, header_index: int, scene_data: str, list_name: str):
+ global animated_material_first_list_name
+
+ data_match = getDataMatch(scene_data, list_name, "AnimatedMaterial", "animated material")
+ anim_mat_data = data_match.strip().split("\n")
+
+ if header_index == 0:
+ animated_material_first_list_name = list_name
+ anim_mat_obj = get_new_empty_object("Animated Material")
+ anim_mat_obj.ootEmptyType = "Animated Materials"
+ parentObject(scene_obj, anim_mat_obj)
+ else:
+ obj_list = getObjectList(scene_obj.children_recursive, "EMPTY", "Animated Materials")
+ anim_mat_obj = obj_list[0]
+
+ # if the alternate header is using the first header's data then don't do anything
+ if header_index > 0 and list_name == animated_material_first_list_name:
+ return
+
+ anim_mat_props = anim_mat_obj.z64_anim_mats_property
+ anim_mat_item = anim_mat_props.items.add()
+ anim_mat_item.header_index = header_index
+
+ for data in anim_mat_data:
+ data = data.replace("{", "").replace("}", "").removesuffix(",").strip()
+
+ split = data.split(", ")
+ segment = int(split[0], base=0)
+ type_num = int(split[1], base=0)
+ data_ptr = split[2].removeprefix("&")
+
+ is_array = type_num in {0, 1}
+ struct_name, data_match = getDataMatch(
+ scene_data, data_ptr, r"(AnimatedMat[a-zA-Z]*Params)", "animated params", is_array, False
+ )
+
+ if is_array:
+ params_data = data_match.replace("{", "").replace("}", "").replace(" ", "").split("\n")
+ else:
+ params_data = data_match.replace("\n", "").replace(" ", "").split(",")
+
+ entry = anim_mat_item.entries.add()
+ entry.segment_num = abs(segment) + 7
+ entry.type = enum_anim_mat_type[type_num + 1][0]
+
+ if struct_name == "AnimatedMatTexScrollParams":
+ for params in params_data:
+ if len(params) > 0:
+ split = params.split(",")
+ scroll_entry = entry.tex_scroll_params.entries.add()
+ scroll_entry.step_x = int(split[0], base=0)
+ scroll_entry.step_y = int(split[1], base=0)
+ scroll_entry.width = int(split[2], base=0)
+ scroll_entry.height = int(split[3], base=0)
+ elif struct_name == "AnimatedMatColorParams":
+ entry.color_params.frame_count = int(params_data[0], base=0)
+
+ prim_match = getDataMatch(scene_data, params_data[2], "F3DPrimColor", "animated material prim color", True)
+ prim_data = prim_match.strip().replace(" ", "").replace("}", "").replace("{", "").split("\n")
+
+ env_match = getDataMatch(scene_data, params_data[3], "F3DEnvColor", "animated material env color", True)
+ env_data = env_match.strip().replace(" ", "").replace("}", "").replace("{", "").split("\n")
+
+ frame_match = getDataMatch(scene_data, params_data[4], "u16", "animated material color frame data", True)
+ frame_data = frame_match.strip().replace(" ", "").removesuffix(",").replace(",", "\n").split("\n")
+
+ assert len(prim_data) == len(env_data) == len(frame_data)
+
+ for prim_color_raw, env_color_raw, frame in zip(prim_data, env_data, frame_data):
+ prim_color = prim_color_raw.split(",")
+ env_color = env_color_raw.split(",")
+
+ color_entry = entry.color_params.keyframes.add()
+ color_entry.frame_num = int(frame, base=0)
+ color_entry.prim_lod_frac = int(prim_color[4].strip(), base=0)
+ color_entry.prim_color = parseColor(prim_color[0:3]) + (1,)
+ color_entry.env_color = parseColor(env_color[0:3]) + (1,)
+ elif struct_name == "AnimatedMatTexCycleParams":
+ entry.tex_cycle_params.frame_count = int(params_data[0], base=0)
+ textures: list[str] = []
+ frames: list[int] = []
+
+ data_match = getDataMatch(scene_data, params_data[1], "TexturePtr", "animated material texture ptr", True)
+ for texture_ptr in data_match.replace(",", "\n").strip().split("\n"):
+ textures.append(texture_ptr.strip())
+
+ data_match = getDataMatch(scene_data, params_data[2], "u8", "animated material frame data", True)
+ for frame_num in data_match.replace(",", "\n").strip().split("\n"):
+ frames.append(int(frame_num.strip(), base=0))
+
+ while len(textures) < len(frames):
+ textures.append("")
+
+ for texture_ptr, frame_num in zip(textures, frames):
+ cycle_entry = entry.tex_cycle_params.keyframes.add()
+ cycle_entry.frame_num = frame_num
+ cycle_entry.texture = texture_ptr
+
+
+def set_actor_cs_property(value: str, enum: tuple[str, str, str], data, prop_name: str, is_cam: bool = False):
+ enum_index = None
+
+ try:
+ if is_cam:
+ # since the value is negative it will simply go backwards in the list
+ enum_index = int(value, base=0)
+
+ # use camera obj pointer mode if the value is not negative
+ if enum_index >= 0:
+ enum_index = 1
+ else:
+ # accounting for custom value
+ enum_index = int(value, base=0) + 1
+ except:
+ enum_index = getEnumIndex(enum, value)
+
+ if enum_index is not None:
+ setattr(data, prop_name, enum[enum_index][0])
+ else:
+ setattr(data, prop_name, "Custom")
+ setattr(data, f"{prop_name}_custom", value)
+
+
+def parse_actor_cs(scene_obj: Object, header_index: int, scene_data: str, list_name: str):
+ data_match = getDataMatch(scene_data, list_name, "CutsceneEntry", "actor cs")
+ actor_cs_data = data_match.strip().replace(" ", "").replace("{", "").replace("}", "").split("\n")
+
+ # TODO: implement alt headers
+ if header_index != 0:
+ return
+
+ actor_cs_obj = get_new_empty_object("Actor Cutscene")
+ actor_cs_obj.ootEmptyType = "Actor Cutscene"
+ parentObject(scene_obj, actor_cs_obj)
+
+ props = actor_cs_obj.z64_actor_cs_property
+
+ for data in actor_cs_data:
+ split = data.removesuffix(",").split(",")
+
+ new_entry = props.entries.add()
+ new_entry.priority = int(split[0], base=0)
+ new_entry.length = int(split[1], base=0)
+ set_actor_cs_property(split[2], enum_cs_cam_id, new_entry, "cs_cam_id", True)
+ new_entry.script_index = int(split[3], base=0)
+ new_entry.additional_cs_id = int(split[4], base=0)
+ set_actor_cs_property(split[5], enum_end_sfx, new_entry, "end_sfx")
+ new_entry.custom_value = split[6]
+ set_actor_cs_property(split[7], enum_hud_visibility, new_entry, "hud_visibility")
+ set_actor_cs_property(split[8], enum_end_cam, new_entry, "end_cam")
+ new_entry.letterbox_size = int(split[9], base=0)
+
+ if new_entry.cs_cam_id == "Camera":
+ for obj in bpy.data.objects:
+ cam_props = obj.ootCameraPositionProperty
+ if obj.type == "CAMERA" and cam_props.is_actor_cs_cam and cam_props.index == int(split[2], base=0):
+ new_entry.cs_cam_obj = obj
+ break
+
+ bpy.context.scene.fast64.oot.global_actor_cs_count = len(props.entries)
+
+
+def parseSceneCommands(
+ sceneName: str | None,
+ sceneObj: bpy.types.Object | None,
+ roomObjs: list[bpy.types.Object] | None,
+ sceneCommandsName: str,
+ sceneData: str,
+ f3dContext: OOTF3DContext,
+ headerIndex: int,
+ sharedSceneData: SharedSceneData,
+):
+ if sceneObj is None:
+ sceneObj = bpy.data.objects.new(sceneCommandsName, None)
+ bpy.context.scene.collection.objects.link(sceneObj)
+ sceneObj.empty_display_type = "SPHERE"
+ sceneObj.ootEmptyType = "Scene"
+ sceneObj.name = sceneName
+
+ if headerIndex == 0:
+ sceneHeader = sceneObj.ootSceneHeader
+ elif game_data.z64.is_oot() and headerIndex < game_data.z64.cs_index_start:
+ sceneHeader = getattr(sceneObj.ootAlternateSceneHeaders, headerNames[headerIndex])
+ sceneHeader.usePreviousHeader = False
+ else:
+ cutsceneHeaders = sceneObj.ootAlternateSceneHeaders.cutsceneHeaders
+ while len(cutsceneHeaders) < headerIndex - (game_data.z64.cs_index_start - 1):
+ cutsceneHeaders.add()
+ sceneHeader = cutsceneHeaders[headerIndex - game_data.z64.cs_index_start]
+
+ commands = getDataMatch(sceneData, sceneCommandsName, ["SceneCmd", "SCmdBase"], "scene commands")
+ entranceList = None
+ # command to delay: command args
+ delayed_commands: dict[str, list[str]] = {}
+ command_map: dict[str, list[str]] = {}
+
+ # store the commands to process with the corresponding args
+ for commandMatch in re.finditer(rf"(SCENE\_CMD\_[a-zA-Z0-9\_]*)\s*\((.*?)\)\s*,", commands, flags=re.DOTALL):
+ command = commandMatch.group(1)
+ args = [arg.strip() for arg in commandMatch.group(2).split(",")]
+ command_map[command] = args
+
+ command_list = list(command_map.keys())
+
+ for command, args in command_map.items():
+ if command == "SCENE_CMD_SOUND_SETTINGS":
+ setCustomProperty(sceneHeader, "audioSessionPreset", args[0], ootEnumAudioSessionPreset)
+ setCustomProperty(
+ sceneHeader,
+ "nightSeq",
+ args[1],
+ game_data.z64.ootEnumNightSeq,
+ "nightSeqCustom",
+ )
+
+ if args[2].startswith("NA_BGM_"):
+ enum_id = args[2]
+ else:
+ enum_id = game_data.z64.enums.enumByKey["seq_id"].item_by_index[int(args[2])].id
+
+ setCustomProperty(sceneHeader, "musicSeq", enum_id, game_data.z64.get_enum("musicSeq"), "musicSeqCustom")
+ command_list.remove(command)
+ elif command == "SCENE_CMD_ROOM_LIST":
+ # Delay until actor cutscenes are processed
+ delayed_commands[command] = args
+ command_list.remove(command)
+ elif command == "SCENE_CMD_TRANSITION_ACTOR_LIST":
+ if sharedSceneData.includeActors:
+ # This must be handled after rooms, so that room objs can be referenced
+ delayed_commands[command] = args
+ command_list.remove(command)
+ elif game_data.z64.is_oot() and command == "SCENE_CMD_MISC_SETTINGS":
+ setCustomProperty(sceneHeader, "cameraMode", args[0], ootEnumCameraMode)
+ setCustomProperty(sceneHeader, "mapLocation", args[1], ootEnumMapLocation)
+ command_list.remove(command)
+ elif command == "SCENE_CMD_COL_HEADER":
+ # Delay until after rooms are processed
+ delayed_commands[command] = args
+ command_list.remove(command)
+ elif command == "SCENE_CMD_ENTRANCE_LIST":
+ if sharedSceneData.includeActors:
+ # Delay until after rooms are processed
+ delayed_commands[command] = args
+ command_list.remove(command)
+ elif command == "SCENE_CMD_SPECIAL_FILES":
+ if game_data.z64.is_oot():
+ setCustomProperty(sceneHeader, "naviCup", args[0], ootEnumNaviHints)
+ setCustomProperty(
+ sceneHeader,
+ "globalObject",
+ args[1],
+ game_data.z64.get_enum("globalObject"),
+ "globalObjectCustom",
+ )
+ command_list.remove(command)
+ elif command == "SCENE_CMD_PATH_LIST":
+ if sharedSceneData.includePaths:
+ pathListName = stripName(args[0])
+ parsePathList(sceneObj, sceneData, pathListName, headerIndex, sharedSceneData)
+ command_list.remove(command)
+ elif command == "SCENE_CMD_SPAWN_LIST":
+ if sharedSceneData.includeActors:
+ # This must be handled after entrance list, so that entrance list and room list can be referenced
+ delayed_commands[command] = args
+ command_list.remove(command)
+ elif command == "SCENE_CMD_SKYBOX_SETTINGS":
+ args_index = 0
+ if game_data.z64.is_mm():
+ sceneHeader.skybox_texture_id = args[args_index]
+ args_index += 1
+ setCustomProperty(
+ sceneHeader,
+ "skyboxID",
+ args[args_index],
+ game_data.z64.ootEnumSkybox,
+ "skyboxIDCustom",
+ )
+ setCustomProperty(
+ sceneHeader,
+ "skyboxCloudiness",
+ args[args_index + 1],
+ game_data.z64.ootEnumCloudiness,
+ "skyboxCloudinessCustom",
+ )
+ setCustomProperty(sceneHeader, "skyboxLighting", args[args_index + 2], ootEnumSkyboxLighting)
+ command_list.remove(command)
+ elif command == "SCENE_CMD_EXIT_LIST":
+ exitListName = stripName(args[0])
+ parseExitList(sceneHeader, sceneData, exitListName)
+ command_list.remove(command)
+ elif command == "SCENE_CMD_ENV_LIGHT_SETTINGS":
+ if sharedSceneData.includeLights:
+ if not (args[1] == "NULL" or args[1] == "0" or args[1] == "0x00"):
+ lightsListName = stripName(args[1])
+ parseLightList(sceneObj, sceneHeader, sceneData, lightsListName, headerIndex)
+ command_list.remove(command)
+ elif command == "SCENE_CMD_CUTSCENE_DATA":
+ if sharedSceneData.includeCutscenes:
+ sceneHeader.writeCutscene = True
+ sceneHeader.csWriteType = "Object"
+ csObjName = f"Cutscene.{args[0]}"
+ try:
+ sceneHeader.csWriteObject = bpy.data.objects[csObjName]
+ except:
+ print(f"ERROR: Cutscene ``{csObjName}`` do not exist!")
+ command_list.remove(command)
+ elif command == "SCENE_CMD_ALTERNATE_HEADER_LIST":
+ # Delay until after rooms are processed
+ delayed_commands[command] = args
+ command_list.remove(command)
+ elif command == "SCENE_CMD_END":
+ command_list.remove(command)
+
+ # handle Majora's Mask exclusive commands
+ elif game_data.z64.is_mm():
+ if command == "SCENE_CMD_SET_REGION_VISITED":
+ sceneHeader.set_region_visited = True
+ command_list.remove(command)
+ elif command in {"SCENE_CMD_MINIMAP_INFO", "SCENE_CMD_MAP_DATA"}:
+ parse_mm_map_data(sceneHeader, sceneData, stripName(args[0]))
+ command_list.remove(command)
+ elif command in {"SCENE_CMD_MINIMAP_COMPASS_ICON_INFO", "SCENE_CMD_MAP_DATA_CHESTS"}:
+ # Delay until rooms and actors are processed
+ delayed_commands[command] = args
+ command_list.remove(command)
+ elif command == "SCENE_CMD_ANIMATED_MATERIAL_LIST":
+ if sharedSceneData.includeAnimatedMats:
+ parse_animated_material(sceneObj, headerIndex, sceneData, stripName(args[0]))
+ command_list.remove(command)
+ elif command == "SCENE_CMD_ACTOR_CUTSCENE_CAM_LIST":
+ if sharedSceneData.includeActorCs:
+ # TODO: implement alt headers
+ if headerIndex == 0:
+ parseCamDataList(sceneObj, stripName(args[1]), sceneData, True)
+ command_list.remove(command)
+ elif command == "SCENE_CMD_ACTOR_CUTSCENE_LIST":
+ if sharedSceneData.includeActorCs:
+ # Delay until cameras are processed, if used
+ delayed_commands[command] = args
+ command_list.remove(command)
+
+ # requires `SCENE_CMD_ACTOR_CUTSCENE_CAM_LIST`
+ if "SCENE_CMD_ACTOR_CUTSCENE_LIST" in delayed_commands:
+ if sharedSceneData.includeActorCs:
+ args = delayed_commands["SCENE_CMD_ACTOR_CUTSCENE_LIST"]
+ parse_actor_cs(sceneObj, headerIndex, sceneData, stripName(args[1]))
+ delayed_commands.pop("SCENE_CMD_ACTOR_CUTSCENE_LIST")
+
+ # requires `SCENE_CMD_ACTOR_CUTSCENE_LIST`
+ if "SCENE_CMD_ROOM_LIST" in delayed_commands:
+ args = delayed_commands["SCENE_CMD_ROOM_LIST"]
+ # Assumption that all scenes use the same room list.
+ if headerIndex == 0:
+ if roomObjs is not None:
+ raise PluginError("Attempting to parse a room list while room objs already loaded.")
+ roomListName = stripName(args[1])
+ roomObjs = parseRoomList(sceneObj, sceneData, roomListName, f3dContext, sharedSceneData, headerIndex)
+ delayed_commands.pop("SCENE_CMD_ROOM_LIST")
+ else:
+ raise PluginError("ERROR: no room command found for this scene!")
+
+ # any other delayed command requires rooms to be processed
+ for command, args in delayed_commands.items():
+ if command == "SCENE_CMD_TRANSITION_ACTOR_LIST" and sharedSceneData.includeActors:
+ transActorListName = stripName(args[1])
+ parseTransActorList(roomObjs, sceneData, transActorListName, sharedSceneData, headerIndex)
+ elif command == "SCENE_CMD_COL_HEADER":
+ # Assumption that all scenes use the same collision.
+ if headerIndex == 0:
+ collisionHeaderName = args[0][1:] # remove '&'
+ parseCollisionHeader(sceneObj, roomObjs, sceneData, collisionHeaderName, sharedSceneData)
+ elif command == "SCENE_CMD_ENTRANCE_LIST" and sharedSceneData.includeActors:
+ if not (args[0] == "NULL" or args[0] == "0" or args[0] == "0x00"):
+ entranceListName = stripName(args[0])
+ entranceList = parseEntranceList(sceneHeader, roomObjs, sceneData, entranceListName)
+ elif command == "SCENE_CMD_SPAWN_LIST" and sharedSceneData.includeActors:
+ if not (args[1] == "NULL" or args[1] == "0" or args[1] == "0x00"):
+ spawnListName = stripName(args[1])
+ parseSpawnList(roomObjs, sceneData, spawnListName, entranceList, sharedSceneData, headerIndex)
+
+ # Clear entrance list
+ entranceList = None
+ elif command == "SCENE_CMD_ALTERNATE_HEADER_LIST":
+ parseAlternateSceneHeaders(sceneObj, roomObjs, sceneData, stripName(args[0]), f3dContext, sharedSceneData)
+ elif command in {"SCENE_CMD_MINIMAP_COMPASS_ICON_INFO", "SCENE_CMD_MAP_DATA_CHESTS"}:
+ if sharedSceneData.includeActors:
+ parse_mm_map_data_chest(roomObjs, sceneHeader, sceneData, int(args[0], base=0), stripName(args[1]))
+
+ if len(command_list) > 0:
+ print(f"INFO: The following scene commands weren't processed for header {headerIndex}:")
+ for command in command_list:
+ print(f"- {repr(command)}")
+
+ return sceneObj
diff --git a/fast64_internal/oot/importer/scene_pathways.py b/fast64_internal/z64/importer/scene_pathways.py
similarity index 57%
rename from fast64_internal/oot/importer/scene_pathways.py
rename to fast64_internal/z64/importer/scene_pathways.py
index 55e435553..e09042631 100644
--- a/fast64_internal/oot/importer/scene_pathways.py
+++ b/fast64_internal/z64/importer/scene_pathways.py
@@ -1,6 +1,8 @@
import bpy
+from typing import Optional
from ...utility import hexOrDecInt, parentObject
+from ...game_data import game_data
from .utility import getDataMatch, createCurveFromPoints, unsetAllHeadersExceptSpecified
from .classes import SharedSceneData
@@ -8,12 +10,14 @@
def parsePath(
sceneObj: bpy.types.Object,
sceneData: str,
- pathName: str,
+ points_ptr: str,
+ opt_path_idx: Optional[str],
+ custom_value: Optional[str],
headerIndex: int,
sharedSceneData: SharedSceneData,
orderIndex: int,
):
- pathData = getDataMatch(sceneData, pathName, "Vec3s", "path")
+ pathData = getDataMatch(sceneData, points_ptr, "Vec3s", "path")
pathPointsEntries = [value.replace("{", "").strip() for value in pathData.split("},") if value.strip() != ""]
pathPointsInfo = []
for pathPoint in pathPointsEntries:
@@ -23,11 +27,15 @@ def parsePath(
if sharedSceneData.addHeaderIfItemExists(pathPoints, "Curve", headerIndex):
return
- curveObj = createCurveFromPoints(pathPoints, pathName)
+ curveObj = createCurveFromPoints(pathPoints, points_ptr)
splineProp = curveObj.ootSplineProperty
splineProp.index = orderIndex
- unsetAllHeadersExceptSpecified(splineProp.headerSettings, headerIndex)
+ if game_data.z64.is_mm() and opt_path_idx is not None and custom_value is not None:
+ splineProp.opt_path_index = int(opt_path_idx)
+ splineProp.custom_value = int(custom_value)
+
+ unsetAllHeadersExceptSpecified(curveObj.ootSplineProperty.headerSettings, headerIndex)
sharedSceneData.pathDict[pathPoints] = curveObj
parentObject(sceneObj, curveObj)
@@ -43,5 +51,10 @@ def parsePathList(
pathData = getDataMatch(sceneData, pathListName, "Path", "path list")
pathList = [value.replace("{", "").strip() for value in pathData.split("},") if value.strip() != ""]
for i, pathEntry in enumerate(pathList):
- numPoints, pathName = [value.strip() for value in pathEntry.split(",")]
- parsePath(sceneObj, sceneData, pathName, headerIndex, sharedSceneData, i)
+ if game_data.z64.is_oot():
+ count, points_ptr = [value.strip() for value in pathEntry.split(",")]
+ opt_path_idx = custom_value = None
+ else:
+ count, opt_path_idx, custom_value, points_ptr = [value.strip() for value in pathEntry.split(",")]
+
+ parsePath(sceneObj, sceneData, points_ptr, opt_path_idx, custom_value, headerIndex, sharedSceneData, i)
diff --git a/fast64_internal/oot/importer/utility.py b/fast64_internal/z64/importer/utility.py
similarity index 73%
rename from fast64_internal/oot/importer/utility.py
rename to fast64_internal/z64/importer/utility.py
index ec435ac94..bd599c184 100644
--- a/fast64_internal/oot/importer/utility.py
+++ b/fast64_internal/z64/importer/utility.py
@@ -2,9 +2,10 @@
import bpy
import mathutils
+from ...game_data import game_data
from ...utility import PluginError, hexOrDecInt, removeComments, yUpToZUp
-from ..actor.properties import OOTActorProperty, OOTActorHeaderProperty
-from ..oot_utility import ootParseRotation
+from ..actor.properties import Z64_ActorProperty, Z64_ActorHeaderProperty
+from ..utility import ootParseRotation
from .constants import headerNames, actorsWithRotAsParam
@@ -16,12 +17,17 @@ def getBits(value: int, index: int, size: int) -> int:
return ((1 << size) - 1) & (value >> index)
-def unsetAllHeadersExceptSpecified(headerSettings: OOTActorHeaderProperty, headerIndex: int):
- headerSettings.sceneSetupPreset = "Custom"
- for i in range(len(headerNames)):
- setattr(headerSettings, headerNames[i], i == headerIndex)
+def unsetAllHeadersExceptSpecified(headerSettings: Z64_ActorHeaderProperty, headerIndex: int):
+ if game_data.z64.is_oot():
+ headerSettings.sceneSetupPreset = "Custom"
- if headerIndex >= 4:
+ for i in range(len(headerNames)):
+ setattr(headerSettings, headerNames[i], i == headerIndex)
+ else:
+ headerSettings.include_in_all_setups = False
+ headerSettings.childDayHeader = headerIndex == 0
+
+ if headerIndex >= game_data.z64.cs_index_start:
headerSettings.cutsceneHeaders.add().headerIndex = headerIndex
@@ -45,7 +51,7 @@ def getDisplayNameFromActorID(actorID: str):
return " ".join([word.lower().capitalize() for word in actorID.split("_") if word != "ACTOR"])
-def handleActorWithRotAsParam(actorProp: OOTActorProperty, actorID: str, rotation: list[int]):
+def handleActorWithRotAsParam(actorProp: Z64_ActorProperty, actorID: str, rotation: list[int]):
if actorID in actorsWithRotAsParam:
if actorProp.actor_id != "Custom":
actorProp.rot_x = hex(rotation[0])
@@ -59,25 +65,36 @@ def handleActorWithRotAsParam(actorProp: OOTActorProperty, actorID: str, rotatio
def getDataMatch(
- sceneData: str, name: str, dataType: str | list[str], errorMessageID: str, isArray: bool = True
-) -> str:
+ sceneData: str,
+ name: str,
+ dataType: str | list[str],
+ errorMessageID: str,
+ isArray: bool = True,
+ is_type_known: bool = True,
+):
arrayText = rf"\[[\s0-9A-Za-z_]*\]\s*" if isArray else ""
+ dataTypeRegex = dataType
if isinstance(dataType, list):
dataTypeRegex = "(?:"
for i in dataType:
dataTypeRegex += f"(?:{re.escape(i)})|"
dataTypeRegex = dataTypeRegex[:-1] + ")"
- else:
+ elif is_type_known:
dataTypeRegex = re.escape(dataType)
regex = rf"{dataTypeRegex}\s*{re.escape(name)}\s*{arrayText}=\s*\{{(.*?)\}}\s*;"
match = re.search(regex, sceneData, flags=re.DOTALL)
- if not match:
+ if match is None:
raise PluginError(f"Could not find {errorMessageID} {name}.")
- # return the match with comments removed
- return removeComments(match.group(1))
+ if is_type_known:
+ # return the match with comments removed
+ return removeComments(match.group(1))
+ else:
+ f = match.groups()
+ # return the struct name and the match
+ return removeComments(match.group(1)), removeComments(match.group(2))
def stripName(name: str):
diff --git a/fast64_internal/oot/oot_model_classes.py b/fast64_internal/z64/model_classes.py
similarity index 98%
rename from fast64_internal/oot/oot_model_classes.py
rename to fast64_internal/z64/model_classes.py
index d734cebc3..359192897 100644
--- a/fast64_internal/oot/oot_model_classes.py
+++ b/fast64_internal/z64/model_classes.py
@@ -35,12 +35,14 @@
def ootGetIncludedAssetData(basePath: str, currentPaths: list[str], data: str) -> str:
includeData = ""
searchedPaths = currentPaths[:]
+ extracted_path = f"{bpy.context.scene.fast64.oot.get_extracted_path()}/"
print("Included paths:")
# search assets
for includeMatch in re.finditer(r"\#include\s*\"(assets/objects/(.*?))\.h\"", data):
- path = os.path.join(basePath, includeMatch.group(1) + ".c")
+ # TODO: use pathlib and check if the path exists
+ path = os.path.join(basePath, extracted_path + includeMatch.group(1) + ".c")
if path in searchedPaths:
continue
searchedPaths.append(path)
@@ -49,7 +51,7 @@ def ootGetIncludedAssetData(basePath: str, currentPaths: list[str], data: str) -
print(path)
for subIncludeMatch in re.finditer(r"\#include\s*\"(((?![/\"]).)*)\.c\"", subIncludeData):
- subPath = os.path.join(os.path.dirname(path), subIncludeMatch.group(1) + ".c")
+ subPath = os.path.join(os.path.dirname(path), extracted_path + subIncludeMatch.group(1) + ".c")
if subPath in searchedPaths:
continue
searchedPaths.append(subPath)
diff --git a/fast64_internal/oot/oot_object.py b/fast64_internal/z64/object.py
similarity index 76%
rename from fast64_internal/oot/oot_object.py
rename to fast64_internal/z64/object.py
index a4eb89f89..e42d388eb 100644
--- a/fast64_internal/oot/oot_object.py
+++ b/fast64_internal/z64/object.py
@@ -1,6 +1,8 @@
+import bpy
+from ..game_data import game_data
+
from bpy.types import Object
from ..utility import ootGetSceneOrRoomHeader
-from .oot_constants import ootData
from .exporter.room.header import RoomHeader
@@ -17,12 +19,18 @@ def addMissingObjectToProp(roomObj: Object, headerIndex: int, objectKey: str):
def addMissingObjectsToRoomHeader(roomObj: Object, curHeader: RoomHeader, headerIndex: int):
"""Adds missing objects to the object list"""
if len(curHeader.actors.actorList) > 0:
+ game_data.z64.update(bpy.context, None)
+
for roomActor in curHeader.actors.actorList:
- actor = ootData.actorData.actorsByID.get(roomActor.id)
+ actor = game_data.z64.actors.actorsByID.get(roomActor.id)
if actor is not None and actor.key != "player" and len(actor.tiedObjects) > 0:
for objKey in actor.tiedObjects:
- if objKey not in ["obj_gameplay_keep", "obj_gameplay_field_keep", "obj_gameplay_dangeon_keep"]:
- objID = ootData.objectData.objectsByKey[objKey].id
+ if objKey.replace("obj_", "") not in {
+ "gameplay_keep",
+ "gameplay_field_keep",
+ "gameplay_dangeon_keep",
+ }:
+ objID = game_data.z64.objects.objects_by_key[objKey].id
if not (objID in curHeader.objects.objectList):
curHeader.objects.objectList.append(objID)
addMissingObjectToProp(roomObj, headerIndex, objKey)
diff --git a/fast64_internal/oot/props_panel_main.py b/fast64_internal/z64/props_panel_main.py
similarity index 76%
rename from fast64_internal/oot/props_panel_main.py
rename to fast64_internal/z64/props_panel_main.py
index 331a54d04..be394d941 100644
--- a/fast64_internal/oot/props_panel_main.py
+++ b/fast64_internal/z64/props_panel_main.py
@@ -1,11 +1,14 @@
import bpy
from bpy.utils import register_class, unregister_class
-from ..utility import prop_split, gammaInverse
-from .oot_utility import getSceneObj, getRoomObj
+from ..utility import PluginError, prop_split, gammaInverse
+from ..game_data import game_data
+from .utility import getSceneObj, getRoomObj, is_oot_features
from .scene.properties import OOTSceneProperties
-from .room.properties import OOTObjectProperty, OOTRoomHeaderProperty, OOTAlternateRoomHeaderProperty
+from .room.properties import Z64_ObjectProperty, Z64_RoomHeaderProperty, Z64_AlternateRoomHeaderProperty
from .collision.properties import OOTWaterBoxProperty
from .cutscene.properties import OOTCutsceneProperty
+from .animated_mats.properties import Z64_AnimatedMaterialProperty
+from .actor_cutscene.properties import Z64_ActorCutsceneProperty
from .cutscene.motion.properties import (
OOTCutsceneMotionProperty,
CutsceneCmdActorCueListProperty,
@@ -13,9 +16,9 @@
)
from .actor.properties import (
- OOTActorProperty,
- OOTTransitionActorProperty,
- OOTEntranceProperty,
+ Z64_ActorProperty,
+ Z64_TransitionActorProperty,
+ Z64_EntranceProperty,
)
# Make sure to add exceptions in utility.py - selectMeshChildrenOnly
@@ -37,15 +40,19 @@
("CS Actor Cue Preview", "CS Actor Cue Preview", "CS Actor Cue Preview"),
("CS Player Cue Preview", "CS Player Cue Preview", "CS Player Cue Preview"),
("CS Dummy Cue", "CS Dummy Cue", "CS Dummy Cue"),
+ ("Animated Materials", "Animated Materials", "Animated Materials"),
+ ("Actor Cutscene", "Actor Cutscene", "Actor Cutscene"),
# ('Camera Volume', 'Camera Volume', 'Camera Volume'),
]
def drawSceneHeader(box: bpy.types.UILayout, obj: bpy.types.Object):
objName = obj.name
- obj.ootSceneHeader.draw_props(box, None, None, objName)
- if obj.ootSceneHeader.menuTab == "Alternate":
- obj.ootAlternateSceneHeaders.draw_props(box, objName)
+ props = obj.ootSceneHeader
+ props.draw_props(box, None, None, objName)
+ if props.menuTab == "Alternate":
+ alt_props = obj.ootAlternateSceneHeaders
+ alt_props.draw_props(box, objName)
box.prop(obj.fast64.oot.scene, "write_dummy_room_list")
@@ -67,6 +74,11 @@ def setLightPropertyValues(lightProp, ambient, diffuse0, diffuse1, fogColor, fog
def onUpdateOOTEmptyType(self, context):
+ # OoT doesn't have this feature
+ if is_oot_features() and self.ootEmptyType == "Animated Materials":
+ self.ootEmptyType = "None"
+ print("INFO: Ocarina of Time doesn't support that feature.")
+
isNoneEmpty = self.ootEmptyType == "None"
isBoxEmpty = self.ootEmptyType == "Water Box"
isSphereEmpty = self.ootEmptyType == "Cull Group"
@@ -79,12 +91,14 @@ def onUpdateOOTEmptyType(self, context):
if isSphereEmpty:
self.empty_display_type = "SPHERE"
+ header_props = self.ootSceneHeader
+
if self.ootEmptyType == "Scene":
- if len(self.ootSceneHeader.lightList) == 0:
- light = self.ootSceneHeader.lightList.add()
- if not self.ootSceneHeader.timeOfDayLights.defaultsSet:
- self.ootSceneHeader.timeOfDayLights.defaultsSet = True
- timeOfDayLights = self.ootSceneHeader.timeOfDayLights
+ if len(header_props.lightList) == 0:
+ light = header_props.lightList.add()
+ if not header_props.timeOfDayLights.defaultsSet:
+ header_props.timeOfDayLights.defaultsSet = True
+ timeOfDayLights = header_props.timeOfDayLights
setLightPropertyValues(
timeOfDayLights.dawn, [70, 45, 57], [180, 154, 138], [20, 20, 60], [140, 120, 100], 0x3E1
)
@@ -117,12 +131,14 @@ class OOTObjectPanel(bpy.types.Panel):
@classmethod
def poll(cls, context):
- return context.scene.gameEditorMode == "OOT" and (context.object is not None and context.object.type == "EMPTY")
+ return context.scene.gameEditorMode in {"OOT", "MM"} and (
+ context.object is not None and context.object.type == "EMPTY"
+ )
def draw(self, context):
prop_split(self.layout, context.scene, "gameEditorMode", "Game")
box = self.layout.box()
- box.box().label(text="OOT Object Inspector")
+ box.box().label(text="Object Inspector")
obj = context.object
objName = obj.name
prop_split(box, obj, "ootEmptyType", "Object Type")
@@ -134,11 +150,11 @@ def draw(self, context):
altRoomProp = roomObj.ootAlternateRoomHeaders if roomObj is not None else None
if obj.ootEmptyType == "Actor":
- actorProp: OOTActorProperty = obj.ootActorProperty
- actorProp.draw_props(box, altRoomProp, obj)
+ actorProp: Z64_ActorProperty = obj.ootActorProperty
+ actorProp.draw_props(box, obj, altSceneProp, altRoomProp)
elif obj.ootEmptyType == "Transition Actor":
- transActorProp: OOTTransitionActorProperty = obj.ootTransitionActorProperty
+ transActorProp: Z64_TransitionActorProperty = obj.ootTransitionActorProperty
transActorProp.draw_props(box, altSceneProp, roomObj, objName)
elif obj.ootEmptyType == "Water Box":
@@ -149,14 +165,14 @@ def draw(self, context):
drawSceneHeader(box, obj)
elif obj.ootEmptyType == "Room":
- roomProp: OOTRoomHeaderProperty = obj.ootRoomHeader
+ roomProp: Z64_RoomHeaderProperty = obj.ootRoomHeader
roomProp.draw_props(box, None, None, objName)
if obj.ootRoomHeader.menuTab == "Alternate":
- roomAltProp: OOTAlternateRoomHeaderProperty = obj.ootAlternateRoomHeaders
+ roomAltProp: Z64_AlternateRoomHeaderProperty = obj.ootAlternateRoomHeaders
roomAltProp.draw_props(box, objName)
elif obj.ootEmptyType == "Entrance":
- entranceProp: OOTEntranceProperty = obj.ootEntranceProperty
+ entranceProp: Z64_EntranceProperty = obj.ootEntranceProperty
entranceProp.draw_props(box, obj, altSceneProp, objName)
elif obj.ootEmptyType == "Cull Group":
@@ -170,6 +186,14 @@ def draw(self, context):
csProp: OOTCutsceneProperty = obj.ootCutsceneProperty
csProp.draw_props(box, obj)
+ elif obj.ootEmptyType == "Animated Materials":
+ anim_props: Z64_AnimatedMaterialProperty = obj.z64_anim_mats_property
+ anim_props.draw_props(box, obj)
+
+ elif obj.ootEmptyType == "Actor Cutscene":
+ actor_cs_props: Z64_ActorCutsceneProperty = obj.z64_actor_cs_property
+ actor_cs_props.draw_props(box, obj, altSceneProp)
+
elif obj.ootEmptyType in [
"CS Actor Cue List",
"CS Player Cue List",
@@ -194,12 +218,17 @@ class OOT_ObjectProperties(bpy.types.PropertyGroup):
@staticmethod
def upgrade_changed_props():
+ if bpy.context.scene.fast64.get_addon_version() < (2, 3, 1):
+ raise PluginError(
+ "ERROR: Upgrading on this version is deprecated. Please update to an older version and update again to this one."
+ )
+
for obj in bpy.data.objects:
- if obj.type == "EMPTY":
+ if obj.type == "EMPTY" and game_data.z64.is_oot():
if obj.ootEmptyType == "Room":
- OOTObjectProperty.upgrade_object(obj)
+ Z64_ObjectProperty.upgrade_object(obj)
if obj.ootEmptyType in {"Actor", "Entrance", "Transition Actor"}:
- OOTActorProperty.upgrade_object(obj)
+ Z64_ActorProperty.upgrade_object(obj)
if obj.ootEmptyType == "Cutscene":
OOTCutsceneProperty.upgrade_object(obj)
if any(obj.name.startswith(elem) for elem in ["ActionList.", "Point.", "Preview."]):
@@ -212,7 +241,7 @@ def upgrade_changed_props():
obj.ootEmptyType = "CS Dummy Cue"
else:
print("WARNING: An Actor Cue has been detected outside an Actor Cue List: " + obj.name)
- elif obj.type == "ARMATURE":
+ elif obj.type == "ARMATURE" and game_data.z64.is_oot():
parentObj = obj.parent
if parentObj is not None and (
parentObj.name.startswith("Cutscene.") or parentObj.ootEmptyType == "Cutscene"
@@ -236,8 +265,6 @@ def draw_props(self, layout: bpy.types.UILayout):
oot_obj_classes = (
- OOTSceneProperties,
- OOT_ObjectProperties,
OOTCullGroupProperty,
OOT_ManualUpgrade,
)
@@ -260,7 +287,7 @@ def oot_obj_register():
register_class(cls)
bpy.types.Object.ootEmptyType = bpy.props.EnumProperty(
- name="OOT Object Type", items=ootEnumEmptyType, default="None", update=onUpdateOOTEmptyType
+ name="Object Type", items=ootEnumEmptyType, default="None", update=onUpdateOOTEmptyType
)
bpy.types.Scene.ootActiveHeaderLock = bpy.props.BoolProperty(default=False)
diff --git a/fast64_internal/oot/room/operators.py b/fast64_internal/z64/room/operators.py
similarity index 89%
rename from fast64_internal/oot/room/operators.py
rename to fast64_internal/z64/room/operators.py
index 954f2ee32..f71e2133b 100644
--- a/fast64_internal/oot/room/operators.py
+++ b/fast64_internal/z64/room/operators.py
@@ -3,7 +3,7 @@
from bpy.utils import register_class, unregister_class
from bpy.props import EnumProperty, IntProperty, StringProperty
from ...utility import ootGetSceneOrRoomHeader
-from ..oot_constants import ootData
+from ...game_data import game_data
class OOT_SearchObjectEnumOperator(Operator):
@@ -12,7 +12,7 @@ class OOT_SearchObjectEnumOperator(Operator):
bl_property = "objectKey"
bl_options = {"REGISTER", "UNDO"}
- objectKey: EnumProperty(items=ootData.objectData.ootEnumObjectKey, default="obj_human")
+ objectKey: EnumProperty(items=lambda self, context: game_data.z64.get_enum("objectKey"), default=1)
headerIndex: IntProperty(default=0, min=0)
index: IntProperty(default=0, min=0)
objName: StringProperty()
diff --git a/fast64_internal/oot/room/properties.py b/fast64_internal/z64/room/properties.py
similarity index 58%
rename from fast64_internal/oot/room/properties.py
rename to fast64_internal/z64/room/properties.py
index 733b2b37e..581c47d16 100644
--- a/fast64_internal/oot/room/properties.py
+++ b/fast64_internal/z64/room/properties.py
@@ -1,10 +1,12 @@
import bpy
-from bpy.types import PropertyGroup, UILayout, Image, Object
+
+from bpy.types import PropertyGroup, UILayout, Image, Object, Context
from bpy.utils import register_class, unregister_class
from ...utility import prop_split
+from ...game_data import game_data
from ..collection_utility import drawCollectionOps, drawAddButton
-from ..oot_utility import onMenuTabChange, onHeaderMenuTabChange, drawEnumWithCustom
-from ..oot_upgrade import upgradeRoomHeaders
+from ..utility import onMenuTabChange, onHeaderMenuTabChange, drawEnumWithCustom, is_oot_features
+from ..upgrade import upgradeRoomHeaders
from .operators import OOT_SearchObjectEnumOperator
from bpy.props import (
@@ -18,10 +20,7 @@
IntVectorProperty,
)
-from ..oot_constants import (
- ootData,
- ootEnumRoomBehaviour,
- ootEnumLinkIdle,
+from ..constants import (
ootEnumRoomShapeType,
ootEnumHeaderMenu,
)
@@ -35,29 +34,33 @@
]
-class OOTObjectProperty(PropertyGroup):
+class Z64_ObjectProperty(PropertyGroup):
expandTab: BoolProperty(name="Expand Tab")
- objectKey: EnumProperty(items=ootData.objectData.ootEnumObjectKey, default="obj_human")
+ objectKey: EnumProperty(items=lambda self, context: game_data.z64.get_enum("objectKey"), default=1)
objectIDCustom: StringProperty(default="OBJECT_CUSTOM")
@staticmethod
def upgrade_object(obj: Object):
- print(f"Processing '{obj.name}'...")
- upgradeRoomHeaders(obj, ootData.objectData)
+ if game_data.z64.is_oot():
+ print(f"Processing '{obj.name}'...")
+ game_data.z64.update(bpy.context, None)
+ upgradeRoomHeaders(obj, game_data.z64.objects)
def draw_props(self, layout: UILayout, headerIndex: int, index: int, objName: str):
- isLegacy = True if "objectID" in self else False
-
- if isLegacy:
- objectName = ootData.objectData.ootEnumObjectIDLegacy[self["objectID"]][1]
- elif self.objectKey != "Custom":
- objectName = ootData.objectData.objectsByKey[self.objectKey].name
+ is_legacy = True if "objectID" in self else False
+ obj_key: str = getattr(self, "objectKey")
+ game_data.z64.update(bpy.context, None)
+
+ if game_data.z64.is_oot() and is_legacy:
+ obj_name = game_data.z64.objects.ootEnumObjectIDLegacy[self["objectID"]][1]
+ elif obj_key != "Custom":
+ obj_name = game_data.z64.objects.objects_by_key[obj_key].name
else:
- objectName = self.objectIDCustom
+ obj_name = self.objectIDCustom
objItemBox = layout.column()
row = objItemBox.row()
- row.label(text=f"{objectName}")
+ row.label(text=f"{obj_name}")
buttons = row.row(align=True)
objSearch = buttons.operator(OOT_SearchObjectEnumOperator.bl_idname, icon="VIEWZOOM", text="Select")
drawCollectionOps(buttons, index, "Object", headerIndex, objName, compact=True)
@@ -65,11 +68,11 @@ def draw_props(self, layout: UILayout, headerIndex: int, index: int, objName: st
objSearch.headerIndex = headerIndex if headerIndex is not None else 0
objSearch.index = index
- if self.objectKey == "Custom":
+ if obj_key == "Custom":
prop_split(objItemBox, self, "objectIDCustom", "Object ID Custom")
-class OOTBGProperty(PropertyGroup):
+class Z64_BGProperty(PropertyGroup):
image: PointerProperty(type=Image)
# camera: IntProperty(name="Camera Index", min=0)
otherModeFlags: StringProperty(
@@ -86,44 +89,58 @@ def draw_props(self, layout: UILayout, index: int, objName: str, isMulti: bool):
drawCollectionOps(box, index, "BgImage", None, objName)
-class OOTRoomHeaderProperty(PropertyGroup):
+class Z64_RoomHeaderProperty(PropertyGroup):
expandTab: BoolProperty(name="Expand Tab")
menuTab: EnumProperty(items=ootEnumRoomMenu, update=onMenuTabChange)
altMenuTab: EnumProperty(items=ootEnumRoomMenuAlternate)
+
+ # OoT exclusive
usePreviousHeader: BoolProperty(name="Use Previous Header", default=True)
+ # SCENE_CMD_ROOM_BEHAVIOR
roomIndex: IntProperty(name="Room Index", default=0, min=0)
- roomBehaviour: EnumProperty(items=ootEnumRoomBehaviour, default="0x00")
+ roomBehaviour: EnumProperty(items=lambda self, context: game_data.z64.get_enum("roomBehaviour"), default=1)
roomBehaviourCustom: StringProperty(default="0x00")
- disableWarpSongs: BoolProperty(name="Disable Warp Songs")
showInvisibleActors: BoolProperty(name="Show Invisible Actors")
- linkIdleMode: EnumProperty(name="Link Idle Mode", items=ootEnumLinkIdle, default="0x00")
- linkIdleModeCustom: StringProperty(name="Link Idle Mode Custom", default="0x00")
- roomIsHot: BoolProperty(
- name="Use Hot Room Behavior",
- description="Use heat timer/screen effect, overrides Link Idle Mode",
- default=False,
+ linkIdleMode: EnumProperty(
+ name="Environment Type",
+ items=lambda self, context: game_data.z64.get_enum("linkIdleMode"),
+ default=1,
)
+ linkIdleModeCustom: StringProperty(name="Environment Type Custom", default="0x00")
+ # OoT exclusive
+ disableWarpSongs: BoolProperty(name="Disable Warp Songs")
+
+ # MM exclusive
+ enable_pos_lights: BoolProperty(name="Enable Pos Lights")
+ enable_storm: BoolProperty(name="Enable Storm")
+
+ # SCENE_CMD_WIND_SETTINGS
setWind: BoolProperty(name="Set Wind")
windVector: IntVectorProperty(name="Wind Vector", size=3, min=-127, max=127)
windStrength: IntProperty(name="Wind Strength", min=0, max=255)
+ # SCENE_CMD_TIME_SETTINGS
leaveTimeUnchanged: BoolProperty(name="Leave Time Unchanged", default=True)
timeHours: IntProperty(name="Hours", default=0, min=0, max=23) # 0xFFFE
timeMinutes: IntProperty(name="Minutes", default=0, min=0, max=59)
- timeSpeed: FloatProperty(name="Time Speed", default=1, min=-13, max=13) # 0xA
+ timeSpeed: FloatProperty(name="Time Speed", default=1.0, min=-13.0, max=13.0) # 0xA
+ # SCENE_CMD_SKYBOX_DISABLES
disableSkybox: BoolProperty(name="Disable Skybox")
disableSunMoon: BoolProperty(name="Disable Sun/Moon")
+ # SCENE_CMD_ECHO_SETTINGS
echo: StringProperty(name="Echo", default="0x00")
- objectList: CollectionProperty(type=OOTObjectProperty)
+ # SCENE_CMD_OBJECT_LIST
+ objectList: CollectionProperty(type=Z64_ObjectProperty)
+ # SCENE_CMD_ROOM_SHAPE
roomShape: EnumProperty(items=ootEnumRoomShapeType, default="ROOM_SHAPE_TYPE_NORMAL")
defaultCullDistance: IntProperty(name="Default Cull Distance", min=1, default=100)
- bgImageList: CollectionProperty(type=OOTBGProperty)
+ bgImageList: CollectionProperty(type=Z64_BGProperty)
bgImageTab: BoolProperty(name="BG Images")
def drawBGImageList(self, layout: UILayout, objName: str):
@@ -149,10 +166,15 @@ def draw_props(self, layout: UILayout, dropdownLabel: str, headerIndex: int, obj
layout.prop(self, "expandTab", text=dropdownLabel, icon="TRIA_DOWN" if self.expandTab else "TRIA_RIGHT")
if not self.expandTab:
return
- if headerIndex is not None and headerIndex > 3:
- drawCollectionOps(layout, headerIndex - 4, "Room", None, objName)
-
- if headerIndex is not None and headerIndex > 0 and headerIndex < 4:
+ if headerIndex is not None and headerIndex > (game_data.z64.cs_index_start - 1):
+ drawCollectionOps(layout, headerIndex - game_data.z64.cs_index_start, "Room", None, objName)
+
+ if (
+ game_data.z64.is_oot()
+ and headerIndex is not None
+ and headerIndex > 0
+ and headerIndex < game_data.z64.cs_index_start
+ ):
layout.prop(self, "usePreviousHeader", text="Use Previous Header")
if self.usePreviousHeader:
return
@@ -165,6 +187,7 @@ def draw_props(self, layout: UILayout, dropdownLabel: str, headerIndex: int, obj
menuTab = self.altMenuTab
if menuTab == "General":
+ # General
if headerIndex is None or headerIndex == 0:
general = layout.column()
general.box().label(text="General")
@@ -177,13 +200,31 @@ def draw_props(self, layout: UILayout, dropdownLabel: str, headerIndex: int, obj
general.label(text="and requires meshes to be parented to Custom Cull Group empties.")
general.label(text="RSP culling is done automatically regardless of room shape.")
prop_split(general, self, "defaultCullDistance", "Default Cull (Blender Units)")
- # Behaviour
- behaviourBox = layout.column()
- behaviourBox.box().label(text="Behaviour")
- drawEnumWithCustom(behaviourBox, self, "roomBehaviour", "Room Behaviour", "")
- drawEnumWithCustom(behaviourBox, self, "linkIdleMode", "Link Idle Mode", "")
- behaviourBox.prop(self, "disableWarpSongs", text="Disable Warp Songs")
- behaviourBox.prop(self, "showInvisibleActors", text="Show Invisible Actors")
+ if self.roomShape == "ROOM_SHAPE_TYPE_NONE" and is_oot_features():
+ general.label(text="This shape type is only implemented on MM", icon="INFO")
+
+ # Behavior
+ behaviorBox = layout.column()
+ behaviorBox.box().label(text="Behavior")
+ drawEnumWithCustom(behaviorBox, self, "roomBehaviour", "Room Type", "", "roomBehaviourCustom")
+ drawEnumWithCustom(behaviorBox, self, "linkIdleMode", "Environment Type", "", "linkIdleModeCustom")
+
+ if game_data.z64.is_oot():
+ behaviorBox.prop(self, "disableWarpSongs", text="Disable Warp Songs")
+
+ if not is_oot_features():
+ behaviorBox.prop(self, "enable_pos_lights")
+ behaviorBox.prop(self, "enable_storm")
+
+ behaviorBox.prop(self, "showInvisibleActors", text="Show Invisible Actors")
+
+ if not is_oot_features():
+ if self.roomBehaviour in {"ROOM_TYPE_DUNGEON", "ROOM_TYPE_BOSS"}:
+ behaviorBox.label(text="The Three-Day Events actor will be automatically", icon="INFO")
+ behaviorBox.label(text="spawned by `Play_Init` (see 'ACTOR_EN_TEST4' usage).")
+ else:
+ behaviorBox.label(text="You will need to add the Three-Day Events actor", icon="INFO")
+ behaviorBox.label(text="to that room (see 'ACTOR_EN_TEST4' usage).")
# Time
skyboxAndTime = layout.column()
@@ -198,8 +239,9 @@ def draw_props(self, layout: UILayout, dropdownLabel: str, headerIndex: int, obj
timeRow = skyboxAndTime.row()
timeRow.prop(self, "timeHours", text="Hours")
timeRow.prop(self, "timeMinutes", text="Minutes")
- # prop_split(skyboxAndTime, self, "timeValue", "Time Of Day")
- prop_split(skyboxAndTime, self, "timeSpeed", "Time Speed")
+ prop_split(
+ skyboxAndTime, self, "timeSpeed", f"Time Speed (default: {game_data.z64.default_time_speed:.2f})"
+ )
# Echo
prop_split(skyboxAndTime, self, "echo", "Echo")
@@ -212,68 +254,80 @@ def draw_props(self, layout: UILayout, dropdownLabel: str, headerIndex: int, obj
windBoxRow = windBox.row()
windBoxRow.prop(self, "windVector", text="")
windBox.prop(self, "windStrength", text="Strength")
- # prop_split(windBox, self, "windVector", "Wind Vector")
elif menuTab == "Objects":
upgradeLayout = layout.column()
objBox = layout.column()
objBox.box().label(text="Objects")
- if len(self.objectList) > 16:
+ if is_oot_features() and len(self.objectList) > 16:
objBox.label(text="You are over the 16 object limit.", icon="ERROR")
objBox.label(text="You must allocate more memory in code.")
- isLegacy = False
+ is_legacy = False
for i, objProp in enumerate(self.objectList):
objProp.draw_props(objBox, headerIndex, i, objName)
- if "objectID" in objProp:
- isLegacy = True
+ if game_data.z64.is_oot() and "objectID" in objProp:
+ is_legacy = True
- if isLegacy:
+ if game_data.z64.is_oot() and is_legacy:
upgradeLayout.label(text="Legacy data has not been upgraded!")
upgradeLayout.operator(OOT_ManualUpgrade.bl_idname, text="Upgrade Data Now!")
- objBox.enabled = False if isLegacy else True
+ objBox.enabled = False if is_legacy else True
drawAddButton(objBox, len(self.objectList), "Object", headerIndex, objName)
-class OOTAlternateRoomHeaderProperty(PropertyGroup):
- childNightHeader: PointerProperty(name="Child Night Header", type=OOTRoomHeaderProperty)
- adultDayHeader: PointerProperty(name="Adult Day Header", type=OOTRoomHeaderProperty)
- adultNightHeader: PointerProperty(name="Adult Night Header", type=OOTRoomHeaderProperty)
- cutsceneHeaders: CollectionProperty(type=OOTRoomHeaderProperty)
+def update_cutscene_index(self: "Z64_AlternateRoomHeaderProperty", context: Context):
+ if self.currentCutsceneIndex < game_data.z64.cs_index_start:
+ self.currentCutsceneIndex = game_data.z64.cs_index_start
+
+ onHeaderMenuTabChange(self, context)
+
+
+class Z64_AlternateRoomHeaderProperty(PropertyGroup):
+ cutsceneHeaders: CollectionProperty(type=Z64_RoomHeaderProperty)
+ currentCutsceneIndex: IntProperty(default=1, update=update_cutscene_index)
+ # OoT exclusive
+ childNightHeader: PointerProperty(name="Child Night Header", type=Z64_RoomHeaderProperty)
+ adultDayHeader: PointerProperty(name="Adult Day Header", type=Z64_RoomHeaderProperty)
+ adultNightHeader: PointerProperty(name="Adult Night Header", type=Z64_RoomHeaderProperty)
headerMenuTab: EnumProperty(name="Header Menu", items=ootEnumHeaderMenu, update=onHeaderMenuTabChange)
- currentCutsceneIndex: IntProperty(min=4, default=4, update=onHeaderMenuTabChange)
def draw_props(self, layout: UILayout, objName: str):
headerSetup = layout.column()
- # headerSetup.box().label(text = "Alternate Headers")
- headerSetupBox = headerSetup.column()
-
- headerSetupBox.row().prop(self, "headerMenuTab", expand=True)
- if self.headerMenuTab == "Child Night":
- self.childNightHeader.draw_props(headerSetupBox, None, 1, objName)
- elif self.headerMenuTab == "Adult Day":
- self.adultDayHeader.draw_props(headerSetupBox, None, 2, objName)
- elif self.headerMenuTab == "Adult Night":
- self.adultNightHeader.draw_props(headerSetupBox, None, 3, objName)
- elif self.headerMenuTab == "Cutscene":
+ oot_can_draw_cs_header = False
+
+ if game_data.z64.is_oot():
+ headerSetupBox = headerSetup.column()
+ headerSetupBox.row().prop(self, "headerMenuTab", expand=True)
+
+ if self.headerMenuTab == "Child Night":
+ self.childNightHeader.draw_props(headerSetupBox, None, 1, objName)
+ elif self.headerMenuTab == "Adult Day":
+ self.adultDayHeader.draw_props(headerSetupBox, None, 2, objName)
+ elif self.headerMenuTab == "Adult Night":
+ self.adultNightHeader.draw_props(headerSetupBox, None, 3, objName)
+ elif self.headerMenuTab == "Cutscene":
+ oot_can_draw_cs_header = True
+
+ if game_data.z64.is_mm() or oot_can_draw_cs_header:
prop_split(headerSetup, self, "currentCutsceneIndex", "Cutscene Index")
drawAddButton(headerSetup, len(self.cutsceneHeaders), "Room", None, objName)
index = self.currentCutsceneIndex
- if index - 4 < len(self.cutsceneHeaders):
- self.cutsceneHeaders[index - 4].draw_props(headerSetup, None, index, objName)
+ if index - game_data.z64.cs_index_start < len(self.cutsceneHeaders):
+ self.cutsceneHeaders[index - game_data.z64.cs_index_start].draw_props(headerSetup, None, index, objName)
else:
headerSetup.label(text="No cutscene header for this index.", icon="QUESTION")
classes = (
- OOTObjectProperty,
- OOTBGProperty,
- OOTRoomHeaderProperty,
- OOTAlternateRoomHeaderProperty,
+ Z64_ObjectProperty,
+ Z64_BGProperty,
+ Z64_RoomHeaderProperty,
+ Z64_AlternateRoomHeaderProperty,
)
@@ -281,8 +335,8 @@ def room_props_register():
for cls in classes:
register_class(cls)
- Object.ootRoomHeader = PointerProperty(type=OOTRoomHeaderProperty)
- Object.ootAlternateRoomHeaders = PointerProperty(type=OOTAlternateRoomHeaderProperty)
+ Object.ootRoomHeader = PointerProperty(type=Z64_RoomHeaderProperty)
+ Object.ootAlternateRoomHeaders = PointerProperty(type=Z64_AlternateRoomHeaderProperty)
def room_props_unregister():
diff --git a/fast64_internal/oot/scene/operators.py b/fast64_internal/z64/scene/operators.py
similarity index 79%
rename from fast64_internal/oot/scene/operators.py
rename to fast64_internal/z64/scene/operators.py
index 72e8b1971..bfcb5e9da 100644
--- a/fast64_internal/oot/scene/operators.py
+++ b/fast64_internal/z64/scene/operators.py
@@ -7,10 +7,10 @@
from bpy.utils import register_class, unregister_class
from bpy.ops import object
from mathutils import Matrix, Vector
-from ...f3d.f3d_gbi import TextureExportSettings, DLFormat
from ...utility import PluginError, raisePluginError, ootGetSceneOrRoomHeader
-from ..oot_utility import ExportInfo, RemoveInfo, sceneNameFromID
-from ..oot_constants import ootEnumMusicSeq, ootEnumSceneID
+from ..utility import ExportInfo, RemoveInfo, sceneNameFromID
+from ...game_data import game_data
+from ..constants import ootEnumSceneID, mm_enum_scene_id
from ..importer import parseScene
from ..exporter.decomp_edit.config import Config
from ..exporter import SceneExport, Files
@@ -33,8 +33,9 @@ def dummy_view_layer_update(context):
def parseSceneFunc():
+ game_data.z64.update(bpy.context, None)
settings = bpy.context.scene.ootSceneImportSettings
- parseScene(settings, settings.option)
+ parseScene(settings, settings.option if game_data.z64.is_oot() else settings.mm_option)
class OOT_SearchSceneEnumOperator(Operator):
@@ -65,13 +66,41 @@ def invoke(self, context, event):
return {"RUNNING_MODAL"}
+class MM_SearchSceneEnumOperator(Operator):
+ bl_idname = "object.mm_search_scene_enum_operator"
+ bl_label = "Choose Scene"
+ bl_property = "scene_id"
+ bl_options = {"REGISTER", "UNDO"}
+
+ scene_id: EnumProperty(items=mm_enum_scene_id, default="SCENE_20SICHITAI2")
+ op_name: StringProperty(default="Export")
+
+ def execute(self, context):
+ if self.op_name == "Export":
+ context.scene.ootSceneExportSettings.mm_option = self.scene_id
+ elif self.op_name == "Import":
+ context.scene.ootSceneImportSettings.mm_option = self.scene_id
+ elif self.op_name == "Remove":
+ context.scene.ootSceneRemoveSettings.mm_option = self.scene_id
+ else:
+ raise Exception(f'Invalid MM scene search operator name: "{self.op_name}"')
+
+ context.region.tag_redraw()
+ self.report({"INFO"}, "Selected: " + self.scene_id)
+ return {"FINISHED"}
+
+ def invoke(self, context, event):
+ context.window_manager.invoke_search_popup(self)
+ return {"RUNNING_MODAL"}
+
+
class OOT_SearchMusicSeqEnumOperator(Operator):
bl_idname = "object.oot_search_music_seq_enum_operator"
bl_label = "Search Music Sequence"
bl_property = "ootMusicSeq"
bl_options = {"REGISTER", "UNDO"}
- ootMusicSeq: EnumProperty(items=ootEnumMusicSeq, default="NA_BGM_FIELD_LOGIC")
+ ootMusicSeq: EnumProperty(items=lambda self, context: game_data.z64.get_enum("musicSeq"), default=1)
headerIndex: IntProperty(default=0, min=0)
objName: StringProperty()
@@ -152,10 +181,12 @@ def execute(self, context):
try:
settings = context.scene.ootSceneExportSettings
levelName = settings.name
- option = settings.option
+ option = settings.option if game_data.z64.is_oot() else settings.mm_option
+ hackerFeaturesEnabled = False
- bootOptions = context.scene.fast64.oot.bootupSceneOptions
- hackerFeaturesEnabled = context.scene.fast64.oot.hackerFeaturesEnabled
+ if game_data.z64.is_oot():
+ bootOptions = context.scene.fast64.oot.bootupSceneOptions
+ hackerFeaturesEnabled = context.scene.fast64.oot.hackerFeaturesEnabled
if settings.customExport:
isCustomExport = True
@@ -214,15 +245,15 @@ def execute(self, context):
class OOT_RemoveScene(Operator):
- """Remove an OOT scene from an existing decomp directory."""
+ """Remove a scene from an existing decomp directory."""
- bl_idname = "object.oot_remove_level"
- bl_label = "OOT Remove Scene"
+ bl_idname = "object.z64_remove_level"
+ bl_label = "Remove Scene"
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
- settings = context.scene.ootSceneRemoveSettings # Type: OOTRemoveSceneSettingsProperty
- option = settings.option
+ settings = context.scene.ootSceneRemoveSettings # Type: Z64_RemoveSceneSettingsProperty
+ option = settings.option if game_data.z64.is_oot() else settings.mm_option
if settings.customExport:
self.report({"ERROR"}, "You can only remove scenes from your decomp path.")
@@ -256,6 +287,7 @@ def draw(self, context):
OOT_ImportScene,
OOT_ExportScene,
OOT_RemoveScene,
+ MM_SearchSceneEnumOperator,
)
diff --git a/fast64_internal/oot/scene/panels.py b/fast64_internal/z64/scene/panels.py
similarity index 51%
rename from fast64_internal/oot/scene/panels.py
rename to fast64_internal/z64/scene/panels.py
index 009c4273b..d12ff54c4 100644
--- a/fast64_internal/oot/scene/panels.py
+++ b/fast64_internal/z64/scene/panels.py
@@ -1,16 +1,14 @@
-import bpy
-import os
-
from bpy.types import UILayout
from bpy.utils import register_class, unregister_class
-from ...panels import OOT_Panel
-from ..oot_constants import ootEnumSceneID
-from ..oot_utility import getEnumName
+from ...game_data import game_data
+from ...panels import Z64_Panel
+from ..constants import ootEnumSceneID, mm_enum_scene_id
+from ..utility import getEnumName
from .properties import (
- OOTExportSceneSettingsProperty,
- OOTImportSceneSettingsProperty,
- OOTRemoveSceneSettingsProperty,
- OOTBootupSceneOptions,
+ Z64_ExportSceneSettingsProperty,
+ Z64_ImportSceneSettingsProperty,
+ Z64_RemoveSceneSettingsProperty,
+ OOT_BootupSceneOptions,
)
from .operators import (
@@ -19,17 +17,23 @@
OOT_RemoveScene,
OOT_ClearBootupScene,
OOT_SearchSceneEnumOperator,
+ MM_SearchSceneEnumOperator,
)
-class OOT_ExportScenePanel(OOT_Panel):
- bl_idname = "OOT_PT_export_level"
- bl_label = "OOT Scene Exporter"
+class OOT_ExportScenePanel(Z64_Panel):
+ bl_idname = "Z64_PT_export_level"
+ bl_label = "Scenes"
def drawSceneSearchOp(self, layout: UILayout, enumValue: str, opName: str):
searchBox = layout.box().row()
- searchBox.operator(OOT_SearchSceneEnumOperator.bl_idname, icon="VIEWZOOM", text="").opName = opName
- searchBox.label(text=getEnumName(ootEnumSceneID, enumValue))
+
+ if game_data.z64.is_oot():
+ searchBox.operator(OOT_SearchSceneEnumOperator.bl_idname, icon="VIEWZOOM", text="").opName = opName
+ searchBox.label(text=getEnumName(ootEnumSceneID, enumValue))
+ else:
+ searchBox.operator(MM_SearchSceneEnumOperator.bl_idname, icon="VIEWZOOM", text="").op_name = opName
+ searchBox.label(text=getEnumName(mm_enum_scene_id, enumValue))
def draw(self, context):
col = self.layout.column()
@@ -38,16 +42,18 @@ def draw(self, context):
exportBox = col.box().column()
exportBox.label(text="Scene Exporter")
- settings: OOTExportSceneSettingsProperty = context.scene.ootSceneExportSettings
+ settings: Z64_ExportSceneSettingsProperty = context.scene.ootSceneExportSettings
if not settings.customExport:
- self.drawSceneSearchOp(exportBox, settings.option, "Export")
+ self.drawSceneSearchOp(
+ exportBox, settings.option if game_data.z64.is_oot() else settings.mm_option, "Export"
+ )
settings.draw_props(exportBox)
if context.scene.fast64.oot.hackerFeaturesEnabled:
hackerOoTBox = exportBox.box().column()
hackerOoTBox.label(text="HackerOoT Options")
- bootOptions: OOTBootupSceneOptions = context.scene.fast64.oot.bootupSceneOptions
+ bootOptions: OOT_BootupSceneOptions = context.scene.fast64.oot.bootupSceneOptions
bootOptions.draw_props(hackerOoTBox)
hackerOoTBox.label(
@@ -61,20 +67,22 @@ def draw(self, context):
importBox = col.box().column()
importBox.label(text="Scene Importer")
- importSettings: OOTImportSceneSettingsProperty = context.scene.ootSceneImportSettings
+ importSettings: Z64_ImportSceneSettingsProperty = context.scene.ootSceneImportSettings
+ option = importSettings.option if game_data.z64.is_oot() else importSettings.mm_option
if not importSettings.isCustomDest:
- self.drawSceneSearchOp(importBox, importSettings.option, "Import")
+ self.drawSceneSearchOp(importBox, option, "Import")
- importSettings.draw_props(importBox, importSettings.option)
+ importSettings.draw_props(importBox, option)
importBox.operator(OOT_ImportScene.bl_idname)
# Remove Scene
removeBox = col.box().column()
removeBox.label(text="Remove Scene")
- removeSettings: OOTRemoveSceneSettingsProperty = context.scene.ootSceneRemoveSettings
- self.drawSceneSearchOp(removeBox, removeSettings.option, "Remove")
+ removeSettings: Z64_RemoveSceneSettingsProperty = context.scene.ootSceneRemoveSettings
+ option = removeSettings.option if game_data.z64.is_oot() else removeSettings.mm_option
+ self.drawSceneSearchOp(removeBox, option, "Remove")
removeSettings.draw_props(removeBox)
removeRow = removeBox.row()
diff --git a/fast64_internal/oot/scene/properties.py b/fast64_internal/z64/scene/properties.py
similarity index 61%
rename from fast64_internal/oot/scene/properties.py
rename to fast64_internal/z64/scene/properties.py
index b530e3bd4..1f897fb5c 100644
--- a/fast64_internal/oot/scene/properties.py
+++ b/fast64_internal/z64/scene/properties.py
@@ -1,5 +1,5 @@
import bpy
-from bpy.types import PropertyGroup, Object, Light, UILayout, Scene
+from bpy.types import PropertyGroup, Object, Light, UILayout, Scene, Context
from bpy.props import (
EnumProperty,
IntProperty,
@@ -12,25 +12,21 @@
from bpy.utils import register_class, unregister_class
from ...render_settings import on_update_oot_render_settings
from ...utility import prop_split, customExportWarning
+from ...game_data import game_data
from ..cutscene.constants import ootEnumCSWriteType
from ..collection_utility import drawCollectionOps, drawAddButton
-from ..oot_utility import onMenuTabChange, onHeaderMenuTabChange, drawEnumWithCustom
+from ..utility import onMenuTabChange, onHeaderMenuTabChange, drawEnumWithCustom, is_oot_features
-from ..oot_constants import (
- ootEnumMusicSeq,
+from ..constants import (
ootEnumSceneID,
- ootEnumGlobalObject,
ootEnumNaviHints,
- ootEnumSkybox,
- ootEnumCloudiness,
ootEnumSkyboxLighting,
ootEnumMapLocation,
ootEnumCameraMode,
- ootEnumNightSeq,
ootEnumAudioSessionPreset,
ootEnumHeaderMenu,
- ootEnumDrawConfig,
ootEnumHeaderMenuComplete,
+ mm_enum_scene_id,
)
ootEnumSceneMenuAlternate = [
@@ -94,7 +90,7 @@ class OOTSceneProperties(PropertyGroup):
)
-class OOTExitProperty(PropertyGroup):
+class Z64_ExitProperty(PropertyGroup):
expandTab: BoolProperty(name="Expand Tab")
exitIndex: EnumProperty(items=ootEnumExitIndex, default="Default")
@@ -115,21 +111,27 @@ class OOTExitProperty(PropertyGroup):
def draw_props(self, layout: UILayout, index: int, headerIndex: int, objName: str):
box = layout.box()
box.prop(self, "expandTab", text="Exit " + str(index + 1), icon="TRIA_DOWN" if self.expandTab else "TRIA_RIGHT")
+
if self.expandTab:
drawCollectionOps(box, index, "Exit", headerIndex, objName)
- drawEnumWithCustom(box, self, "exitIndex", "Exit Index", "")
- if self.exitIndex != "Custom":
- box.label(text='This is unfinished, use "Custom".')
- exitGroup = box.column()
- exitGroup.enabled = False
- drawEnumWithCustom(exitGroup, self, "scene", "Scene", "")
- exitGroup.prop(self, "continueBGM", text="Continue BGM")
- exitGroup.prop(self, "displayTitleCard", text="Display Title Card")
- drawEnumWithCustom(exitGroup, self, "fadeInAnim", "Fade In Animation", "")
- drawEnumWithCustom(exitGroup, self, "fadeOutAnim", "Fade Out Animation", "")
-
-
-class OOTLightProperty(PropertyGroup):
+
+ if game_data.z64.is_oot():
+ drawEnumWithCustom(box, self, "exitIndex", "Exit Index", "")
+
+ if self.exitIndex != "Custom":
+ box.label(text='This is unfinished, use "Custom".')
+ exitGroup = box.column()
+ exitGroup.enabled = False
+ drawEnumWithCustom(exitGroup, self, "scene", "Scene", "")
+ exitGroup.prop(self, "continueBGM", text="Continue BGM")
+ exitGroup.prop(self, "displayTitleCard", text="Display Title Card")
+ drawEnumWithCustom(exitGroup, self, "fadeInAnim", "Fade In Animation", "")
+ drawEnumWithCustom(exitGroup, self, "fadeOutAnim", "Fade Out Animation", "")
+ else:
+ prop_split(box, self, "exitIndexCustom", "Exit Index")
+
+
+class Z64_LightProperty(PropertyGroup):
ambient: FloatVectorProperty(
name="Ambient Color",
size=4,
@@ -211,13 +213,13 @@ def draw_props(
prop_split(box, self, "transitionSpeed", "Transition Speed")
-class OOTLightGroupProperty(PropertyGroup):
+class Z64_LightGroupProperty(PropertyGroup):
expandTab: BoolProperty()
menuTab: EnumProperty(items=ootEnumLightGroupMenu)
- dawn: PointerProperty(type=OOTLightProperty)
- day: PointerProperty(type=OOTLightProperty)
- dusk: PointerProperty(type=OOTLightProperty)
- night: PointerProperty(type=OOTLightProperty)
+ dawn: PointerProperty(type=Z64_LightProperty)
+ day: PointerProperty(type=Z64_LightProperty)
+ dusk: PointerProperty(type=Z64_LightProperty)
+ night: PointerProperty(type=Z64_LightProperty)
defaultsSet: BoolProperty()
def draw_props(self, layout: UILayout):
@@ -233,16 +235,17 @@ def draw_props(self, layout: UILayout):
self.night.draw_props(box, "Night", False, None, None, None)
-class OOTSceneTableEntryProperty(PropertyGroup):
- drawConfig: EnumProperty(items=ootEnumDrawConfig, name="Scene Draw Config", default="SDC_DEFAULT")
+class Z64_SceneTableEntryProperty(PropertyGroup):
+ drawConfig: EnumProperty(
+ items=lambda self, context: game_data.z64.get_enum("drawConfig"), name="Scene Draw Config", default=1
+ )
drawConfigCustom: StringProperty(name="Scene Draw Config Custom")
- hasTitle: BoolProperty(default=True)
def draw_props(self, layout: UILayout):
- drawEnumWithCustom(layout, self, "drawConfig", "Draw Config", "")
+ drawEnumWithCustom(layout, self, "drawConfig", "Draw Config", "", "drawConfigCustom")
-class OOTExtraCutsceneProperty(PropertyGroup):
+class Z64_ExtraCutsceneProperty(PropertyGroup):
csObject: PointerProperty(
name="Cutscene Object",
type=Object,
@@ -250,18 +253,79 @@ class OOTExtraCutsceneProperty(PropertyGroup):
)
-class OOTSceneHeaderProperty(PropertyGroup):
+def minimap_chest_poll(self: "Z64_MapDataChestProperty", object: Object):
+ return (
+ object.type == "EMPTY" and object.ootEmptyType == "Actor" and object.ootActorProperty.actor_id == "ACTOR_EN_BOX"
+ )
+
+
+class Z64_MapDataChestProperty(PropertyGroup):
+ expand_tab: BoolProperty(name="Expand Tab")
+
+ # used to get the room index, the coordinates and the chest flag
+ chest_obj: PointerProperty(type=Object, poll=minimap_chest_poll, name="Chest Empty Object")
+
+ def draw_props(self, layout: UILayout, index: int, header_index: int, obj_name: str):
+ box = layout.box()
+ box.prop(
+ self,
+ "expand_tab",
+ text=f"Chest Actor No.{index + 1}",
+ icon="TRIA_DOWN" if self.expand_tab else "TRIA_RIGHT",
+ )
+
+ if self.expand_tab:
+ drawCollectionOps(box, index, "Minimap Chest", header_index, obj_name)
+ box.prop(self, "chest_obj")
+
+
+class Z64_MapDataRoomProperty(PropertyGroup):
+ expand_tab: BoolProperty(name="Expand Tab")
+
+ map_id: StringProperty(name="Minimap ID", default="MAP_DATA_NO_MAP")
+ center_x: IntProperty(name="Center X", default=0)
+ floor_y: IntProperty(name="Floor Y", default=0)
+ center_z: IntProperty(name="Center Z", default=0)
+ flags: StringProperty(name="Flags", default="0x0000")
+
+ def draw_props(self, layout: UILayout, index: int, header_index: int, obj_name: str):
+ box = layout.box()
+ box.prop(
+ self,
+ "expand_tab",
+ text=f"Minimap Room Index {index}",
+ icon="TRIA_DOWN" if self.expand_tab else "TRIA_RIGHT",
+ )
+
+ if self.expand_tab:
+ drawCollectionOps(box, index, "Minimap Room", header_index, obj_name)
+ prop_split(box, self, "map_id", "Minimap ID")
+ prop_split(box, self, "center_x", "Center X")
+ prop_split(box, self, "floor_y", "Floor Y")
+ prop_split(box, self, "center_z", "Center Z")
+ prop_split(box, self, "flags", "Flags")
+
+
+class Z64_SceneHeaderProperty(PropertyGroup):
expandTab: BoolProperty(name="Expand Tab")
usePreviousHeader: BoolProperty(name="Use Previous Header", default=True)
- globalObject: EnumProperty(name="Global Object", default="OBJECT_GAMEPLAY_DANGEON_KEEP", items=ootEnumGlobalObject)
+ # SCENE_CMD_SPECIAL_FILES
+ globalObject: EnumProperty(
+ name="Global Object", default=1, items=lambda self, context: game_data.z64.get_enum("globalObject")
+ )
globalObjectCustom: StringProperty(name="Global Object Custom", default="0x00")
- naviCup: EnumProperty(name="Navi Hints", default="0x00", items=ootEnumNaviHints)
+
+ # OoT exclusive
+ naviCup: EnumProperty(name="Navi Hints", default="NAVI_QUEST_HINTS_NONE", items=ootEnumNaviHints)
naviCupCustom: StringProperty(name="Navi Hints Custom", default="0x00")
- skyboxID: EnumProperty(name="Skybox", items=ootEnumSkybox, default="0x01")
+ # SCENE_CMD_SKYBOX_SETTINGS
+ skyboxID: EnumProperty(name="Skybox", items=lambda self, context: game_data.z64.get_enum("skyboxID"), default=1)
skyboxIDCustom: StringProperty(name="Skybox ID", default="0")
- skyboxCloudiness: EnumProperty(name="Cloudiness", items=ootEnumCloudiness, default="0x00")
+ skyboxCloudiness: EnumProperty(
+ name="Cloudiness", items=lambda self, context: game_data.z64.get_enum("skyboxCloudiness"), default=1
+ )
skyboxCloudinessCustom: StringProperty(name="Cloudiness ID", default="0x00")
skyboxLighting: EnumProperty(
name="Skybox Lighting",
@@ -273,21 +337,27 @@ class OOTSceneHeaderProperty(PropertyGroup):
name="Skybox Lighting Custom", default="0x00", update=on_update_oot_render_settings
)
- mapLocation: EnumProperty(name="Map Location", items=ootEnumMapLocation, default="0x00")
- mapLocationCustom: StringProperty(name="Skybox Lighting Custom", default="0x00")
- cameraMode: EnumProperty(name="Camera Mode", items=ootEnumCameraMode, default="0x00")
- cameraModeCustom: StringProperty(name="Camera Mode Custom", default="0x00")
+ # MM exclusive
+ skybox_texture_id: StringProperty(name="Skybox Texture ID", default="0x00")
- musicSeq: EnumProperty(name="Music Sequence", items=ootEnumMusicSeq, default="NA_BGM_FIELD_LOGIC")
+ # SCENE_CMD_SOUND_SETTINGS
+ musicSeq: EnumProperty(
+ name="Music Sequence", items=lambda self, context: game_data.z64.get_enum("musicSeq"), default=1
+ )
musicSeqCustom: StringProperty(name="Music Sequence ID", default="0x00")
- nightSeq: EnumProperty(name="Nighttime SFX", items=ootEnumNightSeq, default="0x00")
+ nightSeq: EnumProperty(
+ name="Nighttime SFX", items=lambda self, context: game_data.z64.get_enum("nightSeq"), default=1
+ )
nightSeqCustom: StringProperty(name="Nighttime SFX ID", default="0x00")
audioSessionPreset: EnumProperty(name="Audio Session Preset", items=ootEnumAudioSessionPreset, default="0x00")
audioSessionPresetCustom: StringProperty(name="Audio Session Preset", default="0x00")
- timeOfDayLights: PointerProperty(type=OOTLightGroupProperty, name="Time Of Day Lighting")
- lightList: CollectionProperty(type=OOTLightProperty, name="Lighting List")
- exitList: CollectionProperty(type=OOTExitProperty, name="Exit List")
+ # SCENE_CMD_ENV_LIGHT_SETTINGS
+ timeOfDayLights: PointerProperty(type=Z64_LightGroupProperty, name="Time Of Day Lighting")
+ lightList: CollectionProperty(type=Z64_LightProperty, name="Lighting List")
+
+ # SCENE_CMD_EXIT_LIST
+ exitList: CollectionProperty(type=Z64_ExitProperty, name="Exit List")
writeCutscene: BoolProperty(name="Write Cutscene")
csWriteType: EnumProperty(name="Cutscene Data Type", items=ootEnumCSWriteType, default="Object")
@@ -298,8 +368,8 @@ class OOTSceneHeaderProperty(PropertyGroup):
poll=lambda self, object: object.type == "EMPTY" and object.ootEmptyType == "Cutscene",
)
- extraCutscenes: CollectionProperty(type=OOTExtraCutsceneProperty, name="Extra Cutscenes")
- sceneTableEntry: PointerProperty(type=OOTSceneTableEntryProperty)
+ extraCutscenes: CollectionProperty(type=Z64_ExtraCutsceneProperty, name="Extra Cutscenes")
+ sceneTableEntry: PointerProperty(type=Z64_SceneTableEntryProperty)
menuTab: EnumProperty(name="Menu", items=ootEnumSceneMenu, update=onMenuTabChange)
altMenuTab: EnumProperty(name="Menu", items=ootEnumSceneMenuAlternate)
@@ -309,10 +379,35 @@ class OOTSceneHeaderProperty(PropertyGroup):
default=False,
)
+ ## OoT exclusive
+
+ # SCENE_CMD_MISC_SETTINGS
+ mapLocation: EnumProperty(name="Map Location", items=ootEnumMapLocation, default="0x00")
+ mapLocationCustom: StringProperty(name="Skybox Lighting Custom", default="0x00")
+ cameraMode: EnumProperty(name="Camera Mode", items=ootEnumCameraMode, default="0x00")
+ cameraModeCustom: StringProperty(name="Camera Mode Custom", default="0x00")
title_card_name: StringProperty(
name="Title Card", default="none", description="Segment name of the title card to use"
)
+ ## MM exclusive
+
+ # SCENE_CMD_SET_REGION_VISITED
+ set_region_visited: BoolProperty(
+ name="Set Region Visited Flag",
+ default=False,
+ description="Sets a flag that indicates the region has been visited. Scene indices are mapped to their region in `gSceneIdsPerRegion` from `z_inventory.c`",
+ )
+
+ # SCENE_CMD_MINIMAP_INFO (note: room informations are defined in `Z64_RoomHeaderProperty`)
+ minimap_room_expand: BoolProperty(name="Expand Tab")
+ minimap_room_list: CollectionProperty(type=Z64_MapDataRoomProperty, name="Minimap Room List")
+ minimap_scale: IntProperty(name="Minimap Scale", default=0)
+
+ # SCENE_CMD_MINIMAP_COMPASS_ICON_INFO
+ minimap_chest_expand: BoolProperty(name="Expand Tab")
+ minimap_chest_list: CollectionProperty(type=Z64_MapDataChestProperty, name="Minimap Chest List")
+
def draw_props(self, layout: UILayout, dropdownLabel: str, headerIndex: int, objName: str):
from .operators import OOT_SearchMusicSeqEnumOperator # temp circular import fix
@@ -320,10 +415,15 @@ def draw_props(self, layout: UILayout, dropdownLabel: str, headerIndex: int, obj
layout.prop(self, "expandTab", text=dropdownLabel, icon="TRIA_DOWN" if self.expandTab else "TRIA_RIGHT")
if not self.expandTab:
return
- if headerIndex is not None and headerIndex > 3:
- drawCollectionOps(layout, headerIndex - 4, "Scene", None, objName)
-
- if headerIndex is not None and headerIndex > 0 and headerIndex < 4:
+ if headerIndex is not None and headerIndex > (game_data.z64.cs_index_start - 1):
+ drawCollectionOps(layout, headerIndex - game_data.z64.cs_index_start, "Scene", None, objName)
+
+ if (
+ game_data.z64.is_oot()
+ and headerIndex is not None
+ and headerIndex > 0
+ and headerIndex < game_data.z64.cs_index_start
+ ):
layout.prop(self, "usePreviousHeader", text="Use Previous Header")
if self.usePreviousHeader:
return
@@ -338,30 +438,75 @@ def draw_props(self, layout: UILayout, dropdownLabel: str, headerIndex: int, obj
if menuTab == "General":
general = layout.column()
general.box().label(text="General")
- drawEnumWithCustom(general, self, "globalObject", "Global Object", "")
- drawEnumWithCustom(general, self, "naviCup", "Navi Hints", "")
+
+ # General
+ drawEnumWithCustom(general, self, "globalObject", "Global Object", "", "globalObjectCustom")
+ if game_data.z64.is_oot():
+ drawEnumWithCustom(general, self, "naviCup", "Navi Hints", "")
if headerIndex is None or headerIndex == 0:
self.sceneTableEntry.draw_props(general)
prop_split(general, self, "title_card_name", "Title Card")
if bpy.context.scene.ootSceneExportSettings.customExport:
general.label(text="Custom Export Path enabled, title card will be ignored.", icon="INFO")
+
+ if not is_oot_features():
+ general.prop(self, "set_region_visited")
+
general.prop(self, "appendNullEntrance")
+ # Skybox And Sound
skyboxAndSound = layout.column()
skyboxAndSound.box().label(text="Skybox And Sound")
- drawEnumWithCustom(skyboxAndSound, self, "skyboxID", "Skybox", "")
- drawEnumWithCustom(skyboxAndSound, self, "skyboxCloudiness", "Cloudiness", "")
- drawEnumWithCustom(skyboxAndSound, self, "musicSeq", "Music Sequence", "")
+
+ prop_split(skyboxAndSound, self, "skybox_texture_id", "Skybox Texture ID")
+ drawEnumWithCustom(skyboxAndSound, self, "skyboxID", "Skybox", "", "skyboxIDCustom")
+ drawEnumWithCustom(skyboxAndSound, self, "skyboxCloudiness", "Skybox Config", "", "skyboxCloudinessCustom")
+ drawEnumWithCustom(skyboxAndSound, self, "musicSeq", "Music Sequence", "", "musicSeqCustom")
+
musicSearch = skyboxAndSound.operator(OOT_SearchMusicSeqEnumOperator.bl_idname, icon="VIEWZOOM")
musicSearch.objName = objName
musicSearch.headerIndex = headerIndex if headerIndex is not None else 0
- drawEnumWithCustom(skyboxAndSound, self, "nightSeq", "Nighttime SFX", "")
+ drawEnumWithCustom(skyboxAndSound, self, "nightSeq", "Nighttime SFX", "", "nightSeqCustom")
drawEnumWithCustom(skyboxAndSound, self, "audioSessionPreset", "Audio Session Preset", "")
- cameraAndWorldMap = layout.column()
- cameraAndWorldMap.box().label(text="Camera And World Map")
- drawEnumWithCustom(cameraAndWorldMap, self, "mapLocation", "Map Location", "")
- drawEnumWithCustom(cameraAndWorldMap, self, "cameraMode", "Camera Mode", "")
+ # Camera And World Map | Minimap Settings
+ if is_oot_features():
+ cameraAndWorldMap = layout.column()
+ cameraAndWorldMap.box().label(text="Camera And World Map")
+ drawEnumWithCustom(cameraAndWorldMap, self, "mapLocation", "Map Location", "")
+ drawEnumWithCustom(cameraAndWorldMap, self, "cameraMode", "Camera Mode", "")
+ else:
+ minimap = layout.column()
+ minimap.box().label(text="Minimap Settings")
+ prop_split(minimap, self, "minimap_scale", "Minimap Scale")
+
+ map_room_box = minimap.column().box()
+ list_length = len(self.minimap_room_list)
+ item_count = "Empty" if list_length == 0 else f"{list_length} item{'s' if list_length > 1 else ''}"
+ map_room_box.prop(
+ self,
+ "minimap_room_expand",
+ text=f"Minimap Room List ({item_count})",
+ icon="TRIA_DOWN" if self.minimap_room_expand else "TRIA_RIGHT",
+ )
+ if self.minimap_room_expand:
+ for i in range(list_length):
+ self.minimap_room_list[i].draw_props(map_room_box, i, headerIndex, objName)
+ drawAddButton(map_room_box, list_length, "Minimap Room", headerIndex, objName)
+
+ chest_box = minimap.column().box()
+ list_length = len(self.minimap_chest_list)
+ item_count = "Empty" if list_length == 0 else f"{list_length} item{'s' if list_length > 1 else ''}"
+ chest_box.prop(
+ self,
+ "minimap_chest_expand",
+ text=f"Minimap Chest List ({item_count})",
+ icon="TRIA_DOWN" if self.minimap_chest_expand else "TRIA_RIGHT",
+ )
+ if self.minimap_chest_expand:
+ for i in range(list_length):
+ self.minimap_chest_list[i].draw_props(chest_box, i, headerIndex, objName)
+ drawAddButton(chest_box, list_length, "Minimap Chest", headerIndex, objName)
elif menuTab == "Lighting":
lighting = layout.column()
@@ -403,38 +548,51 @@ def draw_props(self, layout: UILayout, dropdownLabel: str, headerIndex: int, obj
drawAddButton(exitBox, len(self.exitList), "Exit", headerIndex, objName)
-class OOTAlternateSceneHeaderProperty(PropertyGroup):
- childNightHeader: PointerProperty(name="Child Night Header", type=OOTSceneHeaderProperty)
- adultDayHeader: PointerProperty(name="Adult Day Header", type=OOTSceneHeaderProperty)
- adultNightHeader: PointerProperty(name="Adult Night Header", type=OOTSceneHeaderProperty)
- cutsceneHeaders: CollectionProperty(type=OOTSceneHeaderProperty)
+def update_cutscene_index(self: "Z64_AlternateSceneHeaderProperty", context: Context):
+ if self.currentCutsceneIndex < game_data.z64.cs_index_start:
+ self.currentCutsceneIndex = game_data.z64.cs_index_start
+
+ onHeaderMenuTabChange(self, context)
+
+class Z64_AlternateSceneHeaderProperty(PropertyGroup):
+ cutsceneHeaders: CollectionProperty(type=Z64_SceneHeaderProperty)
+ currentCutsceneIndex: IntProperty(default=1, update=update_cutscene_index)
+
+ # OoT exclusive
+ childNightHeader: PointerProperty(name="Child Night Header", type=Z64_SceneHeaderProperty)
+ adultDayHeader: PointerProperty(name="Adult Day Header", type=Z64_SceneHeaderProperty)
+ adultNightHeader: PointerProperty(name="Adult Night Header", type=Z64_SceneHeaderProperty)
headerMenuTab: EnumProperty(name="Header Menu", items=ootEnumHeaderMenu, update=onHeaderMenuTabChange)
- currentCutsceneIndex: IntProperty(min=4, default=4, update=onHeaderMenuTabChange)
def draw_props(self, layout: UILayout, objName: str):
headerSetup = layout.column()
- # headerSetup.box().label(text = "Alternate Headers")
- headerSetupBox = headerSetup.column()
-
- headerSetupBox.row().prop(self, "headerMenuTab", expand=True)
- if self.headerMenuTab == "Child Night":
- self.childNightHeader.draw_props(headerSetupBox, None, 1, objName)
- elif self.headerMenuTab == "Adult Day":
- self.adultDayHeader.draw_props(headerSetupBox, None, 2, objName)
- elif self.headerMenuTab == "Adult Night":
- self.adultNightHeader.draw_props(headerSetupBox, None, 3, objName)
- elif self.headerMenuTab == "Cutscene":
+ oot_can_draw_cs_header = False
+
+ if game_data.z64.is_oot():
+ headerSetupBox = headerSetup.column()
+ headerSetupBox.row().prop(self, "headerMenuTab", expand=True)
+
+ if self.headerMenuTab == "Child Night":
+ self.childNightHeader.draw_props(headerSetupBox, None, 1, objName)
+ elif self.headerMenuTab == "Adult Day":
+ self.adultDayHeader.draw_props(headerSetupBox, None, 2, objName)
+ elif self.headerMenuTab == "Adult Night":
+ self.adultNightHeader.draw_props(headerSetupBox, None, 3, objName)
+ elif self.headerMenuTab == "Cutscene":
+ oot_can_draw_cs_header = True
+
+ if game_data.z64.is_mm() or oot_can_draw_cs_header:
prop_split(headerSetup, self, "currentCutsceneIndex", "Cutscene Index")
drawAddButton(headerSetup, len(self.cutsceneHeaders), "Scene", None, objName)
index = self.currentCutsceneIndex
- if index - 4 < len(self.cutsceneHeaders):
- self.cutsceneHeaders[index - 4].draw_props(headerSetup, None, index, objName)
+ if index - game_data.z64.cs_index_start < len(self.cutsceneHeaders):
+ self.cutsceneHeaders[index - game_data.z64.cs_index_start].draw_props(headerSetup, None, index, objName)
else:
headerSetup.label(text="No cutscene header for this index.", icon="QUESTION")
-class OOTBootupSceneOptions(PropertyGroup):
+class OOT_BootupSceneOptions(PropertyGroup):
bootToScene: BoolProperty(default=False, name="Boot To Scene")
overrideHeader: BoolProperty(default=False, name="Override Header")
headerOption: EnumProperty(items=ootEnumHeaderMenuComplete, name="Header", default="Child Day")
@@ -466,19 +624,20 @@ def draw_props(self, layout: UILayout):
prop_split(layout, self, "cutsceneIndex", "Cutscene Index")
-class OOTRemoveSceneSettingsProperty(PropertyGroup):
+class Z64_RemoveSceneSettingsProperty(PropertyGroup):
name: StringProperty(name="Name", default="spot03")
subFolder: StringProperty(name="Subfolder", default="overworld")
customExport: BoolProperty(name="Custom Export Path")
option: EnumProperty(items=ootEnumSceneID, default="SCENE_DEKU_TREE")
+ mm_option: EnumProperty(items=mm_enum_scene_id, default="SCENE_20SICHITAI2")
def draw_props(self, layout: UILayout):
- if self.option == "Custom":
+ if game_data.z64.is_oot() and self.option == "Custom" or self.mm_option == "Custom":
prop_split(layout, self, "subFolder", "Subfolder")
prop_split(layout, self, "name", "Name")
-class OOTExportSceneSettingsProperty(PropertyGroup):
+class Z64_ExportSceneSettingsProperty(PropertyGroup):
name: StringProperty(name="Name", default="spot03")
subFolder: StringProperty(name="Subfolder", default="overworld")
exportPath: StringProperty(name="Directory", subtype="FILE_PATH")
@@ -489,9 +648,7 @@ class OOTExportSceneSettingsProperty(PropertyGroup):
description="Does not split the scene and rooms into multiple files.",
)
option: EnumProperty(items=ootEnumSceneID, default="SCENE_DEKU_TREE")
-
- # keeping this on purpose, will be removed once old code is cleaned-up
- useNewExporter: BoolProperty(name="Use New Exporter", default=True)
+ mm_option: EnumProperty(items=mm_enum_scene_id, default="SCENE_20SICHITAI2")
def draw_props(self, layout: UILayout):
if self.customExport:
@@ -499,7 +656,7 @@ def draw_props(self, layout: UILayout):
prop_split(layout, self, "name", "Name")
customExportWarning(layout)
else:
- if self.option == "Custom":
+ if game_data.z64.is_oot() and self.option == "Custom" or self.mm_option == "Custom":
prop_split(layout, self, "subFolder", "Subfolder")
prop_split(layout, self, "name", "Name")
@@ -507,10 +664,9 @@ def draw_props(self, layout: UILayout):
layout.prop(self, "singleFile")
layout.prop(self, "customExport")
- # layout.prop(self, "useNewExporter")
-class OOTImportSceneSettingsProperty(PropertyGroup):
+class Z64_ImportSceneSettingsProperty(PropertyGroup):
name: StringProperty(name="Name", default="spot03")
subFolder: StringProperty(name="Subfolder", default="overworld")
destPath: StringProperty(name="Directory", subtype="FILE_PATH")
@@ -524,7 +680,10 @@ class OOTImportSceneSettingsProperty(PropertyGroup):
includePaths: BoolProperty(name="Paths", default=True)
includeWaterBoxes: BoolProperty(name="Water Boxes", default=True)
includeCutscenes: BoolProperty(name="Cutscenes", default=False)
+ includeAnimatedMats: BoolProperty(name="Animated Materials", default=False)
+ includeActorCs: BoolProperty(name="Actor Cutscenes", default=False)
option: EnumProperty(items=ootEnumSceneID, default="SCENE_DEKU_TREE")
+ mm_option: EnumProperty(items=mm_enum_scene_id, default="SCENE_20SICHITAI2")
def draw_props(self, layout: UILayout, sceneOption: str):
col = layout.column()
@@ -542,32 +701,46 @@ def draw_props(self, layout: UILayout, sceneOption: str):
includeButtons3.prop(self, "includePaths", toggle=1)
includeButtons3.prop(self, "includeWaterBoxes", toggle=1)
includeButtons3.prop(self, "includeCutscenes", toggle=1)
+
+ includeButtons4 = col.row(align=True)
+ if not is_oot_features():
+ includeButtons4.prop(self, "includeAnimatedMats", toggle=1)
+ includeButtons4.prop(self, "includeActorCs", toggle=1)
+
col.prop(self, "isCustomDest")
if self.isCustomDest:
prop_split(col, self, "destPath", "Directory")
prop_split(col, self, "name", "Name")
else:
- if self.option == "Custom":
+ if game_data.z64.is_oot() and self.option == "Custom" or self.mm_option == "Custom":
prop_split(col, self, "subFolder", "Subfolder")
prop_split(col, self, "name", "Name")
- if "SCENE_JABU_JABU" in sceneOption:
- col.label(text="Pulsing wall effect won't be imported.", icon="ERROR")
+ if game_data.z64.is_oot():
+ if "SCENE_JABU_JABU" in sceneOption:
+ col.label(text="Pulsing wall effect won't be imported.", icon="ERROR")
+
+ if not is_oot_features():
+ if not self.includeActors:
+ col.label(text="MapDataChest won't be imported.", icon="ERROR")
classes = (
- OOTExitProperty,
- OOTLightProperty,
- OOTLightGroupProperty,
- OOTSceneTableEntryProperty,
- OOTExtraCutsceneProperty,
- OOTSceneHeaderProperty,
- OOTAlternateSceneHeaderProperty,
- OOTBootupSceneOptions,
- OOTRemoveSceneSettingsProperty,
- OOTExportSceneSettingsProperty,
- OOTImportSceneSettingsProperty,
+ # MM exclusive
+ Z64_MapDataChestProperty,
+ Z64_MapDataRoomProperty,
+ # Common
+ Z64_ExitProperty,
+ Z64_LightProperty,
+ Z64_LightGroupProperty,
+ Z64_SceneTableEntryProperty,
+ Z64_ExtraCutsceneProperty,
+ Z64_SceneHeaderProperty,
+ Z64_AlternateSceneHeaderProperty,
+ Z64_RemoveSceneSettingsProperty,
+ Z64_ExportSceneSettingsProperty,
+ Z64_ImportSceneSettingsProperty,
)
@@ -575,12 +748,12 @@ def scene_props_register():
for cls in classes:
register_class(cls)
- Object.ootSceneHeader = PointerProperty(type=OOTSceneHeaderProperty)
- Object.ootAlternateSceneHeaders = PointerProperty(type=OOTAlternateSceneHeaderProperty)
+ Object.ootSceneHeader = PointerProperty(type=Z64_SceneHeaderProperty)
+ Object.ootAlternateSceneHeaders = PointerProperty(type=Z64_AlternateSceneHeaderProperty)
Scene.ootSceneExportObj = PointerProperty(type=Object, poll=OOTSceneCommon.isSceneObj)
- Scene.ootSceneExportSettings = PointerProperty(type=OOTExportSceneSettingsProperty)
- Scene.ootSceneImportSettings = PointerProperty(type=OOTImportSceneSettingsProperty)
- Scene.ootSceneRemoveSettings = PointerProperty(type=OOTRemoveSceneSettingsProperty)
+ Scene.ootSceneExportSettings = PointerProperty(type=Z64_ExportSceneSettingsProperty)
+ Scene.ootSceneImportSettings = PointerProperty(type=Z64_ImportSceneSettingsProperty)
+ Scene.ootSceneRemoveSettings = PointerProperty(type=Z64_RemoveSceneSettingsProperty)
def scene_props_unregister():
diff --git a/fast64_internal/oot/skeleton/exporter/__init__.py b/fast64_internal/z64/skeleton/exporter/__init__.py
similarity index 100%
rename from fast64_internal/oot/skeleton/exporter/__init__.py
rename to fast64_internal/z64/skeleton/exporter/__init__.py
diff --git a/fast64_internal/oot/skeleton/exporter/classes.py b/fast64_internal/z64/skeleton/exporter/classes.py
similarity index 100%
rename from fast64_internal/oot/skeleton/exporter/classes.py
rename to fast64_internal/z64/skeleton/exporter/classes.py
diff --git a/fast64_internal/oot/skeleton/exporter/functions.py b/fast64_internal/z64/skeleton/exporter/functions.py
similarity index 97%
rename from fast64_internal/oot/skeleton/exporter/functions.py
rename to fast64_internal/z64/skeleton/exporter/functions.py
index 92054039f..519b66c70 100644
--- a/fast64_internal/oot/skeleton/exporter/functions.py
+++ b/fast64_internal/z64/skeleton/exporter/functions.py
@@ -5,9 +5,9 @@
from pathlib import Path
from ....f3d.f3d_gbi import DLFormat, FMesh, TextureExportSettings, ScrollMethod
from ....f3d.f3d_writer import getInfoDict
-from ...oot_f3d_writer import ootProcessVertexGroup, writeTextureArraysNew, writeTextureArraysExisting
-from ...oot_model_classes import OOTModel, OOTGfxFormatter
-from ..constants import ootSkeletonImportDict
+from ...f3d_writer import ootProcessVertexGroup, writeTextureArraysNew, writeTextureArraysExisting
+from ...model_classes import OOTModel, OOTGfxFormatter
+from ....game_data import game_data
from ..properties import OOTSkeletonExportSettings
from ..utility import ootDuplicateArmatureAndRemoveRotations, getGroupIndices, ootRemoveSkeleton
from .classes import OOTLimb, OOTSkeleton
@@ -21,7 +21,7 @@
cleanupDuplicatedObjects,
)
-from ...oot_utility import (
+from ...utility import (
checkEmptyName,
checkForStartBone,
getStartBone,
@@ -220,7 +220,7 @@ def ootConvertArmatureToC(
settings: OOTSkeletonExportSettings,
):
if settings.mode != "Generic" and not settings.isCustom:
- importInfo = ootSkeletonImportDict[settings.mode]
+ importInfo = game_data.z64.skeleton_dict[settings.mode]
skeletonName = importInfo.skeletonName
filename = skeletonName
folderName = importInfo.folderName
diff --git a/fast64_internal/oot/skeleton/importer/__init__.py b/fast64_internal/z64/skeleton/importer/__init__.py
similarity index 100%
rename from fast64_internal/oot/skeleton/importer/__init__.py
rename to fast64_internal/z64/skeleton/importer/__init__.py
diff --git a/fast64_internal/oot/skeleton/importer/functions.py b/fast64_internal/z64/skeleton/importer/functions.py
similarity index 95%
rename from fast64_internal/oot/skeleton/importer/functions.py
rename to fast64_internal/z64/skeleton/importer/functions.py
index 030168d8f..da67a8468 100644
--- a/fast64_internal/oot/skeleton/importer/functions.py
+++ b/fast64_internal/z64/skeleton/importer/functions.py
@@ -2,13 +2,13 @@
from typing import List
import mathutils, bpy, math
from ....f3d.f3d_gbi import F3D, get_F3D_GBI
-from ....f3d.f3d_parser import getImportData, parseF3D
+from ....f3d.f3d_parser import getImportData, parseF3D, parseMatrices
from ....utility import hexOrDecInt, applyRotation, PluginError
-from ...oot_f3d_writer import ootReadActorScale
-from ...oot_model_classes import OOTF3DContext, ootGetIncludedAssetData
-from ...oot_utility import OOTEnum, ootGetObjectPath, getOOTScale, ootGetObjectHeaderPath, ootGetEnums, ootStripComments
-from ...oot_texture_array import ootReadTextureArrays
-from ..constants import ootSkeletonImportDict
+from ...f3d_writer import ootReadActorScale
+from ...model_classes import OOTF3DContext, ootGetIncludedAssetData
+from ...utility import OOTEnum, ootGetObjectPath, getOOTScale, ootGetObjectHeaderPath, ootGetEnums, ootStripComments
+from ...texture_array import ootReadTextureArrays
+from ....game_data import game_data
from ..properties import OOTSkeletonImportSettings
from ..utility import ootGetLimb, ootGetLimbs, ootGetSkeleton, applySkeletonRestPose
@@ -235,12 +235,16 @@ def ootBuildSkeleton(
return isLOD, armatureObj
+def parse_included_objects():
+ pass
+
+
def ootImportSkeletonC(basePath: str, importSettings: OOTSkeletonImportSettings):
importPath = bpy.path.abspath(importSettings.customPath)
isCustomImport = importSettings.isCustom
if importSettings.mode != "Generic" and not importSettings.isCustom:
- importInfo = ootSkeletonImportDict[importSettings.mode]
+ importInfo = game_data.z64.skeleton_dict[importSettings.mode]
skeletonName = importInfo.skeletonName
folderName = importInfo.folderName
overlayName = importInfo.actorOverlayName
@@ -288,6 +292,8 @@ def ootImportSkeletonC(basePath: str, importSettings: OOTSkeletonImportSettings)
if actorScale is None:
actorScale = getOOTScale(importSettings.actorScale)
+ parseMatrices(skeletonData, f3dContext, actorScale)
+
# print(limbList)
isLOD, armatureObj = ootBuildSkeleton(
skeletonName,
diff --git a/fast64_internal/oot/skeleton/operators.py b/fast64_internal/z64/skeleton/operators.py
similarity index 98%
rename from fast64_internal/oot/skeleton/operators.py
rename to fast64_internal/z64/skeleton/operators.py
index 043bf8040..8d0d95ba5 100644
--- a/fast64_internal/oot/skeleton/operators.py
+++ b/fast64_internal/z64/skeleton/operators.py
@@ -5,7 +5,7 @@
from mathutils import Matrix
from ...f3d.f3d_gbi import DLFormat
from ...utility import PluginError, raisePluginError
-from ..oot_utility import getStartBone, getNextBone, getOOTScale
+from ..utility import getStartBone, getNextBone, getOOTScale
from .exporter import ootConvertArmatureToC
from .importer import ootImportSkeletonC
from .properties import OOTSkeletonImportSettings, OOTSkeletonExportSettings
diff --git a/fast64_internal/oot/skeleton/panels.py b/fast64_internal/z64/skeleton/panels.py
similarity index 79%
rename from fast64_internal/oot/skeleton/panels.py
rename to fast64_internal/z64/skeleton/panels.py
index f598e7b2e..1c1dc5c23 100644
--- a/fast64_internal/oot/skeleton/panels.py
+++ b/fast64_internal/z64/skeleton/panels.py
@@ -1,14 +1,14 @@
from bpy.types import Armature, Panel
from bpy.utils import register_class, unregister_class
from ...utility import prop_split
-from ...panels import OOT_Panel
+from ...panels import Z64_Panel
from .properties import OOTSkeletonImportSettings, OOTSkeletonExportSettings
from .operators import OOT_ImportSkeleton, OOT_ExportSkeleton
class OOT_SkeletonPanel(Panel):
- bl_idname = "OOT_PT_skeleton"
- bl_label = "OOT Skeleton Properties"
+ bl_idname = "Z64_PT_skeleton"
+ bl_label = "Skeleton Properties"
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "object"
@@ -17,7 +17,7 @@ class OOT_SkeletonPanel(Panel):
@classmethod
def poll(cls, context):
return (
- context.scene.gameEditorMode == "OOT"
+ context.scene.gameEditorMode in {"OOT", "MM"}
and hasattr(context, "object")
and context.object is not None
and isinstance(context.object.data, Armature)
@@ -26,7 +26,7 @@ def poll(cls, context):
# called every frame
def draw(self, context):
col = self.layout.box().column()
- col.box().label(text="OOT Skeleton Inspector")
+ col.box().label(text="Skeleton Inspector")
prop_split(col, context.object, "ootDrawLayer", "Draw Layer")
context.object.ootSkeleton.draw_props(col)
@@ -34,8 +34,8 @@ def draw(self, context):
class OOT_BonePanel(Panel):
- bl_idname = "OOT_PT_bone"
- bl_label = "OOT Bone Properties"
+ bl_idname = "Z64_PT_bone"
+ bl_label = "Bone Properties"
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "bone"
@@ -43,18 +43,18 @@ class OOT_BonePanel(Panel):
@classmethod
def poll(cls, context):
- return context.scene.gameEditorMode == "OOT" and context.bone is not None
+ return context.scene.gameEditorMode in {"OOT", "MM"} and context.bone is not None
# called every frame
def draw(self, context):
col = self.layout.box().column()
- col.box().label(text="OOT Bone Inspector")
+ col.box().label(text="Bone Inspector")
context.bone.ootBone.draw_props(col)
-class OOT_ExportSkeletonPanel(OOT_Panel):
- bl_idname = "OOT_PT_export_skeleton"
- bl_label = "OOT Skeleton Exporter"
+class OOT_ExportSkeletonPanel(Z64_Panel):
+ bl_idname = "Z64_PT_export_skeleton"
+ bl_label = "Skeletons"
# called every frame
def draw(self, context):
diff --git a/fast64_internal/oot/skeleton/properties.py b/fast64_internal/z64/skeleton/properties.py
similarity index 97%
rename from fast64_internal/oot/skeleton/properties.py
rename to fast64_internal/z64/skeleton/properties.py
index bdeb02adb..8b3c604d2 100644
--- a/fast64_internal/oot/skeleton/properties.py
+++ b/fast64_internal/z64/skeleton/properties.py
@@ -5,7 +5,7 @@
from bpy.utils import register_class, unregister_class
from ...f3d.f3d_material import ootEnumDrawLayers
from ...utility import prop_split
-from .constants import ootEnumSkeletonImportMode
+from ...game_data import game_data
ootEnumBoneType = [
@@ -56,7 +56,7 @@ class OOTSkeletonExportSettings(PropertyGroup):
name="Use Custom Filename", description="Override filename instead of basing it off of the Blender name"
)
filename: StringProperty(name="Filename")
- mode: EnumProperty(name="Mode", items=ootEnumSkeletonImportMode)
+ mode: EnumProperty(name="Mode", items=lambda self, context: game_data.z64.get_enum("skeleton_mode"))
folder: StringProperty(name="Skeleton Folder", default="object_geldb")
customPath: StringProperty(name="Custom Skeleton Path", subtype="FILE_PATH")
isCustom: BoolProperty(
@@ -109,7 +109,7 @@ def draw_props(self, layout: UILayout):
class OOTSkeletonImportSettings(PropertyGroup):
- mode: EnumProperty(name="Mode", items=ootEnumSkeletonImportMode)
+ mode: EnumProperty(name="Mode", items=lambda self, context: game_data.z64.get_enum("skeleton_mode"))
applyRestPose: BoolProperty(name="Apply Friendly Rest Pose (If Available)", default=True)
name: StringProperty(name="Skeleton Name", default="gGerudoRedSkel")
folder: StringProperty(name="Skeleton Folder", default="object_geldb")
@@ -164,8 +164,6 @@ def draw_props(self, layout: UILayout):
OOTDynamicTransformProperty,
OOTBoneProperty,
OOTSkeletonProperty,
- OOTSkeletonExportSettings,
- OOTSkeletonImportSettings,
)
diff --git a/fast64_internal/oot/skeleton/utility.py b/fast64_internal/z64/skeleton/utility.py
similarity index 98%
rename from fast64_internal/oot/skeleton/utility.py
rename to fast64_internal/z64/skeleton/utility.py
index 0a565c355..9296a6329 100644
--- a/fast64_internal/oot/skeleton/utility.py
+++ b/fast64_internal/z64/skeleton/utility.py
@@ -1,7 +1,11 @@
-import mathutils, bpy, os, re
+import mathutils
+import bpy
+import os
+import re
+
from ...utility_anim import armatureApplyWithMesh
-from ..oot_model_classes import OOTVertexGroupInfo
-from ..oot_utility import checkForStartBone, getStartBone, getNextBone, ootStripComments
+from ..model_classes import OOTVertexGroupInfo
+from ..utility import checkForStartBone, getStartBone, getNextBone, ootStripComments
from ...utility import (
PluginError,
diff --git a/fast64_internal/oot/spline/panels.py b/fast64_internal/z64/spline/panels.py
similarity index 75%
rename from fast64_internal/oot/spline/panels.py
rename to fast64_internal/z64/spline/panels.py
index e899b3075..2ed982327 100644
--- a/fast64_internal/oot/spline/panels.py
+++ b/fast64_internal/z64/spline/panels.py
@@ -1,9 +1,7 @@
from bpy.types import Panel, Curve
from bpy.utils import register_class, unregister_class
-from ...utility import prop_split
-from ..oot_utility import getSceneObj, drawEnumWithCustom
-from ..actor.properties import OOTActorHeaderProperty
-from .properties import OOTSplineProperty
+from ..utility import getSceneObj
+from .properties import Z64_SplineProperty
class OOTSplinePanel(Panel):
@@ -16,20 +14,20 @@ class OOTSplinePanel(Panel):
@classmethod
def poll(cls, context):
- return context.scene.gameEditorMode == "OOT" and (
+ return context.scene.gameEditorMode in {"OOT", "MM"} and (
context.object is not None and type(context.object.data) == Curve
)
def draw(self, context):
box = self.layout.box().column()
- box.box().label(text="OOT Spline Inspector")
+ box.box().label(text="Spline Inspector")
curve = context.object.data
if curve.splines[0].type != "NURBS":
box.label(text="Only NURBS curves are compatible.")
else:
sceneObj = getSceneObj(context.object)
altSceneProp = sceneObj.ootAlternateSceneHeaders if sceneObj is not None else None
- splineProp: OOTSplineProperty = context.object.ootSplineProperty
+ splineProp: Z64_SplineProperty = context.object.ootSplineProperty
splineProp.draw_props(box, altSceneProp, context.object.name)
diff --git a/fast64_internal/z64/spline/properties.py b/fast64_internal/z64/spline/properties.py
new file mode 100644
index 000000000..05aca7d65
--- /dev/null
+++ b/fast64_internal/z64/spline/properties.py
@@ -0,0 +1,66 @@
+import bpy
+
+from bpy.types import PropertyGroup, Object, UILayout
+from bpy.props import EnumProperty, PointerProperty, StringProperty, IntProperty
+from bpy.utils import register_class, unregister_class
+from ...utility import prop_split
+from ...game_data import game_data
+from ..utility import drawEnumWithCustom, is_oot_features
+from ..collision.constants import enum_camera_crawlspace_stype
+from ..actor.properties import Z64_ActorHeaderProperty
+from ..scene.properties import Z64_AlternateSceneHeaderProperty
+
+
+enum_spline = [("Path", "Path", "Path"), ("Crawlspace", "Crawlspace", "Crawlspace")]
+
+
+class Z64_SplineProperty(PropertyGroup):
+ splineType: EnumProperty(items=enum_spline, default="Path")
+ index: IntProperty(min=0) # only used for crawlspace, not path
+ headerSettings: PointerProperty(type=Z64_ActorHeaderProperty)
+ camSType: EnumProperty(items=enum_camera_crawlspace_stype, default="CAM_SET_CRAWLSPACE")
+ camSTypeCustom: StringProperty(default="CAM_SET_CRAWLSPACE")
+
+ # MM exclusive
+ opt_path_index: IntProperty(name="Additional Path Index", min=-1, default=-1)
+ custom_value: IntProperty(name="Custom Value", min=-1, default=-1)
+
+ def draw_props(
+ self,
+ layout: UILayout,
+ altSceneProp: Z64_AlternateSceneHeaderProperty,
+ objName: str,
+ ):
+ camIndexName = "Path Index" if self.splineType == "Path" else "Camera Index"
+ prop_split(layout, self, "splineType", "Type")
+ prop_split(layout, self, "index", camIndexName)
+
+ if not is_oot_features():
+ prop_split(layout, self, "opt_path_index", "Additional Path Index")
+ prop_split(layout, self, "custom_value", "Custom Value")
+
+ if self.splineType == "Path":
+ headerProp: Z64_ActorHeaderProperty = bpy.data.objects[objName].ootSplineProperty.headerSettings
+ headerProp.draw_props(layout, "Curve", altSceneProp, objName)
+ elif self.splineType == "Crawlspace":
+ if game_data.z64.is_mm():
+ layout.label(text="Warning: MM doesn't have crawlspaces implemented.", icon="ERROR")
+ layout.label(text="This counts as a camera for index purposes.", icon="INFO")
+ drawEnumWithCustom(layout, self, "camSType", "Camera S Type", "")
+
+
+oot_spline_classes = (Z64_SplineProperty,)
+
+
+def spline_props_register():
+ for cls in oot_spline_classes:
+ register_class(cls)
+
+ Object.ootSplineProperty = PointerProperty(type=Z64_SplineProperty)
+
+
+def spline_props_unregister():
+ for cls in reversed(oot_spline_classes):
+ unregister_class(cls)
+
+ del Object.ootSplineProperty
diff --git a/fast64_internal/oot/oot_texture_array.py b/fast64_internal/z64/texture_array.py
similarity index 76%
rename from fast64_internal/oot/oot_texture_array.py
rename to fast64_internal/z64/texture_array.py
index 8803312ad..5db73fa3f 100644
--- a/fast64_internal/oot/oot_texture_array.py
+++ b/fast64_internal/z64/texture_array.py
@@ -1,8 +1,11 @@
-import os, re
+import os
+import re
+
from typing import Callable
from ..utility import hexOrDecInt
+from ..game_data import game_data
-from .oot_model_classes import (
+from .model_classes import (
OOTF3DContext,
TextureFlipbook,
ootGetActorData,
@@ -143,10 +146,15 @@ def getTextureArrays(actorData: str, flipbookArrayIndex2D: int) -> dict[str, Tex
flipbookList = {} # {array name : TextureFlipbook}
if flipbookArrayIndex2D is not None:
+ if game_data.z64.is_oot():
+ file_regex = r"void\s*\*\s*([0-9a-zA-Z\_]*)\s*\[\s*\]\s*\[[0-9a-zA-Z_]*\]\s*=\s*\{(.*?)\}\s*;"
+ else:
+ file_regex = r"TexturePtr\s*([0-9a-zA-Z\_]*)\s*\[[0-9a-zA-Z_]*\s*\]\s*\[[0-9a-zA-Z_]*\]\s*=\s*\{(.*?)\}\s*;"
+
for texArray2DMatch in re.finditer(
- r"void\s*\*\s*([0-9a-zA-Z\_]*)\s*\[\s*\]\s*\[[0-9a-zA-Z_]*\]\s*=\s*\{(.*?)\}\s*;",
+ file_regex,
actorData,
- flags=re.DOTALL,
+ flags=re.DOTALL | re.MULTILINE,
):
arrayMatchData = [
arrayMatch.group(1)
@@ -157,15 +165,19 @@ def getTextureArrays(actorData: str, flipbookArrayIndex2D: int) -> dict[str, Tex
continue
arrayName = texArray2DMatch.group(1).strip()
- textureList = stripComments([item for item in arrayMatchData[flipbookArrayIndex2D].split(",")])
+ temp = [item for item in arrayMatchData[flipbookArrayIndex2D].split(",")]
+
+ # handle trailing comma and NULL pointers
+ textureList = [item for item in stripComments(temp) if item != "NULL" and item != ""]
- # handle trailing comma
- if textureList[-1] == "":
- textureList.pop()
flipbookList[arrayName] = TextureFlipbook(arrayName, "Array", textureList)
else:
+ array_type = "void" if game_data.z64.is_oot() else "TexturePtr"
+
for texArrayMatch in re.finditer(
- r"void\s*\*\s*([0-9a-zA-Z\_]*)\s*\[\s*\]\s*=\s*\{(((?!\}).)*)\}", actorData, flags=re.DOTALL
+ array_type + r"\s*\*\s*([0-9a-zA-Z\_]*)\s*\[\s*\]\s*=\s*\{(((?!\}).)*)\}",
+ actorData,
+ flags=re.DOTALL | re.MULTILINE,
):
arrayName = texArrayMatch.group(1).strip()
textureList = stripComments([item for item in texArrayMatch.group(2).split(",")])
@@ -193,18 +205,43 @@ def getSPSegmentCalls(actorData: str) -> list[tuple[tuple[int, str], str, re.Mat
segmentCalls = []
# find gSPSegment() calls that reference texture arrays
- for spSegmentMatch in re.finditer(
- r"gSPSegment\s*\(\s*POLY\_(OPA)?(XLU)?\_DISP\s*\+\+\s*,\s*([0-9a-fA-Fx]*)\s*,\s*SEGMENTED\_TO\_VIRTUAL\s*\(\s*(((?!;).)*)\)\s*\)\s*;",
- actorData,
- flags=re.DOTALL,
- ):
- # see ootEnumDrawLayers
- drawLayer = "Transparent" if spSegmentMatch.group(2) else "Opaque"
- segment = hexOrDecInt(spSegmentMatch.group(3))
- flipbookKey = (segment, drawLayer)
- segmentParam = spSegmentMatch.group(4).strip()
-
- segmentCalls.append((flipbookKey, segmentParam, spSegmentMatch))
+ if game_data.z64.is_oot():
+ for spSegmentMatch in re.finditer(
+ r"gSPSegment\s*\(\s*POLY\_(OPA)?(XLU)?\_DISP\s*\+\+\s*,\s*([0-9a-fA-Fx]*)\s*,\s*SEGMENTED\_TO\_VIRTUAL\s*\(\s*(((?!;).)*)\)\s*\)\s*;",
+ actorData,
+ flags=re.DOTALL,
+ ):
+ # see ootEnumDrawLayers
+ drawLayer = "Transparent" if spSegmentMatch.group(2) else "Opaque"
+ segment = hexOrDecInt(spSegmentMatch.group(3))
+ flipbookKey = (segment, drawLayer)
+ segmentParam = spSegmentMatch.group(4).strip()
+
+ segmentCalls.append((flipbookKey, segmentParam, spSegmentMatch))
+ else:
+ for spSegmentMatch in re.finditer(
+ r"gSPSegment\((.*),\s*([0-9a-fA-Fx]*)\s*,\s*Lib\_SegmentedToVirtual\s*\(\s*(((?!;).)*)\)\s*\)\s*;",
+ actorData,
+ flags=re.MULTILINE,
+ ):
+ # see ootEnumDrawLayers
+ segment = hexOrDecInt(spSegmentMatch.group(2))
+ segmentParam = spSegmentMatch.group(3).strip()
+
+ ## find the disp by looking for the next POLY_XXX_DISP before the next CLOSE_DISPS
+
+ # get a string containing the end of the function we're reading
+ string_1 = actorData[actorData.index(spSegmentMatch.group(0)) :]
+ string_2 = string_1[: string_1.find("CLOSE_DISPS")]
+
+ # find the next disp usage
+ disp = re.search(
+ r"POLY\_(XLU)\_DISP\s*=\s[a-zA-Z0-9_,\n&;*\s\[\]=()]*CLOSE\_DISPS", string_2, re.DOTALL | re.MULTILINE
+ )
+ drawLayer = "Transparent" if disp is not None else "Opaque"
+
+ flipbookKey = (segment, drawLayer)
+ segmentCalls.append((flipbookKey, segmentParam, spSegmentMatch))
return segmentCalls
diff --git a/fast64_internal/oot/tools/__init__.py b/fast64_internal/z64/tools/__init__.py
similarity index 100%
rename from fast64_internal/oot/tools/__init__.py
rename to fast64_internal/z64/tools/__init__.py
diff --git a/fast64_internal/oot/tools/operators.py b/fast64_internal/z64/tools/operators.py
similarity index 76%
rename from fast64_internal/oot/tools/operators.py
rename to fast64_internal/z64/tools/operators.py
index 1e834fce3..07c916635 100644
--- a/fast64_internal/oot/tools/operators.py
+++ b/fast64_internal/z64/tools/operators.py
@@ -1,13 +1,16 @@
import bpy
from mathutils import Vector
-from bpy.ops import mesh, object, curve
+from bpy.ops import mesh, object
from bpy.types import Operator, Object, Context
from bpy.props import FloatProperty, StringProperty, EnumProperty, BoolProperty
+
from ...operators import AddWaterBox, addMaterialByName
-from ...utility import parentObject, setOrigin
+from ...utility import PluginError, parentObject, setOrigin
+from ...game_data import game_data
from ..cutscene.motion.utility import setupCutscene, createNewCameraShot
-from ..oot_utility import getNewPath
+from ..utility import getNewPath, get_new_empty_object, is_oot_features
+from ..actor_cutscene.properties import enum_end_cam, enum_end_sfx, enum_hud_visibility
from .quick_import import QuickImportAborted, quick_import_exec
@@ -97,6 +100,7 @@ def execute(self, context):
roomObj = context.view_layer.objects.active
roomObj.ootEmptyType = "Room"
roomObj.name = "Room"
+ roomObj.ootRoomHeader.timeSpeed = game_data.z64.default_time_speed
entranceObj.ootEntranceProperty.tiedRoom = roomObj
parentObject(roomObj, planeObj)
@@ -128,6 +132,7 @@ def execute(self, context):
roomObj = context.view_layer.objects.active
roomObj.ootEmptyType = "Room"
roomObj.name = "Room"
+ roomObj.ootRoomHeader.timeSpeed = game_data.z64.default_time_speed
sceneObj = context.scene.ootSceneExportObj
if sceneObj is not None:
indices = []
@@ -293,3 +298,59 @@ def execute(self, context: Context):
self.report({"ERROR"}, e.message)
return {"CANCELLED"}
return {"FINISHED"}
+
+
+class Z64_AddActorCutscenes(Operator):
+ bl_idname = "object.z64_add_actor_cs"
+ bl_label = "Add Actor CS"
+ bl_options = {"REGISTER", "UNDO"}
+ bl_description = "Adds a basic actor cutscene with the necessary entries for a scene"
+
+ obj_name: StringProperty(name="Object Name", default="Actor Cutscene")
+
+ def invoke(self, context, event):
+ return context.window_manager.invoke_props_dialog(self)
+
+ def draw(self, context):
+ self.layout.prop(self, "obj_name")
+
+ if is_oot_features():
+ self.layout.label(text="Warning: This is only useful when using MM features.", icon="ERROR")
+
+ def execute(self, context: Context):
+ for obj in bpy.data.objects:
+ if obj.type == "EMPTY" and obj.ootEmptyType == "Actor Cutscene":
+ raise PluginError(f"ERROR: an actor cutscene object already exists! ({repr(obj.name)})")
+
+ actor_cs_obj = get_new_empty_object(self.obj_name)
+ actor_cs_obj.ootEmptyType = "Actor Cutscene"
+
+ entry_data_map = {
+ # CS Cam ID: [Priority, Length, Script Index, Additional CS ID, End SFX, Custom Value, HUD visibility, End Cam, Letterbox Size]
+ "CS_CAM_ID_GLOBAL_ITEM_OCARINA": [550, -1, -1, 1, 1, "255", 1, 1, 27],
+ "CS_CAM_ID_GLOBAL_ITEM_GET": [600, -1, -1, 2, 1, "255", 1, 1, 27],
+ "CS_CAM_ID_GLOBAL_ITEM_BOTTLE": [700, -1, -1, 3, 1, "255", 1, 2, 27],
+ "CS_CAM_ID_GLOBAL_ITEM_SHOW": [700, -1, -1, 4, 1, "255", 1, 1, 27],
+ "CS_CAM_ID_GLOBAL_WARP_PAD_MOON": [500, -1, -1, 5, 1, "255", 1, 1, 27],
+ "CS_CAM_ID_GLOBAL_MASK_TRANSFORMATION": [400, -1, -1, 6, 1, "255", 1, 2, 32],
+ "CS_CAM_ID_GLOBAL_DEATH": [100, -1, -1, 7, 1, "255", 1, 1, 32],
+ "CS_CAM_ID_GLOBAL_REVIVE": [200, -1, -1, 8, 1, "255", 1, 1, 32],
+ "CS_CAM_ID_GLOBAL_SONG_WARP": [700, -1, -1, 9, 1, "255", 1, 1, 32],
+ "CS_CAM_ID_GLOBAL_WARP_PAD_ENTRANCE": [700, -1, -1, 10, 1, "255", 1, 1, 32],
+ "CS_CAM_ID_GLOBAL_LONG_CHEST_OPENING": [900, 135, -1, -1, 1, "1", 1, 1, 32],
+ }
+
+ for cs_cam_id, data in entry_data_map.items():
+ new_entry = actor_cs_obj.z64_actor_cs_property.entries.add()
+ new_entry.cs_cam_id = cs_cam_id
+ new_entry.priority = data[0]
+ new_entry.length = data[1]
+ new_entry.script_index = data[2]
+ new_entry.additional_cs_id = data[3]
+ new_entry.end_sfx = enum_end_sfx[data[4]][0]
+ new_entry.custom_value = data[5]
+ new_entry.hud_visibility = enum_hud_visibility[data[6]][0]
+ new_entry.end_cam = enum_end_cam[data[7]][0]
+ new_entry.letterbox_size = data[8]
+
+ return {"FINISHED"}
diff --git a/fast64_internal/oot/tools/panel.py b/fast64_internal/z64/tools/panel.py
similarity index 74%
rename from fast64_internal/oot/tools/panel.py
rename to fast64_internal/z64/tools/panel.py
index e72ac48a8..ab4140327 100644
--- a/fast64_internal/oot/tools/panel.py
+++ b/fast64_internal/z64/tools/panel.py
@@ -1,5 +1,5 @@
from bpy.utils import register_class, unregister_class
-from ...panels import OOT_Panel
+from ...panels import Z64_Panel
from .operators import (
OOT_AddWaterBox,
OOT_AddDoor,
@@ -9,24 +9,34 @@
OOT_AddPath,
OOTClearTransformAndLock,
OOTQuickImport,
+ Z64_AddActorCutscenes,
)
-class OoT_ToolsPanel(OOT_Panel):
- bl_idname = "OOT_PT_tools"
- bl_label = "OOT Tools"
+class OoT_ToolsPanel(Z64_Panel):
+ bl_idname = "Z64_PT_tools"
+ bl_label = "Tools"
def draw(self, context):
- col = self.layout.column()
+ col = self.layout.row(align=True)
col.operator(OOT_AddWaterBox.bl_idname)
col.operator(OOT_AddDoor.bl_idname)
+
+ col = self.layout.row(align=True)
col.operator(OOT_AddScene.bl_idname)
col.operator(OOT_AddRoom.bl_idname)
+
+ col = self.layout.row(align=True)
col.operator(OOT_AddCutscene.bl_idname)
col.operator(OOT_AddPath.bl_idname)
+
+ col = self.layout.row(align=True)
col.operator(OOTClearTransformAndLock.bl_idname)
col.operator(OOTQuickImport.bl_idname)
+ col = self.layout.row(align=True)
+ col.operator(Z64_AddActorCutscenes.bl_idname)
+
oot_operator_panel_classes = [
OoT_ToolsPanel,
@@ -41,6 +51,7 @@ def draw(self, context):
OOT_AddPath,
OOTClearTransformAndLock,
OOTQuickImport,
+ Z64_AddActorCutscenes,
]
diff --git a/fast64_internal/oot/tools/quick_import.py b/fast64_internal/z64/tools/quick_import.py
similarity index 100%
rename from fast64_internal/oot/tools/quick_import.py
rename to fast64_internal/z64/tools/quick_import.py
diff --git a/fast64_internal/oot/oot_upgrade.py b/fast64_internal/z64/upgrade.py
similarity index 90%
rename from fast64_internal/oot/oot_upgrade.py
rename to fast64_internal/z64/upgrade.py
index d575615c0..fd680634a 100644
--- a/fast64_internal/oot/oot_upgrade.py
+++ b/fast64_internal/z64/upgrade.py
@@ -5,9 +5,9 @@
import bpy
from bpy.types import Object, CollectionProperty
from ..utility import PluginError
-from .data import OoT_ObjectData
-from .oot_utility import getEvalParams, get_actor_prop_from_obj
-from .oot_constants import ootData
+from ..data import Z64_ObjectData
+from ..game_data import game_data
+from .utility import getEvalParams, get_actor_prop_from_obj
from .cutscene.constants import ootEnumCSMotionCamMode
if TYPE_CHECKING:
@@ -17,7 +17,7 @@
#####################################
# Room Header
#####################################
-def upgradeObjectList(objList: CollectionProperty, objData: OoT_ObjectData):
+def upgradeObjectList(objList: CollectionProperty, objData: Z64_ObjectData):
"""Transition to the XML object system"""
for obj in objList:
# In order to check whether the data in the current blend needs to be updated,
@@ -31,12 +31,12 @@ def upgradeObjectList(objList: CollectionProperty, objData: OoT_ObjectData):
if objectID == "Custom":
obj.objectKey = objectID
else:
- obj.objectKey = objData.objectsByID[objectID].key
+ obj.objectKey = objData.objects_by_id[objectID].key
del obj["objectID"]
-def upgradeRoomHeaders(roomObj: Object, objData: OoT_ObjectData):
+def upgradeRoomHeaders(roomObj: Object, objData: Z64_ObjectData):
"""Main upgrade logic for room headers"""
altHeaders = roomObj.ootAlternateRoomHeaders
for sceneLayer in [
@@ -124,8 +124,8 @@ def convertOldDataToEnumData(data, oldDataToEnumData: list[Cutscene_UpgradeData]
raise NotImplementedError
# if the value is in the list find the identifier
- if value < len(csUpgradeData.enumData):
- setattr(data, csUpgradeData.newPropName, csUpgradeData.enumData[value][0])
+ if value < len(csUpgradeData.enums):
+ setattr(data, csUpgradeData.newPropName, csUpgradeData.enums[value][0])
isUpgraded = True
else:
# else raise an error to default to custom
@@ -150,6 +150,8 @@ def upgradeCutsceneSubProps(csListSubProp):
# ``csListSubProp`` types: OOTCSTextProperty | OOTCSSeqProperty | OOTCSMiscProperty | OOTCSRumbleProperty
# based on ``upgradeObjectList``
+ game_data.z64.update(bpy.context, None)
+
subPropsOldToNew = {
# TextBox
"messageId": "textID",
@@ -180,12 +182,12 @@ def upgradeCutsceneSubProps(csListSubProp):
subPropsToEnum = [
# TextBox
- Cutscene_UpgradeData("ocarinaSongAction", "ocarinaAction", ootData.enumData.ootEnumOcarinaSongActionId),
- Cutscene_UpgradeData("type", "csTextType", ootData.enumData.ootEnumCsTextType),
+ Cutscene_UpgradeData("ocarinaSongAction", "ocarinaAction", game_data.z64.enums.enum_ocarina_song_action_id),
+ Cutscene_UpgradeData("type", "csTextType", game_data.z64.enums.enum_cs_text_type),
# Seq
- Cutscene_UpgradeData("value", "csSeqID", ootData.enumData.ootEnumSeqId),
+ Cutscene_UpgradeData("value", "csSeqID", game_data.z64.enums.enum_seq_id),
# Misc
- Cutscene_UpgradeData("operation", "csMiscType", ootData.enumData.ootEnumCsMiscType),
+ Cutscene_UpgradeData("operation", "csMiscType", game_data.z64.enums.enum_cs_misc_type),
]
transferOldDataToNew(csListSubProp, subPropsOldToNew)
@@ -195,6 +197,8 @@ def upgradeCutsceneSubProps(csListSubProp):
def upgradeCSListProps(csListProp):
# ``csListProp`` type: ``OOTCSListProperty``
+ game_data.z64.update(bpy.context, None)
+
csListPropOldToNew = {
"textbox": "textList",
"lighting": "lightSettingsList",
@@ -208,13 +212,10 @@ def upgradeCSListProps(csListProp):
transferOldDataToNew(csListProp, csListPropOldToNew)
- # both are enums but the item list is different (the old one doesn't have a "custom" entry)
- convertOldDataToEnumData(
- csListProp, [Cutscene_UpgradeData("fxType", "transitionType", ootData.enumData.ootEnumCsTransitionType)]
- )
-
def upgradeCutsceneProperty(csProp: "OOTCutsceneProperty"):
+ game_data.z64.update(bpy.context, None)
+
csPropOldToNew = {
"csWriteTerminator": "csUseDestination",
"csTermStart": "csDestinationStartFrame",
@@ -223,13 +224,14 @@ def upgradeCutsceneProperty(csProp: "OOTCutsceneProperty"):
transferOldDataToNew(csProp, csPropOldToNew)
convertOldDataToEnumData(
- csProp, [Cutscene_UpgradeData("csTermIdx", "csDestination", ootData.enumData.ootEnumCsDestination)]
+ csProp, [Cutscene_UpgradeData("csTermIdx", "csDestination", game_data.z64.enums.enum_cs_destination)]
)
def upgradeCutsceneMotion(csMotionObj: Object):
"""Main upgrade logic for Cutscene Motion data from zcamedit"""
objName = csMotionObj.name
+ game_data.z64.update(None, "OOT", True)
if csMotionObj.type == "EMPTY":
csMotionProp = csMotionObj.ootCSMotionProperty
@@ -242,8 +244,8 @@ def upgradeCutsceneMotion(csMotionObj: Object):
if "actor_id" in legacyData:
index = legacyData["actor_id"]
if index >= 0:
- cmdEnum = ootData.enumData.enumByKey["csCmd"]
- cmdType = cmdEnum.itemByIndex.get(index)
+ cmdEnum = game_data.z64.enums.enumByKey["cs_cmd"]
+ cmdType = cmdEnum.item_by_index.get(index)
if cmdType is not None:
csMotionProp.actorCueListProp.commandType = cmdType.key
else:
@@ -263,10 +265,10 @@ def upgradeCutsceneMotion(csMotionObj: Object):
del legacyData["start_frame"]
if "action_id" in legacyData:
- playerEnum = ootData.enumData.enumByKey["csPlayerCueId"]
+ playerEnum = game_data.z64.enums.enumByKey["cs_player_cue_id"]
item = None
if isPlayer:
- item = playerEnum.itemByIndex.get(int(legacyData["action_id"], base=16))
+ item = playerEnum.item_by_index.get(int(legacyData["action_id"], base=16))
if isPlayer and item is not None:
csMotionProp.actorCueProp.playerCueID = item.key
@@ -302,11 +304,15 @@ def upgradeCutsceneMotion(csMotionObj: Object):
camShotPointProp.shotPointRoll = bone["camroll"]
del bone["camroll"]
+ game_data.z64.update(bpy.context, None, True)
+
#####################################
# Actors
#####################################
def upgradeActors(actorObj: Object):
+ game_data.z64.update(bpy.context, None)
+
# parameters
actorProp = get_actor_prop_from_obj(actorObj)
isCustom = False
@@ -315,7 +321,7 @@ def upgradeActors(actorObj: Object):
isCustom = actorObj.ootEntranceProperty.customActor
else:
if "actorID" in actorProp:
- actorProp.actor_id = ootData.actorData.ootEnumActorID[actorProp["actorID"]][0]
+ actorProp.actor_id = game_data.z64.actors.ootEnumActorID[actorProp["actorID"]][0]
del actorProp["actorID"]
if "actorIDCustom" in actorProp:
diff --git a/fast64_internal/oot/oot_utility.py b/fast64_internal/z64/utility.py
similarity index 86%
rename from fast64_internal/oot/oot_utility.py
rename to fast64_internal/z64/utility.py
index 1f8bc4a32..a2930b9e5 100644
--- a/fast64_internal/oot/oot_utility.py
+++ b/fast64_internal/z64/utility.py
@@ -3,12 +3,16 @@
import os
import re
+from dataclasses import dataclass
from ast import parse, Expression, Constant, UnaryOp, USub, Invert, BinOp
from mathutils import Vector
from bpy.types import Object
from typing import Callable, Optional, TYPE_CHECKING, List
-from .oot_constants import ootSceneIDToName
-from dataclasses import dataclass
+from ..game_data import game_data
+from .constants import (
+ ootSceneIDToName,
+ mm_scene_id_to_name,
+)
from ..utility import (
PluginError,
@@ -20,12 +24,17 @@
applyRotation,
cleanupDuplicatedObjects,
hexOrDecInt,
+ gammaInverse,
binOps,
)
if TYPE_CHECKING:
- from .scene.properties import OOTBootupSceneOptions
- from .actor.properties import OOTActorProperty
+ from .scene.properties import OOT_BootupSceneOptions, Z64_SceneHeaderProperty
+ from .actor.properties import Z64_ActorProperty
+
+
+def is_oot_features():
+ return game_data.z64.is_oot() and not bpy.context.scene.fast64.oot.mm_features
def isPathObject(obj: bpy.types.Object) -> bool:
@@ -171,11 +180,16 @@ def isPathObject(obj: bpy.types.Object) -> bool:
}
-def sceneNameFromID(sceneID):
- if sceneID in ootSceneIDToName:
- return ootSceneIDToName[sceneID]
+def sceneNameFromID(scene_id: str):
+ if game_data.z64.is_oot():
+ scene_id_to_name = ootSceneIDToName
else:
- raise PluginError("Cannot find scene ID " + str(sceneID))
+ scene_id_to_name = mm_scene_id_to_name
+
+ if scene_id in scene_id_to_name:
+ return scene_id_to_name[scene_id]
+ else:
+ raise PluginError("Cannot find scene ID " + str(scene_id))
def getOOTScale(actorScale: float) -> float:
@@ -248,9 +262,14 @@ def addIncludeFilesExtension(objectName, objectPath, assetName, extension):
def getSceneDirFromLevelName(name: str, include_extracted: bool = False):
extracted = bpy.context.scene.fast64.oot.get_extracted_path() if include_extracted else "."
- for sceneDir, dirLevels in ootSceneDirs.items():
- if name in dirLevels:
- return f"{extracted}/" + sceneDir + name
+
+ if game_data.z64.is_oot():
+ for sceneDir, dirLevels in ootSceneDirs.items():
+ if name in dirLevels:
+ return f"{extracted}/" + sceneDir + name
+ else:
+ return f"{extracted}/assets/scenes/{name}"
+
return None
@@ -290,7 +309,7 @@ class ExportInfo:
useMacros: bool
""" Whether to use macros or numeric/binary representations of certain values."""
- hackerootBootOption: "OOTBootupSceneOptions"
+ hackerootBootOption: "OOT_BootupSceneOptions"
""" Options for setting the bootup scene in HackerOoT."""
@@ -568,7 +587,9 @@ def __init__(self, position, scale, emptyScale):
self.cullDepth = abs(int(round(scale[0] * emptyScale)))
-def setCustomProperty(data: any, prop: str, value: str, enumList: list[tuple[str, str, str]] | None):
+def setCustomProperty(
+ data: any, prop: str, value: str, enumList: list[tuple[str, str, str]] | None, custom_name: Optional[str] = None
+):
if enumList is not None:
if value in [enumItem[0] for enumItem in enumList]:
setattr(data, prop, value)
@@ -584,22 +605,32 @@ def setCustomProperty(data: any, prop: str, value: str, enumList: list[tuple[str
pass
setattr(data, prop, "Custom")
- setattr(data, prop + str("Custom"), value)
+ setattr(data, custom_name if custom_name is not None else f"{prop}Custom", value)
-def getCustomProperty(data, prop):
+def getCustomProperty(data, prop, custom_prop_name: Optional[str] = None):
+ # TODO: cleanup prop names and remove `custom_prop_name`
value = getattr(data, prop)
- return value if value != "Custom" else getattr(data, prop + str("Custom"))
+
+ if value != "Custom":
+ return value
+ elif custom_prop_name is not None:
+ return getattr(data, custom_prop_name)
+
+ return getattr(data, prop + str("Custom"))
-def convertIntTo2sComplement(value, length, signed):
+def convertIntTo2sComplement(value: int, length: int, signed: bool):
return int.from_bytes(int(round(value)).to_bytes(length, "big", signed=signed), "big")
-def drawEnumWithCustom(panel, data, attribute, name, customName):
+def drawEnumWithCustom(panel, data, attribute, name, customName, custom_prop_name: Optional[str] = None):
+ # TODO: cleanup prop names and remove `custom_prop_name`
prop_split(panel, data, attribute, name)
if getattr(data, attribute) == "Custom":
- prop_split(panel, data, attribute + "Custom", customName)
+ if custom_prop_name is None:
+ custom_prop_name = attribute + "Custom"
+ prop_split(panel, data, custom_prop_name, customName)
def getEnumName(enumItems, value):
@@ -649,9 +680,9 @@ def getHeaderSettings(actorObj: bpy.types.Object):
if itemType == "Actor":
headerSettings = actorObj.ootActorProperty.headerSettings
elif itemType == "Entrance":
- headerSettings = actorObj.ootEntranceProperty.actor.headerSettings
- elif itemType == "Transition Actor":
headerSettings = actorObj.ootTransitionActorProperty.actor.headerSettings
+ elif itemType == "Transition Actor":
+ headerSettings = actorObj.ootEntranceProperty.actor.headerSettings
else:
headerSettings = None
elif isPathObject(actorObj):
@@ -665,6 +696,7 @@ def getHeaderSettings(actorObj: bpy.types.Object):
def getActiveHeaderIndex() -> int:
# All scenes/rooms should have synchronized tabs from property callbacks
headerObjs = [obj for obj in bpy.data.objects if obj.ootEmptyType == "Scene" or obj.ootEmptyType == "Room"]
+
if len(headerObjs) == 0:
return 0
@@ -679,21 +711,27 @@ def getActiveHeaderIndex() -> int:
if header.menuTab != "Alternate":
headerIndex = 0
else:
- if altHeader.headerMenuTab == "Child Night":
- headerIndex = 1
- elif altHeader.headerMenuTab == "Adult Day":
- headerIndex = 2
- elif altHeader.headerMenuTab == "Adult Night":
- headerIndex = 3
+ if game_data.z64.is_oot():
+ if altHeader.headerMenuTab == "Child Night":
+ headerIndex = 1
+ elif altHeader.headerMenuTab == "Adult Day":
+ headerIndex = 2
+ elif altHeader.headerMenuTab == "Adult Night":
+ headerIndex = 3
+ else:
+ headerIndex = altHeader.currentCutsceneIndex
else:
headerIndex = altHeader.currentCutsceneIndex
- return (
- headerIndex,
- altHeader.childNightHeader.usePreviousHeader,
- altHeader.adultDayHeader.usePreviousHeader,
- altHeader.adultNightHeader.usePreviousHeader,
- )
+ if game_data.z64.is_oot():
+ return (
+ headerIndex,
+ altHeader.childNightHeader.usePreviousHeader,
+ altHeader.adultDayHeader.usePreviousHeader,
+ altHeader.adultNightHeader.usePreviousHeader,
+ )
+ else:
+ return headerIndex, None, None, None
def setAllActorsVisibility(self, context: bpy.types.Context):
@@ -709,24 +747,37 @@ def setAllActorsVisibility(self, context: bpy.types.Context):
setActorVisibility(actorObj, activeHeaderInfo)
-def setActorVisibility(actorObj: bpy.types.Object, activeHeaderInfo: tuple[int, bool, bool, bool]):
+def setActorVisibility(
+ actorObj: bpy.types.Object, activeHeaderInfo: tuple[int, Optional[bool], Optional[bool], Optional[bool]]
+):
headerIndex, childNightHeader, adultDayHeader, adultNightHeader = activeHeaderInfo
- usePreviousHeader = [False, childNightHeader, adultDayHeader, adultNightHeader]
- if headerIndex < 4:
- while usePreviousHeader[headerIndex]:
- headerIndex -= 1
+
+ if game_data.z64.is_oot():
+ usePreviousHeader = [False, childNightHeader, adultDayHeader, adultNightHeader]
+
+ if headerIndex < 4:
+ while usePreviousHeader[headerIndex]:
+ headerIndex -= 1
headerSettings = getHeaderSettings(actorObj)
+
if headerSettings is None:
return
- if headerSettings.sceneSetupPreset == "All Scene Setups":
- actorObj.hide_set(False)
- elif headerSettings.sceneSetupPreset == "All Non-Cutscene Scene Setups":
- actorObj.hide_set(headerIndex >= 4)
- elif headerSettings.sceneSetupPreset == "Custom":
- actorObj.hide_set(not headerSettings.checkHeader(headerIndex))
+
+ if game_data.z64.is_oot():
+ if headerSettings.sceneSetupPreset == "All Scene Setups":
+ actorObj.hide_set(False)
+ elif headerSettings.sceneSetupPreset == "All Non-Cutscene Scene Setups":
+ actorObj.hide_set(headerIndex >= 4)
+ elif headerSettings.sceneSetupPreset == "Custom":
+ actorObj.hide_set(not headerSettings.checkHeader(headerIndex))
+ else:
+ print("Error: unhandled header case")
else:
- print("Error: unhandled header case")
+ if headerSettings.include_in_all_setups:
+ actorObj.hide_set(False)
+ else:
+ actorObj.hide_set(not headerSettings.checkHeader(headerIndex))
def onMenuTabChange(self, context: bpy.types.Context):
@@ -962,9 +1013,27 @@ def getObjectList(
return ret
-def get_actor_prop_from_obj(actor_obj: Object) -> "OOTActorProperty":
+def get_list_tab_text(base_text: str, list_length: int):
+ if list_length > 0:
+ items_amount = f"{list_length} Item{'s' if list_length > 1 else ''}"
+ else:
+ items_amount = "Empty"
+
+ return f"{base_text} ({items_amount})"
+
+
+def get_new_empty_object(name: str):
+ new_obj = bpy.data.objects.new(name, None)
+ bpy.context.scene.collection.objects.link(new_obj)
+ new_obj.location = [0.0, 0.0, 0.0]
+ new_obj.rotation_euler = [0.0, 0.0, 0.0]
+ new_obj.scale = [1.0, 1.0, 1.0]
+ return new_obj
+
+
+def get_actor_prop_from_obj(actor_obj: Object) -> "Z64_ActorProperty":
"""
- Returns the reference to `OOTActorProperty`
+ Returns the reference to `Z64_ActorProperty`
Parameters:
- `actor_obj`: the Blender object to use to find the actor properties
@@ -982,3 +1051,15 @@ def get_actor_prop_from_obj(actor_obj: Object) -> "OOTActorProperty":
raise PluginError(f"ERROR: Empty type not supported: {actor_obj.ootEmptyType}")
return actor_prop
+
+
+# from https://stackoverflow.com/a/6727975
+def twos_complement(hexstr: str, bits: int):
+ value = int(hexstr, 16)
+ if value & (1 << (bits - 1)):
+ value -= 1 << bits
+ return value
+
+
+def parseColor(values: tuple[str, str, str]) -> tuple[float, float, float]:
+ return tuple(gammaInverse([hexOrDecInt(value) / 0xFF for value in values]))