mod bolt;
mod strikes;
mod visual;
use bevy::prelude::*;
pub use strikes::{LightningStrike, SPEED_OF_SOUND_MS, STRIKE_RADIUS_M, StrikeLightning, Thunder};
use crate::climate::Climate;
use crate::flash::LightningFlash;
use crate::plugin::{ElementsSystems, ensure_core};
use strikes::{ActiveBolt, Strikes};
pub const FLASH_LUMENS: f32 = 1.0e8;
pub(crate) const FLASH_HEIGHT_M: f32 = 300.0;
pub(crate) const FLASH_RANGE_M: f32 = 5000.0;
pub struct LightningPlugin;
impl Plugin for LightningPlugin {
fn build(&self, app: &mut App) {
ensure_core(app);
app.init_resource::<Climate>()
.init_resource::<LightningFlash>()
.init_resource::<Strikes>()
.add_message::<StrikeLightning>()
.add_message::<LightningStrike>()
.add_message::<Thunder>()
.add_systems(
Update,
(strikes::run_strikes, advance_bolts, write_flash)
.chain()
.in_set(ElementsSystems::Simulation),
);
if app.world().contains_resource::<Assets<Mesh>>() {
visual::plugin(app);
}
}
}
fn advance_bolts(
time: Res<Time<Virtual>>,
mut bolts: Query<(Entity, &mut ActiveBolt)>,
mut commands: Commands,
) {
let dt = time.delta_secs_f64();
for (entity, mut bolt) in &mut bolts {
let (from, to) = (bolt.age, bolt.age + dt);
bolt.mean = bolt.strokes.mean(from, to) as f32;
bolt.mean_first = bolt.strokes.mean_first(from, to) as f32;
bolt.age = to;
if bolt.strokes.finished(to) {
commands.entity(entity).despawn();
}
}
}
pub(crate) fn write_flash(bolts: Query<&ActiveBolt>, mut flash: ResMut<LightningFlash>) {
let brightest = bolts
.iter()
.filter(|b| b.mean > 0.0)
.max_by(|a, b| a.mean.total_cmp(&b.mean));
let next = match brightest {
Some(bolt) => LightningFlash {
position: bolt.bolt.top(),
intensity_cd: FLASH_LUMENS * bolt.mean / (4.0 * std::f32::consts::PI),
},
None => LightningFlash {
position: flash.position,
intensity_cd: 0.0,
},
};
if *flash != next {
*flash = next;
}
}