split project

This commit is contained in:
2026-01-04 13:59:47 +00:00
parent 8a06185b98
commit 29813a46ef
10 changed files with 220 additions and 8 deletions

View File

@@ -0,0 +1,10 @@
[package]
name = "cursebreaker-parser"
version = "0.1.0"
edition = "2021"
[dependencies]
unity-parser = { path = "../unity-parser" }
serde_yaml = "0.9"
inventory = "0.3"
sparsey = "0.13"

View File

@@ -0,0 +1,128 @@
//! 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
mod types;
use types::InteractableResource;
use unity_parser::UnityFile;
use std::fs::File;
use std::io::Write;
use std::path::Path;
fn main() -> Result<(), Box<dyn std::error::Error>> {
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::<InteractableResource>();
let transform_view = scene.world.borrow::<unity_parser::Transform>();
let gameobject_view = scene.world.borrow::<unity_parser::GameObject>();
// 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(())
}

View File

@@ -0,0 +1,47 @@
/// 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
/// }
/// ```
use unity_parser::{UnityComponent, ComponentContext, EcsInsertable};
use serde_yaml::Mapping;
#[derive(Debug, Clone)]
pub struct InteractableResource {
pub max_health: i64,
pub type_id: i64,
}
impl UnityComponent for InteractableResource {
fn parse(yaml: &Mapping, _ctx: &ComponentContext) -> Option<Self> {
Some(Self {
max_health: unity_parser::yaml_helpers::get_i64(yaml, "maxHealth").unwrap_or(0),
type_id: unity_parser::yaml_helpers::get_i64(yaml, "typeId").unwrap_or(0),
})
}
}
impl EcsInsertable for InteractableResource {
fn insert_into_world(self, world: &mut sparsey::World, entity: sparsey::Entity) {
world.insert(entity, (self,));
}
}
// Register component with inventory
inventory::submit! {
unity_parser::ComponentRegistration {
type_id: 114,
class_name: "Interactable_Resource",
parse_and_insert: |yaml, ctx, world, entity| {
<InteractableResource as EcsInsertable>::parse_and_insert(yaml, ctx, world, entity)
},
register: |builder| builder.register::<InteractableResource>(),
}
}

View File

@@ -0,0 +1,3 @@
mod interactable_resource;
pub use interactable_resource::InteractableResource;