bevy_combat 0.1.0

A sci-fi battle simulation implemented in the bevy engine.
Documentation
use bevy::{
    diagnostic::{FrameTimeDiagnosticsPlugin, LogDiagnosticsPlugin},
    math::Quat,
    prelude::*,
};

use bevy_combat::ai::{idle::IdleBehavior, movement::TurnToDestinationBehavior, AIPlugin};
use bevy_combat::combat::{
    mortal::{Health, MaxHealth},
    Target, Team,
};
use bevy_combat::{ai::aggression::*, combat::shields::Shield};
use bevy_combat::{
    combat::mortal::Mortal,
    fx::{animated::AnimatedEffects, death::DeathEffect},
    game::BaseGamePlugin,
    movement::*,
};
use rand::Rng;

pub struct PrintTimer(Timer);
pub struct Position(Transform);

fn main() {
    let mut app = App::build();
        app.add_plugin(LogDiagnosticsPlugin::default())
        .add_plugin(FrameTimeDiagnosticsPlugin::default())
        .add_plugin(BaseGamePlugin)
        .add_plugin(AIPlugin)
        .add_plugin(MovementPlugin)
        .add_plugin(bevy_combat::combat::CombatPlugin)
        .add_plugin(bevy_combat::fx::animated::AnimatedEffectsPlugin)
        .add_plugin(bevy_combat::fx::EffectsPlugin)
        .add_plugin(bevy_combat::fx::beams::BeamEffectPlugin)
        .insert_resource(bevy::log::LogSettings {
            level: bevy::log::Level::DEBUG,
            ..Default::default()
        })
        .add_plugins(DefaultPlugins)
        .add_startup_system(setup.system())
        .add_system(tick.system().label("Tick"));
        
        #[cfg(target_arch = "wasm32")]
        app.add_plugin(bevy_webgl2::WebGL2Plugin);

        app.run()
}

