Add Pressure Plates
This commit is contained in:
parent
f79167cece
commit
81c45b7c78
@ -3,6 +3,8 @@ use std::ops::{Add, AddAssign, Div, Mul, Sub};
|
||||
|
||||
use num_traits::{Float, Num};
|
||||
|
||||
use super::vector2::Vector2;
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Hash, Eq, Default)]
|
||||
pub struct Vector3<T> {
|
||||
pub x: T,
|
||||
@ -209,6 +211,15 @@ where
|
||||
z: z.round() as i32,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_vec2_i32(&self) -> Vector2<i32> {
|
||||
let x: f64 = self.x.into();
|
||||
let z: f64 = self.z.into();
|
||||
Vector2 {
|
||||
x: x.round() as i32,
|
||||
z: z.round() as i32,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Math:
|
||||
|
||||
@ -13,6 +13,7 @@ use crate::world::World;
|
||||
pub mod buttons;
|
||||
pub mod lever;
|
||||
pub mod observer;
|
||||
pub mod pressure_plate;
|
||||
pub mod rails;
|
||||
pub mod redstone_block;
|
||||
pub mod redstone_lamp;
|
||||
|
||||
84
pumpkin/src/block/blocks/redstone/pressure_plate/mod.rs
Normal file
84
pumpkin/src/block/blocks/redstone/pressure_plate/mod.rs
Normal file
@ -0,0 +1,84 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use pumpkin_data::{Block, BlockState};
|
||||
use pumpkin_util::math::position::BlockPos;
|
||||
use pumpkin_world::{BlockStateId, chunk::TickPriority, world::BlockFlags};
|
||||
|
||||
use crate::world::World;
|
||||
|
||||
pub mod plate;
|
||||
pub mod weighted;
|
||||
|
||||
pub trait PressurePlate {
|
||||
async fn on_entity_collision_pp(
|
||||
&self,
|
||||
world: &Arc<World>,
|
||||
pos: BlockPos,
|
||||
block: Block,
|
||||
state: BlockState,
|
||||
) {
|
||||
let output = self.get_redstone_output(&block, state.id);
|
||||
if output == 0 {
|
||||
self.update_plate_state(world, pos, &block, state, output)
|
||||
.await;
|
||||
}
|
||||
}
|
||||
|
||||
async fn on_scheduled_tick_pp(&self, world: &Arc<World>, block: &Block, pos: &BlockPos) {
|
||||
let state = world.get_block_state(pos).await.unwrap();
|
||||
let output = self.get_redstone_output(block, state.id);
|
||||
if output > 0 {
|
||||
self.update_plate_state(world, *pos, block, state, output)
|
||||
.await;
|
||||
}
|
||||
}
|
||||
|
||||
async fn on_state_replaced_pp(
|
||||
&self,
|
||||
world: &Arc<World>,
|
||||
block: &Block,
|
||||
pos: BlockPos,
|
||||
old_state_id: BlockStateId,
|
||||
moved: bool,
|
||||
) {
|
||||
if !moved && self.get_redstone_output(block, old_state_id) > 0 {
|
||||
world.update_neighbors(&pos, None).await;
|
||||
world.update_neighbors(&pos.down(), None).await;
|
||||
}
|
||||
}
|
||||
|
||||
async fn update_plate_state(
|
||||
&self,
|
||||
world: &Arc<World>,
|
||||
pos: BlockPos,
|
||||
block: &Block,
|
||||
state: BlockState,
|
||||
output: u8,
|
||||
) {
|
||||
let calc_output = self.calculate_redstone_output(world, block, &pos).await;
|
||||
let has_output = calc_output > 0;
|
||||
if calc_output != output {
|
||||
let state = self.set_redstone_output(block, &state, calc_output);
|
||||
world
|
||||
.set_block_state(&pos, state, BlockFlags::NOTIFY_LISTENERS)
|
||||
.await;
|
||||
world.update_neighbors(&pos, None).await;
|
||||
world.update_neighbors(&pos.down(), None).await;
|
||||
}
|
||||
if has_output {
|
||||
world
|
||||
.schedule_block_tick(block, pos, self.tick_rate(), TickPriority::Normal)
|
||||
.await;
|
||||
}
|
||||
}
|
||||
|
||||
fn get_redstone_output(&self, block: &Block, state: BlockStateId) -> u8;
|
||||
|
||||
fn set_redstone_output(&self, block: &Block, state: &BlockState, output: u8) -> BlockStateId;
|
||||
|
||||
async fn calculate_redstone_output(&self, world: &World, block: &Block, pos: &BlockPos) -> u8;
|
||||
|
||||
fn tick_rate(&self) -> u16 {
|
||||
20
|
||||
}
|
||||
}
|
||||
133
pumpkin/src/block/blocks/redstone/pressure_plate/plate.rs
Normal file
133
pumpkin/src/block/blocks/redstone/pressure_plate/plate.rs
Normal file
@ -0,0 +1,133 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use async_trait::async_trait;
|
||||
use pumpkin_data::{
|
||||
Block, BlockState,
|
||||
block_properties::BlockProperties,
|
||||
tag::{RegistryKey, get_tag_values},
|
||||
};
|
||||
use pumpkin_util::math::{boundingbox::BoundingBox, position::BlockPos};
|
||||
use pumpkin_world::{BlockStateId, block::BlockDirection};
|
||||
|
||||
use crate::{
|
||||
block::pumpkin_block::{BlockMetadata, PumpkinBlock},
|
||||
entity::Entity,
|
||||
world::World,
|
||||
};
|
||||
|
||||
use super::PressurePlate;
|
||||
|
||||
/// This is for Normal Pressure plates, so not Gold or Iron
|
||||
pub struct PressurePlateBlock;
|
||||
|
||||
type PressurePlateProps = pumpkin_data::block_properties::StonePressurePlateLikeProperties;
|
||||
|
||||
impl BlockMetadata for PressurePlateBlock {
|
||||
fn namespace(&self) -> &'static str {
|
||||
"minecraft"
|
||||
}
|
||||
|
||||
fn ids(&self) -> &'static [&'static str] {
|
||||
let mut combined = Vec::new();
|
||||
combined.extend_from_slice(
|
||||
get_tag_values(RegistryKey::Block, "minecraft:wooden_pressure_plates").unwrap(),
|
||||
);
|
||||
combined.extend_from_slice(
|
||||
get_tag_values(RegistryKey::Block, "minecraft:stone_pressure_plates").unwrap(),
|
||||
);
|
||||
combined.leak()
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl PumpkinBlock for PressurePlateBlock {
|
||||
async fn on_entity_collision(
|
||||
&self,
|
||||
world: &Arc<World>,
|
||||
_entity: &Entity,
|
||||
pos: BlockPos,
|
||||
block: Block,
|
||||
state: BlockState,
|
||||
) {
|
||||
self.on_entity_collision_pp(world, pos, block, state).await;
|
||||
}
|
||||
|
||||
async fn on_scheduled_tick(&self, world: &Arc<World>, block: &Block, pos: &BlockPos) {
|
||||
self.on_scheduled_tick_pp(world, block, pos).await;
|
||||
}
|
||||
|
||||
async fn on_state_replaced(
|
||||
&self,
|
||||
world: &Arc<World>,
|
||||
block: &Block,
|
||||
pos: BlockPos,
|
||||
old_state_id: BlockStateId,
|
||||
moved: bool,
|
||||
) {
|
||||
self.on_state_replaced_pp(world, block, pos, old_state_id, moved)
|
||||
.await;
|
||||
}
|
||||
|
||||
async fn get_weak_redstone_power(
|
||||
&self,
|
||||
block: &Block,
|
||||
_world: &World,
|
||||
_pos: &BlockPos,
|
||||
state: &BlockState,
|
||||
_direction: BlockDirection,
|
||||
) -> u8 {
|
||||
self.get_redstone_output(block, state.id)
|
||||
}
|
||||
|
||||
async fn get_strong_redstone_power(
|
||||
&self,
|
||||
block: &Block,
|
||||
_world: &World,
|
||||
_pos: &BlockPos,
|
||||
state: &BlockState,
|
||||
direction: BlockDirection,
|
||||
) -> u8 {
|
||||
if direction == BlockDirection::Up {
|
||||
return self.get_redstone_output(block, state.id);
|
||||
}
|
||||
0
|
||||
}
|
||||
|
||||
async fn emits_redstone_power(
|
||||
&self,
|
||||
_block: &Block,
|
||||
_state: &BlockState,
|
||||
_direction: BlockDirection,
|
||||
) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
impl PressurePlate for PressurePlateBlock {
|
||||
fn get_redstone_output(&self, block: &Block, state: BlockStateId) -> u8 {
|
||||
let props = PressurePlateProps::from_state_id(state, block);
|
||||
if props.powered { 15 } else { 0 }
|
||||
}
|
||||
|
||||
async fn calculate_redstone_output(&self, world: &World, _block: &Block, pos: &BlockPos) -> u8 {
|
||||
// TODO: this is bad use real box
|
||||
let aabb = BoundingBox::from_block(pos);
|
||||
if !world.get_entities_at_box(&aabb).await.is_empty()
|
||||
|| !world.get_players_at_box(&aabb).await.is_empty()
|
||||
{
|
||||
return 15;
|
||||
}
|
||||
0
|
||||
}
|
||||
|
||||
fn set_redstone_output(
|
||||
&self,
|
||||
block: &Block,
|
||||
state: &BlockState,
|
||||
output: u8,
|
||||
) -> pumpkin_world::BlockStateId {
|
||||
let mut props = PressurePlateProps::from_state_id(state.id, block);
|
||||
props.powered = output > 0;
|
||||
props.to_state_id(block)
|
||||
}
|
||||
}
|
||||
145
pumpkin/src/block/blocks/redstone/pressure_plate/weighted.rs
Normal file
145
pumpkin/src/block/blocks/redstone/pressure_plate/weighted.rs
Normal file
@ -0,0 +1,145 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use async_trait::async_trait;
|
||||
use pumpkin_data::{
|
||||
Block, BlockState,
|
||||
block_properties::{BlockProperties, EnumVariants, Integer0To15},
|
||||
};
|
||||
use pumpkin_util::math::{boundingbox::BoundingBox, position::BlockPos};
|
||||
use pumpkin_world::{BlockStateId, block::BlockDirection};
|
||||
|
||||
use crate::{
|
||||
block::pumpkin_block::{BlockMetadata, PumpkinBlock},
|
||||
entity::Entity,
|
||||
world::World,
|
||||
};
|
||||
|
||||
use super::PressurePlate;
|
||||
|
||||
/// This is for Gold and Iron Pressure Plate
|
||||
pub struct WeightedPressurePlateBlock;
|
||||
|
||||
type PressurePlateProps = pumpkin_data::block_properties::LightWeightedPressurePlateLikeProperties;
|
||||
|
||||
impl BlockMetadata for WeightedPressurePlateBlock {
|
||||
fn namespace(&self) -> &'static str {
|
||||
"minecraft"
|
||||
}
|
||||
|
||||
fn ids(&self) -> &'static [&'static str] {
|
||||
// light = Gold
|
||||
// heavy = Iron
|
||||
&[
|
||||
"light_weighted_pressure_plate",
|
||||
"heavy_weighted_pressure_plate",
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl PumpkinBlock for WeightedPressurePlateBlock {
|
||||
async fn on_entity_collision(
|
||||
&self,
|
||||
world: &Arc<World>,
|
||||
_entity: &Entity,
|
||||
pos: BlockPos,
|
||||
block: Block,
|
||||
state: BlockState,
|
||||
) {
|
||||
self.on_entity_collision_pp(world, pos, block, state).await;
|
||||
}
|
||||
|
||||
async fn on_scheduled_tick(&self, world: &Arc<World>, block: &Block, pos: &BlockPos) {
|
||||
self.on_scheduled_tick_pp(world, block, pos).await;
|
||||
}
|
||||
|
||||
async fn on_state_replaced(
|
||||
&self,
|
||||
world: &Arc<World>,
|
||||
block: &Block,
|
||||
pos: BlockPos,
|
||||
old_state_id: BlockStateId,
|
||||
moved: bool,
|
||||
) {
|
||||
self.on_state_replaced_pp(world, block, pos, old_state_id, moved)
|
||||
.await;
|
||||
}
|
||||
|
||||
async fn get_weak_redstone_power(
|
||||
&self,
|
||||
block: &Block,
|
||||
_world: &World,
|
||||
_pos: &BlockPos,
|
||||
state: &BlockState,
|
||||
_direction: BlockDirection,
|
||||
) -> u8 {
|
||||
self.get_redstone_output(block, state.id)
|
||||
}
|
||||
|
||||
async fn get_strong_redstone_power(
|
||||
&self,
|
||||
block: &Block,
|
||||
_world: &World,
|
||||
_pos: &BlockPos,
|
||||
state: &BlockState,
|
||||
direction: BlockDirection,
|
||||
) -> u8 {
|
||||
if direction == BlockDirection::Up {
|
||||
return self.get_redstone_output(block, state.id);
|
||||
}
|
||||
0
|
||||
}
|
||||
|
||||
async fn emits_redstone_power(
|
||||
&self,
|
||||
_block: &Block,
|
||||
_state: &BlockState,
|
||||
_direction: BlockDirection,
|
||||
) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
impl PressurePlate for WeightedPressurePlateBlock {
|
||||
fn get_redstone_output(&self, block: &Block, state: BlockStateId) -> u8 {
|
||||
let props = PressurePlateProps::from_state_id(state, block);
|
||||
props.power.to_index() as u8
|
||||
}
|
||||
|
||||
async fn calculate_redstone_output(&self, world: &World, block: &Block, pos: &BlockPos) -> u8 {
|
||||
// light = Gold
|
||||
// heavy = Iron
|
||||
let weight = if block == &Block::LIGHT_WEIGHTED_PRESSURE_PLATE {
|
||||
// Gold
|
||||
15
|
||||
} else {
|
||||
// Iron
|
||||
150
|
||||
};
|
||||
// TODO: this is bad use real box
|
||||
let aabb = BoundingBox::from_block(pos);
|
||||
let len = world.get_entities_at_box(&aabb).await.len()
|
||||
+ world.get_players_at_box(&aabb).await.len();
|
||||
let len = len.min(weight);
|
||||
if len > 0 {
|
||||
let f = (weight.min(len) / weight) as f32;
|
||||
return (f * 15.0).ceil() as u8;
|
||||
}
|
||||
0
|
||||
}
|
||||
|
||||
fn set_redstone_output(
|
||||
&self,
|
||||
block: &Block,
|
||||
state: &BlockState,
|
||||
output: u8,
|
||||
) -> pumpkin_world::BlockStateId {
|
||||
let mut props = PressurePlateProps::from_state_id(state.id, block);
|
||||
props.power = Integer0To15::from_index(u16::from(output));
|
||||
props.to_state_id(block)
|
||||
}
|
||||
|
||||
fn tick_rate(&self) -> u16 {
|
||||
10
|
||||
}
|
||||
}
|
||||
@ -19,6 +19,8 @@ use blocks::piston::piston_head::PistonHeadBlock;
|
||||
use blocks::pumpkin::PumpkinBlock;
|
||||
use blocks::redstone::buttons::ButtonBlock;
|
||||
use blocks::redstone::observer::ObserverBlock;
|
||||
use blocks::redstone::pressure_plate::plate::PressurePlateBlock;
|
||||
use blocks::redstone::pressure_plate::weighted::WeightedPressurePlateBlock;
|
||||
use blocks::redstone::rails::activator_rail::ActivatorRailBlock;
|
||||
use blocks::redstone::rails::detector_rail::DetectorRailBlock;
|
||||
use blocks::redstone::rails::powered_rail::PoweredRailBlock;
|
||||
@ -94,6 +96,8 @@ pub fn default_registry() -> Arc<BlockRegistry> {
|
||||
manager.register(NetherPortalBlock);
|
||||
manager.register(NoteBlock);
|
||||
manager.register(PumpkinBlock);
|
||||
manager.register(PressurePlateBlock);
|
||||
manager.register(WeightedPressurePlateBlock);
|
||||
|
||||
// Fire
|
||||
manager.register(SoulFireBlock);
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
use crate::block::registry::BlockActionResult;
|
||||
use crate::entity::Entity;
|
||||
use crate::entity::player::Player;
|
||||
use crate::server::Server;
|
||||
use crate::world::World;
|
||||
@ -49,6 +50,16 @@ pub trait PumpkinBlock: Send + Sync {
|
||||
BlockActionResult::Continue
|
||||
}
|
||||
|
||||
async fn on_entity_collision(
|
||||
&self,
|
||||
_world: &Arc<World>,
|
||||
_entity: &Entity,
|
||||
_pos: BlockPos,
|
||||
_block: Block,
|
||||
_state: BlockState,
|
||||
) {
|
||||
}
|
||||
|
||||
fn should_drop_items_on_explosion(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
use crate::block::pumpkin_block::{BlockMetadata, PumpkinBlock};
|
||||
use crate::entity::Entity;
|
||||
use crate::entity::player::Player;
|
||||
use crate::server::Server;
|
||||
use crate::world::World;
|
||||
@ -55,6 +56,22 @@ impl BlockRegistry {
|
||||
false
|
||||
}
|
||||
|
||||
pub async fn on_entity_collision(
|
||||
&self,
|
||||
block: Block,
|
||||
world: &Arc<World>,
|
||||
entity: &Entity,
|
||||
pos: BlockPos,
|
||||
state: BlockState,
|
||||
) {
|
||||
let pumpkin_block = self.get_pumpkin_block(&block);
|
||||
if let Some(pumpkin_block) = pumpkin_block {
|
||||
pumpkin_block
|
||||
.on_entity_collision(world, entity, pos, block, state)
|
||||
.await;
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn on_use(
|
||||
&self,
|
||||
block: &Block,
|
||||
|
||||
@ -262,7 +262,7 @@ impl LivingEntity {
|
||||
.await;
|
||||
}
|
||||
|
||||
fn tick_move(&self) {
|
||||
async fn tick_move(&self) {
|
||||
let velo = self.entity.velocity.load();
|
||||
let pos = self.entity.pos.load();
|
||||
self.entity
|
||||
@ -272,6 +272,7 @@ impl LivingEntity {
|
||||
self.entity
|
||||
.velocity
|
||||
.store(velo.multiply(multiplier, 1.0, multiplier));
|
||||
self.entity.check_block_collision().await;
|
||||
}
|
||||
|
||||
async fn tick_effects(&self) {
|
||||
@ -297,7 +298,7 @@ impl LivingEntity {
|
||||
impl EntityBase for LivingEntity {
|
||||
async fn tick(&self, server: &Server) {
|
||||
self.entity.tick(server).await;
|
||||
self.tick_move();
|
||||
self.tick_move().await;
|
||||
self.tick_effects().await;
|
||||
if self.time_until_regen.load(Relaxed) > 0 {
|
||||
self.time_until_regen.fetch_sub(1, Relaxed);
|
||||
|
||||
@ -484,6 +484,34 @@ impl Entity {
|
||||
// } else {
|
||||
// }
|
||||
}
|
||||
|
||||
pub async fn check_block_collision(&self) {
|
||||
let aabb = self.bounding_box.load();
|
||||
let blockpos = BlockPos::new(
|
||||
(aabb.min.x + 0.001).floor() as i32,
|
||||
(aabb.min.y + 0.001).floor() as i32,
|
||||
(aabb.min.z + 0.001).floor() as i32,
|
||||
);
|
||||
let blockpos1 = BlockPos::new(
|
||||
(aabb.max.x - 0.001).floor() as i32,
|
||||
(aabb.max.y - 0.001).floor() as i32,
|
||||
(aabb.max.z - 0.001).floor() as i32,
|
||||
);
|
||||
let world = self.world.read().await;
|
||||
|
||||
for x in blockpos.0.x..=blockpos1.0.x {
|
||||
for y in blockpos.0.y..=blockpos1.0.y {
|
||||
for z in blockpos.0.z..=blockpos1.0.z {
|
||||
let pos = BlockPos::new(x, y, z);
|
||||
let (block, state) = world.get_block_and_block_state(&pos).await.unwrap();
|
||||
world
|
||||
.block_registry
|
||||
.on_entity_collision(block, &world, self, pos, state)
|
||||
.await;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
|
||||
@ -64,7 +64,7 @@ use pumpkin_protocol::{
|
||||
codec::var_int::VarInt,
|
||||
};
|
||||
use pumpkin_registry::DimensionType;
|
||||
use pumpkin_util::math::{position::BlockPos, vector3::Vector3};
|
||||
use pumpkin_util::math::{boundingbox::BoundingBox, position::BlockPos, vector3::Vector3};
|
||||
use pumpkin_util::math::{position::chunk_section_from_pos, vector2::Vector2};
|
||||
use pumpkin_util::text::{TextComponent, color::NamedColor};
|
||||
use pumpkin_world::world::BlockFlags;
|
||||
@ -1070,6 +1070,23 @@ impl World {
|
||||
None
|
||||
}
|
||||
|
||||
pub async fn get_entities_at_box(&self, aabb: &BoundingBox) -> Vec<Arc<dyn EntityBase>> {
|
||||
let entities_guard = self.entities.read().await;
|
||||
entities_guard
|
||||
.values()
|
||||
.filter(|entity| entity.get_entity().bounding_box.load().intersects(aabb))
|
||||
.cloned()
|
||||
.collect()
|
||||
}
|
||||
pub async fn get_players_at_box(&self, aabb: &BoundingBox) -> Vec<Arc<Player>> {
|
||||
let players_guard = self.players.read().await;
|
||||
players_guard
|
||||
.values()
|
||||
.filter(|player| player.get_entity().bounding_box.load().intersects(aabb))
|
||||
.cloned()
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Retrieves a player by their unique UUID.
|
||||
///
|
||||
/// This function searches the world's active player list for a player with the specified UUID.
|
||||
|
||||
Loading…
Reference in New Issue
Block a user