diff --git a/.claude/settings.local.json b/.claude/settings.local.json index aa40627..48969ef 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -14,7 +14,10 @@ "Bash(grep:*)", "Bash(wc:*)", "Bash(pgrep:*)", - "Bash(cargo doc:*)" + "Bash(cargo doc:*)", + "Bash(xargs dirname:*)", + "Bash(xargs -I {} find {} -name \"*.cs\")", + "Bash(RUST_LOG=debug cargo run:*)" ], "additionalDirectories": [ "/home/connor/repos/CBAssets/" diff --git a/cursebreaker-parser/src/main.rs b/cursebreaker-parser/src/main.rs index 6d6927b..e6322f5 100644 --- a/cursebreaker-parser/src/main.rs +++ b/cursebreaker-parser/src/main.rs @@ -52,15 +52,15 @@ fn main() -> Result<(), Box> { log::logger().flush(); scene.world - .query_all::<(&InteractableResource, &unity_parser::Transform, &unity_parser::GameObject)>() + .query_all::<(&InteractableResource, &unity_parser::WorldTransform, &unity_parser::GameObject)>() .for_each(|(resource, transform, object)| { info!(" 📦 Resource: \"{}\"", object.name); info!(" • typeId: {}", resource.type_id); - info!(" • maxHealth: {}", resource.max_health); // Extract world position from WorldTransform - let world_pos = transform.local_position; - info!(" • Local Position: ({:.2}, {:.2}, {:.2})", world_pos.x, world_pos.y, world_pos.z); + let world_pos = transform.position(); + info!(" • Position: ({:.2}, {:.2}, {:.2})", world_pos.x, world_pos.y, world_pos.z); + log::logger().flush(); }); log::logger().flush(); diff --git a/unity-parser/src/types/unity_types/prefab_instance.rs b/unity-parser/src/types/unity_types/prefab_instance.rs index 4ac75f2..6efdecb 100644 --- a/unity-parser/src/types/unity_types/prefab_instance.rs +++ b/unity-parser/src/types/unity_types/prefab_instance.rs @@ -383,6 +383,10 @@ pub struct PrefabModification { /// The FileID of the target object within the nested prefab pub target_file_id: FileID, + /// Optional GUID for cross-file references + /// When present, indicates the target is in a different prefab file + pub target_guid: Option, + /// The property path to modify (dot notation) pub property_path: String, @@ -392,9 +396,25 @@ pub struct PrefabModification { /// Parse modifications array from Unity YAML fn parse_modifications(yaml: &Mapping) -> Option> { - let mods_array = yaml - .get(&Value::String("m_Modification".to_string())) - .and_then(|v| v.as_sequence())?; + // First check if m_Modification exists + let modification_value = yaml.get(&Value::String("m_Modification".to_string())); + + if modification_value.is_none() { + return Some(Vec::new()); + } + + // Try to get the m_Modifications.m_Modifications nested structure + let mods_array = if let Some(mod_obj) = modification_value.and_then(|v| v.as_mapping()) { + // Unity sometimes nests it as m_Modification: { m_Modifications: [...] } + mod_obj + .get(&Value::String("m_Modifications".to_string())) + .and_then(|v| v.as_sequence()) + } else { + // Or it might be directly an array: m_Modification: [...] + modification_value.and_then(|v| v.as_sequence()) + }; + + let mods_array = mods_array?; let mut mods = Vec::new(); for mod_yaml in mods_array { @@ -412,12 +432,20 @@ fn parse_modifications(yaml: &Mapping) -> Option> { /// Parse a single modification entry fn parse_single_modification(yaml: &Mapping) -> Option { - // Get target FileID + // Get target FileID and optional GUID let target = yaml .get(&Value::String("target".to_string())) .and_then(|v| v.as_mapping())?; + let target_file_id = yaml_helpers::get_file_ref_from_mapping(target)?.file_id; + // Extract GUID if present (for cross-file references) + // Format: {fileID: N, guid: "...", type: 3} + let target_guid = target + .get(&Value::String("guid".to_string())) + .and_then(|v| v.as_str()) + .map(|s| s.to_string()); + // Get property path let property_path = yaml .get(&Value::String("propertyPath".to_string())) @@ -431,6 +459,7 @@ fn parse_single_modification(yaml: &Mapping) -> Option { Some(PrefabModification { target_file_id, + target_guid, property_path, value, }) @@ -542,12 +571,21 @@ impl<'a> PrefabResolver<'a> { let mut instance = prefab.instantiate(Some(self.file_id_counter.clone())); // 4. Apply component.modifications using override_value() + // Only apply modifications that target this prefab (matching GUID or no GUID) for modification in &component.modifications { - instance.override_value( - modification.target_file_id, - &modification.property_path, - modification.value.clone(), - )?; + // Check if this modification targets this prefab + let should_apply = match &modification.target_guid { + Some(target_guid) => target_guid == guid, + None => true, // No GUID means local reference, apply it + }; + + if should_apply { + instance.override_value( + modification.target_file_id, + &modification.property_path, + modification.value.clone(), + )?; + } } // 5. Spawn the instance @@ -633,12 +671,21 @@ impl<'a> PrefabResolver<'a> { if let Ok(nested_prefab) = self.load_prefab(&nested_component.prefab_ref.guid) { // Apply modifications with shared FileID counter let mut nested_instance = nested_prefab.instantiate(Some(self.file_id_counter.clone())); + let nested_guid = &nested_component.prefab_ref.guid; for modification in &nested_component.modifications { - nested_instance.override_value( - modification.target_file_id, - &modification.property_path, - modification.value.clone(), - )?; + // Only apply modifications that target this nested prefab + let should_apply = match &modification.target_guid { + Some(target_guid) => target_guid == nested_guid, + None => true, // No GUID means local reference, apply it + }; + + if should_apply { + nested_instance.override_value( + modification.target_file_id, + &modification.property_path, + modification.value.clone(), + )?; + } } // Recursively spawn nested prefab