fn setup(
    mut commands: Commands,
    assets: Res<AssetServer>,
    mut materials: ResMut<Assets<ColorMaterial>>,
) {
    let mut rng = rand::thread_rng();

    let tile_size = Vec2::splat(16.0);

    let sprite_handle = materials.add(assets.load("art/smallship.png").into());
    let drones = materials.add(assets.load("art/drone.png").into());

    commands
        .spawn()
        .insert_bundle(OrthographicCameraBundle::new_2d())
        .insert(PrintTimer(Timer::from_seconds(1.0, true)))
        .insert(Position(Transform::from_translation(Vec3::new(
            0.0, 0.0, 1000.0,
        ))));

    commands.insert_resource(ClearColor(Color::rgb(0.8, 0.8, 0.8)));

    // Team 1
    for _i in 0..20 {
        let position =
            Vec2::new(-20.0, 0.0) + Vec2::new(rng.gen_range(-5.0..5.0), rng.gen_range(-20.0..20.0));
        let translation = (position * tile_size).extend(0.0);
        let rotation = Quat::from_rotation_z(rng.gen::<f32>());
        let scale = Vec3::splat(1.0);

        commands
            .spawn()
            .insert_bundle(SpriteBundle {
                material: sprite_handle.clone(),
                transform: Transform {
                    translation,
                    rotation,
                    scale,
                },
                sprite: Sprite::new(tile_size),
                ..Default::default()
            })
            .insert_bundle(MovementBundle {
                velocity: Velocity::default(),
                speed: Speed::default(),
                max_speed: MaxSpeed::default(),
                turn_speed: TurnSpeed::default(),
                max_turn_speed: MaxTurnSpeed::new(3.0),
                mass: Mass(1.0),
                thrust: Thrust(150.0),
                heading: Heading::default(),
            })
            .insert(IdleBehavior)
            .insert(TurnToDestinationBehavior {
                destination: Vec3::default(),
            })
            .insert(bevy_combat::ai::idle::RoamBehavior {
                centre: Vec3::default(),
                radius: 10.0,
            })
            .insert_bundle((
                AggroRadius { 0: 1000.0 },
                AggroLocation::default(),
                TargetingOrders {
                    preferred: AgentCategory::FIGHTER,
                    discouraged: AgentCategory::CRUISER,
                    target_same_team: false,
                },
                Target::default(),
                Team { 0: 1 },
                Health { 0: 100.0 },
                MaxHealth { 0: 100.0 },
                AgentCategory::FIGHTER,
                Mortal,
                RetargetBehavior {
                    interval: 4.0,
                    remaining_time: 4.0,
                },
            ))
            .insert_bundle((
                bevy_combat::combat::tools::Cooldown::new(1.0),
                bevy_combat::combat::tools::TargettedTool {
                    range: 100.0,
                    cone: 0.15,
                    armed: true,
                    firing: false,
                },
                bevy_combat::combat::effects::Effector {
                    spawn_effect: bevy_combat::templates::weapons::pulse_laser_attack,
                },
            ))
            .insert(DeathEffect {
                time_to_explosion: 0.1,
                time_to_smoke: 0.05,
                dying_explosion: AnimatedEffects::SmallExplosion,
                death_explosion: AnimatedEffects::MediumExplosion,
            })
            .insert(Shield {
                health: 100.0,
                radius: 32.0,
            });
    }

    // Team 2
    for _i in 0..60 {
        let drone_size = Vec2::splat(8.0);
        let position =
            Vec2::new(60.0, 0.0) + Vec2::new(rng.gen_range(-5.0..5.0), rng.gen_range(-20.0..20.0));
        let translation = (position * drone_size).extend(0.0);
        let rotation = Quat::from_rotation_z(rng.gen::<f32>());
        let scale = Vec3::splat(1.0);

        commands
            .spawn()
            .insert_bundle(SpriteBundle {
                material: drones.clone(),
                transform: Transform {
                    translation,
                    rotation,
                    scale,
                },
                sprite: Sprite::new(drone_size),
                ..Default::default()
            })
            .insert_bundle(MovementBundle {
                velocity: Velocity::default(),
                speed: Speed::default(),
                max_speed: MaxSpeed::default(),
                turn_speed: TurnSpeed::default(),
                max_turn_speed: MaxTurnSpeed::new(4.0),
                mass: Mass(1.0),
                thrust: Thrust(250.0),
                heading: Heading::default(),
            })
            .insert(IdleBehavior)
            .insert(TurnToDestinationBehavior {
                destination: Vec3::default(),
            })
            .insert(bevy_combat::ai::idle::RoamBehavior {
                centre: Vec3::default(),
                radius: 10.0,
            })
            .insert_bundle((
                AggroRadius { 0: 1000.0 },
                AggroLocation::default(),
                TargetingOrders {
                    preferred: AgentCategory::FIGHTER,
                    discouraged: AgentCategory::CRUISER,
                    target_same_team: false,
                },
                Target::default(),
                Team { 0: 2 },
                Health { 0: 50.0 },
                MaxHealth { 0: 50.0 },
                AgentCategory::FIGHTER,
                Mortal,
                RetargetBehavior {
                    interval: 4.0,
                    remaining_time: 4.0,
                },
            ))
            .insert_bundle((
                bevy_combat::combat::tools::Cooldown::new(0.2),
                bevy_combat::combat::tools::TargettedTool {
                    range: 80.0,
                    cone: 0.3,
                    armed: true,
                    firing: false,
                },
                bevy_combat::combat::effects::Effector {
                    spawn_effect: bevy_combat::templates::weapons::small_pulse_laser_attack,
                },
            ))
            .insert(DeathEffect {
                time_to_explosion: 0.1,
                time_to_smoke: 0.05,
                dying_explosion: AnimatedEffects::SmallExplosion,
                death_explosion: AnimatedEffects::MediumExplosion,
            });
    }
}

fn tick(time: Res<Time>, sprites: Query<&Sprite>, mut query: Query<&mut PrintTimer>) {
    for mut timer in query.iter_mut() {
        timer.0.tick(time.delta());

        if timer.0.just_finished() {
            println!("Sprites: {}", sprites.iter().count(),);
        }
    }
}