mirror of
https://github.com/Pumpkin-MC/Pumpkin.git
synced 2026-08-30 20:14:23 +00:00
Add EquippableComponent; Handle armour items more elegantly (#1052)
* Partially revert #1008
commit a378e48a04
* feat: equippable component
* chore
* chore
* fix
* fix
* fix: slot & equip_sound
* fix: reg
This commit is contained in:
@@ -33,6 +33,8 @@ pub struct ItemComponents {
|
||||
pub tool: Option<ToolComponent>,
|
||||
#[serde(rename = "minecraft:food")]
|
||||
pub food: Option<FoodComponent>,
|
||||
#[serde(rename = "minecraft:equippable")]
|
||||
pub equippable: Option<EquippableComponent>,
|
||||
}
|
||||
|
||||
impl ToTokens for ItemComponents {
|
||||
@@ -215,6 +217,104 @@ impl ToTokens for ItemComponents {
|
||||
can_always_eat: #can_always_eat,
|
||||
}), });
|
||||
};
|
||||
|
||||
if let Some(equippable) = &self.equippable {
|
||||
let slot = match equippable.slot.as_str() {
|
||||
"mainhand" => quote! { &EquipmentSlot::MAIN_HAND },
|
||||
"offhand" => quote! { &EquipmentSlot::OFF_HAND },
|
||||
"head" => quote! { &EquipmentSlot::HEAD },
|
||||
"chest" => quote! { &EquipmentSlot::CHEST },
|
||||
"legs" => quote! { &EquipmentSlot::LEGS },
|
||||
"feet" => quote! { &EquipmentSlot::FEET },
|
||||
"body" => quote! { &EquipmentSlot::BODY },
|
||||
"saddle" => quote! { &EquipmentSlot::SADDLE },
|
||||
_ => panic!("Unknown equippable slot: {}", equippable.slot),
|
||||
};
|
||||
let equip_sound = equippable
|
||||
.equip_sound
|
||||
.as_ref()
|
||||
.map(|s| {
|
||||
let equip_sound = LitStr::new(s, Span::call_site());
|
||||
quote! { #equip_sound }
|
||||
})
|
||||
.unwrap_or(quote! { "item.armor.equip_generic" });
|
||||
let asset_id = equippable
|
||||
.asset_id
|
||||
.as_ref()
|
||||
.map(|s| {
|
||||
let asset_id = LitStr::new(s, Span::call_site());
|
||||
quote! { Some(#asset_id) }
|
||||
})
|
||||
.unwrap_or(quote! { None });
|
||||
let camera_overlay = equippable
|
||||
.camera_overlay
|
||||
.as_ref()
|
||||
.map(|s| {
|
||||
let camera_overlay = LitStr::new(s, Span::call_site());
|
||||
quote! { Some(#camera_overlay) }
|
||||
})
|
||||
.unwrap_or(quote! { None });
|
||||
let allowed_entities = equippable
|
||||
.allowed_entities
|
||||
.clone()
|
||||
.map(|list| {
|
||||
let vec: Vec<_> = list
|
||||
.get_values()
|
||||
.iter()
|
||||
.map(|reg| {
|
||||
match reg {
|
||||
TagType::Item(item) => {
|
||||
let ident = format_ident!(
|
||||
"{}",
|
||||
item.strip_prefix("minecraft:").unwrap().to_uppercase()
|
||||
);
|
||||
quote! { EntityTypeOrTag::Single(&crate::entity_type::EntityType::#ident) }
|
||||
},
|
||||
TagType::Tag(tag) => {
|
||||
let ident = format_ident!(
|
||||
"{}",
|
||||
tag.replace(":", "_").replace("/", "_").to_uppercase()
|
||||
);
|
||||
quote! { EntityTypeOrTag::Tag(&crate::tag::EntityType::#ident) }
|
||||
}
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
quote! {
|
||||
Some(&[#(#vec),*])
|
||||
}
|
||||
})
|
||||
.unwrap_or(quote! { None });
|
||||
let dispensable = LitBool::new(equippable.dispensable, Span::call_site());
|
||||
let swappable = LitBool::new(equippable.swappable, Span::call_site());
|
||||
let damage_on_hurt = LitBool::new(equippable.damage_on_hurt, Span::call_site());
|
||||
let equip_on_interact = LitBool::new(equippable.equip_on_interact, Span::call_site());
|
||||
let can_be_sheared = LitBool::new(equippable.can_be_sheared, Span::call_site());
|
||||
let shearing_sound = equippable
|
||||
.shearing_sound
|
||||
.as_ref()
|
||||
.map(|s| {
|
||||
let shearing_sound = LitStr::new(s, Span::call_site());
|
||||
quote! {
|
||||
Some(#shearing_sound)
|
||||
}
|
||||
})
|
||||
.unwrap_or(quote! { None });
|
||||
|
||||
tokens.extend(quote! { (Equippable, &EquippableImpl {
|
||||
slot: #slot,
|
||||
equip_sound: #equip_sound,
|
||||
asset_id: #asset_id,
|
||||
camera_overlay: #camera_overlay,
|
||||
allowed_entities: #allowed_entities,
|
||||
dispensable: #dispensable,
|
||||
swappable: #swappable,
|
||||
damage_on_hurt: #damage_on_hurt,
|
||||
equip_on_interact: #equip_on_interact,
|
||||
can_be_sheared: #can_be_sheared,
|
||||
shearing_sound: #shearing_sound
|
||||
}), });
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -269,6 +369,31 @@ pub struct Modifier {
|
||||
pub slot: AttributeModifierSlot,
|
||||
}
|
||||
|
||||
fn _true() -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Deserialize, Clone, Debug)]
|
||||
pub struct EquippableComponent {
|
||||
pub slot: String,
|
||||
pub equip_sound: Option<String>,
|
||||
pub asset_id: Option<String>,
|
||||
pub camera_overlay: Option<String>,
|
||||
pub allowed_entities: Option<RegistryEntryList>,
|
||||
#[serde(default = "_true")]
|
||||
pub dispensable: bool,
|
||||
#[serde(default = "_true")]
|
||||
pub swappable: bool,
|
||||
#[serde(default = "_true")]
|
||||
pub damage_on_hurt: bool,
|
||||
#[serde(default)]
|
||||
pub equip_on_interact: bool,
|
||||
#[serde(default)]
|
||||
pub can_be_sheared: bool,
|
||||
pub shearing_sound: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Clone, Debug, PartialEq)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
#[allow(clippy::enum_variant_names)]
|
||||
@@ -368,6 +493,7 @@ pub(crate) fn build() -> TokenStream {
|
||||
|
||||
#[doc = "Try to parse an item from a resource location string."]
|
||||
pub fn from_registry_key(name: &str) -> Option<&'static Self> {
|
||||
let name = name.strip_prefix("minecraft:").unwrap_or(name);
|
||||
match name {
|
||||
#type_from_name
|
||||
_ => None
|
||||
|
||||
@@ -3,8 +3,10 @@
|
||||
use crate::attributes::Attributes;
|
||||
use crate::data_component::DataComponent;
|
||||
use crate::data_component::DataComponent::*;
|
||||
use crate::entity_type::EntityType;
|
||||
use crate::tag::Tag;
|
||||
use crate::{AttributeModifierSlot, Block};
|
||||
use pumpkin_util::registry::RegistryEntryList;
|
||||
use pumpkin_util::text::TextComponent;
|
||||
use std::any::Any;
|
||||
use std::borrow::Cow;
|
||||
@@ -320,10 +322,218 @@ impl Hash for ToolImpl {
|
||||
}
|
||||
#[derive(Clone, Debug, Hash)]
|
||||
pub struct WeaponImpl;
|
||||
|
||||
#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
|
||||
pub enum EquipmentType {
|
||||
Hand,
|
||||
HumanoidArmor,
|
||||
AnimalArmor,
|
||||
Saddle,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Hash, Eq, PartialEq)]
|
||||
pub struct EquipmentSlotData {
|
||||
pub slot_type: EquipmentType,
|
||||
pub entity_id: i32,
|
||||
pub max_count: i32,
|
||||
pub index: i32,
|
||||
pub name: Cow<'static, str>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Hash, Eq, PartialEq)]
|
||||
#[repr(i8)]
|
||||
pub enum EquipmentSlot {
|
||||
MainHand(EquipmentSlotData),
|
||||
OffHand(EquipmentSlotData),
|
||||
Feet(EquipmentSlotData),
|
||||
Legs(EquipmentSlotData),
|
||||
Chest(EquipmentSlotData),
|
||||
Head(EquipmentSlotData),
|
||||
Body(EquipmentSlotData),
|
||||
Saddle(EquipmentSlotData),
|
||||
}
|
||||
|
||||
impl EquipmentSlot {
|
||||
pub const MAIN_HAND: Self = Self::MainHand(EquipmentSlotData {
|
||||
slot_type: EquipmentType::Hand,
|
||||
entity_id: 0,
|
||||
index: 0,
|
||||
max_count: 0,
|
||||
name: Cow::Borrowed("mainhand"),
|
||||
});
|
||||
pub const OFF_HAND: Self = Self::OffHand(EquipmentSlotData {
|
||||
slot_type: EquipmentType::Hand,
|
||||
entity_id: 1,
|
||||
index: 5,
|
||||
max_count: 0,
|
||||
name: Cow::Borrowed("offhand"),
|
||||
});
|
||||
pub const FEET: Self = Self::Feet(EquipmentSlotData {
|
||||
slot_type: EquipmentType::HumanoidArmor,
|
||||
entity_id: 0,
|
||||
index: 1,
|
||||
max_count: 1,
|
||||
name: Cow::Borrowed("feet"),
|
||||
});
|
||||
pub const LEGS: Self = Self::Legs(EquipmentSlotData {
|
||||
slot_type: EquipmentType::HumanoidArmor,
|
||||
entity_id: 1,
|
||||
index: 2,
|
||||
max_count: 1,
|
||||
name: Cow::Borrowed("legs"),
|
||||
});
|
||||
pub const CHEST: Self = Self::Chest(EquipmentSlotData {
|
||||
slot_type: EquipmentType::HumanoidArmor,
|
||||
entity_id: 2,
|
||||
index: 3,
|
||||
max_count: 1,
|
||||
name: Cow::Borrowed("chest"),
|
||||
});
|
||||
pub const HEAD: Self = Self::Head(EquipmentSlotData {
|
||||
slot_type: EquipmentType::HumanoidArmor,
|
||||
entity_id: 3,
|
||||
index: 4,
|
||||
max_count: 1,
|
||||
name: Cow::Borrowed("head"),
|
||||
});
|
||||
pub const BODY: Self = Self::Body(EquipmentSlotData {
|
||||
slot_type: EquipmentType::AnimalArmor,
|
||||
entity_id: 0,
|
||||
index: 6,
|
||||
max_count: 1,
|
||||
name: Cow::Borrowed("body"),
|
||||
});
|
||||
pub const SADDLE: Self = Self::Saddle(EquipmentSlotData {
|
||||
slot_type: EquipmentType::Saddle,
|
||||
entity_id: 0,
|
||||
index: 7,
|
||||
max_count: 1,
|
||||
name: Cow::Borrowed("saddle"),
|
||||
});
|
||||
|
||||
pub fn get_entity_slot_id(&self) -> i32 {
|
||||
match self {
|
||||
Self::MainHand(data) => data.entity_id,
|
||||
Self::OffHand(data) => data.entity_id,
|
||||
Self::Feet(data) => data.entity_id,
|
||||
Self::Legs(data) => data.entity_id,
|
||||
Self::Chest(data) => data.entity_id,
|
||||
Self::Head(data) => data.entity_id,
|
||||
Self::Body(data) => data.entity_id,
|
||||
Self::Saddle(data) => data.entity_id,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_from_name(name: &str) -> Option<Self> {
|
||||
match name {
|
||||
"mainhand" => Some(Self::MAIN_HAND),
|
||||
"offhand" => Some(Self::OFF_HAND),
|
||||
"feet" => Some(Self::FEET),
|
||||
"legs" => Some(Self::LEGS),
|
||||
"chest" => Some(Self::CHEST),
|
||||
"head" => Some(Self::HEAD),
|
||||
"body" => Some(Self::BODY),
|
||||
"saddle" => Some(Self::SADDLE),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_offset_entity_slot_id(&self, offset: i32) -> i32 {
|
||||
self.get_entity_slot_id() + offset
|
||||
}
|
||||
|
||||
pub fn slot_type(&self) -> EquipmentType {
|
||||
match self {
|
||||
Self::MainHand(data) => data.slot_type,
|
||||
Self::OffHand(data) => data.slot_type,
|
||||
Self::Feet(data) => data.slot_type,
|
||||
Self::Legs(data) => data.slot_type,
|
||||
Self::Chest(data) => data.slot_type,
|
||||
Self::Head(data) => data.slot_type,
|
||||
Self::Body(data) => data.slot_type,
|
||||
Self::Saddle(data) => data.slot_type,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_armor_slot(&self) -> bool {
|
||||
matches!(
|
||||
self.slot_type(),
|
||||
EquipmentType::HumanoidArmor | EquipmentType::AnimalArmor
|
||||
)
|
||||
}
|
||||
|
||||
pub const fn discriminant(&self) -> i8 {
|
||||
match self {
|
||||
Self::MainHand(_) => 0,
|
||||
Self::OffHand(_) => 1,
|
||||
Self::Feet(_) => 2,
|
||||
Self::Legs(_) => 3,
|
||||
Self::Chest(_) => 4,
|
||||
Self::Head(_) => 5,
|
||||
Self::Body(_) => 6,
|
||||
Self::Saddle(_) => 7,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum EntityTypeOrTag {
|
||||
Tag(&'static Tag),
|
||||
Single(&'static EntityType),
|
||||
}
|
||||
|
||||
impl Hash for EntityTypeOrTag {
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||
match self {
|
||||
EntityTypeOrTag::Tag(tag) => {
|
||||
for x in tag.0 {
|
||||
x.hash(state);
|
||||
}
|
||||
}
|
||||
EntityTypeOrTag::Single(entity_type) => {
|
||||
entity_type.id.hash(state);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Hash)]
|
||||
pub struct EnchantableImpl;
|
||||
#[derive(Clone, Debug, Hash)]
|
||||
pub struct EquippableImpl;
|
||||
pub struct EquippableImpl {
|
||||
pub slot: &'static EquipmentSlot,
|
||||
pub equip_sound: &'static str,
|
||||
pub asset_id: Option<&'static str>,
|
||||
pub camera_overlay: Option<&'static str>,
|
||||
// pub allowed_entities: Option<&'static [&'static str]>,
|
||||
pub allowed_entities: Option<&'static [EntityTypeOrTag]>,
|
||||
pub dispensable: bool,
|
||||
pub swappable: bool,
|
||||
pub damage_on_hurt: bool,
|
||||
pub equip_on_interact: bool,
|
||||
pub can_be_sheared: bool,
|
||||
pub shearing_sound: Option<&'static str>,
|
||||
}
|
||||
impl DataComponentImpl for EquippableImpl {
|
||||
fn get_enum() -> DataComponent
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
Equippable
|
||||
}
|
||||
|
||||
fn clone_dyn(&self) -> Box<dyn DataComponentImpl> {
|
||||
Box::new(self.clone())
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
|
||||
fn as_mut_any(&mut self) -> &mut dyn Any {
|
||||
self
|
||||
}
|
||||
}
|
||||
#[derive(Clone, Debug, Hash)]
|
||||
pub struct RepairableImpl;
|
||||
#[derive(Clone, Debug, Hash)]
|
||||
|
||||
Reference in New Issue
Block a user