diff --git a/schema/randomprime.schema.json b/schema/randomprime.schema.json index c28d2302..d867ce36 100644 --- a/schema/randomprime.schema.json +++ b/schema/randomprime.schema.json @@ -3103,7 +3103,8 @@ } }, "cameraHints": { - "description": "Add camera hint + camera hint trigger pairs to this room. They are somewhat broken.", + "deprecated": true, + "description": "[Deprecated] Add camera hint + camera hint trigger pairs to this room. They are somewhat broken. Use `newCameraHints` instead.", "type": "array", "items": { "type": "object", @@ -4967,6 +4968,340 @@ ], "additionalProperties": false } + }, + "newCameraHints": { + "description": "Add CameraHint objects to this room. They are used to manipulate the Morph Ball camera in various ways. Recommended to use in conjunction with `cameraHintTriggers`", + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "$ref": "#/$defs/addModifyId" + }, + "layer": { + "$ref": "#/$defs/addModifyLayer" + }, + "position": { + "description": "The position of the CameraHint.", + "$ref": "#/$defs/vector3" + }, + "rotation": { + "description": "The rotation of the CameraHint.", + "$ref": "#/$defs/vector3" + }, + "active": { + "description": "Default active state of the CameraHint.", + "type": "boolean", + "default": true + }, + "priority": { + "type": "integer", + "default": 10 + }, + "behaviour": { + "type": "string", + "enum": [ + "Default", + "FreezeLookPosition", + "HintBallToCam", + "HintInitializePosition", + "HintFixedPosition", + "HintFixedTransform", + "PathCameraDesiredPos", + "PathCamera", + "SpindleCamera" + ], + "default": "Default" + }, + "calculateCamPos": { + "type": "boolean", + "default": false + }, + "chaseAllowed": { + "type": "boolean", + "default": false + }, + "boostAllowed": { + "type": "boolean", + "default": false + }, + "obscureAvoidance": { + "type": "boolean", + "default": false + }, + "volumeCollider": { + "type": "boolean", + "default": false + }, + "applyImmediately": { + "type": "boolean", + "default": false + }, + "lookAtBall": { + "type": "boolean", + "default": false + }, + "hintDistanceSelection": { + "type": "boolean", + "default": false + }, + "hintDistanceSelfPos": { + "type": "boolean", + "default": false + }, + "sinusoidalInterpolation": { + "type": "boolean", + "default": false + }, + "sinusoidalInterpolationHintless": { + "type": "boolean", + "default": false + }, + "clampVelocity": { + "type": "boolean", + "default": false + }, + "skipCinematic": { + "type": "boolean", + "default": false + }, + "noElevationInterp": { + "type": "boolean", + "default": false + }, + "overrideLookDir": { + "type": "boolean", + "default": false + }, + "noElevationVelClamp": { + "type": "boolean", + "default": false + }, + "calculateTransformFromPrevCam": { + "type": "boolean", + "default": false + }, + "noSpline": { + "type": "boolean", + "default": false + }, + "unknown1": { + "type": "boolean", + "default": false + }, + "unknown2": { + "type": "boolean", + "default": false + }, + "overrideMinDist": { + "type": "boolean", + "default": false + }, + "minDist": { + "description": "Requires `overrideMinDist` to be true to work.", + "type": "number", + "minimum": 0.0, + "default": 8.0 + }, + "overrideMaxDist": { + "type": "boolean", + "default": false + }, + "maxDist": { + "description": "Requires `overrideMaxDist` to be true to work.", + "type": "number", + "minimum": 0.0, + "default": 8.0 + }, + "overrideBackwardsDist": { + "type": "boolean", + "default": false + }, + "backwardsDist": { + "description": "Requires `overrideBackwardsDist` to be true to work.", + "type": "number", + "minimum": 0.0, + "default": 8.0 + }, + "overrideLookAtOffset": { + "type": "boolean", + "default": false + }, + "lookAtOffset": { + "description": "Requires `overrideLookAtOffset` to be true to work.", + "$ref": "#/$defs/vector3", + "default": [ + 0.0, + 0.0, + 0.0 + ] + }, + "overrideChaseLookAtOffset": { + "type": "boolean", + "default": false + }, + "chaseLookAtOffset": { + "description": "Requires `overrideChaseLookAtOffset` to be true to work.", + "$ref": "#/$defs/vector3", + "default": [ + 0.0, + 0.0, + 0.0 + ] + }, + "ballToCam": { + "$ref": "#/$defs/vector3", + "default": [ + 0.0, + 0.0, + 0.0 + ] + }, + "overrideFov": { + "type": "boolean", + "default": false + }, + "fov": { + "description": "Requires `overrideFov` to be true to work. 55.0 = Morph Ball Camera Fov", + "type": "number", + "minimum": 0.0, + "default": 55.0 + }, + "overrideAttitudeRange": { + "type": "boolean", + "default": false + }, + "attitudeRange": { + "description": "Requires `overrideAttitudeRange` to be true to work", + "type": "number", + "minimum": 0.0, + "default": 90.0 + }, + "overrideAzimuthRange": { + "type": "boolean", + "default": false + }, + "azimuthRange": { + "description": "Requires `overrideAzimuthRange` to be true to work", + "type": "number", + "minimum": 0.0, + "default": 90.0 + }, + "overrideAnglePerSecond": { + "type": "boolean", + "default": false + }, + "anglePerSecond": { + "description": "Requires `overrideAnglePerSecond` to be true to work", + "type": "number", + "minimum": 0.0, + "default": 120.0 + }, + "clampVelRange": { + "type": "number", + "minimum": 0.0, + "default": 10.0 + }, + "clampRotRange": { + "type": "number", + "minimum": 0.0, + "default": 120.0 + }, + "overrideElevation": { + "type": "boolean", + "default": false + }, + "elevation": { + "description": "Requires `overrideElevation` to be true to work", + "type": "number", + "minimum": 0.0, + "default": 2.7 + }, + "interpolateTime": { + "type": "number", + "minimum": 0.0, + "default": 1.5 + }, + "clampVelTime": { + "type": "number", + "minimum": 0.0, + "default": 2.0 + }, + "controlInterpDur": { + "type": "number", + "minimum": 0.0, + "default": 1.0 + } + }, + "required": [ + "id", + "position", + "rotation", + "behaviour" + ], + "additionalProperties": false + } + }, + "cameraHintTriggers": { + "description": "Add CameraHintTrigger objects to this room. They behave like regular triggers but they can be rotated and have less properties", + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "$ref": "#/$defs/addModifyId" + }, + "layer": { + "$ref": "#/$defs/addModifyLayer" + }, + "active": { + "description": "Default activate state of the trigger", + "type": "boolean", + "default": true + }, + "position": { + "description": "Position of the trigger.", + "$ref": "#/$defs/vector3", + "default": [ + 0.0, + 0.0, + 0.0 + ] + }, + "rotation": { + "description": "Rotation of the trigger.", + "$ref": "#/$defs/vector3", + "default": [ + 0.0, + 0.0, + 0.0 + ] + }, + "scale": { + "description": "Extent of the trigger.", + "$ref": "#/$defs/vector3Positive", + "default": [ + 5.0, + 5.0, + 5.0 + ] + }, + "deactivateOnEnter": { + "description": "Disable this trigger when entered.", + "type": "boolean", + "default": false + }, + "deactivateOnExit": { + "description": "Disable this trigger when exited.", + "type": "boolean", + "default": false + } + }, + "required": [ + "id" + ], + "additionalProperties": false + } } }, "additionalProperties": false diff --git a/src/add_modify_obj_patches.rs b/src/add_modify_obj_patches.rs index e96a8a25..3a6af6ae 100644 --- a/src/add_modify_obj_patches.rs +++ b/src/add_modify_obj_patches.rs @@ -9,9 +9,9 @@ use crate::{ mlvl_wrapper, patch_config::{ ActorKeyFrameConfig, ActorRotateConfig, BlockConfig, BombSlotConfig, CameraConfig, - CameraFilterKeyframeConfig, CameraWaypointConfig, ControllerActionConfig, CounterConfig, - DamageType, FogConfig, GenericTexture, HudmemoConfig, LockOnPoint, PlatformConfig, - PlatformType, PlayerActorConfig, PlayerHintConfig, RelayConfig, SpawnPointConfig, + CameraHintTriggerConfig, CameraFilterKeyframeConfig, CameraWaypointConfig, ControllerActionConfig, + CounterConfig, DamageType, FogConfig, GenericTexture, HudmemoConfig, LockOnPoint, NewCameraHintConfig, + PlatformConfig, PlatformType, PlayerActorConfig, PlayerHintConfig, RelayConfig, SpawnPointConfig, SpecialFunctionConfig, StreamedAudioConfig, SwitchConfig, TimerConfig, TriggerConfig, WaterConfig, WaypointConfig, WorldLightFaderConfig, }, @@ -1937,6 +1937,339 @@ pub fn patch_add_camera_filter_keyframe( ); } +#[allow(clippy::too_many_arguments)] +pub fn patch_add_new_camera_hint( + _ps: &mut PatcherState, + area: &mut mlvl_wrapper::MlvlArea, + config: NewCameraHintConfig, +) -> Result<(), String> { + macro_rules! new { + () => { + structs::CameraHint { + name: b"my camerahint\0".as_cstr(), + position: config.position.unwrap_or([0.0, 0.0, 0.0]).into(), + rotation: config.rotation.unwrap_or([0.0, 0.0, 0.0]).into(), + active: config.active.unwrap_or(true) as u8, + priority: config.priority.unwrap_or(10) as u32, + behavior: config.behaviour as u32, + + camera_hint_params: structs::scly_props::structs::CameraHintParameters { + calculate_cam_pos: config.calculate_cam_pos.unwrap_or(false) as u8, + chase_allowed: config.chase_allowed.unwrap_or(false) as u8, + boost_allowed: config.boost_allowed.unwrap_or(false) as u8, + obscure_avoidance: config.obscure_avoidance.unwrap_or(false) as u8, + volume_collider: config.volume_collider.unwrap_or(false) as u8, + apply_immediately: config.apply_immediately.unwrap_or(false) as u8, + look_at_ball: config.look_at_ball.unwrap_or(false) as u8, + hint_distance_selection: config.hint_distance_selection.unwrap_or(false) as u8, + hint_distance_self_pos: config.hint_distance_self_pos.unwrap_or(false) as u8, + control_interpolation: config.control_interpolation.unwrap_or(false) as u8, + sinusoidal_interpolation: config.sinusoidal_interpolation.unwrap_or(false) as u8, + sinusoidal_interpolation_hintless: config.sinusoidal_interpolation_hintless.unwrap_or(false) as u8, + clamp_velocity: config.clamp_velocity.unwrap_or(false) as u8, + skip_cinematic: config.skip_cinematic.unwrap_or(false) as u8, + no_elevation_interp: config.no_elevation_interp.unwrap_or(false) as u8, + direct_elevation: config.direct_elevation.unwrap_or(false) as u8, + override_look_dir: config.override_look_dir.unwrap_or(false) as u8, + no_elevation_vel_clamp: config.no_elevation_vel_clamp.unwrap_or(false) as u8, + calculate_transform_from_prev_cam: config.calculate_transform_from_prev_cam.unwrap_or(false) as u8, + no_spline: config.no_spline.unwrap_or(false) as u8, + unknown21: config.unknown1.unwrap_or(false) as u8, + unknown22: config.unknown2.unwrap_or(false) as u8, + } + .into(), + + min_dist: structs::scly_props::structs::BoolFloat { + override_flags: config.override_min_dist.unwrap_or(false) as u8, + value: config.min_dist.unwrap_or(8.0) as f32, + } + .into(), + max_dist: structs::scly_props::structs::BoolFloat { + override_flags: config.override_max_dist.unwrap_or(false) as u8, + value: config.max_dist.unwrap_or(8.0) as f32, + } + .into(), + backwards_dist: structs::scly_props::structs::BoolFloat { + override_flags: config.override_backwards_dist.unwrap_or(false) as u8, + value: config.backwards_dist.unwrap_or(8.0) as f32, + } + .into(), + + look_at_offset: structs::scly_props::structs::BoolVec3 { + override_flags: config.override_look_at_offset.unwrap_or(false) as u8, + value: config.look_at_offset.unwrap_or([0.0, 0.0, 0.0]).into(), + } + .into(), + chase_look_at_offset: structs::scly_props::structs::BoolVec3 { + override_flags: config.override_chase_look_at_offset.unwrap_or(false) as u8, + value: config.chase_look_at_offset.unwrap_or([0.0, 0.0, 0.0]).into(), + } + .into(), + + ball_to_cam: config.ball_to_cam.unwrap_or([0.0, 0.0, 0.0]).into(), + + fov: structs::scly_props::structs::BoolFloat { + override_flags: config.override_fov.unwrap_or(false) as u8, + value: config.fov.unwrap_or(55.0) as f32, + } + .into(), + + attitude_range: structs::scly_props::structs::BoolFloat { + override_flags: config.override_attitude_range.unwrap_or(false) as u8, + value: config.attitude_range.unwrap_or(90.0) as f32, + } + .into(), + + azimuth_range: structs::scly_props::structs::BoolFloat { + override_flags: config.override_azimuth_range.unwrap_or(false) as u8, + value: config.azimuth_range.unwrap_or(90.0) as f32, + } + .into(), + + angle_per_second: structs::scly_props::structs::BoolFloat { + override_flags: config.override_angle_per_second.unwrap_or(false) as u8, + value: config.angle_per_second.unwrap_or(120.0) as f32, + } + .into(), + + clamp_vel_range: config.clamp_vel_range.unwrap_or(10.0) as f32, + clamp_rot_range: config.clamp_rot_range.unwrap_or(120.0) as f32, + + elevation: structs::scly_props::structs::BoolFloat { + override_flags: config.override_elevation.unwrap_or(false) as u8, + value: config.elevation.unwrap_or(2.7) as f32, + } + .into(), + + interpolate_time: config.interpolate_time.unwrap_or(1.5) as f32, + clamp_vel_time: config.clamp_vel_time.unwrap_or(2.0) as f32, + control_interp_dur: config.control_interp_dur.unwrap_or(1.0) as f32, + } + }; + } + + macro_rules! update { + ($obj:expr) => { + let property_data = $obj.property_data.as_camera_hint_mut().unwrap(); + + property_data.behavior = config.behaviour as u32; + + if let Some(position) = config.position { + property_data.position = position.into() + } + if let Some(rotation) = config.rotation { + property_data.rotation = rotation.into() + } + if let Some(active) = config.active { + property_data.active = active as u8 + } + if let Some(priority) = config.priority { + property_data.priority = priority as u32 + } + if let Some(calculate_cam_pos) = config.calculate_cam_pos { + property_data.camera_hint_params.calculate_cam_pos = calculate_cam_pos as u8 + } + if let Some(chase_allowed) = config.chase_allowed { + property_data.camera_hint_params.chase_allowed = chase_allowed as u8 + } + if let Some(boost_allowed) = config.boost_allowed { + property_data.camera_hint_params.boost_allowed = boost_allowed as u8 + } + if let Some(obscure_avoidance) = config.obscure_avoidance { + property_data.camera_hint_params.obscure_avoidance = obscure_avoidance as u8 + } + if let Some(volume_collider) = config.volume_collider { + property_data.camera_hint_params.volume_collider = volume_collider as u8 + } + if let Some(apply_immediately) = config.apply_immediately { + property_data.camera_hint_params.apply_immediately = apply_immediately as u8 + } + if let Some(look_at_ball) = config.look_at_ball { + property_data.camera_hint_params.look_at_ball = look_at_ball as u8 + } + if let Some(hint_distance_selection) = config.hint_distance_selection { + property_data.camera_hint_params.hint_distance_selection = hint_distance_selection as u8 + } + if let Some(hint_distance_self_pos) = config.hint_distance_self_pos { + property_data.camera_hint_params.hint_distance_self_pos = hint_distance_self_pos as u8 + } + if let Some(control_interpolation) = config.control_interpolation { + property_data.camera_hint_params.control_interpolation = control_interpolation as u8 + } + if let Some(sinusoidal_interpolation) = config.sinusoidal_interpolation { + property_data.camera_hint_params.sinusoidal_interpolation = sinusoidal_interpolation as u8 + } + if let Some(sinusoidal_interpolation_hintless) = config.sinusoidal_interpolation_hintless { + property_data.camera_hint_params.sinusoidal_interpolation_hintless = sinusoidal_interpolation_hintless as u8 + } + if let Some(clamp_velocity) = config.clamp_velocity { + property_data.camera_hint_params.clamp_velocity = clamp_velocity as u8 + } + if let Some(skip_cinematic) = config.skip_cinematic { + property_data.camera_hint_params.skip_cinematic = skip_cinematic as u8 + } + if let Some(no_elevation_interp) = config.no_elevation_interp { + property_data.camera_hint_params.no_elevation_interp = no_elevation_interp as u8 + } + if let Some(direct_elevation) = config.direct_elevation { + property_data.camera_hint_params.direct_elevation = direct_elevation as u8 + } + if let Some(override_look_dir) = config.override_look_dir { + property_data.camera_hint_params.override_look_dir = override_look_dir as u8 + } + if let Some(no_elevation_vel_clamp) = config.no_elevation_vel_clamp { + property_data.camera_hint_params.no_elevation_vel_clamp = no_elevation_vel_clamp as u8 + } + if let Some(calculate_transform_from_prev_cam) = config.calculate_transform_from_prev_cam { + property_data.camera_hint_params.calculate_transform_from_prev_cam = calculate_transform_from_prev_cam as u8 + } + if let Some(no_spline) = config.no_spline { + property_data.camera_hint_params.no_spline = no_spline as u8 + } + if let Some(unknown1) = config.unknown1 { + property_data.camera_hint_params.unknown21 = unknown1 as u8 + } + if let Some(unknown2) = config.unknown2 { + property_data.camera_hint_params.unknown22 = unknown2 as u8 + } + if let Some(override_min_dist) = config.override_min_dist { + property_data.min_dist.override_flags = override_min_dist as u8 + } + if let Some(min_dist) = config.min_dist { + property_data.min_dist.value = min_dist as f32 + } + if let Some(override_max_dist) = config.override_max_dist { + property_data.max_dist.override_flags = override_max_dist as u8 + } + if let Some(max_dist) = config.max_dist { + property_data.max_dist.value = max_dist as f32 + } + if let Some(override_backwards_dist) = config.override_backwards_dist { + property_data.backwards_dist.override_flags = override_backwards_dist as u8 + } + if let Some(backwards_dist) = config.backwards_dist { + property_data.backwards_dist.value = backwards_dist as f32 + } + if let Some(override_look_at_offset) = config.override_look_at_offset { + property_data.look_at_offset.override_flags = override_look_at_offset as u8 + } + if let Some(look_at_offset) = config.look_at_offset { + property_data.look_at_offset.value = look_at_offset.into() + } + if let Some(override_chase_look_at_offset) = config.override_chase_look_at_offset { + property_data.chase_look_at_offset.override_flags = override_chase_look_at_offset as u8 + } + if let Some(chase_look_at_offset) = config.chase_look_at_offset { + property_data.chase_look_at_offset.value = chase_look_at_offset.into() + } + if let Some(ball_to_cam) = config.ball_to_cam { + property_data.ball_to_cam = ball_to_cam.into() + } + if let Some(override_fov) = config.override_fov { + property_data.fov.override_flags = override_fov as u8 + } + if let Some(fov) = config.fov { + property_data.fov.value = fov as f32 + } + if let Some(override_attitude_range) = config.override_attitude_range { + property_data.attitude_range.override_flags = override_attitude_range as u8 + } + if let Some(attitude_range) = config.attitude_range { + property_data.attitude_range.value = attitude_range as f32 + } + if let Some(override_azimuth_range) = config.override_azimuth_range { + property_data.azimuth_range.override_flags = override_azimuth_range as u8 + } + if let Some(azimuth_range) = config.azimuth_range { + property_data.azimuth_range.value = azimuth_range as f32 + } + if let Some(override_angle_per_second) = config.override_angle_per_second { + property_data.angle_per_second.override_flags = override_angle_per_second as u8 + } + if let Some(angle_per_second) = config.angle_per_second { + property_data.angle_per_second.value = angle_per_second as f32 + } + if let Some(clamp_vel_range) = config.clamp_vel_range { + property_data.clamp_vel_range = clamp_vel_range as f32 + } + if let Some(clamp_rot_range) = config.clamp_rot_range { + property_data.clamp_rot_range = clamp_rot_range as f32 + } + if let Some(override_elevation) = config.override_elevation { + property_data.elevation.override_flags = override_elevation as u8 + } + if let Some(elevation) = config.elevation { + property_data.elevation.value = elevation as f32 + } + if let Some(interpolate_time) = config.interpolate_time { + property_data.interpolate_time = interpolate_time as f32 + } + if let Some(clamp_vel_time) = config.clamp_vel_time { + property_data.clamp_vel_time = clamp_vel_time as f32 + } + if let Some(control_interp_dur) = config.control_interp_dur { + property_data.control_interp_dur = control_interp_dur as f32 + } + }; + } + + add_edit_obj_helper!( + area, + Some(config.id), + config.layer, + CameraHint, + new, + update + ); +} + +pub fn patch_add_camera_hint_trigger( + _ps: &mut PatcherState, + area: &mut mlvl_wrapper::MlvlArea, + config: CameraHintTriggerConfig, +) -> Result<(), String> { + macro_rules! new { + () => { + structs::CameraHintTrigger { + name: b"my camerahinttrigger\0".as_cstr(), + position: config.position.unwrap_or([0.0, 0.0, 0.0]).into(), + rotation: config.rotation.unwrap_or([0.0, 0.0, 0.0]).into(), + scale: config.scale.unwrap_or([5.0, 5.0, 5.0]).into(), + active: config.active.unwrap_or(true) as u8, + deactivate_on_enter: config.deactivate_on_enter.unwrap_or(false) as u8, + deactivate_on_exit: config.deactivate_on_exit.unwrap_or(false) as u8, + } + }; + } + + macro_rules! update { + ($obj:expr) => { + let property_data = $obj.property_data.as_camera_hint_trigger_mut().unwrap(); + + if let Some(position) = config.position { + property_data.position = position.into() + } + if let Some(rotation) = config.rotation { + property_data.rotation = rotation.into() + } + if let Some(scale) = config.scale { + property_data.scale = scale.into() + } + if let Some(active) = config.active { + property_data.active = active as u8 + } + if let Some(deactivate_on_enter) = config.deactivate_on_enter { + property_data.deactivate_on_enter = deactivate_on_enter as u8 + } + if let Some(deactivate_on_exit) = config.deactivate_on_exit { + property_data.deactivate_on_exit = deactivate_on_exit as u8 + } + }; + } + + add_edit_obj_helper!(area, config.id, config.layer, CameraHintTrigger, new, update); +} + pub fn patch_add_platform<'r>( _ps: &mut PatcherState, area: &mut mlvl_wrapper::MlvlArea<'r, '_, '_, '_>, @@ -3134,7 +3467,7 @@ pub fn add_camera_hint<'r>( active: 1, priority: 8, behavior, - camera_hint_params: structs::CameraHintParameters { + camera_hint_params: structs::scly_props::structs::CameraHintParameters { calculate_cam_pos: 0, chase_allowed: 0, boost_allowed: 0, @@ -3158,47 +3491,47 @@ pub fn add_camera_hint<'r>( unknown21: 0, unknown22: 0, }, - min_dist: structs::BoolFloat { - active: 0, + min_dist: structs::scly_props::structs::BoolFloat { + override_flags: 0, value: 8.0, }, - max_dist: structs::BoolFloat { - active: 0, + max_dist: structs::scly_props::structs::BoolFloat { + override_flags: 0, value: 50.0, }, - backwards_dist: structs::BoolFloat { - active: 0, + backwards_dist: structs::scly_props::structs::BoolFloat { + override_flags: 0, value: 8.0, }, - look_at_offset: structs::BoolVec3 { - active: 0, + look_at_offset: structs::scly_props::structs::BoolVec3 { + override_flags: 0, value: [0.0, 1.0, 1.0].into(), }, - chase_look_at_offset: structs::BoolVec3 { - active: 0, + chase_look_at_offset: structs::scly_props::structs::BoolVec3 { + override_flags: 0, value: [0.0, 1.0, 1.0].into(), }, ball_to_cam: [3.0, 3.0, 3.0].into(), - fov: structs::BoolFloat { - active: 0, + fov: structs::scly_props::structs::BoolFloat { + override_flags: 0, value: 55.0, }, - attitude_range: structs::BoolFloat { - active: 0, + attitude_range: structs::scly_props::structs::BoolFloat { + override_flags: 0, value: 90.0, }, - azimuth_range: structs::BoolFloat { - active: 0, + azimuth_range: structs::scly_props::structs::BoolFloat { + override_flags: 0, value: 90.0, }, - angle_per_second: structs::BoolFloat { - active: 0, + angle_per_second: structs::scly_props::structs::BoolFloat { + override_flags: 0, value: 120.0, }, clamp_vel_range: 10.0, clamp_rot_range: 120.0, - elevation: structs::BoolFloat { - active: 0, + elevation: structs::scly_props::structs::BoolFloat { + override_flags: 0, value: 2.7, }, interpolate_time: 1.0, diff --git a/src/patch_config.rs b/src/patch_config.rs index 4b122bb5..2be4b7ab 100644 --- a/src/patch_config.rs +++ b/src/patch_config.rs @@ -832,6 +832,94 @@ pub struct CameraFilterKeyframeConfig { pub overlay_texture: Option, } +#[derive(Serialize, Deserialize, Debug, Copy, Clone, Eq, PartialEq)] +#[serde(deny_unknown_fields)] +pub enum EBallCameraBehaviour { + Default, + FreezeLookPosition, // Unused + HintBallToCam, + HintInitializePosition, + HintFixedPosition, + HintFixedTransform, + PathCameraDesiredPos, // Unused + PathCamera, + SpindleCamera +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +pub struct NewCameraHintConfig { + pub id: u32, + pub layer: Option, + pub position: Option<[f32; 3]>, + pub rotation: Option<[f32; 3]>, + pub active: Option, + pub priority: Option, + pub behaviour: EBallCameraBehaviour, + pub calculate_cam_pos: Option, + pub chase_allowed: Option, + pub boost_allowed: Option, + pub obscure_avoidance: Option, + pub volume_collider: Option, + pub apply_immediately: Option, + pub look_at_ball: Option, + pub hint_distance_selection: Option, + pub hint_distance_self_pos: Option, + pub control_interpolation: Option, + pub sinusoidal_interpolation: Option, + pub sinusoidal_interpolation_hintless: Option, + pub clamp_velocity: Option, + pub skip_cinematic: Option, + pub no_elevation_interp: Option, + pub direct_elevation: Option, + pub override_look_dir: Option, + pub no_elevation_vel_clamp: Option, + pub calculate_transform_from_prev_cam: Option, + pub no_spline: Option, + pub unknown1: Option, + pub unknown2: Option, + pub override_min_dist: Option, + pub min_dist: Option, + pub override_max_dist: Option, + pub max_dist: Option, + pub override_backwards_dist: Option, + pub backwards_dist: Option, + pub override_look_at_offset: Option, + pub look_at_offset: Option<[f32; 3]>, + pub override_chase_look_at_offset: Option, + pub chase_look_at_offset: Option<[f32; 3]>, + pub ball_to_cam: Option<[f32; 3]>, + pub override_fov: Option, + pub fov: Option, + pub override_attitude_range: Option, + pub attitude_range: Option, + pub override_azimuth_range: Option, + pub azimuth_range: Option, + pub override_angle_per_second: Option, + pub angle_per_second: Option, + pub clamp_vel_range: Option, + pub clamp_rot_range: Option, + pub override_elevation: Option, + pub elevation: Option, + pub interpolate_time: Option, + pub clamp_vel_time: Option, + pub control_interp_dur: Option, +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +pub struct CameraHintTriggerConfig { + pub id: Option, + pub layer: Option, + pub active: Option, + pub position: Option<[f32; 3]>, + pub rotation: Option<[f32; 3]>, + pub scale: Option<[f32; 3]>, + pub deactivate_on_enter: Option, + pub deactivate_on_exit: Option, +} + + #[allow(non_camel_case_types)] #[derive(Debug, Serialize, Deserialize, Copy, Clone, Eq, PartialEq)] #[repr(u32)] @@ -1047,6 +1135,8 @@ pub struct RoomConfig { pub cameras: Option>, pub camera_waypoints: Option>, pub camera_filter_keyframes: Option>, + pub new_camera_hints: Option>, + pub camera_hint_triggers: Option>, // Don't forget to update merge_json when adding here } @@ -1948,6 +2038,8 @@ impl PatchConfigPrivate { extend_option_vec!(cameras, self_room_config, other_room_config); extend_option_vec!(camera_waypoints, self_room_config, other_room_config); extend_option_vec!(camera_filter_keyframes, self_room_config, other_room_config); + extend_option_vec!(new_camera_hints, self_room_config, other_room_config); + extend_option_vec!(camera_hint_triggers, self_room_config, other_room_config); if let Some(other_layers) = &other_room_config.layers { if self_room_config.layers.is_none() { diff --git a/src/patches.rs b/src/patches.rs index 94d98cd6..339af817 100644 --- a/src/patches.rs +++ b/src/patches.rs @@ -16641,8 +16641,7 @@ fn build_and_run_patches<'r>( } } - if let Some(camera_filter_keyframes) = room.camera_filter_keyframes.as_ref() - { + if let Some(camera_filter_keyframes) = room.camera_filter_keyframes.as_ref() { for config in camera_filter_keyframes { patcher.add_scly_patch( (pak_name.as_bytes(), room_info.room_id.to_u32()), @@ -16653,6 +16652,29 @@ fn build_and_run_patches<'r>( } } + if let Some(new_camera_hints) = room.new_camera_hints.as_ref() { + for config in new_camera_hints { + patcher.add_scly_patch( + (pak_name.as_bytes(), room_info.room_id.to_u32()), + move |ps, area| { + patch_add_new_camera_hint(ps, area, config.clone()) + }, + ); + } + } + + if let Some(camera_hint_triggers) = room.camera_hint_triggers.as_ref() { + for config in camera_hint_triggers { + patcher.add_scly_patch( + (pak_name.as_bytes(), room_info.room_id.to_u32()), + move |ps, area| { + patch_add_camera_hint_trigger(ps, area, config.clone()) + }, + ); + } + } + + if room.streamed_audios.is_some() { for config in room.streamed_audios.as_ref().unwrap() { patcher.add_scly_patch( diff --git a/structs/src/scly.rs b/structs/src/scly.rs index 3893f81b..93a193f5 100644 --- a/structs/src/scly.rs +++ b/structs/src/scly.rs @@ -496,10 +496,6 @@ macro_rules! build_scly_property { pub fn guess_kind(&mut self) { - if self.object_type() == 0x10 { // camera hint (TODO) - return; - } - let (mut reader, object_type) = match *self { SclyProperty::Unknown { ref data, object_type } => (data.clone(), object_type), diff --git a/structs/src/scly_props/camera_hint.rs b/structs/src/scly_props/camera_hint.rs index 37c85303..2ff80c40 100644 --- a/structs/src/scly_props/camera_hint.rs +++ b/structs/src/scly_props/camera_hint.rs @@ -1,55 +1,15 @@ use auto_struct_macros::auto_struct; use reader_writer::{generic_array::GenericArray, typenum::U3, CStr}; -use crate::SclyPropertyData; - -#[auto_struct(Readable, Writable, FixedSize)] -#[derive(Debug, Clone)] -pub struct CameraHintParameters { - #[auto_struct(expect = 15)] - prop_count: u32, - pub calculate_cam_pos: u8, - pub chase_allowed: u8, - pub boost_allowed: u8, - pub obscure_avoidance: u8, - pub volume_collider: u8, - pub apply_immediately: u8, - pub look_at_ball: u8, - pub hint_distance_selection: u8, - pub hint_distance_self_pos: u8, - pub control_interpolation: u8, - pub sinusoidal_interpolation: u8, - pub sinusoidal_interpolation_hintless: u8, - pub clamp_velocity: u8, - pub skip_cinematic: u8, - pub no_elevation_interp: u8, - pub direct_elevation: u8, - pub override_look_dir: u8, - pub no_elevation_vel_clamp: u8, - pub calculate_transform_from_prev_cam: u8, - pub no_spline: u8, - pub unknown21: u8, - pub unknown22: u8, -} - -#[auto_struct(Readable, Writable, FixedSize)] -#[derive(Debug, Clone)] -pub struct BoolFloat { - pub active: u8, - pub value: f32, -} - -#[auto_struct(Readable, Writable, FixedSize)] -#[derive(Debug, Clone)] -pub struct BoolVec3 { - pub active: u8, - pub value: GenericArray, -} +use crate::{ + scly_props::structs::{CameraHintParameters, BoolFloat, BoolVec3}, + SclyPropertyData, +}; #[auto_struct(Readable, Writable)] #[derive(Debug, Clone)] pub struct CameraHint<'r> { - #[auto_struct(expect = 9)] + #[auto_struct(expect = 23)] prop_count: u32, pub name: CStr<'r>, @@ -58,34 +18,28 @@ pub struct CameraHint<'r> { pub active: u8, pub priority: u32, pub behavior: u32, - pub camera_hint_params: CameraHintParameters, - pub min_dist: BoolFloat, pub max_dist: BoolFloat, pub backwards_dist: BoolFloat, - pub look_at_offset: BoolVec3, pub chase_look_at_offset: BoolVec3, - pub ball_to_cam: GenericArray, - pub fov: BoolFloat, pub attitude_range: BoolFloat, pub azimuth_range: BoolFloat, pub angle_per_second: BoolFloat, - pub clamp_vel_range: f32, pub clamp_rot_range: f32, - pub elevation: BoolFloat, - pub interpolate_time: f32, pub clamp_vel_time: f32, pub control_interp_dur: f32, } -// cannot implement position/rotation until the size of this struct is corrected +use crate::{impl_position, impl_rotation}; impl<'r> SclyPropertyData for CameraHint<'r> { const OBJECT_TYPE: u8 = 0x10; + impl_position!(); + impl_rotation!(); } diff --git a/structs/src/scly_props/structs.rs b/structs/src/scly_props/structs.rs index 843d2353..b9a01af1 100644 --- a/structs/src/scly_props/structs.rs +++ b/structs/src/scly_props/structs.rs @@ -302,3 +302,46 @@ pub struct CameraShakePoint { pub duration: f32, pub magnitude: f32, } + +#[auto_struct(Readable, Writable, FixedSize)] +#[derive(Debug, Clone)] +pub struct CameraHintParameters { + #[auto_struct(expect = 22)] + prop_count: u32, + pub calculate_cam_pos: u8, + pub chase_allowed: u8, + pub boost_allowed: u8, + pub obscure_avoidance: u8, + pub volume_collider: u8, + pub apply_immediately: u8, + pub look_at_ball: u8, + pub hint_distance_selection: u8, + pub hint_distance_self_pos: u8, + pub control_interpolation: u8, + pub sinusoidal_interpolation: u8, + pub sinusoidal_interpolation_hintless: u8, + pub clamp_velocity: u8, + pub skip_cinematic: u8, + pub no_elevation_interp: u8, + pub direct_elevation: u8, + pub override_look_dir: u8, + pub no_elevation_vel_clamp: u8, + pub calculate_transform_from_prev_cam: u8, + pub no_spline: u8, + pub unknown21: u8, + pub unknown22: u8, +} + +#[auto_struct(Readable, Writable, FixedSize)] +#[derive(Debug, Clone)] +pub struct BoolFloat { + pub override_flags: u8, + pub value: f32, +} + +#[auto_struct(Readable, Writable, FixedSize)] +#[derive(Debug, Clone)] +pub struct BoolVec3 { + pub override_flags: u8, + pub value: GenericArray, +}