veloren/world/src/site2/tile.rs

303 lines
8.2 KiB
Rust
Raw Normal View History

2020-12-26 15:53:06 +00:00
use super::*;
use crate::util::DHashSet;
use common::spiral::Spiral2d;
2021-02-12 20:30:30 +00:00
use std::ops::Range;
2020-12-26 15:53:06 +00:00
pub const TILE_SIZE: u32 = 6;
2021-02-04 12:47:46 +00:00
pub const ZONE_SIZE: u32 = 16;
pub const ZONE_RADIUS: u32 = 16;
pub const TILE_RADIUS: u32 = ZONE_SIZE * ZONE_RADIUS;
2021-03-06 17:23:03 +00:00
#[allow(dead_code)]
2021-02-04 12:47:46 +00:00
pub const MAX_BLOCK_RADIUS: u32 = TILE_SIZE * TILE_RADIUS;
2020-12-26 15:53:06 +00:00
pub struct TileGrid {
2021-02-23 12:42:45 +00:00
pub(crate) bounds: Aabr<i32>, // Inclusive
2020-12-26 15:53:06 +00:00
zones: Grid<Option<Grid<Option<Tile>>>>,
}
2021-02-04 12:47:46 +00:00
impl Default for TileGrid {
fn default() -> Self {
2020-12-26 15:53:06 +00:00
Self {
2021-02-23 12:42:45 +00:00
bounds: Aabr::new_empty(Vec2::zero()),
2020-12-26 15:53:06 +00:00
zones: Grid::populate_from(Vec2::broadcast(ZONE_RADIUS as i32 * 2 + 1), |_| None),
}
}
2021-02-04 12:47:46 +00:00
}
2020-12-26 15:53:06 +00:00
2021-02-04 12:47:46 +00:00
impl TileGrid {
pub fn get(&self, tpos: Vec2<i32>) -> &Tile {
static EMPTY: Tile = Tile::empty();
2020-12-26 15:53:06 +00:00
let tpos = tpos + TILE_RADIUS as i32;
self.zones
2021-02-12 20:30:30 +00:00
.get(tpos.map(|e| e.div_euclid(ZONE_SIZE as i32)))
2021-02-06 23:53:25 +00:00
.and_then(|zone| {
zone.as_ref()?
.get(tpos.map(|e| e.rem_euclid(ZONE_SIZE as i32)))
})
2020-12-26 15:53:06 +00:00
.and_then(|tile| tile.as_ref())
.unwrap_or(&EMPTY)
2020-12-26 15:53:06 +00:00
}
2021-02-23 12:42:45 +00:00
// WILL NOT EXPAND BOUNDS!
2020-12-26 15:53:06 +00:00
pub fn get_mut(&mut self, tpos: Vec2<i32>) -> Option<&mut Tile> {
let tpos = tpos + TILE_RADIUS as i32;
2021-03-05 01:23:11 +00:00
self.zones
.get_mut(tpos.map(|e| e.div_euclid(ZONE_SIZE as i32)))
.and_then(|zone| {
zone.get_or_insert_with(|| {
Grid::populate_from(Vec2::broadcast(ZONE_SIZE as i32), |_| None)
})
.get_mut(tpos.map(|e| e.rem_euclid(ZONE_SIZE as i32)))
2021-03-06 17:23:03 +00:00
.map(|tile| tile.get_or_insert_with(Tile::empty))
2021-02-06 23:53:25 +00:00
})
2020-12-26 15:53:06 +00:00
}
2021-02-04 12:47:46 +00:00
pub fn set(&mut self, tpos: Vec2<i32>, tile: Tile) -> Option<Tile> {
2021-02-23 12:42:45 +00:00
self.bounds.expand_to_contain_point(tpos);
self.get_mut(tpos).map(|t| std::mem::replace(t, tile))
}
2021-03-05 01:23:11 +00:00
pub fn find_near<R>(
&self,
tpos: Vec2<i32>,
mut f: impl FnMut(Vec2<i32>, &Tile) -> Option<R>,
) -> Option<(R, Vec2<i32>)> {
2021-02-17 01:47:16 +00:00
const MAX_SEARCH_RADIUS_BLOCKS: u32 = 70;
2021-02-12 20:30:30 +00:00
const MAX_SEARCH_CELLS: u32 = ((MAX_SEARCH_RADIUS_BLOCKS / TILE_SIZE) * 2 + 1).pow(2);
2021-02-17 01:47:16 +00:00
Spiral2d::new()
.take(MAX_SEARCH_CELLS as usize)
.map(|r| tpos + r)
.find_map(|tpos| (&mut f)(tpos, self.get(tpos)).zip(Some(tpos)))
2021-02-12 20:30:30 +00:00
}
2021-03-05 01:23:11 +00:00
pub fn grow_aabr(
&self,
center: Vec2<i32>,
area_range: Range<u32>,
min_dims: Extent2<u32>,
) -> Result<Aabr<i32>, Aabr<i32>> {
let mut aabr = Aabr {
min: center,
max: center + 1,
};
2021-02-17 01:47:16 +00:00
if !self.get(center).is_empty() {
return Err(aabr);
};
2021-02-12 20:30:30 +00:00
let mut last_growth = 0;
for i in 0..32 {
2021-03-06 17:23:03 +00:00
if i - last_growth >= 4
|| aabr.size().product()
+ if i % 2 == 0 {
aabr.size().h
} else {
aabr.size().w
}
> area_range.end as i32
2021-03-05 01:23:11 +00:00
{
2021-02-12 20:30:30 +00:00
break;
} else {
2021-02-17 02:17:19 +00:00
// `center.sum()` to avoid biasing certain directions
match (i + center.sum().abs()) % 4 {
2021-03-05 01:23:11 +00:00
0 if (aabr.min.y..aabr.max.y + 1)
.all(|y| self.get(Vec2::new(aabr.max.x, y)).is_empty()) =>
{
2021-02-12 20:30:30 +00:00
aabr.max.x += 1;
last_growth = i;
},
2021-03-05 01:23:11 +00:00
1 if (aabr.min.x..aabr.max.x + 1)
.all(|x| self.get(Vec2::new(x, aabr.max.y)).is_empty()) =>
{
2021-02-12 20:30:30 +00:00
aabr.max.y += 1;
last_growth = i;
},
2021-03-05 01:23:11 +00:00
2 if (aabr.min.y..aabr.max.y + 1)
.all(|y| self.get(Vec2::new(aabr.min.x - 1, y)).is_empty()) =>
{
2021-02-12 20:30:30 +00:00
aabr.min.x -= 1;
last_growth = i;
},
2021-03-05 01:23:11 +00:00
3 if (aabr.min.x..aabr.max.x + 1)
.all(|x| self.get(Vec2::new(x, aabr.min.y - 1)).is_empty()) =>
{
2021-02-12 20:30:30 +00:00
aabr.min.y -= 1;
last_growth = i;
},
2021-02-12 20:30:30 +00:00
_ => {},
}
}
}
if aabr.size().product() as u32 >= area_range.start
&& aabr.size().w as u32 >= min_dims.w
&& aabr.size().h as u32 >= min_dims.h
{
Ok(aabr)
} else {
Err(aabr)
}
2021-02-04 12:47:46 +00:00
}
2021-11-01 22:53:38 +00:00
#[allow(dead_code)]
2021-03-05 01:23:11 +00:00
pub fn grow_organic(
&self,
rng: &mut impl Rng,
center: Vec2<i32>,
area_range: Range<u32>,
) -> Result<DHashSet<Vec2<i32>>, DHashSet<Vec2<i32>>> {
let mut tiles = DHashSet::default();
let mut open = Vec::new();
tiles.insert(center);
open.push(center);
while tiles.len() < area_range.end as usize && !open.is_empty() {
let tile = open.remove(rng.gen_range(0..open.len()));
for &rpos in CARDINALS.iter() {
let neighbor = tile + rpos;
if self.get(neighbor).is_empty() && !tiles.contains(&neighbor) {
tiles.insert(neighbor);
open.push(neighbor);
}
}
}
if tiles.len() >= area_range.start as usize {
Ok(tiles)
} else {
Err(tiles)
}
}
2021-02-04 12:47:46 +00:00
}
2021-02-12 20:30:30 +00:00
#[derive(Clone, PartialEq)]
2021-02-04 12:47:46 +00:00
pub enum TileKind {
Empty,
2021-02-23 12:42:45 +00:00
Hazard(HazardKind),
2021-02-17 01:47:16 +00:00
Field,
2021-03-05 01:52:19 +00:00
Plaza,
Road { a: u16, b: u16, w: u16 },
2021-03-03 13:31:25 +00:00
Building,
2021-02-17 01:47:16 +00:00
Castle,
2021-03-06 16:21:03 +00:00
Wall(Ori),
2021-03-11 21:17:31 +00:00
Tower(RoofKind),
2021-03-06 16:21:03 +00:00
Keep(KeepKind),
2021-03-11 21:17:31 +00:00
Gate,
2021-12-14 20:45:48 +00:00
GnarlingFortification,
2020-12-26 15:53:06 +00:00
}
2021-02-23 12:42:45 +00:00
#[derive(Clone, PartialEq)]
2020-12-26 15:53:06 +00:00
pub struct Tile {
2021-02-12 20:30:30 +00:00
pub(crate) kind: TileKind,
pub(crate) plot: Option<Id<Plot>>,
2021-09-19 17:24:14 +00:00
pub(crate) hard_alt: Option<i32>,
2020-12-26 15:53:06 +00:00
}
impl Tile {
pub const fn empty() -> Self {
2020-12-26 15:53:06 +00:00
Self {
2021-02-04 12:47:46 +00:00
kind: TileKind::Empty,
2020-12-26 15:53:06 +00:00
plot: None,
2021-09-19 17:24:14 +00:00
hard_alt: None,
2020-12-26 15:53:06 +00:00
}
}
2021-02-04 12:47:46 +00:00
2021-02-12 20:30:30 +00:00
/// Create a tile that is not associated with any plot.
2021-09-19 17:24:14 +00:00
pub const fn free(kind: TileKind) -> Self {
Self {
kind,
plot: None,
hard_alt: None,
}
}
2021-02-12 20:30:30 +00:00
2021-02-06 23:53:25 +00:00
pub fn is_empty(&self) -> bool { self.kind == TileKind::Empty }
2021-02-17 01:47:16 +00:00
2022-01-27 11:48:41 +00:00
pub fn is_natural(&self) -> bool { matches!(self.kind, TileKind::Empty | TileKind::Hazard(_)) }
2021-09-18 21:23:13 +00:00
pub fn is_road(&self) -> bool { matches!(self.kind, TileKind::Plaza | TileKind::Road { .. }) }
2021-02-17 01:47:16 +00:00
pub fn is_obstacle(&self) -> bool {
2021-09-18 17:56:12 +00:00
matches!(self.kind, TileKind::Hazard(_)) || self.is_building()
}
pub fn is_building(&self) -> bool {
2021-02-17 01:47:16 +00:00
matches!(
self.kind,
2021-09-18 17:56:12 +00:00
TileKind::Building | TileKind::Castle | TileKind::Wall(_)
2021-02-17 01:47:16 +00:00
)
}
2020-12-26 15:53:06 +00:00
}
2021-02-23 12:42:45 +00:00
#[derive(Copy, Clone, PartialEq)]
pub enum HazardKind {
Water,
Hill { gradient: f32 },
}
2021-03-06 16:21:03 +00:00
#[derive(Copy, Clone, PartialEq)]
pub enum KeepKind {
Middle,
Corner,
Wall(Ori),
}
2021-03-11 21:17:31 +00:00
#[derive(Copy, Clone, PartialEq)]
pub enum RoofKind {
Parapet,
Pyramid,
}
2021-03-06 16:21:03 +00:00
#[repr(u8)]
#[derive(Copy, Clone, PartialEq)]
pub enum Ori {
North = 0,
East = 1,
South = 2,
West = 3,
}
impl Ori {
2021-03-06 17:23:03 +00:00
pub fn dir(self) -> Vec2<i32> { CARDINALS[self as u8 as usize] }
2021-12-17 21:49:48 +00:00
pub fn cw(self) -> Self {
match self {
Ori::North => Ori::East,
Ori::East => Ori::South,
Ori::South => Ori::West,
Ori::West => Ori::North,
}
}
pub fn ccw(self) -> Self {
match self {
Ori::North => Ori::West,
Ori::East => Ori::North,
Ori::South => Ori::East,
Ori::West => Ori::South,
}
}
pub fn opposite(self) -> Self {
match self {
Ori::North => Ori::South,
Ori::East => Ori::West,
Ori::South => Ori::North,
Ori::West => Ori::East,
}
}
2021-12-19 04:58:10 +00:00
pub fn from_vec2(vec: Vec2<i32>) -> Self {
match vec {
vec if vec.x.abs() > vec.y.abs() && vec.x > 0 => Ori::East,
vec if vec.x.abs() > vec.y.abs() => Ori::West,
vec if vec.y > 0 => Ori::North,
_ => Ori::South,
}
}
2021-03-06 16:21:03 +00:00
}