Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
130 changes: 130 additions & 0 deletions schema/randomprime.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -5600,6 +5600,136 @@
],
"additionalProperties": false
}
},
"sounds": {
"description": "Add Sounds in this room, note that certain sound IDs are exclusive to some worlds, so some sounds may not play outside of them",
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"$ref": "#/$defs/addModifyId"
},
"layer": {
"$ref": "#/$defs/addModifyLayer"
},
"position": {
"description": "Position of the sound.",
"$ref": "#/$defs/vector3",
"default": [
0.0,
0.0,
0.0
]
},
"rotation": {
"description": "Rotation of the sound.",
"$ref": "#/$defs/vector3",
"default": [
0.0,
0.0,
0.0
]
},
"soundId": {
"description": "Specify the ID of the sound.",
"type": "integer",
"default": 1867,
"minimum": -1,
"maximum": 65535
},
"active": {
"description": "Default activate state of the sound.",
"type": "boolean",
"default": true
},
"maxDist": {
"type": "number",
"default": 50.0,
"minimum": 0.0,
"maximum": 1000000.0
},
"distComp": {
"type": "number",
"default": 0.2,
"minimum": 0.0,
"maximum": 1000000.0
},
"startDelay": {
"type": "number",
"default": 0.0,
"minimum": 0.0,
"maximum": 1000000.0
},
"minVolume": {
"type": "integer",
"default": 20,
"minimum": 0,
"maximum": 127
},
"volume": {
"type": "integer",
"default": 127,
"minimum": 0,
"maximum": 127
},
"priority": {
"type": "integer",
"default": 127,
"minimum": 0,
"maximum": 127
},
"pan": {
"type": "integer",
"default": 64,
"minimum": 0
},
"loop": {
"description": "If true, the sound will play again after it's done.",
"type": "boolean",
"default": false
},
"nonEmitter": {
"description": "If true, the sound will always be audible, ignoring it's origin position.",
"type": "boolean",
"default": false
},
"autoStart": {
"description": "If true, the sound will play immediately on room load.",
"type": "boolean",
"default": false
},
"occlusionTest": {
"type": "boolean",
"default": false
},
"acoustics": {
"description": "If true, the sound will use the room's `RoomAcoustics` object properties.",
"type": "boolean",
"default": true
},
"worldSFX": {
"type": "boolean",
"default": false
},
"allowDuplicates": {
"description": "If true, the sound will be able to play multiple instances of itself.",
"type": "boolean",
"default": true
},
"pitch": {
"description": "Pitch level of the sound.",
"type": "integer",
"default": 0,
"minimum": -1000000000,
"maximum": 1000000000
}
},
"required": [
"id"
],
"additionalProperties": false
}
}
},
"additionalProperties": false
Expand Down
103 changes: 101 additions & 2 deletions src/add_modify_obj_patches.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ use crate::{
ControllerActionConfig, CounterConfig, DamageType, FogConfig, GenericTexture,
HudmemoConfig, InitialSplinePosition, LockOnPoint, NewCameraHintConfig, PathCameraConfig,
PlatformConfig, PlatformType, PlayerActorConfig, PlayerHintConfig, RelayConfig,
SpawnPointConfig, SpecialFunctionConfig, StreamedAudioConfig, SwitchConfig, TimerConfig,
TriggerConfig, WaterConfig, WaypointConfig, WorldLightFaderConfig,
SoundConfig, SpawnPointConfig, SpecialFunctionConfig, StreamedAudioConfig, SwitchConfig,
TimerConfig, TriggerConfig, WaterConfig, WaypointConfig, WorldLightFaderConfig,
},
patcher::PatcherState,
patches::{string_to_cstr, WaterType},
Expand Down Expand Up @@ -2535,6 +2535,105 @@ pub fn patch_add_path_camera(
add_edit_obj_helper!(area, config.id, config.layer, PathCamera, new, update);
}

