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
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227

use sc2_proto::raw;
use sc2_proto::spatial::{
    ActionSpatialUnitCommand,
    ActionSpatialCameraMove,
    ActionSpatialUnitSelectionPoint,
    ActionSpatialUnitSelectionRect,
    ActionSpatialUnitSelectionPoint_Type as ProtoPointSelectionType
};

use super::super::{ Result, FromProto, IntoSc2 };
use super::{ Tag, Ability, Point2, Point2I, Rect2I };

/// action target
#[derive(Debug, Copy, Clone)]
pub enum ActionTarget {
    /// target a unit with this action
    UnitTag(Tag),
    /// target a location with this action
    Location(Point2),
}

/// an action (command or ability) applied to a unit or set of units
#[derive(Debug, Clone)]
pub struct Action {
    /// the ability to invoke
    pub ability:            Ability,
    /// units that this action applies to
    pub unit_tags:          Vec<Tag>,
    /// target of the action
    pub target:             Option<ActionTarget>,
}

impl FromProto<raw::ActionRawUnitCommand> for Action {
    /// convert from protobuf data
    fn from_proto(action: raw::ActionRawUnitCommand) -> Result<Self> {
        Ok(
            Self {
                ability: Ability::from_proto(action.get_ability_id()as u32)?,
                unit_tags: {
                    let mut unit_tags = vec![ ];

                    for tag in action.get_unit_tags() {
                        unit_tags.push(*tag);
                    }

                    unit_tags
                },
                target: {
                    if action.has_target_unit_tag() {
                        Some(
                            ActionTarget::UnitTag(action.get_target_unit_tag())
                        )
                    }
                    else if action.has_target_world_space_pos() {
                        let pos = action.get_target_world_space_pos();
                        Some(
                            ActionTarget::Location(
                                Point2::new(pos.get_x(), pos.get_y())
                            )
                        )
                    }
                    else {
                        None
                    }
                },
            }
        )
    }
}

/// target of a feature layer command
#[derive(Debug, Copy, Clone)]
pub enum SpatialUnitCommandTarget {
    /// screen coordinate target
    Screen(Point2I),
    /// minimap coordinate target
    Minimap(Point2I),
}

/// type of point selection
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum PointSelectType {
    /// changes selection to unit (equal to normal click)
    Select,
    /// toggle selection of unit (equal to shift+click)
    Toggle,
    /// select all units of a given type (equal to ctrl+click)
    All,
    /// select all units of a given type additively (equal to shift+ctrl+click)
    AddAll
}

impl FromProto<ProtoPointSelectionType> for PointSelectType {
    fn from_proto(select_type: ProtoPointSelectionType) -> Result<Self> {
        Ok(
            match select_type {
                ProtoPointSelectionType::Select => PointSelectType::Select,
                ProtoPointSelectionType::Toggle => PointSelectType::Toggle,
                ProtoPointSelectionType::AllType => PointSelectType::All,
                ProtoPointSelectionType::AddAllType => PointSelectType::AddAll,
            }
        )
    }
}

/// feature layer action
#[derive(Debug, Clone)]
pub enum SpatialAction {
    /// issue a feature layer unit command
    UnitCommand {
        /// ability to invoke
        ability:            Ability,
        /// target of command
        target:             Option<SpatialUnitCommandTarget>,
        /// whether this action should replace or queue behind other actions
        queued:             bool
    },
    /// move the camera to a new location
    CameraMove {
        /// minimap location
        center_minimap:     Point2I
    },
    /// select a point on the screen
    SelectPoint {
        /// point in screen coordinates
        select_screen:      Point2I,
        /// point selection type
        select_type:        PointSelectType
    },
    /// select a rectangle on the screen
    SelectRect {
        /// rectangle in screen coordinates
        select_screen:      Vec<Rect2I>,
        /// whether selection is additive
        select_add:         bool
    }
}

impl FromProto<ActionSpatialUnitCommand> for SpatialAction {
    fn from_proto(cmd: ActionSpatialUnitCommand) -> Result<Self> {
        Ok(
            SpatialAction::UnitCommand {
                ability: Ability::from_proto(cmd.get_ability_id() as u32)?,
                queued: cmd.get_queue_command(),
                target: {
                    if cmd.has_target_screen_coord() {
                        let pos = cmd.get_target_screen_coord();
                        Some(
                            SpatialUnitCommandTarget::Screen(
                                Point2I::new(pos.get_x(), pos.get_y())
                            )
                        )
                    }
                    else if cmd.has_target_minimap_coord() {
                        let pos = cmd.get_target_minimap_coord();
                        Some(
                            SpatialUnitCommandTarget::Minimap(
                                Point2I::new(pos.get_x(), pos.get_y())
                            )
                        )
                    }
                    else {
                        None
                    }
                }
            }
        )
    }
}

impl FromProto<ActionSpatialCameraMove> for SpatialAction {
    fn from_proto(cmd: ActionSpatialCameraMove) -> Result<Self> {
        Ok(
            SpatialAction::CameraMove {
                center_minimap: {
                    let pos = cmd.get_center_minimap();
                    Point2I::new(pos.get_x(), pos.get_y())
                }
            }
        )
    }
}

impl FromProto<ActionSpatialUnitSelectionPoint> for SpatialAction {
    fn from_proto(cmd: ActionSpatialUnitSelectionPoint) -> Result<Self> {
        Ok(
            SpatialAction::SelectPoint {
                select_screen: {
                    let pos = cmd.get_selection_screen_coord();
                    Point2I::new(pos.get_x(), pos.get_y())
                },
                select_type: cmd.get_field_type().into_sc2()?
            }
        )
    }
}

impl FromProto<ActionSpatialUnitSelectionRect> for SpatialAction {
    fn from_proto(cmd: ActionSpatialUnitSelectionRect) -> Result<Self> {
        Ok(
            SpatialAction::SelectRect {
                select_screen: {
                    let mut rects = vec![ ];

                    for r in cmd.get_selection_screen_coord() {
                        rects.push(
                            Rect2I {
                                from: {
                                    let p = r.get_p0();
                                    Point2I::new(p.get_x(), p.get_y())
                                },
                                to: {
                                    let p = r.get_p1();
                                    Point2I::new(p.get_x(), p.get_y())
                                }
                            }
                        )
                    }

                    rects
                },
                select_add: cmd.get_selection_add()
            }
        )
    }
}