rusteal 0.9.0

Rust bindings for Unreal Engine 5.8.2
use bindings::engine::ActorComponent;
use rusteal_runtime::runtime::{RustealResult, UObjectRef, UeArray};
use rusteal_runtime::{uclass, uclass_impl};

use crate::item::{{ plugin }}Item;
use crate::settings::{{ plugin }}Settings;

#[uclass(parent = ActorComponent)]
pub struct {{ plugin }}Component {
    #[uproperty(VisibleAnywhere, BlueprintReadOnly, category = "{{ plugin }}")]
    items: UeArray<UObjectRef<{{ plugin }}Item>>,
    #[uproperty(VisibleAnywhere, BlueprintReadOnly, category = "{{ plugin }}")]
    counts: UeArray<i32>,
}

#[uclass_impl]
impl {{ plugin }}Component {
    #[udelegate]
    pub fn on_inventory_changed(&self, item: UObjectRef<{{ plugin }}Item>, count: i32) {}

    #[ufunction]
    pub fn add_item(&mut self, item: UObjectRef<{{ plugin }}Item>, count: i32) -> i32 {
        let added = self.try_add(item, count).unwrap_or(0);
        if added > 0 {
            self.on_inventory_changed(item, self.count_of(item));
        }
        added
    }

    #[ufunction]
    pub fn remove_item(&mut self, item: UObjectRef<{{ plugin }}Item>, count: i32) -> i32 {
        let removed = self.try_remove(item, count).unwrap_or(0);
        if removed > 0 {
            self.on_inventory_changed(item, self.count_of(item));
        }
        removed
    }

    #[ufunction(BlueprintPure)]
    pub fn count_of(&self, item: UObjectRef<{{ plugin }}Item>) -> i32 {
        self.stacks()
            .map(|stacks| {
                stacks
                    .iter()
                    .filter(|(i, _)| *i == item)
                    .map(|(_, c)| c)
                    .sum()
            })
            .unwrap_or(0)
    }

    #[ufunction(BlueprintPure)]
    pub fn total_weight(&self) -> f32 {
        self.stacks()
            .map(|stacks| {
                stacks
                    .iter()
                    .map(|(item, count)| weight_of(*item) * *count as f32)
                    .sum()
            })
            .unwrap_or(0.0)
    }

    pub fn stacks(&self) -> RustealResult<Vec<(UObjectRef<{{ plugin }}Item>, i32)>> {
        let items = self.items().to_vec()?;
        let counts = self.counts().to_vec()?;
        Ok(items.into_iter().zip(counts).collect())
    }

    fn try_add(&mut self, item: UObjectRef<{{ plugin }}Item>, count: i32) -> RustealResult<i32> {
        let definition = {{ plugin }}Item::from_obj(item)?;
        let settings = {{ plugin }}Settings::get_default()?;
        let max_stack = definition.max_stack().max(1);
        let mut wanted = count.max(0);
        let weight = definition.weight();
        if settings.max_weight() > 0.0 && weight > 0.0 {
            let room = ((settings.max_weight() - self.total_weight()) / weight).floor() as i32;
            wanted = wanted.min(room.max(0));
        }

        let (items, counts) = (self.items(), self.counts());
        let mut left = wanted;
        for index in 0..items.len()? {
            if left == 0 {
                break;
            }

            let held = counts.get(index)?;

            if items.get(index)? == item && held < max_stack {
                let put = left.min(max_stack - held);
                counts.set(index, &(held + put))?;
                left -= put;
            }
        }
        while left > 0 && (items.len()? as i32) < settings.max_slots() {
            let put = left.min(max_stack);
            items.push(&item)?;
            counts.push(&put)?;
            left -= put;
        }
        Ok(wanted - left)
    }

    fn try_remove(&mut self, item: UObjectRef<{{ plugin }}Item>, count: i32) -> RustealResult<i32> {
        let (items, counts) = (self.items(), self.counts());
        let mut left = count.max(0);
        for index in (0..items.len()?).rev() {
            if left == 0 {
                break;
            }

            if items.get(index)? != item {
                continue;
            }

            let held = counts.get(index)?;
            let take = left.min(held);

            if take == held {
                items.remove(index)?;
                counts.remove(index)?;
            } else {
                counts.set(index, &(held - take))?;
            }

            left -= take;
        }
        Ok(count.max(0) - left)
    }
}

fn weight_of(item: UObjectRef<{{ plugin }}Item>) -> f32 {
    {{ plugin }}Item::from_obj(item)
        .map(|item| item.weight())
        .unwrap_or(0.0)
}