use serde::Deserialize;
#[derive(Debug, Clone, Copy, PartialEq, Deserialize)]
pub struct SlotLayout {
pub x: f32,
pub y: f32,
}
pub const SLOT_SIZE: f32 = 18.0;
pub const DEFAULT_PLAYER_INV_OFFSET: (f32, f32) = (8.0, 84.0);
pub const PLAYER_INV_TO_HOTBAR_GAP: f32 = 4.0;
#[derive(Debug, Clone)]
pub struct InventoryDef {
pub id: String,
pub slot_count: usize,
pub layout: Vec<SlotLayout>,
pub include_player_inventory: bool,
pub player_inv_offset: (f32, f32),
pub background_texture: Option<String>,
pub title: String,
pub background_size: Option<(f32, f32)>,
}
impl InventoryDef {
pub fn new(id: impl Into<String>, slot_count: usize) -> Self {
Self {
id: id.into(),
slot_count,
layout: Vec::new(),
include_player_inventory: true,
player_inv_offset: DEFAULT_PLAYER_INV_OFFSET,
background_texture: None,
title: String::new(),
background_size: None,
}
}
pub fn layout(mut self, layout: Vec<SlotLayout>) -> Self {
self.layout = layout;
self
}
pub fn layout_from_json(mut self, json: &str) -> Self {
if let Ok(parsed) = serde_json::from_str::<Vec<SlotLayout>>(json) {
self.layout = parsed;
}
self
}
pub fn include_player_inventory(mut self, v: bool) -> Self {
self.include_player_inventory = v;
self
}
pub fn player_inv_offset(mut self, x: f32, y: f32) -> Self {
self.player_inv_offset = (x, y);
self
}
pub fn background_texture(mut self, path: impl Into<String>) -> Self {
self.background_texture = Some(path.into());
self
}
pub fn background_size(mut self, w: f32, h: f32) -> Self {
self.background_size = Some((w, h));
self
}
pub fn title(mut self, title: impl Into<String>) -> Self {
self.title = title.into();
self
}
pub fn resolved_layout(&self) -> Vec<SlotLayout> {
if !self.layout.is_empty() {
return self.layout.clone();
}
Self::default_grid(self.slot_count)
}
pub fn default_grid(slot_count: usize) -> Vec<SlotLayout> {
const COLS: usize = 9;
(0..slot_count)
.map(|i| {
let col = (i % COLS) as f32;
let row = (i / COLS) as f32;
SlotLayout {
x: 8.0 + col * SLOT_SIZE,
y: 18.0 + row * SLOT_SIZE,
}
})
.collect()
}
}
pub fn encode_layout(layout: &[SlotLayout]) -> String {
layout
.iter()
.map(|s| format!("{}:{}", s.x, s.y))
.collect::<Vec<_>>()
.join(",")
}
pub fn decode_layout(s: &str) -> Vec<SlotLayout> {
if s.is_empty() {
return Vec::new();
}
s.split(',')
.filter_map(|pair| {
let (x, y) = pair.split_once(':')?;
Some(SlotLayout {
x: x.parse().ok()?,
y: y.parse().ok()?,
})
})
.collect()
}
use std::sync::atomic::{AtomicPtr, Ordering};
use yog_abi::YogStr;
pub const MAX_SNAPSHOT_SLOTS: usize = 64;
#[repr(C)]
#[derive(Copy, Clone)]
pub struct YogInvSlotRaw {
pub item_id: YogStr,
pub count: u32,
pub x: i32,
pub y: i32,
}
impl YogInvSlotRaw {
pub const EMPTY: Self = Self {
item_id: YogStr::EMPTY,
count: 0,
x: 0,
y: 0,
};
}
#[repr(C)]
#[derive(Copy, Clone)]
pub struct YogInvSnapshotRaw {
pub slot_count: u32,
pub slots: [YogInvSlotRaw; MAX_SNAPSHOT_SLOTS],
}
impl YogInvSnapshotRaw {
pub const EMPTY: Self = Self {
slot_count: 0,
slots: [YogInvSlotRaw::EMPTY; MAX_SNAPSHOT_SLOTS],
};
}
static SNAPSHOT: AtomicPtr<YogInvSnapshotRaw> = AtomicPtr::new(std::ptr::null_mut());
#[doc(hidden)]
#[no_mangle]
pub extern "C" fn yog_inventory_set_snapshot(snapshot: *const YogInvSnapshotRaw) {
SNAPSHOT.store(snapshot as *mut _, Ordering::Release);
}
#[derive(Debug, Clone, Default)]
pub struct SlotView {
pub item_id: String,
pub count: u32,
pub x: i32,
pub y: i32,
}
pub fn current_slot_count() -> usize {
let ptr = SNAPSHOT.load(Ordering::Acquire);
if ptr.is_null() {
return 0;
}
unsafe { &*ptr }.slot_count as usize
}
pub fn current_slot(index: usize) -> Option<SlotView> {
let ptr = SNAPSHOT.load(Ordering::Acquire);
if ptr.is_null() {
return None;
}
let snap = unsafe { &*ptr };
if index >= snap.slot_count as usize {
return None;
}
let raw = &snap.slots[index];
let item_id = if raw.item_id.is_empty() {
String::new()
} else {
unsafe { raw.item_id.as_str() }.to_owned()
};
Some(SlotView {
item_id,
count: raw.count,
x: raw.x,
y: raw.y,
})
}