feat(block): implement vault and trial spawner block logic
This commit is contained in:
parent
55312adfa1
commit
d0cc8a2b51
@ -110,3 +110,5 @@ pub mod coral;
|
||||
pub mod abstract_wall_mounting;
|
||||
|
||||
pub mod beacon;
|
||||
pub mod trial_spawner;
|
||||
pub mod vault;
|
||||
|
||||
133
crates/pumpkin/src/block/blocks/trial_spawner.rs
Normal file
133
crates/pumpkin/src/block/blocks/trial_spawner.rs
Normal file
@ -0,0 +1,133 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use pumpkin_data::block_properties::{
|
||||
BlockProperties, TrialSpawnerLikeProperties, TrialSpawnerState,
|
||||
};
|
||||
use pumpkin_data::item::Item;
|
||||
use pumpkin_data::item_stack::ItemStack;
|
||||
use pumpkin_data::sound::{Sound, SoundCategory};
|
||||
use pumpkin_macros::pumpkin_block;
|
||||
use pumpkin_world::world::BlockFlags;
|
||||
|
||||
use crate::block::entities::trial_spawner::TrialSpawnerBlockEntity;
|
||||
use crate::block::registry::BlockActionResult;
|
||||
use crate::block::{BlockBehaviour, BlockFuture, NormalUseArgs, PlacedArgs, UseWithItemArgs};
|
||||
|
||||
#[pumpkin_block("minecraft:trial_spawner")]
|
||||
pub struct TrialSpawnerBlock;
|
||||
|
||||
impl BlockBehaviour for TrialSpawnerBlock {
|
||||
fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> {
|
||||
Box::pin(async move {
|
||||
let entity = TrialSpawnerBlockEntity::new(*args.position);
|
||||
args.world.add_block_entity(Arc::new(entity));
|
||||
})
|
||||
}
|
||||
|
||||
fn normal_use<'a>(&'a self, args: NormalUseArgs<'a>) -> BlockFuture<'a, BlockActionResult> {
|
||||
Box::pin(async move {
|
||||
let state_id = args.world.get_block_state_id(args.position);
|
||||
let mut props = TrialSpawnerLikeProperties::from_state_id(state_id, args.block);
|
||||
|
||||
match props.trial_spawner_state {
|
||||
TrialSpawnerState::Inactive | TrialSpawnerState::WaitingForPlayers => {
|
||||
props.trial_spawner_state = TrialSpawnerState::Active;
|
||||
args.world
|
||||
.set_block_state(
|
||||
args.position,
|
||||
props.to_state_id(args.block),
|
||||
BlockFlags::NOTIFY_ALL,
|
||||
)
|
||||
.await;
|
||||
|
||||
args.world.play_sound(
|
||||
Sound::BlockTrialSpawnerDetectPlayer,
|
||||
SoundCategory::Blocks,
|
||||
&args.position.to_f64(),
|
||||
);
|
||||
args.world.play_sound(
|
||||
Sound::BlockTrialSpawnerOpenShutter,
|
||||
SoundCategory::Blocks,
|
||||
&args.position.to_f64(),
|
||||
);
|
||||
}
|
||||
TrialSpawnerState::Active => {
|
||||
// Eject trial rewards & key drop upon trial wave completion
|
||||
props.trial_spawner_state = TrialSpawnerState::WaitingForRewardEjection;
|
||||
args.world
|
||||
.set_block_state(
|
||||
args.position,
|
||||
props.to_state_id(args.block),
|
||||
BlockFlags::NOTIFY_ALL,
|
||||
)
|
||||
.await;
|
||||
|
||||
args.world.play_sound(
|
||||
Sound::BlockTrialSpawnerSpawnItemBegin,
|
||||
SoundCategory::Blocks,
|
||||
&args.position.to_f64(),
|
||||
);
|
||||
args.world.play_sound(
|
||||
Sound::BlockTrialSpawnerEjectItem,
|
||||
SoundCategory::Blocks,
|
||||
&args.position.to_f64(),
|
||||
);
|
||||
|
||||
let key_stack = ItemStack::new(1, &Item::TRIAL_KEY);
|
||||
args.world.drop_stack(args.position, key_stack).await;
|
||||
|
||||
props.trial_spawner_state = TrialSpawnerState::Cooldown;
|
||||
args.world
|
||||
.set_block_state(
|
||||
args.position,
|
||||
props.to_state_id(args.block),
|
||||
BlockFlags::NOTIFY_ALL,
|
||||
)
|
||||
.await;
|
||||
|
||||
args.world.play_sound(
|
||||
Sound::BlockTrialSpawnerCloseShutter,
|
||||
SoundCategory::Blocks,
|
||||
&args.position.to_f64(),
|
||||
);
|
||||
}
|
||||
TrialSpawnerState::Cooldown => {
|
||||
args.world.play_sound(
|
||||
Sound::BlockTrialSpawnerAmbient,
|
||||
SoundCategory::Blocks,
|
||||
&args.position.to_f64(),
|
||||
);
|
||||
}
|
||||
TrialSpawnerState::WaitingForRewardEjection | TrialSpawnerState::EjectingReward => {
|
||||
props.trial_spawner_state = TrialSpawnerState::Cooldown;
|
||||
args.world
|
||||
.set_block_state(
|
||||
args.position,
|
||||
props.to_state_id(args.block),
|
||||
BlockFlags::NOTIFY_ALL,
|
||||
)
|
||||
.await;
|
||||
}
|
||||
}
|
||||
|
||||
BlockActionResult::Success
|
||||
})
|
||||
}
|
||||
|
||||
fn use_with_item<'a>(
|
||||
&'a self,
|
||||
args: UseWithItemArgs<'a>,
|
||||
) -> BlockFuture<'a, BlockActionResult> {
|
||||
Box::pin(async move {
|
||||
self.normal_use(NormalUseArgs {
|
||||
server: args.server,
|
||||
world: args.world,
|
||||
block: args.block,
|
||||
position: args.position,
|
||||
player: args.player,
|
||||
hit: args.hit,
|
||||
})
|
||||
.await
|
||||
})
|
||||
}
|
||||
}
|
||||
145
crates/pumpkin/src/block/blocks/vault.rs
Normal file
145
crates/pumpkin/src/block/blocks/vault.rs
Normal file
@ -0,0 +1,145 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use pumpkin_data::BlockStateId;
|
||||
use pumpkin_data::block_properties::{
|
||||
BlockProperties, LadderLikeProperties, VaultLikeProperties, VaultState,
|
||||
};
|
||||
use pumpkin_data::item::Item;
|
||||
use pumpkin_data::item_stack::ItemStack;
|
||||
use pumpkin_data::sound::{Sound, SoundCategory};
|
||||
use pumpkin_macros::pumpkin_block;
|
||||
use pumpkin_world::world::BlockFlags;
|
||||
|
||||
use crate::block::entities::vault::VaultBlockEntity;
|
||||
use crate::block::registry::BlockActionResult;
|
||||
use crate::block::{
|
||||
BlockBehaviour, BlockFuture, NormalUseArgs, OnPlaceArgs, PlacedArgs, UseWithItemArgs,
|
||||
};
|
||||
|
||||
#[pumpkin_block("minecraft:vault")]
|
||||
pub struct VaultBlock;
|
||||
|
||||
impl BlockBehaviour for VaultBlock {
|
||||
fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> {
|
||||
Box::pin(async move {
|
||||
let mut props = LadderLikeProperties::default(args.block);
|
||||
props.facing = args
|
||||
.player
|
||||
.living_entity
|
||||
.entity
|
||||
.get_horizontal_facing()
|
||||
.opposite();
|
||||
props.to_state_id(args.block)
|
||||
})
|
||||
}
|
||||
|
||||
fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> {
|
||||
Box::pin(async move {
|
||||
let entity = VaultBlockEntity::new(*args.position);
|
||||
args.world.add_block_entity(Arc::new(entity));
|
||||
})
|
||||
}
|
||||
|
||||
fn use_with_item<'a>(
|
||||
&'a self,
|
||||
args: UseWithItemArgs<'a>,
|
||||
) -> BlockFuture<'a, BlockActionResult> {
|
||||
Box::pin(async move {
|
||||
let item_id = args.item_stack.item.id;
|
||||
let is_trial_key =
|
||||
item_id == Item::TRIAL_KEY.id || item_id == Item::OMINOUS_TRIAL_KEY.id;
|
||||
|
||||
if !is_trial_key {
|
||||
args.world.play_sound(
|
||||
Sound::BlockVaultInsertItemFail,
|
||||
SoundCategory::Blocks,
|
||||
&args.position.to_f64(),
|
||||
);
|
||||
return BlockActionResult::Success;
|
||||
}
|
||||
|
||||
if let Some(block_entity) = args.world.get_block_entity(args.position)
|
||||
&& let Some(vault_entity) = block_entity.as_any().downcast_ref::<VaultBlockEntity>()
|
||||
{
|
||||
let player_uuid = args.player.gameprofile.id;
|
||||
|
||||
if vault_entity.has_rewarded(&player_uuid).await {
|
||||
args.world.play_sound(
|
||||
Sound::BlockVaultRejectRewardedPlayer,
|
||||
SoundCategory::Blocks,
|
||||
&args.position.to_f64(),
|
||||
);
|
||||
return BlockActionResult::Success;
|
||||
}
|
||||
|
||||
vault_entity.mark_rewarded(player_uuid).await;
|
||||
|
||||
args.item_stack
|
||||
.decrement_unless_creative(args.player.gamemode.load(), 1);
|
||||
|
||||
args.world.play_sound(
|
||||
Sound::BlockVaultInsertItem,
|
||||
SoundCategory::Blocks,
|
||||
&args.position.to_f64(),
|
||||
);
|
||||
args.world.play_sound(
|
||||
Sound::BlockVaultOpenShutter,
|
||||
SoundCategory::Blocks,
|
||||
&args.position.to_f64(),
|
||||
);
|
||||
|
||||
let state_id = args.world.get_block_state_id(args.position);
|
||||
let mut props = VaultLikeProperties::from_state_id(state_id, args.block);
|
||||
props.vault_state = VaultState::Ejecting;
|
||||
args.world
|
||||
.set_block_state(
|
||||
args.position,
|
||||
props.to_state_id(args.block),
|
||||
BlockFlags::NOTIFY_ALL,
|
||||
)
|
||||
.await;
|
||||
|
||||
args.world.play_sound(
|
||||
Sound::BlockVaultEjectItem,
|
||||
SoundCategory::Blocks,
|
||||
&args.position.to_f64(),
|
||||
);
|
||||
|
||||
// Spawn trial vault loot (emeralds, diamond, iron)
|
||||
let loot_stacks = vec![
|
||||
ItemStack::new(4, &Item::EMERALD),
|
||||
ItemStack::new(1, &Item::DIAMOND),
|
||||
ItemStack::new(2, &Item::IRON_INGOT),
|
||||
];
|
||||
|
||||
for stack in loot_stacks {
|
||||
args.world.drop_stack(args.position, stack).await;
|
||||
}
|
||||
|
||||
props.vault_state = VaultState::Active;
|
||||
args.world
|
||||
.set_block_state(
|
||||
args.position,
|
||||
props.to_state_id(args.block),
|
||||
BlockFlags::NOTIFY_ALL,
|
||||
)
|
||||
.await;
|
||||
|
||||
return BlockActionResult::Success;
|
||||
}
|
||||
|
||||
BlockActionResult::Pass
|
||||
})
|
||||
}
|
||||
|
||||
fn normal_use<'a>(&'a self, args: NormalUseArgs<'a>) -> BlockFuture<'a, BlockActionResult> {
|
||||
Box::pin(async move {
|
||||
args.world.play_sound(
|
||||
Sound::BlockVaultInsertItemFail,
|
||||
SoundCategory::Blocks,
|
||||
&args.position.to_f64(),
|
||||
);
|
||||
BlockActionResult::Success
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -1,13 +1,16 @@
|
||||
use super::BlockEntity;
|
||||
use pumpkin_nbt::compound::NbtCompound;
|
||||
use pumpkin_util::math::position::BlockPos;
|
||||
use std::collections::HashSet;
|
||||
use std::pin::Pin;
|
||||
use tokio::sync::Mutex;
|
||||
use uuid::Uuid;
|
||||
|
||||
pub struct VaultBlockEntity {
|
||||
pub position: BlockPos,
|
||||
pub config: Mutex<Option<NbtCompound>>,
|
||||
pub server_data: Mutex<Option<NbtCompound>>,
|
||||
pub rewarded_players: Mutex<HashSet<Uuid>>,
|
||||
}
|
||||
|
||||
impl BlockEntity for VaultBlockEntity {
|
||||
@ -27,6 +30,7 @@ impl BlockEntity for VaultBlockEntity {
|
||||
position,
|
||||
config: Mutex::new(nbt.get_compound("config").cloned()),
|
||||
server_data: Mutex::new(nbt.get_compound("server_data").cloned()),
|
||||
rewarded_players: Mutex::new(HashSet::new()),
|
||||
}
|
||||
}
|
||||
|
||||
@ -66,12 +70,22 @@ impl BlockEntity for VaultBlockEntity {
|
||||
|
||||
impl VaultBlockEntity {
|
||||
pub const ID: &'static str = "minecraft:vault";
|
||||
|
||||
#[must_use]
|
||||
pub const fn new(position: BlockPos) -> Self {
|
||||
pub fn new(position: BlockPos) -> Self {
|
||||
Self {
|
||||
position,
|
||||
config: Mutex::const_new(None),
|
||||
server_data: Mutex::const_new(None),
|
||||
config: Mutex::new(None),
|
||||
server_data: Mutex::new(None),
|
||||
rewarded_players: Mutex::new(HashSet::new()),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn has_rewarded(&self, player_id: &Uuid) -> bool {
|
||||
self.rewarded_players.lock().await.contains(player_id)
|
||||
}
|
||||
|
||||
pub async fn mark_rewarded(&self, player_id: Uuid) {
|
||||
self.rewarded_players.lock().await.insert(player_id);
|
||||
}
|
||||
}
|
||||
|
||||
@ -133,6 +133,8 @@ use crate::block::blocks::stairs::StairBlock;
|
||||
use crate::block::blocks::tnt::TNTBlock;
|
||||
use crate::block::blocks::torches::TorchBlock;
|
||||
use crate::block::blocks::trapdoor::TrapDoorBlock;
|
||||
use crate::block::blocks::trial_spawner::TrialSpawnerBlock;
|
||||
use crate::block::blocks::vault::VaultBlock;
|
||||
use crate::block::blocks::vine::VineBlock;
|
||||
use crate::block::blocks::walls::WallBlock;
|
||||
use crate::block::blocks::wither_skull::WitherSkeletonSkullBlock;
|
||||
@ -248,6 +250,8 @@ pub fn default_registry() -> Arc<BlockRegistry> {
|
||||
manager.register(SugarCaneBlock);
|
||||
manager.register(VineBlock);
|
||||
manager.register(TNTBlock);
|
||||
manager.register(TrialSpawnerBlock);
|
||||
manager.register(VaultBlock);
|
||||
manager.register(BushBlock);
|
||||
manager.register(FlowerBlock);
|
||||
manager.register(PotatoBlock);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user