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
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 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).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,
  }
}