alex/beta-minecraft-rust
Minecraft Beta 1.7.3 Rewrite
BRANCH main
SNAPSHOT Draw terrain GPU-driven from packed per-section buffers with GPU occlusion culling, add a render distance slider (2-128) with a quad/disk shape, cut chunk bookkeeping at high render distances, and fix a light underflow at y=0
DOWNLOAD
root crates server src entity lightning.rs
//! `EntityLightningBolt`: kept apart from other entities like `World.weatherEffects`.
use common::JavaRandom;
use common::entity::mob::MobKind;
use common::math::floor_f64;
use common::physics::Aabb;
use common::protocol::Packet;
use common::world::Block;
use glam::DVec3;
use super::Kind;
use crate::level::Level;
pub struct Lightning {
pub id: i32,
pub pos: DVec3,
rand: JavaRandom,
/// `lightningState`: 2 when it strikes, counting down between flashes.
state: i32,
/// `boltLivingTime`: flashes left.
flashes: i32,
}
impl Level {
/// Ignites the block at `position` if fire can burn there.
fn ignite(&mut self, [x, y, z]: [i32; 3]) {
if self.is_air(x, y, z) && crate::blocks::fire_fits(self, x, y, z) {
self.set_block_notify(x, y, z, Block::FIRE);
}
}
/// `World.addLightningBolt` with the `EntityLightningBolt` constructor: on hard difficulty
/// and up it sets fire to the ground around it.
pub fn strike_lightning(&mut self, pos: DVec3) {
let id = self.entities.allocate();
let mut rand = self.entities.random();
rand.next_long();
let flashes = rand.next_int(3) + 1;
let [x, y, z] = pos.to_array().map(floor_f64);
if self.difficulty >= 2 && self.chunks_exist_around(x, y, z, 10) {
self.ignite([x, y, z]);
for _ in 0..4 {
let spot = [x, y, z].map(|v| v + rand.next_int(3) - 1);
self.ignite(spot);
}
}
let [x, y, z] = pos.to_array().map(|v| floor_f64(v * 32.0));
self.events.push(Packet::Weather {
entity: id,
lightning: true,
x,
y,
z,
});
self.lightning.push(Lightning {
id,
pos,
rand,
state: 2,
flashes,
});
}
/// `EntityLightningBolt.onUpdate` for every bolt; their sounds are the clients'.
pub(super) fn tick_lightning(&mut self) {
let mut bolts = std::mem::take(&mut self.lightning);
bolts.retain_mut(|bolt| {
bolt.state -= 1;
if bolt.state < 0 {
if bolt.flashes == 0 {
return false;
}
if bolt.state < -bolt.rand.next_int(10) {
bolt.flashes -= 1;
bolt.state = 1;
bolt.rand.next_long();
let [x, y, z] = bolt.pos.to_array().map(floor_f64);
if self.chunks_exist_around(x, y, z, 10) {
self.ignite([x, y, z]);
}
}
}
if bolt.state >= 0 {
self.strike_entities(bolt.pos);
}
true
});
bolts.append(&mut self.lightning);
self.lightning = bolts;
}
/// `Entity.onStruckByLightning` for everything within three blocks.
fn strike_entities(&mut self, pos: DVec3) {
let area = Aabb::new(pos, pos + DVec3::Y * 6.0).inflate(DVec3::splat(3.0));
let ids: Vec<i32> = self
.entities
.iter()
.filter(|e| e.entity.bb.intersects(&area))
.map(|e| e.entity.id)
.collect();
for id in ids {
let Some(stored) = self.entities.get_mut(id) else {
continue;
};
// `EntityPig.onStruckByLightning`
if matches!(&stored.kind, Kind::Mob(mob) if mob.kind == MobKind::Pig) {
if stored.entity.dead {
continue;
}
stored.entity.dead = true;
let (pos, yaw, pitch) = (stored.entity.pos, stored.entity.yaw, stored.entity.pitch);
let mut pigman = self.mob_entity(MobKind::PigZombie, pos, yaw);
pigman.entity.set_location_and_angles(pos, yaw, pitch);
self.spawn(pigman);
continue;
}
if !stored.entity.immune_to_fire {
self.attack_entity(id, None, 5);
}
let Some(stored) = self.entities.get_mut(id) else {
continue;
};
let entity = &mut stored.entity;
entity.fire += 1;
if entity.fire == 0 {
entity.fire = 300;
}
if let Kind::Mob(mob) = &mut stored.kind
&& mob.kind == MobKind::Creeper
{
mob.powered = true;
}
}
}
}