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,
}
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(),
}
}
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 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()
}