summaryrefslogtreecommitdiff
path: root/src/proc_gen/mod.rs
blob: caec241e2d52d4babf76ebb30076934c0f0a3b24 (plain)
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
pub mod herringbone;
pub mod tileset_loader;

use glam::IVec2;

use crate::{
    config::{CHUNK_SIZE, TILESET_SCALING},
    proc_gen::{
        herringbone::{tiles_overlapping, variant_index},
        tileset_loader::{Tile, TileOrientation, Tileset, load_tileset},
    },
    sim::{cell::Cell, cell_manager::chunk::Chunk, entity::EntityDef},
};

pub struct TileCells {
    pub size: IVec2,
    pub cells: Vec<Cell>,
}

pub trait Biome {
    fn derive_world_from_tile(&self, tile: &Tile) -> TileCells;
    fn derive_entities_from_tile(&self, tile: &Tile) -> Vec<EntityDef> {
        Vec::new()
    }
}

pub struct TilesetWorldGenerator {
    tileset: Tileset,
    horizontal_tiles: Vec<TileCells>,
    vertical_tiles: Vec<TileCells>,
}

impl TilesetWorldGenerator {
    pub fn new(tileset_path: &str, biome: &dyn Biome) -> Self {
        let tileset = load_tileset(tileset_path);

        let horizontal_tiles = tileset
            .horizontal_tiles
            .iter()
            .map(|tile| biome.derive_world_from_tile(tile))
            .collect();
        let vertical_tiles = tileset
            .vertical_tiles
            .iter()
            .map(|tile| biome.derive_world_from_tile(tile))
            .collect();

        TilesetWorldGenerator {
            tileset,
            horizontal_tiles,
            vertical_tiles,
        }
    }

    fn grid_size(&self) -> IVec2 {
        IVec2::splat(self.tileset.dimensions.short as i32 * TILESET_SCALING)
    }

    fn tile_at(&self, grid: IVec2, orientation: TileOrientation) -> &TileCells {
        let tiles = match orientation {
            TileOrientation::Horizontal => &self.horizontal_tiles,
            TileOrientation::Vertical => &self.vertical_tiles,
        };
        &tiles[variant_index(grid, tiles.len())]
    }

    pub fn generate_chunk(&self, chunk_position: IVec2) -> Chunk {
        let mut chunk = Chunk::void();

        let chunk_min = chunk_position * CHUNK_SIZE;
        let chunk_max = chunk_min + CHUNK_SIZE;

        let grid = self.grid_size();
        let tiles = tiles_overlapping(
            chunk_min.div_euclid(grid),
            (chunk_max - 1).div_euclid(grid) + 1,
        );

        for (grid_position, orientation) in tiles {
            let tile = self.tile_at(grid_position, orientation);
            let tile_min = grid_position * grid;

            // subset of tile in chunk
            let min = tile_min.max(chunk_min);
            let max = (tile_min + tile.size).min(chunk_max);
            if min.x >= max.x {
                continue;
            }
            let width = (max.x - min.x) as usize;

            for y in min.y..max.y {
                let src = ((y - tile_min.y) * tile.size.x + (min.x - tile_min.x)) as usize;
                let dst = ((y - chunk_min.y) * CHUNK_SIZE + (min.x - chunk_min.x)) as usize;
                chunk.cells[dst..dst + width].copy_from_slice(&tile.cells[src..src + width]);
            }
        }

        chunk.mark_collider_dirty(0);

        chunk
    }
}