pub fn patch_add_sound(
_ps: &mut PatcherState,
area: &mut mlvl_wrapper::MlvlArea,
config: SoundConfig,
) -> Result<(), String> {
macro_rules! new {
() => {
structs::Sound {
name: b"my sound\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(),
sound_id: config.sound_id.unwrap_or(1867) as u32,
active: config.active.unwrap_or(true) as u8,
max_dist: config.max_dist.unwrap_or(50.0) as f32,
dist_comp: config.dist_comp.unwrap_or(0.2) as f32,
start_delay: config.start_delay.unwrap_or(0.0) as f32,
min_volume: config.min_volume.unwrap_or(20) as u32,
volume: config.volume.unwrap_or(127) as u32,
priority: config.priority.unwrap_or(127) as u32,
pan: config.pan.unwrap_or(64) as u32,
loops: config.loops.unwrap_or(false) as u8,
non_emitter: config.non_emitter.unwrap_or(false) as u8,
auto_start: config.auto_start.unwrap_or(false) as u8,
occlusion_test: config.occlusion_test.unwrap_or(false) as u8,
acoustics: config.acoustics.unwrap_or(true) as u8,
world_sfx: config.world_sfx.unwrap_or(false) as u8,
allow_duplicates: config.allow_duplicates.unwrap_or(true) as u8,
pitch: config.pitch.unwrap_or(0) as u32,
}
};
}

macro_rules! update {
($obj:expr) => {
let property_data = $obj.property_data.as_sound_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(sound_id) = config.sound_id {
property_data.sound_id = sound_id as u32
}
if let Some(active) = config.active {
property_data.active = active as u8
}
if let Some(max_dist) = config.max_dist {
property_data.max_dist = max_dist as f32
}
if let Some(dist_comp) = config.dist_comp {
property_data.dist_comp = dist_comp as f32
}
if let Some(start_delay) = config.start_delay {
property_data.start_delay = start_delay as f32
}
if let Some(min_volume) = config.min_volume {
property_data.min_volume = min_volume as u32
}
if let Some(volume) = config.volume {
property_data.volume = volume as u32
}
if let Some(priority) = config.priority {
property_data.priority = priority as u32
}
if let Some(pan) = config.pan {
property_data.pan = pan as u32
}
if let Some(loops) = config.loops {
property_data.loops = loops as u8
}
if let Some(non_emitter) = config.non_emitter {
property_data.non_emitter = non_emitter as u8
}
if let Some(auto_start) = config.auto_start {
property_data.auto_start = auto_start as u8
}
if let Some(occlusion_test) = config.occlusion_test {
property_data.occlusion_test = occlusion_test as u8
}
if let Some(acoustics) = config.acoustics {
property_data.acoustics = acoustics as u8
}
if let Some(world_sfx) = config.world_sfx {
property_data.world_sfx = world_sfx as u8
}
if let Some(allow_duplicates) = config.allow_duplicates {
property_data.allow_duplicates = allow_duplicates as u8
}
if let Some(pitch) = config.pitch {
property_data.pitch = pitch as u32
}
};
}

add_edit_obj_helper!(area, config.id, config.layer, Sound, new, update);
}

pub fn patch_add_platform<'r>(
_ps: &mut PatcherState,
area: &mut mlvl_wrapper::MlvlArea<'r, '_, '_, '_>,
Expand Down
75 changes: 75 additions & 0 deletions src/custom_assets.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1367,6 +1367,81 @@ pub fn collect_game_resources<'r>(
];
looking_for.extend(custom_scan_point_deps);

