mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Merge branch 'name_areas_based_on_biome' into 'master'
name_areas_based_on_biome See merge request veloren/veloren!3115
This commit is contained in:
commit
4f181c936d
BIN
assets/voxygen/element/ui/map/buttons/biome.png
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/element/ui/map/buttons/biome.png
(Stored with Git LFS)
Normal file
Binary file not shown.
@ -14,6 +14,7 @@
|
||||
"hud.map.caves": "Caves",
|
||||
"hud.map.cave": "Cave",
|
||||
"hud.map.peaks": "Mountains",
|
||||
"hud.map.biomes": "Biomes",
|
||||
"hud.map.voxel_map": "Voxel map",
|
||||
"hud.map.trees": "Giant Trees",
|
||||
"hud.map.tree": "Giant Tree",
|
||||
|
@ -131,7 +131,7 @@ impl TerrainChunkMeta {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn name(&self) -> &str { self.name.as_deref().unwrap_or("Wilderness") }
|
||||
pub fn name(&self) -> Option<&str> { self.name.as_deref() }
|
||||
|
||||
pub fn biome(&self) -> BiomeKind { self.biome }
|
||||
|
||||
|
@ -414,6 +414,7 @@ image_ids! {
|
||||
mmap_site_tree: "voxygen.element.ui.map.buttons.tree",
|
||||
mmap_site_tree_hover: "voxygen.element.ui.map.buttons.tree_hover",
|
||||
mmap_poi_peak: "voxygen.element.ui.map.buttons.peak",
|
||||
mmap_poi_biome: "voxygen.element.ui.map.buttons.biome",
|
||||
mmap_poi_peak_hover: "voxygen.element.ui.map.buttons.peak_hover",
|
||||
|
||||
// Window Parts
|
||||
|
@ -65,6 +65,9 @@ widget_ids! {
|
||||
show_peaks_img,
|
||||
show_peaks_box,
|
||||
show_peaks_text,
|
||||
show_biomes_img,
|
||||
show_biomes_box,
|
||||
show_biomes_text,
|
||||
show_voxel_map_img,
|
||||
show_voxel_map_box,
|
||||
show_voxel_map_text,
|
||||
@ -209,6 +212,7 @@ impl<'a> Widget for Map<'a> {
|
||||
let show_caves = self.global_state.settings.interface.map_show_caves;
|
||||
let show_trees = self.global_state.settings.interface.map_show_trees;
|
||||
let show_peaks = self.global_state.settings.interface.map_show_peaks;
|
||||
let show_biomes = self.global_state.settings.interface.map_show_biomes;
|
||||
let show_voxel_map = self.global_state.settings.interface.map_show_voxel_map;
|
||||
let show_topo_map = self.global_state.settings.interface.map_show_topo_map;
|
||||
let mut events = Vec::new();
|
||||
@ -641,9 +645,43 @@ impl<'a> Widget for Map<'a> {
|
||||
.graphics_for(state.ids.show_trees_box)
|
||||
.color(TEXT_COLOR)
|
||||
.set(state.ids.show_trees_text, ui);
|
||||
// Biomes
|
||||
Image::new(self.imgs.mmap_poi_biome)
|
||||
.down_from(state.ids.show_trees_img, 10.0)
|
||||
.w_h(20.0, 20.0)
|
||||
.set(state.ids.show_biomes_img, ui);
|
||||
if Button::image(if show_biomes {
|
||||
self.imgs.checkbox_checked
|
||||
} else {
|
||||
self.imgs.checkbox
|
||||
})
|
||||
.w_h(18.0, 18.0)
|
||||
.hover_image(if show_biomes {
|
||||
self.imgs.checkbox_checked_mo
|
||||
} else {
|
||||
self.imgs.checkbox_mo
|
||||
})
|
||||
.press_image(if show_biomes {
|
||||
self.imgs.checkbox_checked
|
||||
} else {
|
||||
self.imgs.checkbox_press
|
||||
})
|
||||
.right_from(state.ids.show_biomes_img, 10.0)
|
||||
.set(state.ids.show_biomes_box, ui)
|
||||
.was_clicked()
|
||||
{
|
||||
events.push(Event::SettingsChange(MapShowBiomes(!show_biomes)));
|
||||
}
|
||||
Text::new(i18n.get("hud.map.biomes"))
|
||||
.right_from(state.ids.show_biomes_box, 10.0)
|
||||
.font_size(self.fonts.cyri.scale(14))
|
||||
.font_id(self.fonts.cyri.conrod_id)
|
||||
.graphics_for(state.ids.show_biomes_box)
|
||||
.color(TEXT_COLOR)
|
||||
.set(state.ids.show_biomes_text, ui);
|
||||
// Peaks
|
||||
Image::new(self.imgs.mmap_poi_peak)
|
||||
.down_from(state.ids.show_trees_img, 10.0)
|
||||
.down_from(state.ids.show_biomes_img, 10.0)
|
||||
.w_h(20.0, 20.0)
|
||||
.set(state.ids.show_peaks_img, ui);
|
||||
if Button::image(if show_peaks {
|
||||
@ -1023,7 +1061,7 @@ impl<'a> Widget for Map<'a> {
|
||||
}
|
||||
},
|
||||
PoiKind::Lake(size) => {
|
||||
if zoom.powi(2) * size as f64 > 30.0 {
|
||||
if show_biomes && zoom > 2.0 && zoom.powi(2) * size as f64 > 30.0 {
|
||||
let font_scale_factor = if size > 20 {
|
||||
size as f64 / 25.0
|
||||
} else if size > 10 {
|
||||
|
@ -886,20 +886,22 @@ impl<'a> Widget for MiniMap<'a> {
|
||||
match self.client.current_chunk() {
|
||||
Some(chunk) => {
|
||||
// Count characters in the name to avoid clipping with the name display
|
||||
let name_len = chunk.meta().name().chars().count();
|
||||
Text::new(chunk.meta().name())
|
||||
.mid_top_with_margin_on(state.ids.mmap_frame, match name_len {
|
||||
15..=30 => 4.0,
|
||||
_ => 2.0,
|
||||
})
|
||||
.font_size(self.fonts.cyri.scale(match name_len {
|
||||
0..=15 => 18,
|
||||
16..=30 => 14,
|
||||
_ => 14,
|
||||
}))
|
||||
.font_id(self.fonts.cyri.conrod_id)
|
||||
.color(TEXT_COLOR)
|
||||
.set(state.ids.mmap_location, ui)
|
||||
if let Some(name) = chunk.meta().name() {
|
||||
let name_len = name.chars().count();
|
||||
Text::new(name)
|
||||
.mid_top_with_margin_on(state.ids.mmap_frame, match name_len {
|
||||
15..=30 => 4.0,
|
||||
_ => 2.0,
|
||||
})
|
||||
.font_size(self.fonts.cyri.scale(match name_len {
|
||||
0..=15 => 18,
|
||||
16..=30 => 14,
|
||||
_ => 14,
|
||||
}))
|
||||
.font_id(self.fonts.cyri.conrod_id)
|
||||
.color(TEXT_COLOR)
|
||||
.set(state.ids.mmap_location, ui)
|
||||
}
|
||||
},
|
||||
None => Text::new(" ")
|
||||
.mid_top_with_margin_on(state.ids.mmap_frame, 0.0)
|
||||
|
@ -98,23 +98,24 @@ impl<'a> Widget for Popup<'a> {
|
||||
|
||||
// Push chunk name to message queue
|
||||
if let Some(chunk) = self.client.current_chunk() {
|
||||
let current = chunk.meta().name();
|
||||
// Check if no other popup is displayed and a new one is needed
|
||||
if state.messages.is_empty()
|
||||
&& state
|
||||
.last_region_name
|
||||
.as_ref()
|
||||
.map(|l| l != current)
|
||||
.unwrap_or(true)
|
||||
{
|
||||
// Update last_region
|
||||
state.update(|s| {
|
||||
if s.messages.is_empty() {
|
||||
s.last_message_update = Instant::now();
|
||||
}
|
||||
s.last_region_name = Some(current.to_owned());
|
||||
s.messages.push_back(current.to_owned());
|
||||
});
|
||||
if let Some(current) = chunk.meta().name() {
|
||||
// Check if no other popup is displayed and a new one is needed
|
||||
if state.messages.is_empty()
|
||||
&& state
|
||||
.last_region_name
|
||||
.as_ref()
|
||||
.map(|l| l != current)
|
||||
.unwrap_or(true)
|
||||
{
|
||||
// Update last_region
|
||||
state.update(|s| {
|
||||
if s.messages.is_empty() {
|
||||
s.last_message_update = Instant::now();
|
||||
}
|
||||
s.last_region_name = Some(current.to_owned());
|
||||
s.messages.push_back(current.to_owned());
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -129,6 +129,7 @@ pub enum Interface {
|
||||
MapShowCaves(bool),
|
||||
MapShowTrees(bool),
|
||||
MapShowPeaks(bool),
|
||||
MapShowBiomes(bool),
|
||||
MapShowVoxelMap(bool),
|
||||
|
||||
ResetInterfaceSettings,
|
||||
@ -535,6 +536,9 @@ impl SettingsChange {
|
||||
Interface::MapShowPeaks(map_show_peaks) => {
|
||||
settings.interface.map_show_peaks = map_show_peaks;
|
||||
},
|
||||
Interface::MapShowBiomes(map_show_biomes) => {
|
||||
settings.interface.map_show_biomes = map_show_biomes;
|
||||
},
|
||||
Interface::MapShowVoxelMap(map_show_voxel_map) => {
|
||||
settings.interface.map_show_voxel_map = map_show_voxel_map;
|
||||
},
|
||||
|
@ -38,6 +38,7 @@ pub struct InterfaceSettings {
|
||||
pub map_show_caves: bool,
|
||||
pub map_show_trees: bool,
|
||||
pub map_show_peaks: bool,
|
||||
pub map_show_biomes: bool,
|
||||
pub map_show_voxel_map: bool,
|
||||
pub minimap_show: bool,
|
||||
pub minimap_face_north: bool,
|
||||
@ -77,6 +78,7 @@ impl Default for InterfaceSettings {
|
||||
map_show_caves: true,
|
||||
map_show_trees: false,
|
||||
map_show_peaks: false,
|
||||
map_show_biomes: false,
|
||||
map_show_voxel_map: true,
|
||||
minimap_show: true,
|
||||
minimap_face_north: true,
|
||||
|
@ -112,6 +112,7 @@ impl<'a> CanvasInfo<'a> {
|
||||
surface_veg: 0.0,
|
||||
sites: Vec::new(),
|
||||
place: None,
|
||||
poi: None,
|
||||
path: Default::default(),
|
||||
cave: Default::default(),
|
||||
cliff_height: 0.0,
|
||||
|
@ -4,10 +4,10 @@ mod econ;
|
||||
|
||||
use crate::{
|
||||
config::CONFIG,
|
||||
sim::{RiverKind, WorldSim},
|
||||
sim::WorldSim,
|
||||
site::{namegen::NameGen, Castle, Settlement, Site as WorldSite, Tree},
|
||||
site2,
|
||||
util::{attempt, seed_expan, CARDINALS, NEIGHBORS},
|
||||
util::{attempt, seed_expan, NEIGHBORS},
|
||||
Index, Land,
|
||||
};
|
||||
use common::{
|
||||
@ -15,7 +15,7 @@ use common::{
|
||||
path::Path,
|
||||
spiral::Spiral2d,
|
||||
store::{Id, Store},
|
||||
terrain::{uniform_idx_as_vec2, vec2_as_uniform_idx, MapSizeLg, TerrainChunkSize},
|
||||
terrain::{uniform_idx_as_vec2, MapSizeLg, TerrainChunkSize},
|
||||
vol::RectVolSize,
|
||||
};
|
||||
use core::{fmt, hash::BuildHasherDefault, ops::Range};
|
||||
@ -89,8 +89,8 @@ impl Civs {
|
||||
let mut ctx = GenCtx { sim, rng };
|
||||
info!("starting peak naming");
|
||||
this.name_peaks(&mut ctx);
|
||||
info!("starting lake naming");
|
||||
this.name_lakes(&mut ctx);
|
||||
info!("starting biome naming");
|
||||
this.name_biomes(&mut ctx);
|
||||
|
||||
for _ in 0..ctx.sim.get_size().product() / 10_000 {
|
||||
this.generate_cave(&mut ctx);
|
||||
@ -468,113 +468,220 @@ impl Civs {
|
||||
}
|
||||
|
||||
/// Adds lake POIs and names them
|
||||
fn name_lakes(&mut self, ctx: &mut GenCtx<impl Rng>) {
|
||||
fn name_biomes(&mut self, ctx: &mut GenCtx<impl Rng>) {
|
||||
let map_size_lg = ctx.sim.map_size_lg();
|
||||
let rng = &mut ctx.rng;
|
||||
let sim_chunks = &ctx.sim.chunks;
|
||||
let lakes = sim_chunks
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter(|(posi, chunk)| {
|
||||
let neighbor_alts_min = common::terrain::neighbors(map_size_lg, *posi)
|
||||
.map(|i| sim_chunks[i].alt as u32)
|
||||
.min();
|
||||
chunk
|
||||
.river
|
||||
.river_kind
|
||||
.map_or(false, |r_kind| matches!(r_kind, RiverKind::Lake { .. }))
|
||||
&& neighbor_alts_min.map_or(false, |n_alt| (chunk.alt as u32) < n_alt)
|
||||
})
|
||||
.map(|(posi, chunk)| {
|
||||
(
|
||||
uniform_idx_as_vec2(map_size_lg, posi),
|
||||
chunk.alt as u32,
|
||||
chunk.water_alt as u32,
|
||||
)
|
||||
})
|
||||
.collect::<Vec<(Vec2<i32>, u32, u32)>>();
|
||||
let mut removals = vec![false; lakes.len()];
|
||||
let mut lake_alts = HashSet::new();
|
||||
for (i, (loc, alt, water_alt)) in lakes.iter().enumerate() {
|
||||
for (k, (n_loc, n_alt, n_water_alt)) in lakes.iter().enumerate() {
|
||||
// If the difference in position of this low point and another is
|
||||
// below a threshold and this chunk's altitude is higher, remove the
|
||||
// lake from the list. Also remove shallow ponds.
|
||||
// If this lake water altitude is already accounted for and the lake bed
|
||||
// altitude is lower than the neighboring lake bed altitude, remove this
|
||||
// lake from the list. Otherwise, add this lake water altitude to the list
|
||||
// of counted lake water altitudes.
|
||||
if i != k
|
||||
&& (*water_alt <= CONFIG.sea_level as u32
|
||||
|| (!lake_alts.insert(water_alt)
|
||||
&& water_alt == n_water_alt
|
||||
&& alt > n_alt)
|
||||
|| ((loc).distance_squared(*n_loc) < POI_THINNING_DIST_SQRD
|
||||
&& alt >= n_alt))
|
||||
{
|
||||
// This cannot panic as `removals` is the same length as `lakes`
|
||||
// i is the index in `lakes`
|
||||
removals[i] = true;
|
||||
let mut biomes: Vec<(common::terrain::BiomeKind, Vec<usize>)> = Vec::new();
|
||||
let mut explored = HashSet::new();
|
||||
let mut to_explore = HashSet::new();
|
||||
let mut to_floodfill = HashSet::new();
|
||||
// TODO: have start point in center and ignore ocean?
|
||||
let start_point = 0;
|
||||
to_explore.insert(start_point);
|
||||
explored.insert(start_point);
|
||||
|
||||
while !to_explore.is_empty() {
|
||||
let exploring = *to_explore.iter().next().unwrap();
|
||||
to_explore.remove(&exploring);
|
||||
to_floodfill.insert(exploring);
|
||||
// Should always be a chunk on the map
|
||||
let biome = ctx.sim.chunks[exploring].get_biome();
|
||||
biomes.push((biome, Vec::new()));
|
||||
while !to_floodfill.is_empty() {
|
||||
let filling = *to_floodfill.iter().next().unwrap();
|
||||
to_explore.remove(&filling);
|
||||
to_floodfill.remove(&filling);
|
||||
explored.insert(filling);
|
||||
biomes.last_mut().unwrap().1.push(filling);
|
||||
for neighbour in common::terrain::neighbors(map_size_lg, filling) {
|
||||
if explored.contains(&neighbour) {
|
||||
continue;
|
||||
}
|
||||
let n_biome = ctx.sim.chunks[neighbour].get_biome();
|
||||
if n_biome == biome {
|
||||
to_floodfill.insert(neighbour);
|
||||
} else {
|
||||
to_explore.insert(neighbour);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let mut num_lakes = 0;
|
||||
for (_j, (loc, alt, water_alt)) in lakes.iter().enumerate().filter(|&(i, _)| !removals[i]) {
|
||||
// Recenter the location of the lake POI
|
||||
// Sample every few units to speed this up
|
||||
let sample_step = 3;
|
||||
let mut chords: [i32; 4] = [0, 0, 0, 0];
|
||||
// only search up to 100 chunks in any direction
|
||||
for (j, chord) in chords.iter_mut().enumerate() {
|
||||
for i in 0..100 {
|
||||
let posi = vec2_as_uniform_idx(
|
||||
map_size_lg,
|
||||
Vec2::new(loc.x, loc.y) + CARDINALS[j] * sample_step * i,
|
||||
);
|
||||
if let Some(r_kind) = sim_chunks[posi].river.river_kind {
|
||||
if matches!(r_kind, RiverKind::Lake { .. }) {
|
||||
*chord += sample_step;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
let center_y = ((chords[0] + chords[1]) / 2) - chords[1] + loc.y;
|
||||
let center_x = ((chords[2] + chords[3]) / 2) - chords[3] + loc.x;
|
||||
let new_loc = Vec2::new(center_x, center_y);
|
||||
let size_parameter = ((chords[2] + chords[3]) + (chords[0] + chords[1]) / 4) as u32;
|
||||
let lake = PointOfInterest {
|
||||
name: {
|
||||
let name = NameGen::location(rng).generate();
|
||||
if size_parameter > 30 {
|
||||
format!("{} Sea", name)
|
||||
} else if (water_alt - alt) < 30 {
|
||||
match rng.gen_range(0..5) {
|
||||
0 => format!("{} Shallows", name),
|
||||
1 => format!("{} Pool", name),
|
||||
2 => format!("{} Well", name),
|
||||
_ => format!("{} Pond", name),
|
||||
}
|
||||
} else {
|
||||
match rng.gen_range(0..6) {
|
||||
0 => format!("{} Lake", name),
|
||||
1 => format!("Loch {}", name),
|
||||
_ => format!("Lake {}", name),
|
||||
}
|
||||
}
|
||||
let mut biome_count = 0;
|
||||
for biome in biomes {
|
||||
let name = match biome.0 {
|
||||
common::terrain::BiomeKind::Forest if biome.1.len() as u32 > 750 => Some(format!(
|
||||
"{} {}",
|
||||
NameGen::location(&mut ctx.rng).generate_temp_forest(),
|
||||
[
|
||||
"Forest",
|
||||
"Woodlands",
|
||||
"Woods",
|
||||
"Glades",
|
||||
"Grove",
|
||||
"Thickets",
|
||||
"Weald"
|
||||
]
|
||||
.choose(&mut ctx.rng)
|
||||
.unwrap()
|
||||
)),
|
||||
common::terrain::BiomeKind::Grassland if biome.1.len() as u32 > 750 => {
|
||||
Some(format!(
|
||||
"{} {}",
|
||||
NameGen::location(&mut ctx.rng).generate_grassland(),
|
||||
[
|
||||
"Grasslands",
|
||||
"Flats",
|
||||
"Greens",
|
||||
"Plains",
|
||||
"Meadows",
|
||||
"Fields",
|
||||
"Heath",
|
||||
"Steppe",
|
||||
"Downs"
|
||||
]
|
||||
.choose(&mut ctx.rng)
|
||||
.unwrap()
|
||||
))
|
||||
},
|
||||
// Size parameter is based on the east west chord length with a smaller factor from
|
||||
// the north south chord length. This is used for text scaling on the map
|
||||
kind: PoiKind::Lake(size_parameter),
|
||||
loc: new_loc,
|
||||
common::terrain::BiomeKind::Ocean if biome.1.len() as u32 > 750 => Some(format!(
|
||||
"{} {}",
|
||||
NameGen::location(&mut ctx.rng).generate_biome(),
|
||||
["Ocean", "Blue", "Deep"].choose(&mut ctx.rng).unwrap()
|
||||
)),
|
||||
common::terrain::BiomeKind::Mountain if biome.1.len() as u32 > 750 => {
|
||||
Some(format!(
|
||||
"{} {}",
|
||||
NameGen::location(&mut ctx.rng).generate_biome(),
|
||||
[
|
||||
"Mountains",
|
||||
"Range",
|
||||
"Reach",
|
||||
"Massif",
|
||||
"Rocks",
|
||||
"Cliffs",
|
||||
"Peaks",
|
||||
"Heights",
|
||||
"Bluffs",
|
||||
"Ridge",
|
||||
"Canyon",
|
||||
"Plateau"
|
||||
]
|
||||
.choose(&mut ctx.rng)
|
||||
.unwrap()
|
||||
))
|
||||
},
|
||||
common::terrain::BiomeKind::Snowland if biome.1.len() as u32 > 750 => {
|
||||
Some(format!(
|
||||
"{} {}",
|
||||
NameGen::location(&mut ctx.rng).generate_biome(),
|
||||
[
|
||||
"Snowlands",
|
||||
"Glacier",
|
||||
"Tundra",
|
||||
"Snowfields",
|
||||
"Hills",
|
||||
"Highlands"
|
||||
]
|
||||
.choose(&mut ctx.rng)
|
||||
.unwrap()
|
||||
))
|
||||
},
|
||||
common::terrain::BiomeKind::Desert if biome.1.len() as u32 > 750 => Some(format!(
|
||||
"{} {}",
|
||||
NameGen::location(&mut ctx.rng).generate_biome(),
|
||||
["Desert", "Sands", "Sandsea", "Drifts", "Dunes", "Sandfield"]
|
||||
.choose(&mut ctx.rng)
|
||||
.unwrap()
|
||||
)),
|
||||
common::terrain::BiomeKind::Mountain if biome.1.len() as u32 > 750 => {
|
||||
Some(format!(
|
||||
"{} {}",
|
||||
NameGen::location(&mut ctx.rng).generate_biome(),
|
||||
[
|
||||
"Swamp",
|
||||
"Swamps",
|
||||
"Swamplands",
|
||||
"Marsh",
|
||||
"Marshlands",
|
||||
"Morass",
|
||||
"Mire",
|
||||
"Bog",
|
||||
"Snowlands"
|
||||
]
|
||||
.choose(&mut ctx.rng)
|
||||
.unwrap()
|
||||
))
|
||||
},
|
||||
common::terrain::BiomeKind::Jungle if biome.1.len() as u32 > 750 => Some(format!(
|
||||
"{} {}",
|
||||
NameGen::location(&mut ctx.rng).generate_biome(),
|
||||
[
|
||||
"Jungle",
|
||||
"Rainforest",
|
||||
"Greatwood",
|
||||
"Wilds",
|
||||
"Wildwood",
|
||||
"Tangle",
|
||||
"Tanglewood",
|
||||
"Bush"
|
||||
]
|
||||
.choose(&mut ctx.rng)
|
||||
.unwrap()
|
||||
)),
|
||||
common::terrain::BiomeKind::Savannah if biome.1.len() as u32 > 750 => {
|
||||
Some(format!(
|
||||
"{} {}",
|
||||
NameGen::location(&mut ctx.rng).generate_biome(),
|
||||
["Savannah", "Shrubland", "Sierra", "Prairie", "Lowlands"]
|
||||
.choose(&mut ctx.rng)
|
||||
.unwrap()
|
||||
))
|
||||
},
|
||||
common::terrain::BiomeKind::Taiga if biome.1.len() as u32 > 750 => Some(format!(
|
||||
"{} {}",
|
||||
NameGen::location(&mut ctx.rng).generate_biome(),
|
||||
["Taiga", "Timberlands", "Uplands", "Highlands"]
|
||||
.choose(&mut ctx.rng)
|
||||
.unwrap()
|
||||
)),
|
||||
common::terrain::BiomeKind::Lake if biome.1.len() as u32 > 200 => Some(format!(
|
||||
"{} {}",
|
||||
["Lake", "Loch"].choose(&mut ctx.rng).unwrap(),
|
||||
NameGen::location(&mut ctx.rng).generate()
|
||||
)),
|
||||
common::terrain::BiomeKind::Lake if biome.1.len() as u32 > 10 => Some(format!(
|
||||
"{} {}",
|
||||
NameGen::location(&mut ctx.rng).generate(),
|
||||
["Pool", "Well", "Pond"].choose(&mut ctx.rng).unwrap()
|
||||
)),
|
||||
_ => None,
|
||||
};
|
||||
num_lakes += 1;
|
||||
self.pois.insert(lake);
|
||||
if let Some(name) = name {
|
||||
// find average center of the biome
|
||||
let center = biome
|
||||
.1
|
||||
.iter()
|
||||
.map(|b| uniform_idx_as_vec2(map_size_lg, *b))
|
||||
.sum::<Vec2<i32>>()
|
||||
/ biome.1.len() as i32;
|
||||
// Select the point closest to the center
|
||||
let idx = *biome
|
||||
.1
|
||||
.iter()
|
||||
.min_by_key(|&b| center.distance_squared(uniform_idx_as_vec2(map_size_lg, *b)))
|
||||
.unwrap();
|
||||
let id = self.pois.insert(PointOfInterest {
|
||||
name,
|
||||
loc: uniform_idx_as_vec2(map_size_lg, idx),
|
||||
kind: PoiKind::Biome(biome.1.len() as u32),
|
||||
});
|
||||
for chunk in biome.1 {
|
||||
ctx.sim.chunks[chunk].poi = Some(id);
|
||||
}
|
||||
biome_count += 1;
|
||||
}
|
||||
}
|
||||
info!(?num_lakes, "all lakes named");
|
||||
|
||||
info!(?biome_count, "all biomes named");
|
||||
}
|
||||
|
||||
/// Adds mountain POIs and name them
|
||||
@ -934,5 +1041,5 @@ pub enum PoiKind {
|
||||
/// Peak stores the altitude
|
||||
Peak(u32),
|
||||
/// Lake stores a metric relating to size
|
||||
Lake(u32),
|
||||
Biome(u32),
|
||||
}
|
||||
|
@ -131,7 +131,7 @@ impl World {
|
||||
name: poi.name.clone(),
|
||||
kind: match &poi.kind {
|
||||
civ::PoiKind::Peak(alt) => world_msg::PoiKind::Peak(*alt),
|
||||
civ::PoiKind::Lake(size) => world_msg::PoiKind::Lake(*size),
|
||||
civ::PoiKind::Biome(size) => world_msg::PoiKind::Lake(*size),
|
||||
},
|
||||
wpos: poi.loc * TerrainChunkSize::RECT_SIZE.map(|e| e as i32),
|
||||
}
|
||||
@ -282,7 +282,8 @@ impl World {
|
||||
.get_origin()
|
||||
.distance_squared(chunk_center_wpos2d)
|
||||
})
|
||||
.map(|id| index.sites[*id].name().to_string()),
|
||||
.map(|id| index.sites[*id].name().to_string())
|
||||
.or_else(|| sim_chunk.poi.map(|poi| self.civs.pois[poi].name.clone())),
|
||||
sim_chunk.get_biome(),
|
||||
sim_chunk.alt,
|
||||
sim_chunk.tree_density,
|
||||
|
@ -28,7 +28,7 @@ pub(crate) use self::{
|
||||
use crate::{
|
||||
all::{Environment, ForestKind, TreeAttr},
|
||||
block::BlockGen,
|
||||
civ::Place,
|
||||
civ::{Place, PointOfInterest},
|
||||
column::ColumnGen,
|
||||
layer::spot::Spot,
|
||||
site::Site,
|
||||
@ -2178,6 +2178,7 @@ pub struct SimChunk {
|
||||
|
||||
pub sites: Vec<Id<Site>>,
|
||||
pub place: Option<Id<Place>>,
|
||||
pub poi: Option<Id<PointOfInterest>>,
|
||||
|
||||
pub path: (Way, Path),
|
||||
pub cave: (Way, Cave),
|
||||
@ -2419,6 +2420,7 @@ impl SimChunk {
|
||||
|
||||
sites: Vec::new(),
|
||||
place: None,
|
||||
poi: None,
|
||||
path: Default::default(),
|
||||
cave: Default::default(),
|
||||
cliff_height: 0.0,
|
||||
|
@ -3,6 +3,7 @@ use rand::prelude::*;
|
||||
pub struct NameGen<'a, R: Rng> {
|
||||
// 2..
|
||||
pub approx_syllables: usize,
|
||||
pub approx_syllables_long: usize,
|
||||
|
||||
rng: &'a mut R,
|
||||
}
|
||||
@ -11,6 +12,8 @@ impl<'a, R: Rng> NameGen<'a, R> {
|
||||
pub fn location(rng: &'a mut R) -> Self {
|
||||
Self {
|
||||
approx_syllables: rng.gen_range(1..4),
|
||||
approx_syllables_long: rng.gen_range(2..4),
|
||||
|
||||
rng,
|
||||
}
|
||||
}
|
||||
@ -49,4 +52,94 @@ impl<'a, R: Rng> NameGen<'a, R> {
|
||||
.map(|(i, c)| if i == 0 { c.to_ascii_uppercase() } else { c })
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn generate_biome(self) -> String {
|
||||
let cons = vec![
|
||||
"b", "d", "f", "g", "h", "k", "l", "m", "n", "s", "t", "w", "br", "dr", "gr", "gh",
|
||||
"kh", "kr", "st", "str", "th", "tr", "ar", "ark", "adr", "ath", "an", "el", "elb",
|
||||
"eldr", "estr", "ostr", "ond", "ondr", "ul", "uld", "eld", "eldr",
|
||||
];
|
||||
let start = cons.clone();
|
||||
let mid = vec![
|
||||
"br", "d", "dr", "dn", "dm", "fr", "g", "gr", "gl", "k", "kr", "l", "ll", "m", "mm",
|
||||
"n", "nn", "nd", "st", "th", "rw", "nw", "thr", "lk", "nk", "ng", "rd", "rk", "nr",
|
||||
"nth", "rth", "kn", "rl", "gg", "lg", "str", "nb", "lb", "ld", "rm", "sd", "sb",
|
||||
];
|
||||
let mut middle = mid.clone();
|
||||
middle.extend(vec!["tt"]);
|
||||
let vowel = vec!["o", "e", "a", "u", "ae"];
|
||||
let end = vec![
|
||||
"ul", "um", "un", "uth", "und", "ur", "an", "a", "ar", "a", "amar", "amur", "ath",
|
||||
"or", "on", "oth", "omor", "omur", "omar", "ador", "odor", "en", "end", "eth", "amon",
|
||||
"edur", "aden", "oden", "alas", "elas", "alath", "aloth", "eloth", "eres", "ond",
|
||||
"ondor", "undor", "andor", "od", "ed", "amad", "ud", "amud", "ulud", "alud", "allen",
|
||||
"alad", "and", "an", "as", "es",
|
||||
];
|
||||
|
||||
let mut name = String::new();
|
||||
|
||||
name += start.choose(self.rng).unwrap();
|
||||
for _ in 0..self.approx_syllables_long.saturating_sub(2) {
|
||||
name += vowel.choose(self.rng).unwrap();
|
||||
name += middle.choose(self.rng).unwrap();
|
||||
}
|
||||
name += end.choose(self.rng).unwrap();
|
||||
|
||||
name.chars()
|
||||
.enumerate()
|
||||
.map(|(i, c)| if i == 0 { c.to_ascii_uppercase() } else { c })
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn generate_temp_forest(self) -> String {
|
||||
let cons = vec![
|
||||
"green", "moss", "ever", "briar", "thorn", "oak", "deep", "moon", "star", "sun",
|
||||
"bright", "glare", "fair", "calm", "mistral", "whisper", "clover", "hollow", "spring",
|
||||
"morrow", "dim", "dusk", "dawn", "night", "shimmer", "silver", "gold", "whisper",
|
||||
"fern", "quiet", "still", "gleam", "wild", "blind", "swift", "gnarl", "flutter",
|
||||
"silent", "honey", "bramble", "rose",
|
||||
];
|
||||
let start = cons.clone();
|
||||
let end = vec![
|
||||
"root", "bark", "log", "brook", "well", "shire", "leaf", "more", "bole", "heart",
|
||||
"song", "dew", "bough", "path", "wind", "breeze", "light", "branch", "bloom", "vale",
|
||||
"glen", "rest", "shade", "fall", "sward", "thicket", "shrub", "bush", "grasp", "grip",
|
||||
"gale", "crawl", "run", "shadow", "rise", "glow", "wish", "will", "walk", "wander",
|
||||
"wake", "eye", "blossom", "sprout", "barb",
|
||||
];
|
||||
let mut name = String::new();
|
||||
name += start.choose(self.rng).unwrap();
|
||||
name += end.choose(self.rng).unwrap();
|
||||
|
||||
name.chars()
|
||||
.enumerate()
|
||||
.map(|(i, c)| if i == 0 { c.to_ascii_uppercase() } else { c })
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn generate_grassland(self) -> String {
|
||||
let cons = vec![
|
||||
"green", "heather", "flower", "blue", "yellow", "vast", "moon", "star", "sun",
|
||||
"bright", "fair", "calm", "mistral", "whisper", "clover", "sooth", "spring", "morrow",
|
||||
"dim", "dusk", "dawn", "night", "shimmer", "silver", "gold", "amber", "quiet", "still",
|
||||
"gleam", "wild", "corm", "mint", "feather", "silent", "bronze", "bister", "thistle",
|
||||
"bristle", "dew", "bramble", "sorrel", "broad", "petal",
|
||||
];
|
||||
let start = cons.clone();
|
||||
let end = vec![
|
||||
"brook", "well", "flight", "more", "heart", "song", "barb", "wort", "hoof", "foot",
|
||||
"herd", "path", "wind", "breeze", "light", "bloom", "rest", "balm", "reach", "flow",
|
||||
"graze", "trail", "fall", "thicket", "shrub", "bush", "gale", "run", "stem", "glare",
|
||||
"gaze", "rove", "brew", "rise", "glow", "wish", "will", "walk", "wander", "wake",
|
||||
"sky", "burrow", "cross", "roam",
|
||||
];
|
||||
let mut name = String::new();
|
||||
name += start.choose(self.rng).unwrap();
|
||||
name += end.choose(self.rng).unwrap();
|
||||
|
||||
name.chars()
|
||||
.enumerate()
|
||||
.map(|(i, c)| if i == 0 { c.to_ascii_uppercase() } else { c })
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user