Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Player Inventory #38

Merged
merged 16 commits into from
Aug 20, 2024
Merged
88 changes: 83 additions & 5 deletions pumpkin-inventory/src/player.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,27 @@ use pumpkin_world::item::Item;
#[allow(dead_code)]
pub struct PlayerInventory {
// Main Inventory + Hotbar
crafting: [Option<Item>; 4],
crafting_output: Option<Item>,
items: [Option<Item>; 36],
armor: [Option<Item>; 4],
offhand: Option<Item>,
// current selected slot in hortbar
selected: i16,
Snowiiii marked this conversation as resolved.
Show resolved Hide resolved
// current selected slot in hotbar
selected: usize,
}

pub struct Hotbar<'a>(&'a mut [Option<Item>;9]);

impl Hotbar<'_> {
fn get_mut(&mut self, index: usize) -> &mut Option<Item> {
&mut self.0[index]
}
}

pub struct Armor<'a>(&'a mut [Option<Item>; 4]);



impl Default for PlayerInventory {
fn default() -> Self {
Self::new()
Expand All @@ -19,18 +33,82 @@ impl Default for PlayerInventory {
impl PlayerInventory {
pub fn new() -> Self {
Self {
crafting: [None; 4],
crafting_output: None,
items: [None; 36],
armor: [None; 4],
offhand: None,
// TODO: What when player spawns in with an different index ?
selected: 0,
}
}

/// Set the contents of an item in a slot
///
/// ## Slot
/// The slot according to https://wiki.vg/Inventory#Player_Inventory
///
/// ## Item
/// The optional item to place in the slot
///
/// ## Item allowed override
/// An override, which when enabled, makes it so that invalid items, can be placed in slots they normally can't.
/// Useful functionality for plugins in the future.
pub fn set_slot(&mut self, slot: usize, item: Option<Item>, item_allowed_override: bool) {
match slot {
0 => {
// TODO: Add crafting check here
self.crafting_output = item
}
1..=4 => {
self.crafting[slot-1] = item
}
5..=8 => {
match item {
None => {
self.armor[slot-4] = None
},
Some(item) => {
// TODO: Replace asserts with error handling
match slot-5 {
0 => {
assert!(item.is_helmet() || item_allowed_override);
self.armor[0] = Some(item);
}
1 => {
assert!(item.is_chestplate() || item_allowed_override);
self.armor[1] = Some(item)
}
2 => {
assert!(item.is_leggings() || item_allowed_override);
self.armor[2] = Some(item);
}
3 => {
assert!(item.is_boots() || item_allowed_override);
self.armor[3] = Some(item)
}
_ => unreachable!()
}
}
}
}
9..=44 => {
self.items[slot-9] = item;
}
45 => {
self.offhand = item;
}
_ => unreachable!()
}
}

pub fn set_slot(_slot: u32, _item: Item) {}

pub fn set_selected(&mut self, slot: i16) {
pub fn set_selected(&mut self, slot: usize) {
assert!((0..9).contains(&slot));
self.selected = slot;
}

pub fn held_item(&self) -> Option<&Item> {
debug_assert!((0..9).contains(&self.selected));
self.items[self.selected+36-9].as_ref()
}
}
4 changes: 2 additions & 2 deletions pumpkin-protocol/src/server/play/s_set_creative_slot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,6 @@ use crate::slot::Slot;
#[allow(dead_code)]
#[packet(0x32)]
pub struct SSetCreativeSlot {
slot: i16,
clicked_item: Slot,
pub slot: i16,
pub clicked_item: Slot,
}
22 changes: 21 additions & 1 deletion pumpkin-protocol/src/slot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use serde::{
de::{self, SeqAccess, Visitor},
Deserialize,
};

use pumpkin_world::item::Item;
use crate::VarInt;

#[derive(Debug, Clone)]
Expand All @@ -16,6 +16,9 @@ pub struct Slot {
components_to_remove: Option<Vec<VarInt>>,
}




impl<'de> Deserialize<'de> for Slot {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
Expand Down Expand Up @@ -75,3 +78,20 @@ impl<'de> Deserialize<'de> for Slot {
deserializer.deserialize_seq(VarIntVisitor)
}
}
impl Slot {
pub fn to_item(self) -> Option<Item> {
let item_id = self.item_id?.0.try_into().unwrap();
Some(Item {
item_id,
item_count: self.item_count.0.try_into().unwrap(),
})
}
}
impl From<Slot> for Item {
fn from(slot: Slot) -> Self {
Item {
item_count: slot.item_count.0.try_into().unwrap(),
item_id: slot.item_id.unwrap().0.try_into().unwrap()
}
}
}
8 changes: 4 additions & 4 deletions pumpkin-world/src/block/block_registry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,28 +7,28 @@ use crate::level::WorldError;
const BLOCKS_JSON: &str = include_str!("../../assets/blocks.json");

#[derive(serde::Deserialize, Debug, Clone, PartialEq, Eq)]
struct BlockDefinition {
pub struct BlockDefinition {
#[serde(rename = "type")]
kind: String,
block_set_type: Option<String>,
}

#[derive(serde::Deserialize, Debug, Clone, PartialEq, Eq)]
struct BlockState {
pub struct BlockState {
default: Option<bool>,
id: i64,
properties: Option<HashMap<String, String>>,
}

#[derive(serde::Deserialize, Debug, Clone, PartialEq, Eq)]
struct BlocksElement {
pub struct BlocksElement {
definition: BlockDefinition,
properties: Option<HashMap<String, Vec<String>>>,
states: Vec<BlockState>,
}

lazy_static! {
static ref BLOCKS: HashMap<String, BlocksElement> =
pub static ref BLOCKS: HashMap<String, BlocksElement> =
serde_json::from_str(BLOCKS_JSON).expect("Could not parse block.json registry.");
}

Expand Down
2 changes: 1 addition & 1 deletion pumpkin-world/src/block/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use num_derive::FromPrimitive;
use crate::vector3::Vector3;

pub mod block_registry;

pub use block_registry::BLOCKS;
#[derive(FromPrimitive)]
pub enum BlockFace {
Bottom = 0,
Expand Down
15 changes: 12 additions & 3 deletions pumpkin-world/src/global_registry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@ const REGISTRY_JSON: &str = include_str!("../assets/registries.json");
#[derive(serde::Deserialize, Debug, Clone, PartialEq, Eq)]
pub struct RegistryElement {
default: Option<String>,
entries: HashMap<String, u32>,
pub entries: HashMap<String, HashMap<String,u32>>,
}

lazy_static! {
static ref REGISTRY: HashMap<String, RegistryElement> =
serde_json::from_str(REGISTRY_JSON).expect("Could not parse items.json registry.");
pub static ref REGISTRY: HashMap<String, RegistryElement> =
serde_json::from_str(REGISTRY_JSON).expect("Could not parse registry.json registry.");
}

pub fn get_protocol_id(category: &str, entry: &str) -> u32 {
Expand All @@ -23,6 +23,7 @@ pub fn get_protocol_id(category: &str, entry: &str) -> u32 {
.expect("Invalid Category in registry")
.entries
.get(entry)
.map(|p|p.get("protocol_id").unwrap())
.expect("No Entry found")
}

Expand All @@ -34,3 +35,11 @@ pub fn get_default<'a>(category: &str) -> Option<&'a str> {
.default
.as_deref()
}

pub fn find_minecraft_id(category: &str, protocol_id: u32) -> Option<&str> {
REGISTRY.get(category)?
.entries
.iter()
.find(|(_,other_protocol_id)|*other_protocol_id.get("protocol_id").unwrap()==protocol_id)
.map(|(id,_)|id.as_str())
}
75 changes: 75 additions & 0 deletions pumpkin-world/src/item/item_categories.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
use crate::item::Item;

impl Item {
pub fn is_helmet(&self) -> bool {
[
// Leather
856,
// Netherite
876,
// Turtle helmet
794,
// Chainmail
860,
// Diamond
868,
// Gold
872,
// Iron
864
].contains(&self.item_id)
}

pub fn is_chestplate(&self) -> bool {
[
// Leather
857,
// Netherite
877,
// Chainmail
861,
// Diamond
869,
// Gold
873,
// Iron
865,
// Elytra
773,
].contains(&self.item_id)
}

pub fn is_leggings(&self) -> bool {
[
// Leather
858,
// Netherite
878,
// Chainmail
862,
// Diamond
870,
// Gold
874,
// Iron
866
].contains(&self.item_id)
}

pub fn is_boots(&self) -> bool {
[
// Leather
859,
// Netherite
879,
// Chainmail
863,
// Diamond
871,
// Gold
875,
// Iron
867
].contains(&self.item_id)
}
}
8 changes: 4 additions & 4 deletions pumpkin-world/src/item/item_registry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use lazy_static::lazy_static;

use crate::global_registry::{self, ITEM_REGISTRY};

use super::Raritiy;
use super::Rarity;

const ITEMS_JSON: &str = include_str!("../../assets/items.json");

Expand All @@ -17,18 +17,18 @@ pub struct ItemComponents {
#[serde(rename = "minecraft:max_stack_size")]
max_stack_size: u32,
#[serde(rename = "minecraft:rarity")]
rarity: Raritiy,
rarity: Rarity,
#[serde(rename = "minecraft:repair_cost")]
repair_cost: u32,
}

#[derive(serde::Deserialize, Debug, Clone, PartialEq, Eq)]
struct ItemElement {
pub struct ItemElement {
components: ItemComponents,
}

lazy_static! {
static ref ITEMS: HashMap<String, ItemElement> =
pub static ref ITEMS: HashMap<String, ItemElement> =
serde_json::from_str(ITEMS_JSON).expect("Could not parse items.json registry.");
}

Expand Down
14 changes: 10 additions & 4 deletions pumpkin-world/src/item/mod.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,20 @@
mod item_registry;

mod item_categories;
pub use item_registry::ITEMS;
#[derive(serde::Deserialize, Debug, Clone, PartialEq, Eq)]
#[serde(rename_all = "lowercase")]
/// Item Raritiy
pub enum Raritiy {
/// Item Rarity
pub enum Rarity {
Common,
UnCommon,
Rare,
Epic,
}

#[derive(Clone, Copy)]
pub struct Item {}
pub struct Item {
pub item_count: u32,
// This ID is the numerical protocol ID, not the usual minecraft::block ID.
pub item_id: u32,
// TODO: Add Item Components
}
2 changes: 1 addition & 1 deletion pumpkin-world/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ pub const WORLD_HEIGHT: usize = 384;
pub const WORLD_Y_START_AT: i32 = -64;
pub const DIRECT_PALETTE_BITS: u32 = 15;
pub mod block;
mod global_registry;
pub mod global_registry;
pub mod item;
mod level;
pub mod radial_chunk_iterator;
Expand Down
Loading