let sound_deps: Vec<(u32, FourCC)> = vec![
(0xAE0230B6, FourCC::from_bytes(b"AGSC")), // Atomic
(0x8CCDD026, FourCC::from_bytes(b"AGSC")), // BetaBeetle
(0x33358ABA, FourCC::from_bytes(b"AGSC")), // Bird
(0xF3C33C5E, FourCC::from_bytes(b"AGSC")), // BloodFlower
(0x3118E452, FourCC::from_bytes(b"AGSC")), // Burrower
(0xEA6B4117, FourCC::from_bytes(b"AGSC")), // Chozo Ghost
(0xB2C49B0B, FourCC::from_bytes(b"AGSC")), // ChubbWeed
(0x62414707, FourCC::from_bytes(b"AGSC")), // CineBoots
(0x33A833DA, FourCC::from_bytes(b"AGSC")), // CineGeneral
(0x9BADC20D, FourCC::from_bytes(b"AGSC")), // CineGun
(0x7F8153FE, FourCC::from_bytes(b"AGSC")), // CineMorphball
(0x9D2A6E46, FourCC::from_bytes(b"AGSC")), // CineSuit
(0xBA8F20EA, FourCC::from_bytes(b"AGSC")), // CineVisor
(0x5F6CB3E8, FourCC::from_bytes(b"AGSC")), // Crater
(0xEE9200F4, FourCC::from_bytes(b"AGSC")), // Crystallite
(0x98B283BF, FourCC::from_bytes(b"AGSC")), // Drones
(0x170F408C, FourCC::from_bytes(b"AGSC")), // EliteSpacePirate
(0x35F15F3B, FourCC::from_bytes(b"AGSC")), // FireFlea
(0xA2E59262, FourCC::from_bytes(b"AGSC")), // Flaaghra
(0x17928B51, FourCC::from_bytes(b"AGSC")), // FlickerBat
(0xDB76A972, FourCC::from_bytes(b"AGSC")), // FlyingPirate
(0x1A4AD067, FourCC::from_bytes(b"AGSC")), // FrontEnd
(0x8389DBEC, FourCC::from_bytes(b"AGSC")), // GagantuanBeatle
(0xF6ABC1E1, FourCC::from_bytes(b"AGSC")), // Gnats
(0xE68ECFA5, FourCC::from_bytes(b"AGSC")), // Gryzbee
(0xDA07D390, FourCC::from_bytes(b"AGSC")), // IceCrack
(0x5CE7A3D0, FourCC::from_bytes(b"AGSC")), // IceWorld
(0x93393A50, FourCC::from_bytes(b"AGSC")), // InjuredPirates
(0x6DF11D78, FourCC::from_bytes(b"AGSC")), // IntroBoss
(0x16553E57, FourCC::from_bytes(b"AGSC")), // IntroWorld
(0x5FBC2F67, FourCC::from_bytes(b"AGSC")), // JellyZap
(0xB8C046C0, FourCC::from_bytes(b"AGSC")), // LavaWorld
(0x77C54E1A, FourCC::from_bytes(b"AGSC")), // lumigek
(0x706D7BCF, FourCC::from_bytes(b"AGSC")), // Magdolite
(0x2CA490BB, FourCC::from_bytes(b"AGSC")), // Metaree
(0x52AB7324, FourCC::from_bytes(b"AGSC")), // Metroid
(0xB6931363, FourCC::from_bytes(b"AGSC")), // MetroidPrime
(0xABD06377, FourCC::from_bytes(b"AGSC")), // MinesWorld
(0x57FE7E67, FourCC::from_bytes(b"AGSC")), // Misc
(0x3724095B, FourCC::from_bytes(b"AGSC")), // MiscSamus
(0xCBAE2616, FourCC::from_bytes(b"AGSC")), // OmegaPirate
(0x41475E5F, FourCC::from_bytes(b"AGSC")), // OverWorld
(0xDA7B2C8E, FourCC::from_bytes(b"AGSC")), // Parasite
(0x9D1A8F9F, FourCC::from_bytes(b"AGSC")), // Phazon
(0xC2C1B6FE, FourCC::from_bytes(b"AGSC")), // PhazonGun
(0x2163D60A, FourCC::from_bytes(b"AGSC")), // PuddleSpore
(0x48A8F172, FourCC::from_bytes(b"AGSC")), // PuddleToad
(0xD05B51C4, FourCC::from_bytes(b"AGSC")), // Puffer
(0xF5FE70FB, FourCC::from_bytes(b"AGSC")), // ReactorDoor
(0x60544DEE, FourCC::from_bytes(b"AGSC")), // Ridley
(0xD0AB8A34, FourCC::from_bytes(b"AGSC")), // Ripper
(0xFB9C30B2, FourCC::from_bytes(b"AGSC")), // RuinsWorld
(0x8F586337, FourCC::from_bytes(b"AGSC")), // SamusShip
(0x0800B2FA, FourCC::from_bytes(b"AGSC")), // Scarab
(0xF84C8E18, FourCC::from_bytes(b"AGSC")), // Seedling
(0xFFE302C7, FourCC::from_bytes(b"AGSC")), // SheeGoth
(0xE21C8BC5, FourCC::from_bytes(b"AGSC")), // SnakeWeed
(0xEA6360AF, FourCC::from_bytes(b"AGSC")), // Sova
(0xE8D4F8F1, FourCC::from_bytes(b"AGSC")), // SpacePirate
(0xC363BD5E, FourCC::from_bytes(b"AGSC")), // SpankWeed
(0x1EE1AD21, FourCC::from_bytes(b"AGSC")), // Test
(0x2ACE7B2D, FourCC::from_bytes(b"AGSC")), // Thardus
(0xD258A644, FourCC::from_bytes(b"AGSC")), // TheEnd
(0x2A3AC2AC, FourCC::from_bytes(b"AGSC")), // ToroByte
(0x7CB0AB14, FourCC::from_bytes(b"AGSC")), // Triclops
(0xBF06EDF7, FourCC::from_bytes(b"AGSC")), // Turret
(0xFC838AE4, FourCC::from_bytes(b"AGSC")), // UI
(0x0DB7A10C, FourCC::from_bytes(b"AGSC")), // WarWasp
(0x8081183E, FourCC::from_bytes(b"AGSC")), // Weapons
(0xF3B1B26C, FourCC::from_bytes(b"AGSC")), // Zoomer
(0xC50CC2AE, FourCC::from_bytes(b"AGSC")), // ZZZ
];
looking_for.extend(sound_deps);

