Implement fire/lava damage
Entities entering fire or lava now take damage and are extinguished upon entering water.
This commit is contained in:
parent
831275d155
commit
ee32c60427
@ -1,7 +1,9 @@
|
||||
use pumpkin_data::block_properties::HorizontalAxis;
|
||||
use pumpkin_data::entity::EntityType;
|
||||
use pumpkin_registry::DimensionType;
|
||||
use rand::Rng;
|
||||
use std::sync::Arc;
|
||||
use std::sync::atomic::Ordering;
|
||||
|
||||
use async_trait::async_trait;
|
||||
use pumpkin_data::{Block, BlockState};
|
||||
@ -13,6 +15,7 @@ use pumpkin_world::block::BlockDirection;
|
||||
use pumpkin_world::chunk::TickPriority;
|
||||
|
||||
use crate::block::pumpkin_block::PumpkinBlock;
|
||||
use crate::entity::EntityBase;
|
||||
use crate::entity::player::Player;
|
||||
use crate::server::Server;
|
||||
use crate::world::World;
|
||||
@ -65,6 +68,31 @@ impl PumpkinBlock for FireBlock {
|
||||
.await;
|
||||
}
|
||||
|
||||
async fn on_entity_collision(
|
||||
&self,
|
||||
_world: &Arc<World>,
|
||||
entity: &dyn EntityBase,
|
||||
_pos: BlockPos,
|
||||
_block: Block,
|
||||
_state: BlockState,
|
||||
) {
|
||||
let base_entity = entity.get_entity();
|
||||
if !base_entity.entity_type.fire_immune {
|
||||
let ticks = base_entity.fire_ticks.load(Ordering::Relaxed);
|
||||
if ticks < 0 {
|
||||
base_entity.fire_ticks.store(ticks + 1, Ordering::Relaxed);
|
||||
} else if base_entity.entity_type == EntityType::PLAYER {
|
||||
let rnd_ticks = rand::thread_rng().gen_range(1..3);
|
||||
base_entity
|
||||
.fire_ticks
|
||||
.store(ticks + rnd_ticks, Ordering::Relaxed);
|
||||
}
|
||||
if base_entity.fire_ticks.load(Ordering::Relaxed) >= 0 {
|
||||
base_entity.set_on_fire_for(8.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn get_state_for_neighbor_update(
|
||||
&self,
|
||||
world: &World,
|
||||
|
||||
@ -6,9 +6,9 @@ use pumpkin_macros::pumpkin_block;
|
||||
use pumpkin_util::math::position::BlockPos;
|
||||
use pumpkin_world::BlockStateId;
|
||||
|
||||
use crate::{block::pumpkin_fluid::PumpkinFluid, world::World};
|
||||
use crate::{block::pumpkin_fluid::PumpkinFluid, entity::EntityBase, world::World};
|
||||
|
||||
use super::flowing_fluid::FlowingFluid;
|
||||
use super::flowing::FlowingFluid;
|
||||
|
||||
#[pumpkin_block("minecraft:flowing_lava")]
|
||||
pub struct FlowingLava;
|
||||
@ -46,6 +46,13 @@ impl PumpkinFluid for FlowingLava {
|
||||
.schedule_fluid_tick(fluid.id, *block_pos, LAVA_FLOW_SPEED)
|
||||
.await;
|
||||
}
|
||||
|
||||
async fn on_entity_collision(&self, entity: &dyn EntityBase) {
|
||||
let base_entity = entity.get_entity();
|
||||
if !base_entity.entity_type.fire_immune {
|
||||
base_entity.set_on_fire_for(15.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
@ -1,3 +1,3 @@
|
||||
pub(crate) mod flowing_fluid;
|
||||
pub(crate) mod flowing;
|
||||
pub(crate) mod lava;
|
||||
pub(crate) mod water;
|
||||
@ -6,9 +6,9 @@ use pumpkin_macros::pumpkin_block;
|
||||
use pumpkin_util::math::position::BlockPos;
|
||||
use pumpkin_world::BlockStateId;
|
||||
|
||||
use crate::{block::pumpkin_fluid::PumpkinFluid, world::World};
|
||||
use crate::{block::pumpkin_fluid::PumpkinFluid, entity::EntityBase, world::World};
|
||||
|
||||
use super::flowing_fluid::FlowingFluid;
|
||||
use super::flowing::FlowingFluid;
|
||||
|
||||
#[pumpkin_block("minecraft:flowing_water")]
|
||||
pub struct FlowingWater;
|
||||
@ -46,6 +46,10 @@ impl PumpkinFluid for FlowingWater {
|
||||
.schedule_fluid_tick(fluid.id, *block_pos, WATER_FLOW_SPEED)
|
||||
.await;
|
||||
}
|
||||
|
||||
async fn on_entity_collision(&self, entity: &dyn EntityBase) {
|
||||
entity.get_entity().extinguish();
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
@ -42,8 +42,8 @@ use blocks::walls::WallBlock;
|
||||
use blocks::{
|
||||
chest::ChestBlock, furnace::FurnaceBlock, redstone::lever::LeverBlock, tnt::TNTBlock,
|
||||
};
|
||||
use fluids::lava::FlowingLava;
|
||||
use fluids::water::FlowingWater;
|
||||
use fluid::lava::FlowingLava;
|
||||
use fluid::water::FlowingWater;
|
||||
use loot::LootTableExt;
|
||||
use pumpkin_data::block_properties::Integer0To15;
|
||||
use pumpkin_data::entity::EntityType;
|
||||
@ -63,7 +63,7 @@ use crate::{block::blocks::jukebox::JukeboxBlock, entity::experience_orb::Experi
|
||||
use std::sync::Arc;
|
||||
|
||||
pub(crate) mod blocks;
|
||||
mod fluids;
|
||||
mod fluid;
|
||||
mod loot;
|
||||
pub mod pumpkin_block;
|
||||
pub mod pumpkin_fluid;
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::entity::player::Player;
|
||||
use crate::entity::{EntityBase, player::Player};
|
||||
use async_trait::async_trait;
|
||||
use pumpkin_data::{fluid::Fluid, item::Item};
|
||||
use pumpkin_protocol::server::play::SUseItemOn;
|
||||
@ -78,6 +78,8 @@ pub trait PumpkinFluid: Send + Sync {
|
||||
) {
|
||||
}
|
||||
|
||||
async fn on_entity_collision(&self, _entity: &dyn EntityBase) {}
|
||||
|
||||
async fn on_scheduled_tick(&self, _world: &Arc<World>, _fluid: &Fluid, _block_pos: &BlockPos) {}
|
||||
|
||||
async fn create_legacy_block(&self, _world: &World, _block_pos: &BlockPos) {}
|
||||
|
||||
@ -72,6 +72,13 @@ impl BlockRegistry {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn on_entity_collision_fluid(&self, fluid: &Fluid, entity: &dyn EntityBase) {
|
||||
let pumpkin_fluid = self.get_pumpkin_fluid(fluid);
|
||||
if let Some(pumpkin_fluid) = pumpkin_fluid {
|
||||
pumpkin_fluid.on_entity_collision(entity).await;
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn on_use(
|
||||
&self,
|
||||
block: &Block,
|
||||
|
||||
@ -138,6 +138,8 @@ pub struct Entity {
|
||||
pub invulnerable: AtomicBool,
|
||||
/// List of damage types this entity is immune to
|
||||
pub damage_immunities: Vec<DamageType>,
|
||||
pub fire_ticks: AtomicI32,
|
||||
pub has_visual_fire: AtomicBool,
|
||||
|
||||
pub portal_cooldown: AtomicU32,
|
||||
|
||||
@ -189,6 +191,8 @@ impl Entity {
|
||||
bounding_box_size: AtomicCell::new(bounding_box_size),
|
||||
invulnerable: AtomicBool::new(invulnerable),
|
||||
damage_immunities: Vec::new(),
|
||||
fire_ticks: AtomicI32::new(-1),
|
||||
has_visual_fire: AtomicBool::new(false),
|
||||
portal_cooldown: AtomicU32::new(0),
|
||||
portal_manager: Mutex::new(None),
|
||||
}
|
||||
@ -353,6 +357,22 @@ impl Entity {
|
||||
}
|
||||
}
|
||||
|
||||
/// Extinguishes this entity.
|
||||
pub fn extinguish(&self) {
|
||||
self.fire_ticks.store(0, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
pub fn set_on_fire_for(&self, seconds: f32) {
|
||||
self.set_on_fire_for_ticks((seconds * 20.0).floor() as u32);
|
||||
}
|
||||
|
||||
pub fn set_on_fire_for_ticks(&self, ticks: u32) {
|
||||
if self.fire_ticks.load(Ordering::Relaxed) < ticks as i32 {
|
||||
self.fire_ticks.store(ticks as i32, Ordering::Relaxed);
|
||||
}
|
||||
// TODO: defrost
|
||||
}
|
||||
|
||||
/// Sets the `Entity` yaw & pitch rotation
|
||||
pub fn set_rotation(&self, yaw: f32, pitch: f32) {
|
||||
// TODO
|
||||
@ -424,6 +444,13 @@ impl Entity {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn set_on_fire(&self, on_fire: bool) {
|
||||
if self.has_visual_fire.load(Ordering::Relaxed) != on_fire {
|
||||
self.has_visual_fire.store(on_fire, Ordering::Relaxed);
|
||||
self.set_flag(Flag::OnFire, on_fire).await;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_horizontal_facing(&self) -> HorizontalFacing {
|
||||
let adjusted_yaw = (self.yaw.load() % 360.0 + 360.0) % 360.0; // Normalize yaw to [0, 360)
|
||||
|
||||
@ -572,6 +599,12 @@ impl Entity {
|
||||
.on_entity_collision(block, &world, entity, pos, state)
|
||||
.await;
|
||||
}
|
||||
if let Ok(fluid) = world.get_fluid(&pos).await {
|
||||
world
|
||||
.block_registry
|
||||
.on_entity_collision_fluid(&fluid, entity)
|
||||
.await;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -584,8 +617,25 @@ impl EntityBase for Entity {
|
||||
false
|
||||
}
|
||||
|
||||
async fn tick(&self, _caller: &dyn EntityBase, _: &Server) {
|
||||
async fn tick(&self, caller: &dyn EntityBase, _: &Server) {
|
||||
self.tick_portal().await;
|
||||
let fire_ticks = self.fire_ticks.load(Ordering::Relaxed);
|
||||
if fire_ticks > 0 {
|
||||
if self.entity_type.fire_immune {
|
||||
self.fire_ticks.store(fire_ticks - 4, Ordering::Relaxed);
|
||||
if self.fire_ticks.load(Ordering::Relaxed) < 0 {
|
||||
self.extinguish();
|
||||
}
|
||||
} else {
|
||||
if fire_ticks % 20 == 0 {
|
||||
caller.damage(1.0, DamageType::ON_FIRE).await;
|
||||
}
|
||||
|
||||
self.fire_ticks.store(fire_ticks - 1, Ordering::Relaxed);
|
||||
}
|
||||
}
|
||||
self.set_on_fire(self.fire_ticks.load(Ordering::Relaxed) > 0)
|
||||
.await;
|
||||
// TODO: Tick
|
||||
}
|
||||
|
||||
@ -619,7 +669,13 @@ impl NBTStorage for Entity {
|
||||
"Rotation",
|
||||
NbtTag::List(vec![self.yaw.load().into(), self.pitch.load().into()].into_boxed_slice()),
|
||||
);
|
||||
nbt.put_short("Fire", self.fire_ticks.load(Relaxed) as i16);
|
||||
nbt.put_bool("OnGround", self.on_ground.load(Relaxed));
|
||||
nbt.put_bool("Invulnerable", self.invulnerable.load(Relaxed));
|
||||
nbt.put_int("PortalCooldown", self.portal_cooldown.load(Relaxed) as i32);
|
||||
if self.has_visual_fire.load(Relaxed) {
|
||||
nbt.put_bool("HasVisualFire", true);
|
||||
}
|
||||
|
||||
// todo more...
|
||||
}
|
||||
@ -640,8 +696,16 @@ impl NBTStorage for Entity {
|
||||
let pitch = rotation[1].extract_float().unwrap_or(0.0);
|
||||
self.set_rotation(yaw, pitch);
|
||||
self.head_yaw.store(yaw);
|
||||
self.fire_ticks
|
||||
.store(i32::from(nbt.get_short("Fire").unwrap_or(0)), Relaxed);
|
||||
self.on_ground
|
||||
.store(nbt.get_bool("OnGround").unwrap_or(false), Relaxed);
|
||||
self.invulnerable
|
||||
.store(nbt.get_bool("Invulnerable").unwrap_or(false), Relaxed);
|
||||
self.portal_cooldown
|
||||
.store(nbt.get_int("PortalCooldown").unwrap_or(0) as u32, Relaxed);
|
||||
self.has_visual_fire
|
||||
.store(nbt.get_bool("HasVisualFire").unwrap_or(false), Relaxed);
|
||||
// todo more...
|
||||
}
|
||||
}
|
||||
|
||||
@ -277,7 +277,6 @@ impl Server {
|
||||
}
|
||||
} else {
|
||||
// Player data exists but doesn't have a "Dimension" key.
|
||||
log::warn!("Player data missing 'Dimension' key, defaulting to Overworld.");
|
||||
let default_world_guard = self.worlds.read().await;
|
||||
let default_world = default_world_guard
|
||||
.first()
|
||||
|
||||
Loading…
Reference in New Issue
Block a user