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
use crate::coord::Coord;
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(Clone, Default, Serialize, Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
pub struct Grid {
  pub tiles: Vec<Vec<i32>>,
  pub walkable_tiles: Vec<i32>,
  pub unstoppable_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 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 {
    let tile = self.tiles[y as usize][x as usize];
    if self.unstoppable_tiles.contains(&tile) | 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,
        }
      }
    }
  }

  pub fn get_adjacent(&self, coord: &Coord) -> Vec<Coord> {
    let mut adjacent = vec![];
    if self.in_grid(coord.x, coord.y - 1) {
      adjacent.push(Coord::new(coord.x, coord.y - 1));
    }
    if !self.is_cardinal() & self.in_grid(coord.x + 1, coord.y - 1) {
      adjacent.push(Coord::new(coord.x + 1, coord.y - 1));
    }
    if self.in_grid(coord.x + 1, coord.y) {
      adjacent.push(Coord::new(coord.x + 1, coord.y));
    }
    if self.is_intercardinal() & self.in_grid(coord.x + 1, coord.y + 1) {
      adjacent.push(Coord::new(coord.x + 1, coord.y + 1));
    }
    if self.in_grid(coord.x, coord.y + 1) {
      adjacent.push(Coord::new(coord.x, coord.y + 1));
    }
    if !self.is_cardinal() & self.in_grid(coord.x - 1, coord.y + 1) {
      adjacent.push(Coord::new(coord.x - 1, coord.y + 1));
    }
    if self.in_grid(coord.x - 1, coord.y) {
      adjacent.push(Coord::new(coord.x - 1, coord.y));
    }
    if self.is_intercardinal() & self.in_grid(coord.x - 1, coord.y - 1) {
      adjacent.push(Coord::new(coord.x - 1, coord.y - 1));
    }
    adjacent
  }

  fn get_extra_cost(&self, x: i32, y: i32) -> Option<i32> {
    match self.extra_costs.get(&y) {
      Some(inner_hash) => inner_hash.get(&x).cloned(),
      _ => 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,
  }
}