let player_freeze_deps: Vec<(u32, FourCC)> = vec![
resource_info!("breakFreezeVisor.PART").into(),
resource_info!("Frost1TXTR.TXTR").into(),
Expand Down
29 changes: 29 additions & 0 deletions src/patch_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -978,6 +978,33 @@ pub struct PathCameraConfig {
pub max_ease_dist: Option<f32>,
}

#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct SoundConfig {
pub id: Option<u32>,
pub layer: Option<u32>,
pub position: Option<[f32; 3]>,
pub rotation: Option<[f32; 3]>,
pub sound_id: Option<u16>,
pub active: Option<bool>,
pub max_dist: Option<f32>,
pub dist_comp: Option<f32>,
pub start_delay: Option<f32>,
pub min_volume: Option<u32>,
pub volume: Option<u32>,
pub priority: Option<u32>,
pub pan: Option<u32>,
#[serde(alias = "loop")]
pub loops: Option<bool>,
pub non_emitter: Option<bool>,
pub auto_start: Option<bool>,
pub occlusion_test: Option<bool>,
pub acoustics: Option<bool>,
pub world_sfx: Option<bool>,
pub allow_duplicates: Option<bool>,
pub pitch: Option<u32>,
}

#[allow(non_camel_case_types)]
#[derive(Debug, Serialize, Deserialize, Copy, Clone, Eq, PartialEq)]
#[repr(u32)]
Expand Down Expand Up @@ -1198,6 +1225,7 @@ pub struct RoomConfig {
pub set_memory_relays: Option<Vec<u32>>,
pub ball_triggers: Option<Vec<BallTriggerConfig>>,
pub path_cameras: Option<Vec<PathCameraConfig>>,
pub sounds: Option<Vec<SoundConfig>>,
// Don't forget to update merge_json when adding here
}

Expand Down Expand Up @@ -2108,6 +2136,7 @@ impl PatchConfigPrivate {
extend_option_vec!(camera_hint_triggers, self_room_config, other_room_config);
extend_option_vec!(ball_triggers, self_room_config, other_room_config);
extend_option_vec!(path_cameras, self_room_config, other_room_config);
extend_option_vec!(sounds, self_room_config, other_room_config);

if let Some(other_layers) = &other_room_config.layers {
if self_room_config.layers.is_none() {
Expand Down
Loading
Loading