Initial commit: Terrafier v0.1.0
- Core world model (World, Dimension, Tile, Terrain, Platform) - NBT reader/writer with full tag support - Anvil region (.mca) format reader with chunk parsing - Block palette compression and export - Biome database with Minecraft 1.21 biomes - CLI commands: new, import, export, info, render - GUI application with egui (viewport, tools, undo/redo) - Noise-based heightmap generation and editing operations License: GPL-3.0-or-later
This commit is contained in:
commit
91835402b3
94 changed files with 12372 additions and 0 deletions
10
crates/biome-db/Cargo.toml
Executable file
10
crates/biome-db/Cargo.toml
Executable file
|
|
@ -0,0 +1,10 @@
|
|||
[package]
|
||||
name = "terrafier-biome-db"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
description = "Biome database for Terrafier — biome IDs and names for all Minecraft versions"
|
||||
|
||||
[dependencies]
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
serde_json.workspace = true
|
||||
65
crates/biome-db/data/biomes.json
Executable file
65
crates/biome-db/data/biomes.json
Executable file
|
|
@ -0,0 +1,65 @@
|
|||
[
|
||||
{"id":0,"name":"ocean","temperature":0.5,"downfall":0.5,"precipitation":"rain","water_color":4159204,"sky_color":7907327,"grass_color":9551193,"foliage_color":10387789,"fog_color":12638463},
|
||||
{"id":1,"name":"plains","temperature":0.8,"downfall":0.4,"precipitation":"rain","water_color":4159204,"sky_color":7907327,"grass_color":9551193,"foliage_color":10387789,"fog_color":12638463},
|
||||
{"id":2,"name":"desert","temperature":2.0,"downfall":0.0,"precipitation":"none","water_color":4159204,"sky_color":7254527,"grass_color":13095579,"foliage_color":11759899,"fog_color":12638463},
|
||||
{"id":3,"name":"windswept_hills","temperature":0.2,"downfall":0.3,"precipitation":"rain","water_color":4159204,"sky_color":7907327,"grass_color":10927977,"foliage_color":10854697,"fog_color":12638463},
|
||||
{"id":4,"name":"forest","temperature":0.7,"downfall":0.8,"precipitation":"rain","water_color":4159204,"sky_color":7907327,"grass_color":8107825,"foliage_color":7318464,"fog_color":12638463},
|
||||
{"id":5,"name":"taiga","temperature":0.25,"downfall":0.8,"precipitation":"rain","water_color":4159204,"sky_color":7907327,"grass_color":12207467,"foliage_color":11599129,"fog_color":12638463},
|
||||
{"id":6,"name":"swamp","temperature":0.8,"downfall":0.9,"precipitation":"rain","water_color":9478516,"sky_color":7907327,"grass_color":6975545,"foliage_color":6975545,"fog_color":12638463},
|
||||
{"id":7,"name":"river","temperature":0.5,"downfall":0.5,"precipitation":"rain","water_color":4159204,"sky_color":7907327,"grass_color":9551193,"foliage_color":10387789,"fog_color":12638463},
|
||||
{"id":8,"name":"nether_wastes","temperature":2.0,"downfall":0.0,"precipitation":"none","water_color":4159204,"sky_color":7254527,"grass_color":8792071,"foliage_color":8792071,"fog_color":3348480},
|
||||
{"id":9,"name":"the_end","temperature":0.5,"downfall":0.5,"precipitation":"none","water_color":4159204,"sky_color":0,"grass_color":10065867,"foliage_color":10065867,"fog_color":10526880},
|
||||
{"id":10,"name":"frozen_ocean","temperature":0.0,"downfall":0.5,"precipitation":"snow","water_color":3750089,"sky_color":7907327,"grass_color":12255547,"foliage_color":11599129,"fog_color":12638463},
|
||||
{"id":11,"name":"frozen_river","temperature":0.0,"downfall":0.5,"precipitation":"snow","water_color":3750089,"sky_color":7907327,"grass_color":12255547,"foliage_color":11599129,"fog_color":12638463},
|
||||
{"id":12,"name":"snowy_plains","temperature":0.0,"downfall":0.5,"precipitation":"snow","water_color":4020182,"sky_color":7907327,"grass_color":12255547,"foliage_color":11599129,"fog_color":12638463},
|
||||
{"id":13,"name":"old_growth_pine_taiga","temperature":0.3,"downfall":0.8,"precipitation":"rain","water_color":4159204,"sky_color":7907327,"grass_color":10855236,"foliage_color":10214081,"fog_color":12638463},
|
||||
{"id":14,"name":"old_growth_spruce_taiga","temperature":0.25,"downfall":0.8,"precipitation":"rain","water_color":4159204,"sky_color":7907327,"grass_color":12207467,"foliage_color":11599129,"fog_color":12638463},
|
||||
{"id":15,"name":"snowy_taiga","temperature":-0.5,"downfall":0.4,"precipitation":"snow","water_color":4020182,"sky_color":7907327,"grass_color":12255547,"foliage_color":11599129,"fog_color":12638463},
|
||||
{"id":16,"name":"savanna","temperature":2.0,"downfall":0.0,"precipitation":"none","water_color":4159204,"sky_color":7254527,"grass_color":13551763,"foliage_color":12434877,"fog_color":12638463},
|
||||
{"id":17,"name":"savanna_plateau","temperature":2.0,"downfall":0.0,"precipitation":"none","water_color":4159204,"sky_color":7254527,"grass_color":13551763,"foliage_color":12434877,"fog_color":12638463},
|
||||
{"id":18,"name":"badlands","temperature":2.0,"downfall":0.0,"precipitation":"none","water_color":4159204,"sky_color":7254527,"grass_color":10658679,"foliage_color":10658679,"fog_color":12638463},
|
||||
{"id":19,"name":"wooded_badlands","temperature":2.0,"downfall":0.0,"precipitation":"none","water_color":4159204,"sky_color":7254527,"grass_color":10658679,"foliage_color":10658679,"fog_color":12638463},
|
||||
{"id":20,"name":"eroded_badlands","temperature":2.0,"downfall":0.0,"precipitation":"none","water_color":4159204,"sky_color":7254527,"grass_color":10658679,"foliage_color":10658679,"fog_color":12638463},
|
||||
{"id":21,"name":"warm_ocean","temperature":0.5,"downfall":0.5,"precipitation":"rain","water_color":4020182,"sky_color":7907327,"grass_color":9551193,"foliage_color":10387789,"fog_color":12638463},
|
||||
{"id":22,"name":"lukewarm_ocean","temperature":0.5,"downfall":0.5,"precipitation":"rain","water_color":4566514,"sky_color":7907327,"grass_color":9551193,"foliage_color":10387789,"fog_color":12638463},
|
||||
{"id":23,"name":"cold_ocean","temperature":0.5,"downfall":0.5,"precipitation":"rain","water_color":3750089,"sky_color":7907327,"grass_color":9551193,"foliage_color":10387789,"fog_color":12638463},
|
||||
{"id":24,"name":"deep_lukewarm_ocean","temperature":0.5,"downfall":0.5,"precipitation":"rain","water_color":4566514,"sky_color":7907327,"grass_color":9551193,"foliage_color":10387789,"fog_color":12638463},
|
||||
{"id":25,"name":"deep_cold_ocean","temperature":0.5,"downfall":0.5,"precipitation":"rain","water_color":3750089,"sky_color":7907327,"grass_color":9551193,"foliage_color":10387789,"fog_color":12638463},
|
||||
{"id":26,"name":"deep_frozen_ocean","temperature":0.5,"downfall":0.5,"precipitation":"rain","water_color":3750089,"sky_color":7907327,"grass_color":9551193,"foliage_color":10387789,"fog_color":12638463},
|
||||
{"id":27,"name":"dark_forest","temperature":0.7,"downfall":0.8,"precipitation":"rain","water_color":4159204,"sky_color":7907327,"grass_color":7109967,"foliage_color":6776627,"fog_color":12638463},
|
||||
{"id":28,"name":"snowy_slopes","temperature":-0.3,"downfall":0.9,"precipitation":"snow","water_color":4020182,"sky_color":7907327,"grass_color":12255547,"foliage_color":11599129,"fog_color":12638463},
|
||||
{"id":29,"name":"jagged_peaks","temperature":-0.7,"downfall":0.9,"precipitation":"snow","water_color":4020182,"sky_color":7907327,"grass_color":12255547,"foliage_color":11599129,"fog_color":12638463},
|
||||
{"id":30,"name":"frozen_peaks","temperature":-0.7,"downfall":0.9,"precipitation":"snow","water_color":4020182,"sky_color":7907327,"grass_color":12255547,"foliage_color":11599129,"fog_color":12638463},
|
||||
{"id":31,"name":"stony_peaks","temperature":1.0,"downfall":0.3,"precipitation":"rain","water_color":4159204,"sky_color":7907327,"grass_color":10927977,"foliage_color":10854697,"fog_color":12638463},
|
||||
{"id":32,"name":"bamboo_jungle","temperature":0.95,"downfall":0.9,"precipitation":"rain","water_color":4159204,"sky_color":7907327,"grass_color":12304591,"foliage_color":11437115,"fog_color":12638463},
|
||||
{"id":33,"name":"jungle","temperature":0.95,"downfall":0.9,"precipitation":"rain","water_color":4159204,"sky_color":7907327,"grass_color":12304591,"foliage_color":11437115,"fog_color":12638463},
|
||||
{"id":34,"name":"sparse_jungle","temperature":0.95,"downfall":0.8,"precipitation":"rain","water_color":4159204,"sky_color":7907327,"grass_color":12304591,"foliage_color":11437115,"fog_color":12638463},
|
||||
{"id":35,"name":"deep_ocean","temperature":0.5,"downfall":0.5,"precipitation":"rain","water_color":4159204,"sky_color":7907327,"grass_color":9551193,"foliage_color":10387789,"fog_color":12638463},
|
||||
{"id":36,"name":"mushroom_fields","temperature":0.9,"downfall":1.0,"precipitation":"rain","water_color":4159204,"sky_color":7907327,"grass_color":10215985,"foliage_color":10215985,"fog_color":12638463},
|
||||
{"id":37,"name":"dripstone_caves","temperature":0.8,"downfall":0.4,"precipitation":"rain","water_color":4159204,"sky_color":7907327,"grass_color":9551193,"foliage_color":10387789,"fog_color":12638463},
|
||||
{"id":38,"name":"lush_caves","temperature":0.5,"downfall":0.5,"precipitation":"rain","water_color":4159204,"sky_color":7907327,"grass_color":8107825,"foliage_color":7318464,"fog_color":12638463},
|
||||
{"id":39,"name":"deep_dark","temperature":0.8,"downfall":0.4,"precipitation":"rain","water_color":4159204,"sky_color":7907327,"grass_color":9551193,"foliage_color":10387789,"fog_color":12638463},
|
||||
{"id":40,"name":"meadow","temperature":0.5,"downfall":0.8,"precipitation":"rain","water_color":4159204,"sky_color":7907327,"grass_color":9551193,"foliage_color":10387789,"fog_color":12638463},
|
||||
{"id":41,"name":"cherry_grove","temperature":0.5,"downfall":0.8,"precipitation":"rain","water_color":4159204,"sky_color":7907327,"grass_color":9551193,"foliage_color":10387789,"fog_color":12638463},
|
||||
{"id":42,"name":"grove","temperature":-0.2,"downfall":0.8,"precipitation":"snow","water_color":4020182,"sky_color":7907327,"grass_color":12255547,"foliage_color":11599129,"fog_color":12638463},
|
||||
{"id":43,"name":"snowy_beach","temperature":0.05,"downfall":0.3,"precipitation":"snow","water_color":4020182,"sky_color":7907327,"grass_color":12255547,"foliage_color":11599129,"fog_color":12638463},
|
||||
{"id":44,"name":"beach","temperature":0.8,"downfall":0.4,"precipitation":"rain","water_color":4159204,"sky_color":7907327,"grass_color":13095579,"foliage_color":11759899,"fog_color":12638463},
|
||||
{"id":45,"name":"stone_shore","temperature":0.2,"downfall":0.3,"precipitation":"rain","water_color":4159204,"sky_color":7907327,"grass_color":10927977,"foliage_color":10854697,"fog_color":12638463},
|
||||
{"id":46,"name":"sunflower_plains","temperature":0.8,"downfall":0.4,"precipitation":"rain","water_color":4159204,"sky_color":7907327,"grass_color":9551193,"foliage_color":10387789,"fog_color":12638463},
|
||||
{"id":47,"name":"flower_forest","temperature":0.7,"downfall":0.8,"precipitation":"rain","water_color":4159204,"sky_color":7907327,"grass_color":8107825,"foliage_color":7318464,"fog_color":12638463},
|
||||
{"id":48,"name":"ice_spikes","temperature":0.0,"downfall":0.5,"precipitation":"snow","water_color":4020182,"sky_color":7907327,"grass_color":12255547,"foliage_color":11599129,"fog_color":12638463},
|
||||
{"id":49,"name":"old_growth_birch_forest","temperature":0.6,"downfall":0.6,"precipitation":"rain","water_color":4159204,"sky_color":7907327,"grass_color":8107825,"foliage_color":7318464,"fog_color":12638463},
|
||||
{"id":51,"name":"birch_forest","temperature":0.6,"downfall":0.6,"precipitation":"rain","water_color":4159204,"sky_color":7907327,"grass_color":8107825,"foliage_color":7318464,"fog_color":12638463},
|
||||
{"id":54,"name":"windswept_savanna","temperature":2.0,"downfall":0.0,"precipitation":"none","water_color":4159204,"sky_color":7254527,"grass_color":13551763,"foliage_color":12434877,"fog_color":12638463},
|
||||
{"id":55,"name":"windswept_forest","temperature":0.2,"downfall":0.3,"precipitation":"rain","water_color":4159204,"sky_color":7907327,"grass_color":10927977,"foliage_color":10854697,"fog_color":12638463},
|
||||
{"id":56,"name":"windswept_gravelly_hills","temperature":0.2,"downfall":0.3,"precipitation":"rain","water_color":4159204,"sky_color":7907327,"grass_color":10927977,"foliage_color":10854697,"fog_color":12638463},
|
||||
{"id":66,"name":"mangrove_swamp","temperature":0.8,"downfall":0.9,"precipitation":"rain","water_color":9478516,"sky_color":7907327,"grass_color":6975545,"foliage_color":6975545,"fog_color":12638463},
|
||||
{"id":72,"name":"soul_sand_valley","temperature":2.0,"downfall":0.0,"precipitation":"none","water_color":4159204,"sky_color":7254527,"grass_color":5636761,"foliage_color":5636761,"fog_color":3348480},
|
||||
{"id":73,"name":"crimson_forest","temperature":2.0,"downfall":0.0,"precipitation":"none","water_color":4159204,"sky_color":7254527,"grass_color":9115493,"foliage_color":9115493,"fog_color":3348480},
|
||||
{"id":74,"name":"warped_forest","temperature":2.0,"downfall":0.0,"precipitation":"none","water_color":4159204,"sky_color":7254527,"grass_color":7856483,"foliage_color":7856483,"fog_color":3348480},
|
||||
{"id":75,"name":"basalt_deltas","temperature":2.0,"downfall":0.0,"precipitation":"none","water_color":4159204,"sky_color":7254527,"grass_color":6578527,"foliage_color":6578527,"fog_color":3348480},
|
||||
{"id":77,"name":"end_highlands","temperature":0.5,"downfall":0.5,"precipitation":"none","water_color":4159204,"sky_color":0,"grass_color":10065867,"foliage_color":10065867,"fog_color":10526880},
|
||||
{"id":78,"name":"end_midlands","temperature":0.5,"downfall":0.5,"precipitation":"none","water_color":4159204,"sky_color":0,"grass_color":10065867,"foliage_color":10065867,"fog_color":10526880},
|
||||
{"id":79,"name":"end_barrens","temperature":0.5,"downfall":0.5,"precipitation":"none","water_color":4159204,"sky_color":0,"grass_color":10065867,"foliage_color":10065867,"fog_color":10526880},
|
||||
{"id":80,"name":"small_end_islands","temperature":0.5,"downfall":0.5,"precipitation":"none","water_color":4159204,"sky_color":0,"grass_color":10065867,"foliage_color":10065867,"fog_color":10526880}
|
||||
]
|
||||
BIN
crates/biome-db/data/dry_foliage.png
Executable file
BIN
crates/biome-db/data/dry_foliage.png
Executable file
Binary file not shown.
|
After Width: | Height: | Size: 8.8 KiB |
BIN
crates/biome-db/data/foliage.png
Executable file
BIN
crates/biome-db/data/foliage.png
Executable file
Binary file not shown.
|
After Width: | Height: | Size: 14 KiB |
1
crates/biome-db/data/foliage_colormap.json
Executable file
1
crates/biome-db/data/foliage_colormap.json
Executable file
File diff suppressed because one or more lines are too long
BIN
crates/biome-db/data/grass.png
Executable file
BIN
crates/biome-db/data/grass.png
Executable file
Binary file not shown.
|
After Width: | Height: | Size: 5.8 KiB |
1
crates/biome-db/data/grass_colormap.json
Executable file
1
crates/biome-db/data/grass_colormap.json
Executable file
File diff suppressed because one or more lines are too long
140
crates/biome-db/src/colour.rs
Executable file
140
crates/biome-db/src/colour.rs
Executable file
|
|
@ -0,0 +1,140 @@
|
|||
//! Biome colour utilities.
|
||||
//!
|
||||
//! Converts packed u32 colours to RGB arrays and applies
|
||||
//! temperature/downfall adjustments for grass and foliage.
|
||||
|
||||
use crate::db::BiomeEntry;
|
||||
|
||||
pub struct BiomeColour;
|
||||
|
||||
fn u32_to_rgb(colour: u32) -> [u8; 3] {
|
||||
let r = ((colour >> 16) & 0xFF) as u8;
|
||||
let g = ((colour >> 8) & 0xFF) as u8;
|
||||
let b = (colour & 0xFF) as u8;
|
||||
[r, g, b]
|
||||
}
|
||||
|
||||
impl BiomeColour {
|
||||
pub fn water_color(biome: &BiomeEntry) -> [u8; 3] {
|
||||
u32_to_rgb(biome.water_color)
|
||||
}
|
||||
|
||||
pub fn sky_color(biome: &BiomeEntry) -> [u8; 3] {
|
||||
u32_to_rgb(biome.sky_color)
|
||||
}
|
||||
|
||||
pub fn fog_color(biome: &BiomeEntry) -> [u8; 3] {
|
||||
u32_to_rgb(biome.fog_color)
|
||||
}
|
||||
|
||||
/// Returns grass colour adjusted for temperature and downfall.
|
||||
///
|
||||
/// When `temperature * downfall` is low (cold/dry), the colour is
|
||||
/// muted toward cooler tones. When high (warm/humid), it stays vibrant.
|
||||
pub fn grass_color(biome: &BiomeEntry, temperature: f64, downfall: f64) -> [u8; 3] {
|
||||
let rgb = u32_to_rgb(biome.grass_color);
|
||||
adjust_colour(rgb, temperature, downfall)
|
||||
}
|
||||
|
||||
/// Returns foliage colour adjusted for temperature and downfall.
|
||||
pub fn foliage_color(biome: &BiomeEntry, temperature: f64, downfall: f64) -> [u8; 3] {
|
||||
let rgb = u32_to_rgb(biome.foliage_color);
|
||||
adjust_colour(rgb, temperature, downfall)
|
||||
}
|
||||
}
|
||||
|
||||
/// Applies Minecraft-style temperature/humidity colour adjustment.
|
||||
///
|
||||
/// Low `temp * downfall` mutes the colour (colder/grayer),
|
||||
/// high values preserve the original vibrancy (warmer/greener).
|
||||
fn adjust_colour(rgb: [u8; 3], temperature: f64, downfall: f64) -> [u8; 3] {
|
||||
let temp = temperature.clamp(0.0, 1.0) as f32;
|
||||
let humidity = downfall.clamp(0.0, 1.0) as f32;
|
||||
let factor = (temp * humidity).clamp(0.0, 1.0);
|
||||
|
||||
let [r, g, b] = rgb;
|
||||
[
|
||||
(r as f32 * (0.7 + 0.3 * factor)) as u8,
|
||||
(g as f32 * (0.6 + 0.4 * factor)) as u8,
|
||||
(b as f32 * (0.5 + 0.5 * factor)) as u8,
|
||||
]
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::db::BiomeDb;
|
||||
|
||||
fn sample_biome(name: &str) -> BiomeEntry {
|
||||
let db = BiomeDb::new();
|
||||
db.get_by_name(name).expect("biome must exist").clone()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_colours_non_zero() {
|
||||
let db = BiomeDb::new();
|
||||
for biome in db.all_biomes() {
|
||||
let water = BiomeColour::water_color(biome);
|
||||
let sky = BiomeColour::sky_color(biome);
|
||||
let fog = BiomeColour::fog_color(biome);
|
||||
let grass =
|
||||
BiomeColour::grass_color(biome, biome.temperature as f64, biome.downfall as f64);
|
||||
let foliage =
|
||||
BiomeColour::foliage_color(biome, biome.temperature as f64, biome.downfall as f64);
|
||||
assert!(
|
||||
water != [0, 0, 0] || biome.name == "the_end" || biome.name.contains("end_"),
|
||||
"water colour should not be zero for {}: {:?}",
|
||||
biome.name,
|
||||
water
|
||||
);
|
||||
let all = [water, sky, fog, grass, foliage];
|
||||
for (i, c) in all.iter().enumerate() {
|
||||
assert_eq!(
|
||||
c.len(),
|
||||
3,
|
||||
"colour {} for {} should have 3 components",
|
||||
i,
|
||||
biome.name
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_plains_colours() {
|
||||
let biome = sample_biome("plains");
|
||||
let water = BiomeColour::water_color(&biome);
|
||||
assert_eq!(water, [63, 118, 228]);
|
||||
let grass = BiomeColour::grass_color(&biome, 0.8, 0.4);
|
||||
assert_eq!(grass.len(), 3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_desert_colours() {
|
||||
let biome = sample_biome("desert");
|
||||
let sky = BiomeColour::sky_color(&biome);
|
||||
assert_eq!(sky, [110, 177, 255]);
|
||||
let water = BiomeColour::water_color(&biome);
|
||||
assert_eq!(water, [63, 118, 228]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cold_dry_adjustment() {
|
||||
let biome = sample_biome("plains");
|
||||
let warm = BiomeColour::grass_color(&biome, 1.0, 1.0);
|
||||
let cold = BiomeColour::grass_color(&biome, 0.0, 0.0);
|
||||
assert!(
|
||||
warm[0] >= cold[0] && warm[1] >= cold[1] && warm[2] >= cold[2],
|
||||
"warm climate should produce more vibrant colours: warm={:?} cold={:?}",
|
||||
warm,
|
||||
cold
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_swamp_water() {
|
||||
let biome = sample_biome("swamp");
|
||||
let water = BiomeColour::water_color(&biome);
|
||||
assert_eq!(water, [144, 161, 116]);
|
||||
}
|
||||
}
|
||||
118
crates/biome-db/src/db.rs
Executable file
118
crates/biome-db/src/db.rs
Executable file
|
|
@ -0,0 +1,118 @@
|
|||
//! Biome database with Minecraft 1.21 biome data.
|
||||
//! Loaded from embedded JSON via `include_str!`.
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct BiomeEntry {
|
||||
pub name: String,
|
||||
pub id: i32,
|
||||
pub temperature: f32,
|
||||
pub downfall: f32,
|
||||
pub precipitation: String,
|
||||
pub water_color: u32,
|
||||
pub sky_color: u32,
|
||||
pub grass_color: u32,
|
||||
pub foliage_color: u32,
|
||||
pub fog_color: u32,
|
||||
}
|
||||
|
||||
pub struct BiomeDb {
|
||||
by_name: HashMap<String, BiomeEntry>,
|
||||
by_id: HashMap<i32, BiomeEntry>,
|
||||
}
|
||||
|
||||
impl BiomeDb {
|
||||
pub fn new() -> Self {
|
||||
let data = include_str!("../data/biomes.json");
|
||||
let entries: Vec<BiomeEntry> =
|
||||
serde_json::from_str(data).expect("biomes.json must be valid JSON");
|
||||
let mut by_name = HashMap::new();
|
||||
let mut by_id = HashMap::new();
|
||||
for entry in entries {
|
||||
by_name.insert(entry.name.clone(), entry.clone());
|
||||
by_id.insert(entry.id, entry);
|
||||
}
|
||||
Self { by_name, by_id }
|
||||
}
|
||||
|
||||
pub fn get_by_name(&self, name: &str) -> Option<&BiomeEntry> {
|
||||
self.by_name.get(name)
|
||||
}
|
||||
|
||||
pub fn get_by_id(&self, id: i32) -> Option<&BiomeEntry> {
|
||||
self.by_id.get(&id)
|
||||
}
|
||||
|
||||
pub fn all_biomes(&self) -> impl Iterator<Item = &BiomeEntry> {
|
||||
let mut ids: Vec<_> = self.by_id.keys().copied().collect();
|
||||
ids.sort();
|
||||
ids.into_iter().map(move |id| &self.by_id[&id])
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
self.by_id.len()
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.by_id.is_empty()
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for BiomeDb {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_load_all_biomes() {
|
||||
let db = BiomeDb::new();
|
||||
assert!(!db.is_empty(), "biome db should not be empty");
|
||||
assert!(db.len() > 60, "expected 60+ biomes, got {}", db.len());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_lookup_by_name() {
|
||||
let db = BiomeDb::new();
|
||||
let plains = db.get_by_name("plains").expect("plains should exist");
|
||||
assert_eq!(plains.id, 1);
|
||||
assert_eq!(plains.temperature, 0.8);
|
||||
|
||||
let desert = db.get_by_name("desert").expect("desert should exist");
|
||||
assert_eq!(desert.id, 2);
|
||||
assert_eq!(desert.precipitation, "none");
|
||||
|
||||
let ocean = db.get_by_name("ocean").expect("ocean should exist");
|
||||
assert_eq!(ocean.id, 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_lookup_by_id() {
|
||||
let db = BiomeDb::new();
|
||||
let plains = db.get_by_id(1).expect("id 1 should be plains");
|
||||
assert_eq!(plains.name, "plains");
|
||||
|
||||
let desert = db.get_by_id(2).expect("id 2 should be desert");
|
||||
assert_eq!(desert.name, "desert");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_unknown_biome() {
|
||||
let db = BiomeDb::new();
|
||||
assert!(db.get_by_name("nonexistent").is_none());
|
||||
assert!(db.get_by_id(9999).is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_all_biomes_iter() {
|
||||
let db = BiomeDb::new();
|
||||
let count = db.all_biomes().count();
|
||||
assert_eq!(count, db.len());
|
||||
}
|
||||
}
|
||||
10
crates/biome-db/src/lib.rs
Executable file
10
crates/biome-db/src/lib.rs
Executable file
|
|
@ -0,0 +1,10 @@
|
|||
//! Biome database for Terrafier.
|
||||
//!
|
||||
//! Maps biome IDs to names, colours, and patterns for all supported
|
||||
//! Minecraft versions (1.0 through 1.21+).
|
||||
|
||||
pub mod colour;
|
||||
pub mod db;
|
||||
|
||||
pub use colour::BiomeColour;
|
||||
pub use db::{BiomeDb, BiomeEntry};
|
||||
Loading…
Add table
Add a link
Reference in a new issue