mirror of
https://github.com/horizon-rust/horizon.git
synced 2026-11-07 11:06:11 -10:00
Merge pull request #10022 from DUzzL/fix/9964-burning-powder-snow
Burning players and mobs destroy powder snow they stand in
This commit is contained in:
5 files changed
+100
-9
No files matched your search
@@ -4748,8 +4748,10 @@ impl Game<'_> {
|
||||
let mut events = std::mem::take(&mut w.player.events);
|
||||
// LayeredCauldronBlock.handleEntityOnFireInside along those moves.
|
||||
for event in &events {
|
||||
if let MotionEvent::CauldronExtinguish { pos } = event {
|
||||
game.cauldron_extinguish(*pos);
|
||||
match event {
|
||||
MotionEvent::CauldronExtinguish { pos } => game.cauldron_extinguish(*pos),
|
||||
MotionEvent::PowderSnowMelt { pos } => game.powder_snow_melt(*pos),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
// Entity.baseTick: rain puts out a burning player.
|
||||
|
||||
@@ -34,6 +34,18 @@ impl Game<'_> {
|
||||
}
|
||||
self.extinguish_sound([at.x, at.y, at.z]);
|
||||
}
|
||||
/// `PowderSnowBlock.entityInside` (runBefore EXTINGUISH) for a burning
|
||||
/// player: `Level.destroyBlock(pos, false)`. A client of a remote server
|
||||
/// leaves it to the server.
|
||||
pub(super) fn powder_snow_melt(&mut self, pos: BlockPos) {
|
||||
let at = [pos.x, pos.y, pos.z];
|
||||
if self.remote.is_some() || !self.is_powder_snow(at) {
|
||||
return;
|
||||
}
|
||||
self.with_level(|b, level, ticks| b.destroy_block(level, ticks, pos, false));
|
||||
let _ = self.process_level_events();
|
||||
self.world.modified.insert((at[0] >> 4, at[2] >> 4));
|
||||
}
|
||||
/// Whether the block at `pos` is powder snow.
|
||||
pub(super) fn is_powder_snow(&self, pos: [i32; 3]) -> bool {
|
||||
&*self
|
||||
|
||||
@@ -6411,6 +6411,9 @@ impl Game<'_> {
|
||||
// NetherPortalBlock.entityInside on the client's own level:
|
||||
// the portal overlay and its trigger sound (the server moves
|
||||
// the player).
|
||||
horizon_client::motion::MotionEvent::PowderSnowMelt { pos } => {
|
||||
self.powder_snow_melt(pos);
|
||||
}
|
||||
horizon_client::motion::MotionEvent::InsidePortal { .. } => {
|
||||
self.client_inside_nether_portal();
|
||||
}
|
||||
|
||||
@@ -840,6 +840,17 @@ fn effects_from_blocks(
|
||||
},
|
||||
));
|
||||
visited.push(p);
|
||||
// PowderSnowBlock.entityInside: a burning mob destroys the
|
||||
// block (with `mob_griefing`), then EXTINGUISH puts it out.
|
||||
if w.block_name(x, y, z) == "minecraft:powder_snow" {
|
||||
if m.is_on_fire() && w.mob_griefing {
|
||||
events.push(MobEvent::BreakBlock {
|
||||
pos: p,
|
||||
drop: false,
|
||||
});
|
||||
}
|
||||
m.extinguish_fire();
|
||||
}
|
||||
match w.block_name(x, y, z) {
|
||||
"minecraft:cactus" => {
|
||||
m.damage(1.0, Damage::of(DamageKind::Cactus));
|
||||
|
||||
@@ -229,6 +229,11 @@ pub enum MotionEvent {
|
||||
CauldronExtinguish {
|
||||
pos: BlockPos,
|
||||
},
|
||||
/// `PowderSnowBlock.entityInside` (runBefore EXTINGUISH): a burning
|
||||
/// player in powder snow destroys the block, without drops.
|
||||
PowderSnowMelt {
|
||||
pos: BlockPos,
|
||||
},
|
||||
/// `WitherRoseBlock.entityInside`: the player's box swept through a
|
||||
/// wither rose; the server adds its Wither unless peaceful.
|
||||
InsideWitherRose,
|
||||
@@ -435,9 +440,6 @@ pub struct PlayerMotion {
|
||||
/// `Entity.applyEffectsFromBlocks` runs after travel (always in the game;
|
||||
/// differential fixtures turn it off where their reference does).
|
||||
pub block_effects: bool,
|
||||
/// The powder snow blocks this tick's movement went into: the game
|
||||
/// destroys them if the player is burning, then puts the fire out.
|
||||
pub powder_snow_entered: Vec<BlockPos>,
|
||||
/// The last hurts taken (`hurt_blocking`), newest last, for tests and
|
||||
/// the control socket's `hurtlog`.
|
||||
pub hurt_log: Vec<crate::health::HurtRecord>,
|
||||
@@ -631,7 +633,6 @@ impl PlayerMotion {
|
||||
entity_id: 0,
|
||||
client_loaded: true,
|
||||
block_effects: true,
|
||||
powder_snow_entered: Vec::new(),
|
||||
hurt_log: Vec::new(),
|
||||
attack_log: Vec::new(),
|
||||
border: None,
|
||||
@@ -1677,11 +1678,17 @@ impl MotionWorld<'_, '_, '_> {
|
||||
groups += 1;
|
||||
last_step = step;
|
||||
}
|
||||
if groups == 1 && !damage_only {
|
||||
// A later group finds the fire already out.
|
||||
self.player.powder_snow_entered.push(p);
|
||||
// A later group finds the fire already out.
|
||||
if groups == 1 && self.player.fire_ticks > 0 && !self.player.spectator {
|
||||
self.player
|
||||
.events
|
||||
.push(MotionEvent::PowderSnowMelt { pos: p });
|
||||
}
|
||||
}
|
||||
if groups > 0 {
|
||||
// InsideBlockEffectType.EXTINGUISH: Entity.clearFire.
|
||||
self.player.fire_ticks = self.player.fire_ticks.min(0);
|
||||
}
|
||||
for _ in 0..groups {
|
||||
if self.player.health.dead_or_dying() {
|
||||
break;
|
||||
@@ -3548,6 +3555,62 @@ mod tests {
|
||||
assert_eq!(asks(&dry), 0);
|
||||
}
|
||||
|
||||
/// `PowderSnowBlock.entityInside`: a burning player in powder snow asks
|
||||
/// for the block to be destroyed, and EXTINGUISH puts the fire out.
|
||||
#[test]
|
||||
fn a_burning_player_melts_powder_snow() {
|
||||
let root = std::env::var_os("HORIZON_TEST_RESOURCES")
|
||||
.map(std::path::PathBuf::from)
|
||||
.unwrap_or_else(|| {
|
||||
Path::new(env!("CARGO_MANIFEST_DIR"))
|
||||
.join("../../.local/horizon-client")
|
||||
.join(horizon_core::VANILLA_VERSION)
|
||||
});
|
||||
if !root.join("movement-properties.nbt").exists() {
|
||||
return;
|
||||
}
|
||||
let assets = Assets::load(&root).unwrap();
|
||||
let render = assets.reload(&root).unwrap();
|
||||
let binding = crate::collisions::load(&root, assets.registry).unwrap();
|
||||
let resources = MotionResources::load(&root, &assets).unwrap();
|
||||
let mut scene = World::test_scene(&assets, &render, 1).unwrap();
|
||||
let snow = assets
|
||||
.registry
|
||||
.default_state("minecraft:powder_snow")
|
||||
.unwrap();
|
||||
scene.set(20, 4, 20, snow).unwrap();
|
||||
let mut run = |fire: i32| {
|
||||
let mut player = PlayerMotion::new(&resources, Vec3::new(20.5, 4.0, 20.5)).unwrap();
|
||||
player.fire_ticks = fire;
|
||||
MotionWorld {
|
||||
entities: &[],
|
||||
player: &mut player,
|
||||
scene: &mut scene,
|
||||
assets: &assets,
|
||||
collision_binding: &binding,
|
||||
resources: &resources,
|
||||
health: &resources.health,
|
||||
}
|
||||
.controlled_simulation_tick(Default::default())
|
||||
.unwrap();
|
||||
player
|
||||
};
|
||||
let burning = run(100);
|
||||
assert!(burning.fire_ticks <= 0, "powder snow must put the fire out");
|
||||
assert!(
|
||||
burning
|
||||
.events
|
||||
.iter()
|
||||
.any(|e| matches!(e, MotionEvent::PowderSnowMelt { .. }))
|
||||
);
|
||||
let dry = run(-20);
|
||||
assert!(
|
||||
!dry.events
|
||||
.iter()
|
||||
.any(|e| matches!(e, MotionEvent::PowderSnowMelt { .. }))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn standing_in_a_wither_rose_asks_for_its_effect() {
|
||||
let root = std::env::var_os("HORIZON_TEST_RESOURCES")
|
||||
|
||||
Reference in new issue
Block a user