//! Parse Cursebreaker Resources from 10_3.unity Scene //! //! This example demonstrates: //! 1. Parsing the Cursebreaker Unity project //! 2. Finding Interactable_Resource components //! 3. Extracting typeId and transform positions //! 4. Writing resource data to an output file use unity_parser::{UnityComponent, UnityFile}; use std::fs::File; use std::io::Write; use std::path::Path; /// Interactable_Resource component from Cursebreaker /// /// C# definition from Interactable_Resource.cs: /// ```csharp /// public class Interactable_Resource : Interactable /// { /// public int health; /// public int maxHealth; /// public int typeId; /// // ... other fields /// } /// ``` #[derive(Debug, Clone, UnityComponent)] #[unity_class("Interactable_Resource")] pub struct InteractableResource { #[unity_field("maxHealth")] pub max_health: i64, #[unity_field("typeId")] pub type_id: i64, } fn main() -> Result<(), Box> { println!("🎮 Cursebreaker - Resource Parser"); println!("{}", "=".repeat(70)); println!(); let scene_path = Path::new("/home/connor/repos/CBAssets/_GameAssets/Scenes/Tiles/10_3.unity"); // Check if scene exists if !scene_path.exists() { eprintln!("❌ Error: Scene not found at {}", scene_path.display()); return Err("Scene file not found".into()); } println!("📁 Parsing scene: {}", scene_path.display()); println!(); // Parse the scene match UnityFile::from_path(&scene_path) { Ok(UnityFile::Scene(scene)) => { println!("✅ Scene parsed successfully!"); println!(" Total entities: {}", scene.entity_map.len()); println!(); // Get views for component types we need let resource_view = scene.world.borrow::(); let transform_view = scene.world.borrow::(); let gameobject_view = scene.world.borrow::(); // Find all entities that have Interactable_Resource let mut found_resources = Vec::new(); for entity in scene.entity_map.values() { if let Some(resource) = resource_view.get(*entity) { let transform = transform_view.get(*entity); let game_object = gameobject_view.get(*entity); let name = game_object .and_then(|go| go.name()) .unwrap_or("(unnamed)"); let position = transform .and_then(|t| t.local_position()) .map(|p| (p.x, p.y, p.z)); found_resources.push((name.to_string(), resource.clone(), position)); } } println!("🔍 Found {} Interactable_Resource component(s)", found_resources.len()); println!(); if !found_resources.is_empty() { // Display resources in console for (name, resource, position) in &found_resources { println!(" 📦 Resource: \"{}\"", name); println!(" • typeId: {}", resource.type_id); println!(" • maxHealth: {}", resource.max_health); if let Some((x, y, z)) = position { println!(" • Position: ({:.2}, {:.2}, {:.2})", x, y, z); } else { println!(" • Position: (no transform)"); } println!(); } // Write to output file let output_path = "resources_output.txt"; let mut output_file = File::create(output_path)?; writeln!(output_file, "Cursebreaker Resources - 10_3.unity Scene")?; writeln!(output_file, "{}", "=".repeat(70))?; writeln!(output_file)?; writeln!(output_file, "Total resources found: {}", found_resources.len())?; writeln!(output_file)?; writeln!(output_file, "{}", "-".repeat(70))?; writeln!(output_file)?; for (name, resource, position) in &found_resources { writeln!(output_file, "Resource: {}", name)?; writeln!(output_file, " TypeID: {}", resource.type_id)?; writeln!(output_file, " MaxHealth: {}", resource.max_health)?; if let Some((x, y, z)) = position { writeln!(output_file, " Position: ({:.6}, {:.6}, {:.6})", x, y, z)?; } else { writeln!(output_file, " Position: N/A")?; } writeln!(output_file)?; } writeln!(output_file, "{}", "=".repeat(70))?; writeln!(output_file, "End of resource data")?; println!("📝 Resource data written to: {}", output_path); println!(); } println!("{}", "=".repeat(70)); println!("✅ Parsing complete!"); println!("{}", "=".repeat(70)); } Ok(_) => { eprintln!("❌ Error: File is not a scene"); return Err("Not a Unity scene file".into()); } Err(e) => { eprintln!("❌ Parse error: {}", e); return Err(Box::new(e)); } } Ok(()) }