1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
use bevy::prelude::*;
use de_behaviour::ChaseTargetEvent;
use de_combat::AttackEvent;
use de_construction::{AssemblyLine, ChangeDeliveryLocationEvent};
use de_core::{gamestate::GameState, objects::MovableSolid, schedule::InputSchedule};
use de_pathing::{PathQueryProps, PathTarget, UpdateEntityPathEvent};

use crate::selection::Selected;

pub(super) struct ExecutorPlugin;

impl Plugin for ExecutorPlugin {
    fn build(&self, app: &mut App) {
        app.add_event::<SendSelectedEvent>()
            .add_event::<DeliveryLocationSelectedEvent>()
            .add_event::<GroupAttackEvent>()
            .add_systems(
                InputSchedule,
                (
                    send_selected_system.in_set(CommandsSet::SendSelected),
                    delivery_location_system.in_set(CommandsSet::DeliveryLocation),
                    attack_system.in_set(CommandsSet::Attack),
                )
                    .run_if(in_state(GameState::Playing)),
            );
    }
}

#[derive(Copy, Clone, Hash, Debug, PartialEq, Eq, SystemSet)]
pub(crate) enum CommandsSet {
    SendSelected,
    DeliveryLocation,
    Attack,
}

/// Send this event to send all selected movable units to a point on the map.
#[derive(Event)]
pub(crate) struct SendSelectedEvent(Vec2);

impl SendSelectedEvent {
    pub(crate) fn new(target: Vec2) -> Self {
        Self(target)
    }

    fn target(&self) -> Vec2 {
        self.0
    }
}

/// Send this event to set manufacturing delivery location for all selected
/// building with a factory.
#[derive(Event)]
pub(crate) struct DeliveryLocationSelectedEvent(Vec2);

impl DeliveryLocationSelectedEvent {
    pub(crate) fn new(target: Vec2) -> Self {
        Self(target)
    }

    fn target(&self) -> Vec2 {
        self.0
    }
}

/// Send this event to attack an enemy with all selected movable units. The
/// target must be an enemy entity.
#[derive(Event)]
pub(crate) struct GroupAttackEvent(Entity);

impl GroupAttackEvent {
    pub(crate) fn new(target: Entity) -> Self {
        Self(target)
    }

    fn target(&self) -> Entity {
        self.0
    }
}

type SelectedMovable = (With<Selected>, With<MovableSolid>);

fn send_selected_system(
    mut send_events: EventReader<SendSelectedEvent>,
    selected: Query<Entity, SelectedMovable>,
    mut path_events: EventWriter<UpdateEntityPathEvent>,
    mut chase_events: EventWriter<ChaseTargetEvent>,
) {
    if let Some(send) = send_events.read().last() {
        for entity in selected.iter() {
            chase_events.send(ChaseTargetEvent::new(entity, None));
            path_events.send(UpdateEntityPathEvent::new(
                entity,
                PathTarget::new(send.target(), PathQueryProps::exact(), false),
            ));
        }
    }
}

type SelectedFactory = (With<Selected>, With<AssemblyLine>);

fn delivery_location_system(
    mut in_events: EventReader<DeliveryLocationSelectedEvent>,
    selected: Query<Entity, SelectedFactory>,
    mut out_events: EventWriter<ChangeDeliveryLocationEvent>,
) {
    if let Some(event) = in_events.read().last() {
        for entity in selected.iter() {
            out_events.send(ChangeDeliveryLocationEvent::new(entity, event.target()));
        }
    }
}

fn attack_system(
    mut group_events: EventReader<GroupAttackEvent>,
    selected: Query<Entity, SelectedMovable>,
    mut individual_events: EventWriter<AttackEvent>,
) {
    if let Some(group_event) = group_events.read().last() {
        for attacker in selected.iter() {
            individual_events.send(AttackEvent::new(attacker, group_event.target()));
        }
    }
}