bevy_combat 0.1.0

A sci-fi battle simulation implemented in the bevy engine.
Documentation
//! Illustrate laser beam attacks.

use bevy::prelude::*;

use crate::combat::{
    attack::{Attack, AttackResult},
    effects::{EffectLocation, Instigator, SourceTransform},
    CombatSystems, Target,
};

use super::animated::{AnimatedEffects, CreateAnimatedEffect};

#[derive(PartialEq, Clone, Hash, Debug, Eq, SystemLabel)]
pub enum BeamSystems {
    Set,
}

pub struct BeamEffectPlugin;

impl Plugin for BeamEffectPlugin {
    fn build(&self, app: &mut AppBuilder) {
        app.add_system_set_to_stage(
            CoreStage::Update,
            SystemSet::new()
                .label(BeamSystems::Set)
                .after(CombatSystems::Set)
                .with_system(spawn_beams.system())
                .with_system(beams_track_target.system()),
        );
    }
}

fn get_transform(start: Vec3, end: Vec3, width: f32) -> Transform {
    let delta = end - start;
    let angle = delta.y.atan2(delta.x) + std::f32::consts::FRAC_PI_2;
    let scale = Vec3::new(width, delta.length() / 4.0, 1.0);

    Transform::from_translation((end + start) / 2.0)
        * Transform::from_rotation(Quat::from_rotation_z(angle))
        * Transform::from_scale(scale)
}

fn spawn_beams(
    mut commands: Commands,
    query: Query<(
        &BeamStyle,
        &SourceTransform,
        &EffectLocation,
        &Target,
        &Instigator,
        &Attack,
    )>,
) {
    for (style, source, effect, target, instigator, attack) in query.iter() {
        let transform = get_transform(source.0.translation, effect.0, style.width);
        commands
            .spawn()
            .insert(CreateAnimatedEffect {
                transform,
                effect: style.effect,
                parent: None,
            })
            .insert(BeamTracking {
                target: target.0.expect("no target"),
                source: instigator.0,
                start: source.0.translation,
                end: effect.0,
                width: style.width,
                track_target: attack.result == AttackResult::Hit,
            });
    }
}

fn beams_track_target(
    mut query: Query<(&mut BeamTracking, &mut Transform)>,
    world_query: Query<&GlobalTransform>,
) {
    for (mut tracking, mut transform) in query.iter_mut() {
        if let Ok(start_t) = world_query.get_component::<GlobalTransform>(tracking.source) {
            tracking.start = start_t.translation;
        }
        if tracking.track_target {
            if let Ok(end_t) = world_query.get_component::<GlobalTransform>(tracking.target) {
                tracking.end = end_t.translation;
            }
        }
        *transform = get_transform(tracking.start, tracking.end, tracking.width);
    }
}

pub struct BeamStyle {
    pub effect: AnimatedEffects,
    pub width: f32,
}

#[derive(Clone, Copy)]
pub struct BeamTracking {
    pub target: Entity,
    pub source: Entity,
    pub start: Vec3,
    pub end: Vec3,
    pub width: f32,
    pub track_target: bool,
}