mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Initial implementation of towns
This commit is contained in:
parent
d42485238e
commit
fe2ad92201
BIN
assets/world/structure/human/mage_tower.vox
(Stored with Git LFS)
BIN
assets/world/structure/human/mage_tower.vox
(Stored with Git LFS)
Binary file not shown.
@ -347,13 +347,12 @@ impl<'a> BlockGen<'a> {
|
||||
});
|
||||
|
||||
// Structures (like towns)
|
||||
let block = block.or_else(|| {
|
||||
chunk
|
||||
.structures
|
||||
.town
|
||||
.as_ref()
|
||||
.and_then(|town| TownGen.get((town, wpos, sample)))
|
||||
});
|
||||
let block = chunk
|
||||
.structures
|
||||
.town
|
||||
.as_ref()
|
||||
.and_then(|town| TownGen.get((town, wpos, sample, height)))
|
||||
.or(block);
|
||||
|
||||
let block = structures
|
||||
.iter()
|
||||
@ -515,7 +514,7 @@ impl StructureInfo {
|
||||
}
|
||||
}
|
||||
|
||||
fn block_from_structure(
|
||||
pub fn block_from_structure(
|
||||
sblock: StructureBlock,
|
||||
default_kind: BlockKind,
|
||||
pos: Vec3<i32>,
|
||||
|
@ -1,7 +1,7 @@
|
||||
use crate::{
|
||||
all::ForestKind,
|
||||
block::StructureMeta,
|
||||
sim::{LocationInfo, SimChunk},
|
||||
sim::{LocationInfo, SimChunk, WorldSim},
|
||||
util::{RandomPerm, Sampler, UnitChooser},
|
||||
World, CONFIG,
|
||||
};
|
||||
@ -20,7 +20,7 @@ use std::{
|
||||
use vek::*;
|
||||
|
||||
pub struct ColumnGen<'a> {
|
||||
world: &'a World,
|
||||
pub sim: &'a WorldSim,
|
||||
}
|
||||
|
||||
static UNIT_CHOOSER: UnitChooser = UnitChooser::new(0x700F4EC7);
|
||||
@ -55,14 +55,13 @@ lazy_static! {
|
||||
}
|
||||
|
||||
impl<'a> ColumnGen<'a> {
|
||||
pub fn new(world: &'a World) -> Self {
|
||||
Self { world }
|
||||
pub fn new(sim: &'a WorldSim) -> Self {
|
||||
Self { sim }
|
||||
}
|
||||
|
||||
fn get_local_structure(&self, wpos: Vec2<i32>) -> Option<StructureData> {
|
||||
let (pos, seed) = self
|
||||
.world
|
||||
.sim()
|
||||
.sim
|
||||
.gen_ctx
|
||||
.region_gen
|
||||
.get(wpos)
|
||||
@ -74,7 +73,7 @@ impl<'a> ColumnGen<'a> {
|
||||
let chunk_pos = pos.map2(Vec2::from(TerrainChunkSize::SIZE), |e, sz: u32| {
|
||||
e / sz as i32
|
||||
});
|
||||
let chunk = self.world.sim().get(chunk_pos)?;
|
||||
let chunk = self.sim.get(chunk_pos)?;
|
||||
|
||||
if seed % 5 == 2
|
||||
&& chunk.temp > CONFIG.desert_temp
|
||||
@ -102,8 +101,7 @@ impl<'a> ColumnGen<'a> {
|
||||
|
||||
fn gen_close_structures(&self, wpos: Vec2<i32>) -> [Option<StructureData>; 9] {
|
||||
let mut metas = [None; 9];
|
||||
self.world
|
||||
.sim()
|
||||
self.sim
|
||||
.gen_ctx
|
||||
.structure_gen
|
||||
.get(wpos)
|
||||
@ -131,7 +129,7 @@ impl<'a> Sampler<'a> for ColumnGen<'a> {
|
||||
e / sz as i32
|
||||
});
|
||||
|
||||
let sim = self.world.sim();
|
||||
let sim = &self.sim;
|
||||
|
||||
let turb = Vec2::new(
|
||||
sim.gen_ctx.turb_x_nz.get((wposf.div(48.0)).into_array()) as f32,
|
||||
@ -383,6 +381,7 @@ impl<'a> Sampler<'a> for ColumnGen<'a> {
|
||||
.add((marble_small - 0.5) * 0.5),
|
||||
);
|
||||
|
||||
/*
|
||||
// Work out if we're on a path or near a town
|
||||
let dist_to_path = match &sim_chunk.location {
|
||||
Some(loc) => {
|
||||
@ -419,6 +418,7 @@ impl<'a> Sampler<'a> for ColumnGen<'a> {
|
||||
} else {
|
||||
(alt, ground)
|
||||
};
|
||||
*/
|
||||
|
||||
// Cities
|
||||
// TODO: In a later MR
|
||||
|
@ -8,7 +8,7 @@ use common::terrain::Block;
|
||||
use vek::*;
|
||||
|
||||
pub trait Generator<'a, T: 'a>:
|
||||
Sampler<'a, Index = (&'a T, Vec3<i32>, &'a ColumnSample<'a>), Sample = Option<Block>>
|
||||
Sampler<'a, Index = (&'a T, Vec3<i32>, &'a ColumnSample<'a>, f32), Sample = Option<Block>>
|
||||
{
|
||||
fn get_z_limits(&self, state: &'a T, wpos: Vec2<i32>, sample: &ColumnSample) -> (f32, f32);
|
||||
}
|
||||
|
@ -1,22 +1,88 @@
|
||||
use super::Generator;
|
||||
use crate::{
|
||||
column::ColumnSample,
|
||||
block::block_from_structure,
|
||||
column::{ColumnGen, ColumnSample},
|
||||
sim::WorldSim,
|
||||
util::{seed_expan, Grid, Sampler},
|
||||
util::{seed_expan, Grid, Sampler, UnitChooser},
|
||||
};
|
||||
use common::terrain::{Block, BlockKind};
|
||||
use common::{
|
||||
assets,
|
||||
terrain::{Block, BlockKind, Structure},
|
||||
vol::{ReadVol, Vox},
|
||||
};
|
||||
use lazy_static::lazy_static;
|
||||
use rand::prelude::*;
|
||||
use rand_chacha::ChaChaRng;
|
||||
use std::sync::Arc;
|
||||
use vek::*;
|
||||
|
||||
const CELL_SIZE: i32 = 24;
|
||||
|
||||
static UNIT_CHOOSER: UnitChooser = UnitChooser::new(0x100F4E37);
|
||||
|
||||
lazy_static! {
|
||||
pub static ref HOUSES: Vec<Arc<Structure>> =
|
||||
vec![
|
||||
assets::load_map("world.structure.human.house_1", |s: Structure| s
|
||||
.with_center(Vec3::new(8, 10, 2)))
|
||||
.unwrap(),
|
||||
];
|
||||
pub static ref BLACKSMITHS: Vec<Arc<Structure>> = vec![
|
||||
assets::load_map("world.structure.human.blacksmith", |s: Structure| s
|
||||
.with_center(Vec3::new(16, 19, 9)))
|
||||
.unwrap(),
|
||||
assets::load_map("world.structure.human.mage_tower", |s: Structure| s
|
||||
.with_center(Vec3::new(13, 13, 4)))
|
||||
.unwrap(),
|
||||
];
|
||||
pub static ref TOWNHALLS: Vec<Arc<Structure>> = vec![
|
||||
assets::load_map("world.structure.human.town_hall_spire", |s: Structure| s
|
||||
.with_center(Vec3::new(16, 16, 2)))
|
||||
.unwrap(),
|
||||
assets::load_map("world.structure.human.stables_1", |s: Structure| s
|
||||
.with_center(Vec3::new(16, 23, 2)))
|
||||
.unwrap(),
|
||||
];
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum Building {
|
||||
House,
|
||||
Blacksmith,
|
||||
TownHall,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum TownCell {
|
||||
Empty,
|
||||
Junction,
|
||||
Road,
|
||||
House,
|
||||
Street,
|
||||
Building {
|
||||
kind: Building,
|
||||
wpos: Vec3<i32>,
|
||||
size: Vec2<i32>,
|
||||
units: (Vec2<i32>, Vec2<i32>),
|
||||
seed: u32,
|
||||
},
|
||||
PartOf(Vec2<i32>),
|
||||
}
|
||||
|
||||
impl TownCell {
|
||||
fn is_road(&self) -> bool {
|
||||
match self {
|
||||
TownCell::Junction => true,
|
||||
TownCell::Street => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn mergeable(&self) -> bool {
|
||||
match self {
|
||||
TownCell::Empty => true,
|
||||
TownCell::Building { size, .. } if *size == Vec2::one() => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TownState {
|
||||
@ -26,65 +92,166 @@ pub struct TownState {
|
||||
}
|
||||
|
||||
impl TownState {
|
||||
pub fn generate(center: Vec2<i32>, seed: u32, sim: &mut WorldSim) -> Option<Self> {
|
||||
let center_chunk = sim.get_wpos(center)?;
|
||||
pub fn generate(
|
||||
center: Vec2<i32>,
|
||||
seed: u32,
|
||||
gen: &mut ColumnGen,
|
||||
rng: &mut impl Rng,
|
||||
) -> Option<Self> {
|
||||
let center_chunk = gen.sim.get_wpos(center)?;
|
||||
|
||||
// First, determine whether the location is even appropriate for a town
|
||||
if center_chunk.chaos > 0.5 || center_chunk.near_cliffs {
|
||||
if center_chunk.chaos > 0.7 || center_chunk.near_cliffs {
|
||||
return None;
|
||||
}
|
||||
|
||||
let radius = 150;
|
||||
let radius = 192;
|
||||
|
||||
let mut grid = Grid::new(TownCell::Empty, Vec2::broadcast(radius * 2 / CELL_SIZE));
|
||||
let mut grid = Grid::new(
|
||||
TownCell::Empty,
|
||||
Vec2::broadcast(radius * 3 / (CELL_SIZE * 2)),
|
||||
);
|
||||
|
||||
grid.set(grid.size() / 2, TownCell::Junction);
|
||||
|
||||
let mut create_road = || loop {
|
||||
let junctions = grid
|
||||
.iter()
|
||||
.filter(|(_, cell)| {
|
||||
if let TownCell::Junction = cell {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
let mut create_road = || {
|
||||
for _ in 0..10 {
|
||||
let junctions = grid
|
||||
.iter()
|
||||
.filter(|(_, cell)| {
|
||||
if let TownCell::Junction = cell {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// Choose an existing junction for the road to start from
|
||||
let start_pos = junctions.choose(&mut sim.rng).unwrap().0; // Can't fail
|
||||
// Choose an existing junction for the road to start from
|
||||
let start_pos = junctions.choose(rng).unwrap().0; // Can't fail
|
||||
|
||||
// Choose a random direction and length for the road
|
||||
let road_dir = {
|
||||
let dirs = [-1, 0, 1, 0, -1];
|
||||
let idx = sim.rng.gen_range(0, 4);
|
||||
Vec2::new(dirs[idx], dirs[idx + 1])
|
||||
};
|
||||
let road_len = sim.rng.gen_range(1, 4) * 2 + 1;
|
||||
// Choose a random direction and length for the road
|
||||
let road_dir = {
|
||||
let dirs = [-1, 0, 1, 0, -1];
|
||||
let idx = rng.gen_range(0, 4);
|
||||
Vec2::new(dirs[idx], dirs[idx + 1])
|
||||
};
|
||||
let road_len = 2 + rng.gen_range(1, 3) * 2 + 1;
|
||||
|
||||
// Make sure we aren't trying to create a road where a road already exists!
|
||||
match grid.get(start_pos + road_dir) {
|
||||
Some(TownCell::Empty) => {}
|
||||
_ => continue,
|
||||
}
|
||||
|
||||
// Pave the road
|
||||
for i in 1..road_len {
|
||||
let cell_pos = start_pos + road_dir * i;
|
||||
if let Some(TownCell::Empty) = grid.get(cell_pos) {
|
||||
grid.set(cell_pos, TownCell::Road);
|
||||
// Make sure we aren't trying to create a road where a road already exists!
|
||||
match grid.get(start_pos + road_dir) {
|
||||
Some(TownCell::Empty) => {}
|
||||
_ => continue,
|
||||
}
|
||||
}
|
||||
grid.set(start_pos + road_dir * road_len, TownCell::Junction);
|
||||
|
||||
break;
|
||||
// Pave the road
|
||||
for i in 1..road_len {
|
||||
let cell_pos = start_pos + road_dir * i;
|
||||
if let Some(TownCell::Empty) = grid.get(cell_pos) {
|
||||
grid.set(
|
||||
cell_pos,
|
||||
if i == road_len - 1 {
|
||||
TownCell::Junction
|
||||
} else {
|
||||
TownCell::Street
|
||||
},
|
||||
);
|
||||
} else {
|
||||
grid.set(cell_pos, TownCell::Junction);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
for _ in 0..8 {
|
||||
// Create roads
|
||||
for _ in 0..12 {
|
||||
create_road();
|
||||
}
|
||||
|
||||
// Place houses
|
||||
for x in 0..grid.size().x {
|
||||
for y in 0..grid.size().y {
|
||||
let pos = Vec2::new(x, y);
|
||||
let wpos = center + (pos - grid.size() / 2) * CELL_SIZE + CELL_SIZE / 2;
|
||||
|
||||
// Is this cell near a road?
|
||||
let near_road = 'near_road: {
|
||||
let dirs = [-1, 0, 1, 0];
|
||||
let offs = rng.gen_range(0, 4);
|
||||
for i in 0..4 {
|
||||
let dir = Vec2::new(dirs[(offs + i) % 4], dirs[(offs + i + 1) % 4]);
|
||||
if grid.get(pos + dir).unwrap_or(&TownCell::Empty).is_road() {
|
||||
break 'near_road Some(dir);
|
||||
}
|
||||
}
|
||||
None
|
||||
};
|
||||
|
||||
match (near_road, grid.get_mut(pos)) {
|
||||
(Some(dir), Some(cell @ TownCell::Empty)) if rng.gen_range(0, 6) > 0 => {
|
||||
let alt = gen.get(wpos).map(|sample| sample.alt).unwrap_or(0.0) as i32;
|
||||
|
||||
*cell = TownCell::Building {
|
||||
kind: Building::House,
|
||||
wpos: Vec3::new(wpos.x, wpos.y, alt),
|
||||
size: Vec2::one(),
|
||||
units: (
|
||||
Vec2::new(dir.y, dir.x) * (rng.gen_range(0, 1) * 2 - 1),
|
||||
-dir,
|
||||
),
|
||||
seed: rng.gen(),
|
||||
};
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Merge buildings
|
||||
for x in 0..grid.size().x {
|
||||
for y in 0..grid.size().y {
|
||||
let pos = Vec2::new(x, y);
|
||||
for offx in -1..1 {
|
||||
for offy in -1..1 {
|
||||
if grid
|
||||
.iter_area(pos + Vec2::new(offx, offy), Vec2::broadcast(2))
|
||||
.any(|cell| cell.map(|(_, cell)| !cell.mergeable()).unwrap_or(true))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
match grid.get_mut(pos) {
|
||||
Some(TownCell::Building {
|
||||
kind, wpos, size, ..
|
||||
}) => {
|
||||
*kind = if rng.gen() {
|
||||
Building::Blacksmith
|
||||
} else {
|
||||
Building::TownHall
|
||||
};
|
||||
*wpos += Vec3::new(CELL_SIZE / 2, CELL_SIZE / 2, 0)
|
||||
* (Vec2::new(offx, offy) * 2 + 1);
|
||||
*size = Vec2::broadcast(2);
|
||||
}
|
||||
_ => continue,
|
||||
}
|
||||
|
||||
for i in 0..2 {
|
||||
for j in 0..2 {
|
||||
let p = Vec2::new(i + offx, j + offy);
|
||||
if pos + p != pos {
|
||||
grid.set(pos + p, TownCell::PartOf(pos));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Some(Self {
|
||||
center,
|
||||
radius,
|
||||
@ -93,25 +260,64 @@ impl TownState {
|
||||
}
|
||||
|
||||
fn get_cell(&self, wpos: Vec2<i32>) -> &TownCell {
|
||||
self.grid
|
||||
.get((wpos - self.center + self.radius) / CELL_SIZE)
|
||||
let rpos = wpos - self.center;
|
||||
match self
|
||||
.grid
|
||||
.get(rpos.map(|e| e.div_euclid(CELL_SIZE)) + self.grid.size() / 2)
|
||||
.unwrap_or(&TownCell::Empty)
|
||||
{
|
||||
TownCell::PartOf(pos) => self.grid.get(*pos).unwrap(),
|
||||
cell => cell,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TownGen;
|
||||
|
||||
impl<'a> Sampler<'a> for TownGen {
|
||||
type Index = (&'a TownState, Vec3<i32>, &'a ColumnSample<'a>);
|
||||
type Index = (&'a TownState, Vec3<i32>, &'a ColumnSample<'a>, f32);
|
||||
type Sample = Option<Block>;
|
||||
|
||||
fn get(&self, (town, wpos, sample): Self::Index) -> Self::Sample {
|
||||
fn get(&self, (town, wpos, sample, height): Self::Index) -> Self::Sample {
|
||||
match town.get_cell(Vec2::from(wpos)) {
|
||||
TownCell::Road if wpos.z < sample.alt as i32 + 4 => {
|
||||
Some(Block::new(BlockKind::Normal, Rgb::new(255, 200, 150)))
|
||||
cell if cell.is_road() => {
|
||||
if (wpos.z as f32) < height - 1.0 {
|
||||
Some(Block::new(
|
||||
BlockKind::Normal,
|
||||
Lerp::lerp(
|
||||
Rgb::new(150.0, 120.0, 50.0),
|
||||
Rgb::new(100.0, 70.0, 20.0),
|
||||
sample.marble_small,
|
||||
)
|
||||
.map(|e| e as u8),
|
||||
))
|
||||
} else {
|
||||
Some(Block::empty())
|
||||
}
|
||||
}
|
||||
TownCell::Junction if wpos.z < sample.alt as i32 + 4 => {
|
||||
Some(Block::new(BlockKind::Normal, Rgb::new(255, 200, 250)))
|
||||
TownCell::Building {
|
||||
kind,
|
||||
wpos: building_wpos,
|
||||
units,
|
||||
seed,
|
||||
..
|
||||
} => {
|
||||
let rpos = wpos - building_wpos;
|
||||
let volumes: &'static [_] = match kind {
|
||||
Building::House => &HOUSES,
|
||||
Building::Blacksmith => &BLACKSMITHS,
|
||||
Building::TownHall => &TOWNHALLS,
|
||||
};
|
||||
volumes[*seed as usize % volumes.len()]
|
||||
.get(
|
||||
Vec3::from(units.0) * rpos.x
|
||||
+ Vec3::from(units.1) * rpos.y
|
||||
+ Vec3::unit_z() * rpos.z,
|
||||
)
|
||||
.ok()
|
||||
.and_then(|sb| {
|
||||
block_from_structure(*sb, BlockKind::Normal, wpos, wpos.into(), 0, sample)
|
||||
})
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
@ -125,6 +331,6 @@ impl<'a> Generator<'a, TownState> for TownGen {
|
||||
wpos: Vec2<i32>,
|
||||
sample: &ColumnSample,
|
||||
) -> (f32, f32) {
|
||||
(sample.alt - 32.0, sample.alt + 64.0)
|
||||
(sample.alt - 32.0, sample.alt + 75.0)
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,8 @@
|
||||
const_generics,
|
||||
euclidean_division,
|
||||
bind_by_move_pattern_guards,
|
||||
option_flattening
|
||||
option_flattening,
|
||||
label_break_value
|
||||
)]
|
||||
|
||||
mod all;
|
||||
@ -57,11 +58,11 @@ impl World {
|
||||
pub fn sample_columns(
|
||||
&self,
|
||||
) -> impl Sampler<Index = Vec2<i32>, Sample = Option<ColumnSample>> + '_ {
|
||||
ColumnGen::new(self)
|
||||
ColumnGen::new(&self.sim)
|
||||
}
|
||||
|
||||
pub fn sample_blocks(&self) -> BlockGen {
|
||||
BlockGen::new(self, ColumnGen::new(self))
|
||||
BlockGen::new(self, ColumnGen::new(&self.sim))
|
||||
}
|
||||
|
||||
pub fn generate_chunk(&self, chunk_pos: Vec2<i32>) -> (TerrainChunk, ChunkSupplement) {
|
||||
|
@ -11,6 +11,7 @@ use self::util::{
|
||||
|
||||
use crate::{
|
||||
all::ForestKind,
|
||||
column::ColumnGen,
|
||||
generator::TownState,
|
||||
util::{seed_expan, FastNoise, Sampler, StructureGen2d},
|
||||
CONFIG,
|
||||
@ -441,7 +442,8 @@ impl WorldSim {
|
||||
let maybe_town = maybe_towns
|
||||
.entry(*pos)
|
||||
.or_insert_with(|| {
|
||||
TownState::generate(*pos, *seed, self).map(|t| Arc::new(t))
|
||||
TownState::generate(*pos, *seed, &mut ColumnGen::new(self), &mut rng)
|
||||
.map(|t| Arc::new(t))
|
||||
})
|
||||
.as_mut()
|
||||
// Only care if we're close to the town
|
||||
|
@ -13,8 +13,12 @@ impl<T: Clone> Grid<T> {
|
||||
}
|
||||
}
|
||||
|
||||
fn idx(&self, pos: Vec2<i32>) -> usize {
|
||||
(pos.y * self.size.x + pos.x) as usize
|
||||
fn idx(&self, pos: Vec2<i32>) -> Option<usize> {
|
||||
if pos.map2(self.size, |e, sz| e >= 0 && e < sz).reduce_and() {
|
||||
Some((pos.y * self.size.x + pos.x) as usize)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn size(&self) -> Vec2<i32> {
|
||||
@ -22,24 +26,45 @@ impl<T: Clone> Grid<T> {
|
||||
}
|
||||
|
||||
pub fn get(&self, pos: Vec2<i32>) -> Option<&T> {
|
||||
self.cells.get(self.idx(pos))
|
||||
self.cells.get(self.idx(pos)?)
|
||||
}
|
||||
|
||||
pub fn get_mut(&mut self, pos: Vec2<i32>) -> Option<&mut T> {
|
||||
let idx = self.idx(pos);
|
||||
let idx = self.idx(pos)?;
|
||||
self.cells.get_mut(idx)
|
||||
}
|
||||
|
||||
pub fn set(&mut self, pos: Vec2<i32>, cell: T) {
|
||||
let idx = self.idx(pos);
|
||||
self.cells.get_mut(idx).map(|c| *c = cell);
|
||||
pub fn set(&mut self, pos: Vec2<i32>, cell: T) -> Option<()> {
|
||||
let idx = self.idx(pos)?;
|
||||
self.cells.get_mut(idx).map(|c| *c = cell)
|
||||
}
|
||||
|
||||
pub fn iter(&self) -> impl Iterator<Item = (Vec2<i32>, &T)> + '_ {
|
||||
(0..self.size.x)
|
||||
.map(move |x| {
|
||||
(0..self.size.y)
|
||||
.map(move |y| (Vec2::new(x, y), &self.cells[self.idx(Vec2::new(x, y))]))
|
||||
(0..self.size.y).map(move |y| {
|
||||
(
|
||||
Vec2::new(x, y),
|
||||
&self.cells[self.idx(Vec2::new(x, y)).unwrap()],
|
||||
)
|
||||
})
|
||||
})
|
||||
.flatten()
|
||||
}
|
||||
|
||||
pub fn iter_area(
|
||||
&self,
|
||||
pos: Vec2<i32>,
|
||||
size: Vec2<i32>,
|
||||
) -> impl Iterator<Item = Option<(Vec2<i32>, &T)>> + '_ {
|
||||
(0..size.x)
|
||||
.map(move |x| {
|
||||
(0..size.y).map(move |y| {
|
||||
Some((
|
||||
pos + Vec2::new(x, y),
|
||||
&self.cells[self.idx(pos + Vec2::new(x, y))?],
|
||||
))
|
||||
})
|
||||
})
|
||||
.flatten()
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user