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
use std::collections::HashMap;

#[derive(Copy, Clone, Serialize, Deserialize, Debug)]
pub enum GridType {
  Cardinal,
  Hex,
  Intercardinal,
}

impl Default for GridType {
  fn default() -> GridType {
    GridType::Cardinal
  }
}

#[derive(Default, Serialize, Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
pub struct Grid {
  pub tiles: Vec<Vec<i32>>,
  pub walkable_tiles: Vec<i32>,
  #[serde(default = "HashMap::new")]
  pub costs: HashMap<i32, i32>,
  #[serde(default = "HashMap::new")]
  pub extra_costs: HashMap<i32, HashMap<i32, i32>>,
  #[serde(default = "HashMap::new")]
  pub unstoppable_coords: HashMap<i32, HashMap<i32, bool>>,
  #[serde(default = "HashMap::new")]
  pub unwalkable_coords: HashMap<i32, HashMap<i32, bool>>,
  #[serde(default = "default_grid_type")]
  pub grid_type: GridType,
}

fn default_grid_type() -> GridType {
  GridType::Cardinal
}

impl Grid {
  pub fn new(
    tiles: Vec<Vec<i32>>,
    walkable_tiles: Vec<i32>,
    costs: HashMap<i32, i32>,
    extra_costs: HashMap<i32, HashMap<i32, i32>>,
    unstoppable_coords: HashMap<i32, HashMap<i32, bool>>,
    unwalkable_coords: HashMap<i32, HashMap<i32, bool>>,
    grid_type: GridType,
  ) -> Grid {
    Grid {
      tiles: tiles,
      walkable_tiles: walkable_tiles,
      costs: costs,
      extra_costs: extra_costs,
      unstoppable_coords: unstoppable_coords,
      unwalkable_coords: unwalkable_coords,
      grid_type: grid_type,
    }
  }

  pub fn is_cardinal(&self) -> bool {
    match self.grid_type {
      GridType::Cardinal => true,
      _ => false,
    }
  }

  pub fn is_hex(&self) -> bool {
    match self.grid_type {
      GridType::Hex => true,
      _ => false,
    }
  }

  pub fn is_intercardinal(&self) -> bool {
    match self.grid_type {
      GridType::Intercardinal => true,
      _ => false,
    }
  }

  pub fn in_grid(&self, x: i32, y: i32) -> bool {
    match self {
      _grid if (x < 0) | (y < 0) => false,
      grid if (y as usize) < grid.tiles.len() => (x as usize) < grid.tiles[y as usize].len(),
      _ => false,
    }
  }

  pub fn is_coord_stoppable(&self, x: &i32, y: &i32) -> bool {
    if get_nested_bool(&self.unstoppable_coords, x, y) {
      false
    } else {
      self.is_coord_walkable(x, y)
    }
  }

  pub fn is_coord_walkable(&self, x: &i32, y: &i32) -> bool {
    if get_nested_bool(&self.unwalkable_coords, x, y) {
      false
    } else {
      let tile = self.tiles[*y as usize][*x as usize];
      self.walkable_tiles.contains(&tile)
    }
  }

  pub fn get_coord_cost(&self, x: &i32, y: &i32) -> &i32 {
    match self.get_extra_cost(x, y) {
      Some(extra) => extra,
      _ => {
        let tile = self.tiles[*y as usize][*x as usize];
        match self.costs.get(&tile) {
          None => &1,
          Some(cost) => cost,
        }
      }
    }
  }

  fn get_extra_cost(&self, x: &i32, y: &i32) -> Option<&i32> {
    match self.extra_costs.get(&y) {
      Some(inner_hash) => inner_hash.get(&x),
      _ => None,
    }
  }
}

fn get_nested_bool(map: &HashMap<i32, HashMap<i32, bool>>, x: &i32, y: &i32) -> bool {
  match map.get(y) {
    Some(nested) => match nested.get(x) {
      Some(_) => true,
      _ => false,
    },
    _ => false,
  }
}