rusteal 0.9.0

Rust bindings for Unreal Engine 5.8.2
use bindings::core_ue::Object;
use bindings::engine::GameplayStatics;
use bindings::prelude::*;
use rusteal_runtime::runtime::{CommandContext, RustealResult, UObjectRef, console};

use crate::component::{{ plugin }}Component;
use crate::item::{{ plugin }}Item;
use crate::subsystem::{{ plugin }}Subsystem;

fn player_inventory(world: UObjectRef<World>) -> RustealResult<{{ plugin }}Component> {
    let world = world.upcast_to::<Object>();
    let pawn = GameplayStatics::get_player_pawn(world, 0).upcast_to();
    let mut subsystem = {{ plugin }}Subsystem::get(world)?;
    {{ plugin }}Component::from_obj(subsystem.inventory_of(pawn, true))
}

fn load_item(path: &str) -> RustealResult<UObjectRef<{{ plugin }}Item>> {
    let full = match path.rsplit('/').next() {
        Some(name) if !name.contains('.') => format!("{path}.{name}"),
        _ => path.to_string(),
    };
    load_object::<{{ plugin }}Item>(&full)
}

fn item_name(item: UObjectRef<{{ plugin }}Item>) -> String {
    {{ plugin }}Item::from_obj(item)
        .map(|i| i.display_name())
        .ok()
        .filter(|name| !name.is_empty())
        .or_else(|| item.get_name().ok())
        .unwrap_or_default()
}

fn give_or_take(args: &[&str], context: &CommandContext, give: bool) -> RustealResult<()> {
    let path = args.first().copied().unwrap_or_default();
    let count = args.get(1).and_then(|c| c.parse().ok()).unwrap_or(1);
    let item = load_item(path)?;
    let mut inventory = player_inventory(context.world()?)?;

    let moved = if give {
        inventory.add_item(item, count)
    } else {
        inventory.remove_item(item, count)
    };

    let verb = if give { "added" } else { "removed" };

    log::info!(
        "[{{ plugin }}] {verb} {moved} {}: {} now",
        item_name(item),
        inventory.count_of(item)
    );

    Ok(())
}

fn startup() {
    let prefix = "{{ crate_name | replace(from="-", to="_") }}";

    let commands: [(&str, &str, fn(&[&str], &CommandContext) -> RustealResult<()>); 3] = [
        (
            "give",
            "Give the player an item: give <item path> [count]",
            |args, context| give_or_take(args, context, true),
        ),
        (
            "take",
            "Take an item from the player: take <item path> [count]",
            |args, context| give_or_take(args, context, false),
        ),
        ("list", "List the player's items", |_, context| {
            let inventory = player_inventory(context.world()?)?;
            let stacks = inventory.stacks()?;

            log::info!(
                "[{{ plugin }}] {} stacks, weight {}",
                stacks.len(),
                inventory.total_weight()
            );

            for (item, count) in stacks {
                log::info!("[{{ plugin }}]   {} x{count}", item_name(item));
            }

            Ok(())
        }),
    ];

    for (name, help, run) in commands {
        let full = format!("{prefix}.{name}");
        let label = full.clone();

        let result = console::register_command(&full, help, move |args, context| {
            if let Err(e) = run(args, context) {
                log::error!("[{{ plugin }}] {label}: {e:?}");
            }
        });

        if let Err(e) = result {
            log::error!("[{{ plugin }}] {full}: {e:?}");
        }
    }
}
rusteal_runtime::on_load!(startup);