mirror of
https://github.com/mat-1/azalea.git
synced 2025-08-02 06:16:04 +00:00
add BlockState::property
This commit is contained in:
parent
10ee7e147d
commit
f15f0325c0
5 changed files with 155 additions and 33 deletions
|
@ -42,9 +42,13 @@ struct PropertyDefinitions {
|
|||
/// `snowy: Snowy(false)` or `axis: properties::Axis::Y`
|
||||
#[derive(Debug)]
|
||||
struct PropertyWithNameAndDefault {
|
||||
// "snowy" / "axis"
|
||||
name: Ident,
|
||||
// Snowy / Axis
|
||||
property_type: Ident,
|
||||
property_value_type: Ident,
|
||||
is_enum: bool,
|
||||
// false / properties::Axis::Y
|
||||
default: proc_macro2::TokenStream,
|
||||
}
|
||||
|
||||
|
@ -60,7 +64,7 @@ struct BlockDefinition {
|
|||
}
|
||||
impl Parse for PropertyWithNameAndDefault {
|
||||
fn parse(input: ParseStream) -> Result<Self> {
|
||||
// `snowy: false` or `axis: properties::Axis::Y`
|
||||
// `snowy: Snowy(false)` or `axis: properties::Axis::Y`
|
||||
let property_name = input.parse()?;
|
||||
input.parse::<Token![:]>()?;
|
||||
|
||||
|
@ -68,12 +72,14 @@ impl Parse for PropertyWithNameAndDefault {
|
|||
let mut property_default = quote! { #first_ident };
|
||||
|
||||
let property_type: Ident;
|
||||
let property_value_type: Ident;
|
||||
let mut is_enum = false;
|
||||
|
||||
if input.parse::<Token![::]>().is_ok() {
|
||||
// enum
|
||||
is_enum = true;
|
||||
property_type = first_ident;
|
||||
property_type = first_ident.clone();
|
||||
property_value_type = first_ident;
|
||||
let variant = input.parse::<Ident>()?;
|
||||
property_default = quote! { properties::#property_default::#variant };
|
||||
} else {
|
||||
|
@ -86,7 +92,8 @@ impl Parse for PropertyWithNameAndDefault {
|
|||
let unit_struct_inner_string = unit_struct_inner.to_string();
|
||||
|
||||
if matches!(unit_struct_inner_string.as_str(), "true" | "false") {
|
||||
property_type = Ident::new("bool", first_ident.span());
|
||||
property_value_type = Ident::new("bool", first_ident.span());
|
||||
property_type = first_ident;
|
||||
property_default = quote! { #unit_struct_inner };
|
||||
} else {
|
||||
return Err(input.error("Expected a boolean or an enum variant"));
|
||||
|
@ -96,6 +103,7 @@ impl Parse for PropertyWithNameAndDefault {
|
|||
Ok(PropertyWithNameAndDefault {
|
||||
name: property_name,
|
||||
property_type,
|
||||
property_value_type,
|
||||
is_enum,
|
||||
default: property_default,
|
||||
})
|
||||
|
@ -259,6 +267,12 @@ impl Parse for MakeBlockStates {
|
|||
}
|
||||
}
|
||||
|
||||
struct PropertyVariantData {
|
||||
pub block_state_ids: Vec<u32>,
|
||||
pub ident: Ident,
|
||||
pub is_enum: bool,
|
||||
}
|
||||
|
||||
#[proc_macro]
|
||||
pub fn make_block_states(input: TokenStream) -> TokenStream {
|
||||
let input = parse_macro_input!(input as MakeBlockStates);
|
||||
|
@ -318,6 +332,12 @@ pub fn make_block_states(input: TokenStream) -> TokenStream {
|
|||
#property_enum_variants
|
||||
}
|
||||
|
||||
// impl Property for #property_struct_name {
|
||||
// type Value = Self;
|
||||
|
||||
// fn try_from_block_state
|
||||
// }
|
||||
|
||||
impl From<u32> for #property_struct_name {
|
||||
fn from(value: u32) -> Self {
|
||||
match value {
|
||||
|
@ -337,6 +357,10 @@ pub fn make_block_states(input: TokenStream) -> TokenStream {
|
|||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct #property_struct_name(pub bool);
|
||||
|
||||
// impl Property for #property_struct_name {
|
||||
// type Value = bool;
|
||||
// }
|
||||
|
||||
impl From<u32> for #property_struct_name {
|
||||
fn from(value: u32) -> Self {
|
||||
match value {
|
||||
|
@ -360,14 +384,19 @@ pub fn make_block_states(input: TokenStream) -> TokenStream {
|
|||
let mut from_registry_block_to_blockstate_match = quote! {};
|
||||
let mut from_registry_block_to_blockstates_match = quote! {};
|
||||
|
||||
// a list of block state ids that are waterlogged
|
||||
let mut waterlogged_state_ids: Vec<u32> = Vec::new();
|
||||
// {
|
||||
// Waterlogged: [
|
||||
// [ vec of waterlogged = true state ids ],
|
||||
// [ vec of waterlogged = false state ids ]
|
||||
// }
|
||||
// }
|
||||
let mut properties_to_state_ids: HashMap<String, Vec<PropertyVariantData>> = HashMap::new();
|
||||
|
||||
for block in &input.block_definitions.blocks {
|
||||
let block_property_names = &block
|
||||
.properties_and_defaults
|
||||
.iter()
|
||||
.map(|p| p.property_type.to_string())
|
||||
.map(|p| p.property_value_type.to_string())
|
||||
.collect::<Vec<_>>();
|
||||
let mut block_properties_vec = Vec::new();
|
||||
for property_name in block_property_names {
|
||||
|
@ -421,6 +450,7 @@ pub fn make_block_states(input: TokenStream) -> TokenStream {
|
|||
properties_with_name.push(PropertyWithNameAndDefault {
|
||||
name: Ident::new(&property_name, proc_macro2::Span::call_site()),
|
||||
property_type: property.property_type.clone(),
|
||||
property_value_type: property.property_value_type.clone(),
|
||||
is_enum: property.is_enum,
|
||||
default: property.default.clone(),
|
||||
});
|
||||
|
@ -436,7 +466,7 @@ pub fn make_block_states(input: TokenStream) -> TokenStream {
|
|||
// pub has_bottle_2: HasBottle,
|
||||
let mut block_struct_fields = quote! {};
|
||||
for PropertyWithNameAndDefault {
|
||||
property_type: struct_name,
|
||||
property_value_type,
|
||||
name,
|
||||
is_enum,
|
||||
..
|
||||
|
@ -445,9 +475,9 @@ pub fn make_block_states(input: TokenStream) -> TokenStream {
|
|||
// let property_name_snake =
|
||||
// Ident::new(&property.to_string(), proc_macro2::Span::call_site());
|
||||
block_struct_fields.extend(if *is_enum {
|
||||
quote! { pub #name: properties::#struct_name, }
|
||||
quote! { pub #name: properties::#property_value_type, }
|
||||
} else {
|
||||
quote! { pub #name: #struct_name, }
|
||||
quote! { pub #name: #property_value_type, }
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -483,15 +513,15 @@ pub fn make_block_states(input: TokenStream) -> TokenStream {
|
|||
for i in 0..properties_with_name.len() {
|
||||
let property = &properties_with_name[i];
|
||||
let property_name = &property.name;
|
||||
let property_struct_name_ident = &property.property_type;
|
||||
let property_value_name_ident = &property.property_type;
|
||||
let variant =
|
||||
Ident::new(&combination[i].to_string(), proc_macro2::Span::call_site());
|
||||
|
||||
// this terrible code just gets the property default as a string
|
||||
let property_default_as_string = if let TokenTree::Ident(i) =
|
||||
let property_default_as_string = if let TokenTree::Ident(ident) =
|
||||
property.default.clone().into_iter().last().unwrap()
|
||||
{
|
||||
i.to_string()
|
||||
ident.to_string()
|
||||
} else {
|
||||
panic!()
|
||||
};
|
||||
|
@ -499,20 +529,31 @@ pub fn make_block_states(input: TokenStream) -> TokenStream {
|
|||
is_default = false;
|
||||
}
|
||||
|
||||
let property_value = if property.is_enum {
|
||||
quote! {properties::#property_struct_name_ident::#variant}
|
||||
let property_variant = if property.is_enum {
|
||||
quote! {properties::#property_value_name_ident::#variant}
|
||||
} else {
|
||||
quote! {#variant}
|
||||
};
|
||||
|
||||
from_block_to_state_combination_match_inner.extend(quote! {
|
||||
#property_name: #property_value,
|
||||
#property_name: #property_variant,
|
||||
});
|
||||
|
||||
// if "waterlogged" is a property and it's true for this state then add it to
|
||||
// waterlogged_state_ids
|
||||
if property_name == "waterlogged" && property_value.to_string() == "true" {
|
||||
waterlogged_state_ids.push(state_id)
|
||||
// add to properties_to_state_ids
|
||||
let property_variants = properties_to_state_ids
|
||||
.entry(property_value_name_ident.to_string())
|
||||
.or_insert_with(Vec::new);
|
||||
let property_variant_data = property_variants
|
||||
.iter_mut()
|
||||
.find(|v| v.ident.to_string() == variant.to_string());
|
||||
if let Some(property_variant_data) = property_variant_data {
|
||||
property_variant_data.block_state_ids.push(state_id);
|
||||
} else {
|
||||
property_variants.push(PropertyVariantData {
|
||||
block_state_ids: vec![state_id],
|
||||
ident: variant,
|
||||
is_enum: property.is_enum,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -557,13 +598,14 @@ pub fn make_block_states(input: TokenStream) -> TokenStream {
|
|||
let PropertyWithNameAndDefault {
|
||||
property_type: property_struct_name_ident,
|
||||
name: property_name,
|
||||
property_value_type,
|
||||
..
|
||||
} = &properties_with_name[i];
|
||||
|
||||
let property_variants = &block_properties_vec[i];
|
||||
let property_variants_count = property_variants.len() as u32;
|
||||
let conversion_code = {
|
||||
if &property_struct_name_ident.to_string() == "bool" {
|
||||
if &property_value_type.to_string() == "bool" {
|
||||
assert_eq!(property_variants_count, 2);
|
||||
// this is not a mistake, it starts with true for some reason
|
||||
quote! {(b / #division) % #property_variants_count == 0}
|
||||
|
@ -659,8 +701,6 @@ pub fn make_block_states(input: TokenStream) -> TokenStream {
|
|||
block_structs.extend(block_struct);
|
||||
}
|
||||
|
||||
let waterlogged_state_ids_match = quote! { #(#waterlogged_state_ids)|* };
|
||||
|
||||
let last_state_id = state_id - 1;
|
||||
let mut generated = quote! {
|
||||
impl BlockState {
|
||||
|
@ -670,21 +710,87 @@ pub fn make_block_states(input: TokenStream) -> TokenStream {
|
|||
#last_state_id
|
||||
}
|
||||
|
||||
/// Whether the given block state is waterlogged. This checks for
|
||||
/// the `waterlogged` field, so it'll return false for water.
|
||||
pub fn waterlogged(&self) -> bool {
|
||||
matches!(self.id, #waterlogged_state_ids_match)
|
||||
/// Get a property from this block state. Will be `None` if the block can't have the property.
|
||||
///
|
||||
/// ```
|
||||
/// fn is_waterlogged(block_state: azalea_block::BlockState) -> bool {
|
||||
/// block_state.property::<azalea_block::properties::Waterlogged>().unwrap_or_default()
|
||||
/// }
|
||||
/// ```
|
||||
pub fn property<P: Property>(self) -> Option<P::Value> {
|
||||
P::try_from_block_state(self)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// now impl Property for every property
|
||||
// ```
|
||||
// match state_id {
|
||||
// // this is just an example of how it might look, these state ids are definitely not correct
|
||||
// 0|3|6 => Some(Self::Axis::X),
|
||||
// 1|4|7 => Some(Self::Axis::Y),
|
||||
// 2|5|8 => Some(Self::Axis::Z),
|
||||
// _ => None
|
||||
// }
|
||||
// ```
|
||||
let mut property_impls = quote! {};
|
||||
for (property_struct_name, property_values) in properties_to_state_ids {
|
||||
let mut enum_inner_generated = quote! {};
|
||||
|
||||
let mut is_enum_ = false;
|
||||
|
||||
for PropertyVariantData {
|
||||
block_state_ids,
|
||||
ident,
|
||||
is_enum,
|
||||
} in property_values
|
||||
{
|
||||
enum_inner_generated.extend(if is_enum {
|
||||
quote! {
|
||||
#(#block_state_ids)|* => Some(Self::#ident),
|
||||
}
|
||||
} else {
|
||||
quote! {
|
||||
#(#block_state_ids)|* => Some(#ident),
|
||||
}
|
||||
});
|
||||
is_enum_ = is_enum;
|
||||
}
|
||||
let is_enum = is_enum_;
|
||||
|
||||
let property_struct_name =
|
||||
Ident::new(&property_struct_name, proc_macro2::Span::call_site());
|
||||
|
||||
let value = if is_enum {
|
||||
quote! { Self }
|
||||
} else {
|
||||
quote! { bool }
|
||||
};
|
||||
|
||||
let property_impl = quote! {
|
||||
impl Property for #property_struct_name {
|
||||
type Value = #value;
|
||||
|
||||
fn try_from_block_state(block_state: BlockState) -> Option<Self::Value> {
|
||||
match block_state.id {
|
||||
#enum_inner_generated
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
property_impls.extend(property_impl);
|
||||
}
|
||||
|
||||
generated.extend(quote! {
|
||||
pub mod properties {
|
||||
use super::*;
|
||||
|
||||
#property_enums
|
||||
}
|
||||
};
|
||||
|
||||
generated.extend(quote! {
|
||||
#property_impls
|
||||
}
|
||||
|
||||
pub mod blocks {
|
||||
use super::*;
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use crate::{Block, BlockBehavior, BlockState, BlockStates};
|
||||
use crate::{Block, BlockBehavior, BlockState, BlockStates, Property};
|
||||
use azalea_block_macros::make_block_states;
|
||||
use std::fmt::Debug;
|
||||
|
||||
|
@ -5380,3 +5380,4 @@ make_block_states! {
|
|||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -33,6 +33,12 @@ impl dyn Block {
|
|||
}
|
||||
}
|
||||
|
||||
pub trait Property {
|
||||
type Value;
|
||||
|
||||
fn try_from_block_state(state: BlockState) -> Option<Self::Value>;
|
||||
}
|
||||
|
||||
/// A representation of a state a block can be in.
|
||||
///
|
||||
/// For example, a stone block only has one state but each possible stair
|
||||
|
@ -113,7 +119,10 @@ impl Default for FluidState {
|
|||
|
||||
impl From<BlockState> for FluidState {
|
||||
fn from(state: BlockState) -> Self {
|
||||
if state.waterlogged() {
|
||||
if state
|
||||
.property::<crate::properties::Waterlogged>()
|
||||
.unwrap_or_default()
|
||||
{
|
||||
Self {
|
||||
fluid: azalea_registry::Fluid::Water,
|
||||
height: 15,
|
||||
|
|
|
@ -32,7 +32,10 @@ fn blocks_motion(block_state: BlockState) -> bool {
|
|||
|
||||
fn motion_blocking(block_state: BlockState) -> bool {
|
||||
// TODO
|
||||
!block_state.is_air() || block_state.waterlogged()
|
||||
!block_state.is_air()
|
||||
|| block_state
|
||||
.property::<azalea_block::properties::Waterlogged>()
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
impl HeightmapKind {
|
||||
|
|
|
@ -246,7 +246,10 @@ fn is_block_state_passable(block: BlockState) -> bool {
|
|||
if block == azalea_registry::Block::Water.into() {
|
||||
return false;
|
||||
}
|
||||
if block.waterlogged() {
|
||||
if block
|
||||
.property::<azalea_block::properties::Waterlogged>()
|
||||
.unwrap_or_default()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if block == azalea_registry::Block::Lava.into() {
|
||||
|
|
Loading…
Add table
Reference in a new issue