158 lines
5.2 KiB
Rust
158 lines
5.2 KiB
Rust
use pumpkin_data::Block;
|
|
use pumpkin_data::BlockStateId;
|
|
use pumpkin_data::block_properties::{BlockProperties, TntLikeProperties};
|
|
use pumpkin_data::entity::EntityType;
|
|
use pumpkin_data::item::Item;
|
|
use pumpkin_data::sound::SoundCategory;
|
|
use pumpkin_data::translation;
|
|
use pumpkin_macros::pumpkin_block;
|
|
use pumpkin_util::GameMode;
|
|
use pumpkin_util::math::position::BlockPos;
|
|
use pumpkin_util::math::vector3::Vector3;
|
|
use pumpkin_util::text::TextComponent;
|
|
use pumpkin_world::world::BlockFlags;
|
|
use rand::RngExt;
|
|
use std::sync::Arc;
|
|
|
|
use super::redstone::block_receives_redstone_power;
|
|
use crate::block::registry::BlockActionResult;
|
|
use crate::block::{
|
|
BlockBehaviour, BrokenArgs, ExplodeArgs, OnNeighborUpdateArgs, OnProjectileHitArgs, PlacedArgs,
|
|
UseWithItemArgs,
|
|
};
|
|
use crate::entity::Entity;
|
|
use crate::entity::tnt::TNTEntity;
|
|
use crate::world::World;
|
|
|
|
#[pumpkin_block("minecraft:tnt")]
|
|
pub struct TNTBlock;
|
|
|
|
const DEFAULT_FUSE: u32 = 80;
|
|
const DEFAULT_POWER: f32 = 4.0;
|
|
|
|
impl TNTBlock {
|
|
pub fn prime(world: &Arc<World>, location: &BlockPos) -> bool {
|
|
if !world.level_info.load().game_rules.tnt_explodes {
|
|
return false;
|
|
}
|
|
|
|
let mut event = crate::plugin::api::events::block::tnt_prime::TNTPrimeEvent::new(
|
|
*location,
|
|
"REDSTONE".to_string(),
|
|
);
|
|
if let Some(server) = world.server.upgrade() {
|
|
server.plugin_manager.fire_blocking(&server, &mut event);
|
|
}
|
|
if event.cancelled {
|
|
return false;
|
|
}
|
|
|
|
let spawn_pos = Vector3::new(
|
|
location.0.x as f64 + 0.5,
|
|
location.0.y as f64,
|
|
location.0.z as f64 + 0.5,
|
|
);
|
|
let entity = Entity::new(world.clone(), spawn_pos, &EntityType::TNT);
|
|
let mut prime_event =
|
|
crate::plugin::api::events::entity::explosion_prime::ExplosionPrimeEvent::new(
|
|
entity.entity_id,
|
|
DEFAULT_POWER,
|
|
false,
|
|
);
|
|
if let Some(server) = world.server.upgrade() {
|
|
server
|
|
.plugin_manager
|
|
.fire_blocking(&server, &mut prime_event);
|
|
}
|
|
if prime_event.cancelled {
|
|
return false;
|
|
}
|
|
|
|
let tnt = Arc::new(TNTEntity::new(entity, DEFAULT_POWER, DEFAULT_FUSE));
|
|
world.spawn_entity(tnt);
|
|
world.play_sound(
|
|
pumpkin_data::sound::Sound::EntityTntPrimed,
|
|
SoundCategory::Blocks,
|
|
&spawn_pos,
|
|
);
|
|
world.set_block_state(location, BlockStateId::AIR, BlockFlags::NOTIFY_ALL);
|
|
true
|
|
}
|
|
}
|
|
|
|
impl BlockBehaviour for TNTBlock {
|
|
fn use_with_item(&self, args: UseWithItemArgs<'_>) -> BlockActionResult {
|
|
let item_id = args.item_stack.item.id;
|
|
if item_id != Item::FLINT_AND_STEEL.id && item_id != Item::FIRE_CHARGE.id {
|
|
return BlockActionResult::PassToDefaultBlockAction;
|
|
}
|
|
|
|
if Self::prime(args.world, args.position) {
|
|
if args.player.gamemode.load() != GameMode::Creative {
|
|
if item_id == Item::FLINT_AND_STEEL.id {
|
|
let _ = args.item_stack.damage_item(1);
|
|
} else {
|
|
args.item_stack.decrement(1);
|
|
}
|
|
}
|
|
BlockActionResult::Success
|
|
} else if !args.world.level_info.load().game_rules.tnt_explodes {
|
|
args.player.send_system_message_raw(
|
|
&TextComponent::translate(translation::java::BLOCK_MINECRAFT_TNT_DISABLED, []),
|
|
true,
|
|
);
|
|
BlockActionResult::Pass
|
|
} else {
|
|
BlockActionResult::Success
|
|
}
|
|
}
|
|
|
|
fn placed(&self, args: PlacedArgs<'_>) {
|
|
if args.block != Block::from_state_id(args.old_state_id)
|
|
&& block_receives_redstone_power(args.world, args.position)
|
|
{
|
|
Self::prime(args.world, args.position);
|
|
}
|
|
}
|
|
|
|
fn on_neighbor_update(&self, args: OnNeighborUpdateArgs<'_>) {
|
|
if block_receives_redstone_power(args.world, args.position) {
|
|
Self::prime(args.world, args.position);
|
|
}
|
|
}
|
|
|
|
fn broken(&self, args: BrokenArgs<'_>) {
|
|
if args.player.gamemode.load() != GameMode::Creative {
|
|
let props = TntLikeProperties::from_state_id(args.state.id, args.block);
|
|
if props.r#unstable {
|
|
Self::prime(args.world, args.position);
|
|
}
|
|
}
|
|
}
|
|
|
|
fn on_projectile_hit(&self, args: OnProjectileHitArgs<'_>) {
|
|
if args.projectile.get_entity().is_on_fire() {
|
|
Self::prime(args.world, args.position);
|
|
}
|
|
}
|
|
|
|
fn explode(&self, args: ExplodeArgs<'_>) {
|
|
if !args.world.level_info.load().game_rules.tnt_explodes {
|
|
return;
|
|
}
|
|
let spawn_pos = Vector3::new(
|
|
args.position.0.x as f64 + 0.5,
|
|
args.position.0.y as f64,
|
|
args.position.0.z as f64 + 0.5,
|
|
);
|
|
let entity = Entity::new(args.world.clone(), spawn_pos, &EntityType::TNT);
|
|
let fuse = rand::rng().random_range(0..DEFAULT_FUSE / 4) + DEFAULT_FUSE / 8;
|
|
let tnt = Arc::new(TNTEntity::new(entity, DEFAULT_POWER, fuse));
|
|
args.world.spawn_entity(tnt);
|
|
}
|
|
|
|
fn should_drop_items_on_explosion(&self) -> bool {
|
|
false
|
|
}
|
|
}
|