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
//! contains system for player-controlled interactions

use std::cmp;

use specs::{prelude::*, System};

use crate::{actions::Action, engine::components::UnitComponent, unit::UnitType, Player, Warrior};

/// This system defines all of the interactions that are possible for the
/// player-controlled [`Warrior`](crate::warrior::Warrior). The `play_turn`
/// method is called on [`Player`](crate::player::Player), passing a `&mut`
/// warrior whose actions must be specified.
pub struct PlayerSystem {
    pub player: Box<dyn Player + Send + Sync>,
}

impl PlayerSystem {
    pub fn new(player: impl Player + Send + Sync + 'static) -> PlayerSystem {
        PlayerSystem {
            player: Box::new(player),
        }
    }
}

impl<'a> System<'a> for PlayerSystem {
    type SystemData = (Entities<'a>, WriteStorage<'a, UnitComponent>);

    fn run(&mut self, (entities, mut units): Self::SystemData) {
        let mut all_units = (&entities, &mut units).join();
        let warrior_unit = all_units
            .find(|(_, comp)| match comp.unit.unit_type {
                UnitType::Warrior(_) => true,
                _ => false,
            })
            .unwrap();
        let (_, mut warrior_comp) = warrior_unit;
        let mut other_units: Vec<(Entity, &mut UnitComponent)> = all_units
            .by_ref()
            .filter(|(_, comp)| match comp.unit.unit_type {
                UnitType::Warrior(_) => false,
                _ => true,
            })
            .collect();
        let unit_in_range = {
            let (wx, _) = warrior_comp.unit.position;
            other_units.iter_mut().find(|(_, comp)| {
                let (sx, _) = comp.unit.position;
                (wx - sx).abs() == 1
            })
        };
        let (path_clear, captive_found) = match unit_in_range {
            Some((_, comp)) => (false, comp.unit.unit_type == UnitType::Captive),
            None => (true, false),
        };
        let (health, _) = warrior_comp.unit.hp;
        let mut warrior = Warrior::new(path_clear, captive_found, health);
        self.player.play_turn(&mut warrior);

        if let Some(action) = warrior.action {
            match action {
                Action::Walk => {
                    if path_clear {
                        println!("{:?} walks forward", warrior_comp.unit.unit_type);
                        let (x, y) = warrior_comp.unit.position;
                        warrior_comp.unit.position = (x + 1, y);
                    } else {
                        let (_, enemy_comp) = unit_in_range.unwrap();
                        println!(
                            "{warrior:?} bumps into {enemy:?}",
                            warrior = warrior_comp.unit.unit_type,
                            enemy = enemy_comp.unit.unit_type
                        );
                    }
                }
                Action::Attack => {
                    if path_clear {
                        println!("{:?} attacks and hits nothing", warrior_comp.unit.unit_type);
                    } else {
                        let (enemy_entity, enemy_comp) = unit_in_range.unwrap();
                        println!(
                            "{warrior:?} attacks {enemy:?}",
                            warrior = warrior_comp.unit.unit_type,
                            enemy = enemy_comp.unit.unit_type
                        );
                        let (current, max) = enemy_comp.unit.hp;
                        let remaining = cmp::max(current - warrior_comp.unit.atk, 0);
                        println!(
                            "{enemy:?} takes {atk} damage, {remaining} HP left",
                            enemy = enemy_comp.unit.unit_type,
                            atk = warrior_comp.unit.atk,
                            remaining = remaining
                        );
                        enemy_comp.unit.hp = (remaining, max);

                        if remaining == 0 {
                            println!("{:?} is dead!", enemy_comp.unit.unit_type);
                            entities.delete(*enemy_entity).unwrap();
                        }
                    }
                }
                Action::Rest => {
                    let (current, max) = warrior_comp.unit.hp;
                    if current < max {
                        let restored = if (current + 2) > max {
                            max - current
                        } else {
                            2
                        };
                        println!(
                            "{warrior:?} regains {restored} HP from resting! Now {remaining} left",
                            warrior = warrior_comp.unit.unit_type,
                            restored = restored,
                            remaining = current + restored
                        );
                        warrior_comp.unit.hp = (current + restored, max);
                    } else {
                        println!(
                            "{:?} rests but is already at max HP",
                            warrior_comp.unit.unit_type
                        );
                    };
                }
                Action::Rescue => {
                    if path_clear {
                        println!(
                            "{:?} tries to rescue someone, but nobody is here",
                            warrior_comp.unit.unit_type
                        );
                    } else if captive_found {
                        let (captive_entity, captive_comp) = unit_in_range.unwrap();
                        println!(
                            "{warrior:?} frees {captive:?} from their bindings",
                            warrior = warrior_comp.unit.unit_type,
                            captive = captive_comp.unit.unit_type
                        );
                        println!("{:?} escapes!", captive_comp.unit.unit_type);
                        entities.delete(*captive_entity).unwrap();
                    } else {
                        let (_, enemy_comp) = unit_in_range.unwrap();
                        println!(
                            "{warrior:?} tries to rescue {enemy:?}, but it is not a captive!",
                            warrior = warrior_comp.unit.unit_type,
                            enemy = enemy_comp.unit.unit_type
                        );
                    }
                }
            }
        }
    }
}