mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Add map markers for lakes and mountains
This commit is contained in:
parent
435d515945
commit
f553700e8c
@ -52,6 +52,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- Specific music tracks can now play exclusively in towns.
|
||||
- Custom map markers can be placed now
|
||||
- Fundamentals/prototype for wiring system
|
||||
- Mountain peak and lake markers on the map
|
||||
|
||||
### Changed
|
||||
|
||||
@ -112,6 +113,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- Skillbar buttons correctly account for skill points when checking if player has enough stamina for the ability.
|
||||
- Burning Debuff icon is now displayed correctly.
|
||||
- Villagers in safezones no longer spam messages upon seeing an enemy
|
||||
- Wolf AI will no longer circle into walls and will instead use the power of raycasts to stop early
|
||||
|
||||
## [0.9.0] - 2021-03-20
|
||||
|
||||
|
BIN
assets/voxygen/element/ui/map/buttons/peak.png
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/element/ui/map/buttons/peak.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/element/ui/map/buttons/peak_hover.png
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/element/ui/map/buttons/peak_hover.png
(Stored with Git LFS)
Normal file
Binary file not shown.
@ -13,6 +13,7 @@
|
||||
"hud.map.dungeons": "Dungeons",
|
||||
"hud.map.caves": "Caves",
|
||||
"hud.map.cave": "Cave",
|
||||
"hud.map.peaks": "Mountains",
|
||||
"hud.map.trees": "Giant Trees",
|
||||
"hud.map.tree": "Giant Tree",
|
||||
"hud.map.town": "Town",
|
||||
|
@ -47,7 +47,7 @@ use common_base::span;
|
||||
use common_net::{
|
||||
msg::{
|
||||
self, validate_chat_msg,
|
||||
world_msg::{EconomyInfo, SiteId, SiteInfo},
|
||||
world_msg::{EconomyInfo, PoiInfo, SiteId, SiteInfo},
|
||||
ChatMsgValidationError, ClientGeneral, ClientMsg, ClientRegister, ClientType,
|
||||
DisconnectReason, InviteAnswer, Notification, PingMsg, PlayerInfo, PlayerListUpdate,
|
||||
PresenceKind, RegisterError, ServerGeneral, ServerInit, ServerRegisterAnswer,
|
||||
@ -152,6 +152,7 @@ pub struct Client {
|
||||
player_list: HashMap<Uid, PlayerInfo>,
|
||||
character_list: CharacterList,
|
||||
sites: HashMap<SiteId, SiteInfoRich>,
|
||||
pois: Vec<PoiInfo>,
|
||||
pub chat_mode: ChatMode,
|
||||
recipe_book: RecipeBook,
|
||||
available_recipes: HashMap<String, Option<SpriteKind>>,
|
||||
@ -266,6 +267,7 @@ impl Client {
|
||||
lod_horizon,
|
||||
world_map,
|
||||
sites,
|
||||
pois,
|
||||
recipe_book,
|
||||
max_group_size,
|
||||
client_timeout,
|
||||
@ -628,6 +630,7 @@ impl Client {
|
||||
Grid::from_raw(map_size.map(|e| e as i32), lod_horizon),
|
||||
(world_map_layers, map_size, map_bounds),
|
||||
world_map.sites,
|
||||
world_map.pois,
|
||||
recipe_book,
|
||||
max_group_size,
|
||||
client_timeout,
|
||||
@ -661,6 +664,7 @@ impl Client {
|
||||
})
|
||||
})
|
||||
.collect(),
|
||||
pois,
|
||||
recipe_book,
|
||||
available_recipes: HashMap::default(),
|
||||
chat_mode: ChatMode::default(),
|
||||
@ -1036,6 +1040,9 @@ impl Client {
|
||||
/// Unstable, likely to be removed in a future release
|
||||
pub fn sites(&self) -> &HashMap<SiteId, SiteInfoRich> { &self.sites }
|
||||
|
||||
/// Unstable, likely to be removed in a future release
|
||||
pub fn pois(&self) -> &Vec<PoiInfo> { &self.pois }
|
||||
|
||||
pub fn sites_mut(&mut self) -> &mut HashMap<SiteId, SiteInfoRich> { &mut self.sites }
|
||||
|
||||
pub fn enable_lantern(&mut self) {
|
||||
|
@ -123,6 +123,7 @@ pub struct WorldMapMsg {
|
||||
/// (256 possible angles).
|
||||
pub horizons: [(Vec<u8>, Vec<u8>); 2],
|
||||
pub sites: Vec<SiteInfo>,
|
||||
pub pois: Vec<PoiInfo>,
|
||||
}
|
||||
|
||||
pub type SiteId = common::trade::SiteId;
|
||||
@ -156,3 +157,17 @@ pub struct EconomyInfo {
|
||||
pub last_exports: HashMap<Good, f32>,
|
||||
pub resources: HashMap<Good, f32>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct PoiInfo {
|
||||
pub kind: PoiKind,
|
||||
pub wpos: Vec2<i32>,
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[repr(u8)]
|
||||
pub enum PoiKind {
|
||||
Peak(u32),
|
||||
Lake(u32),
|
||||
}
|
||||
|
@ -190,7 +190,6 @@ pub struct Diary<'a> {
|
||||
created_btns_top_r: usize,
|
||||
created_btns_bot_l: usize,
|
||||
created_btns_bot_r: usize,
|
||||
hovering_exp_bar: bool,
|
||||
}
|
||||
|
||||
impl<'a> Diary<'a> {
|
||||
@ -222,7 +221,6 @@ impl<'a> Diary<'a> {
|
||||
created_btns_top_r: 0,
|
||||
created_btns_bot_l: 0,
|
||||
created_btns_bot_r: 0,
|
||||
hovering_exp_bar: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -454,11 +452,11 @@ impl<'a> Widget for Diary<'a> {
|
||||
.middle_of(state.exp_bar_bg)
|
||||
.set(state.exp_bar_frame, ui);
|
||||
// Show EXP bar text on hover
|
||||
self.hovering_exp_bar = ui
|
||||
if ui
|
||||
.widget_input(state.exp_bar_frame)
|
||||
.mouse()
|
||||
.map_or(false, |m| m.is_over());
|
||||
if self.hovering_exp_bar {
|
||||
.map_or(false, |m| m.is_over())
|
||||
{
|
||||
Text::new(&exp_txt)
|
||||
.mid_top_with_margin_on(state.exp_bar_frame, 47.0)
|
||||
.font_id(self.fonts.cyri.conrod_id)
|
||||
|
@ -382,6 +382,8 @@ image_ids! {
|
||||
mmap_site_excl: "voxygen.element.ui.map.buttons.excl",
|
||||
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_peak_hover: "voxygen.element.ui.map.buttons.peak_hover",
|
||||
|
||||
// Window Parts
|
||||
window_3: "voxygen.element.ui.generic.frames.window_3",
|
||||
|
@ -1,7 +1,7 @@
|
||||
use super::{
|
||||
img_ids::{Imgs, ImgsRot},
|
||||
Show, QUALITY_COMMON, QUALITY_DEBUG, QUALITY_EPIC, QUALITY_HIGH, QUALITY_LOW, QUALITY_MODERATE,
|
||||
TEXT_COLOR, TEXT_GRAY_COLOR, TEXT_VELORITE, UI_HIGHLIGHT_0, UI_MAIN,
|
||||
TEXT_BG, TEXT_BLUE_COLOR, TEXT_COLOR, TEXT_GRAY_COLOR, TEXT_VELORITE, UI_HIGHLIGHT_0, UI_MAIN,
|
||||
};
|
||||
use crate::{
|
||||
i18n::Localization,
|
||||
@ -11,7 +11,7 @@ use crate::{
|
||||
};
|
||||
use client::{self, Client, SiteInfoRich};
|
||||
use common::{comp, comp::group::Role, terrain::TerrainChunkSize, trade::Good, vol::RectVolSize};
|
||||
use common_net::msg::world_msg::{SiteId, SiteKind};
|
||||
use common_net::msg::world_msg::{PoiKind, SiteId, SiteKind};
|
||||
use conrod_core::{
|
||||
color, position,
|
||||
widget::{self, Button, Image, Rectangle, Text},
|
||||
@ -37,6 +37,11 @@ widget_ids! {
|
||||
qlog_title,
|
||||
zoom_slider,
|
||||
mmap_site_icons[],
|
||||
mmap_poi_icons[],
|
||||
mmap_poi_title_bgs[],
|
||||
mmap_poi_titles[],
|
||||
peaks_txt,
|
||||
peaks_txt_bg,
|
||||
site_difs[],
|
||||
member_indicators[],
|
||||
member_height_indicators[],
|
||||
@ -57,6 +62,9 @@ widget_ids! {
|
||||
show_trees_img,
|
||||
show_trees_box,
|
||||
show_trees_text,
|
||||
show_peaks_img,
|
||||
show_peaks_box,
|
||||
show_peaks_text,
|
||||
show_difficulty_img,
|
||||
show_difficulty_box,
|
||||
show_difficulty_text,
|
||||
@ -191,6 +199,7 @@ impl<'a> Widget for Map<'a> {
|
||||
let show_castles = self.global_state.settings.interface.map_show_castles;
|
||||
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_topo_map = self.global_state.settings.interface.map_show_topo_map;
|
||||
let mut events = Vec::new();
|
||||
let i18n = &self.localized_strings;
|
||||
@ -371,6 +380,9 @@ impl<'a> Widget for Map<'a> {
|
||||
}
|
||||
|
||||
// Handle zooming with the mousewheel
|
||||
|
||||
// TODO: Experiment with zooming around cursor position instead of map center
|
||||
// (issue #1111)
|
||||
let scrolled: f64 = ui
|
||||
.widget_input(state.ids.map_layers[0])
|
||||
.scrolls()
|
||||
@ -590,7 +602,61 @@ impl<'a> Widget for Map<'a> {
|
||||
.graphics_for(state.ids.show_trees_box)
|
||||
.color(TEXT_COLOR)
|
||||
.set(state.ids.show_trees_text, ui);
|
||||
// Peaks
|
||||
Image::new(self.imgs.mmap_poi_peak)
|
||||
.down_from(state.ids.show_trees_img, 10.0)
|
||||
.w_h(20.0, 20.0)
|
||||
.set(state.ids.show_peaks_img, ui);
|
||||
if Button::image(if show_peaks {
|
||||
self.imgs.checkbox_checked
|
||||
} else {
|
||||
self.imgs.checkbox
|
||||
})
|
||||
.w_h(18.0, 18.0)
|
||||
.hover_image(if show_peaks {
|
||||
self.imgs.checkbox_checked_mo
|
||||
} else {
|
||||
self.imgs.checkbox_mo
|
||||
})
|
||||
.press_image(if show_peaks {
|
||||
self.imgs.checkbox_checked
|
||||
} else {
|
||||
self.imgs.checkbox_press
|
||||
})
|
||||
.right_from(state.ids.show_peaks_img, 10.0)
|
||||
.set(state.ids.show_peaks_box, ui)
|
||||
.was_clicked()
|
||||
{
|
||||
events.push(Event::SettingsChange(MapShowPeaks(!show_peaks)));
|
||||
}
|
||||
Text::new(i18n.get("hud.map.peaks"))
|
||||
.right_from(state.ids.show_peaks_box, 10.0)
|
||||
.font_size(self.fonts.cyri.scale(14))
|
||||
.font_id(self.fonts.cyri.conrod_id)
|
||||
.graphics_for(state.ids.show_peaks_box)
|
||||
.color(TEXT_COLOR)
|
||||
.set(state.ids.show_peaks_text, ui);
|
||||
// Map icons
|
||||
if state.ids.mmap_poi_icons.len() < self.client.pois().len() {
|
||||
state.update(|state| {
|
||||
state
|
||||
.ids
|
||||
.mmap_poi_icons
|
||||
.resize(self.client.pois().len(), &mut ui.widget_id_generator())
|
||||
});
|
||||
state.update(|state| {
|
||||
state
|
||||
.ids
|
||||
.mmap_poi_titles
|
||||
.resize(self.client.pois().len(), &mut ui.widget_id_generator())
|
||||
});
|
||||
state.update(|state| {
|
||||
state
|
||||
.ids
|
||||
.mmap_poi_title_bgs
|
||||
.resize(self.client.pois().len(), &mut ui.widget_id_generator())
|
||||
});
|
||||
}
|
||||
if state.ids.mmap_site_icons.len() < self.client.sites().len() {
|
||||
state.update(|state| {
|
||||
state
|
||||
@ -780,6 +846,90 @@ impl<'a> Widget for Map<'a> {
|
||||
}
|
||||
}
|
||||
}
|
||||
for (i, poi) in self.client.pois().iter().enumerate() {
|
||||
let rpos = match wpos_to_rpos(poi.wpos.map(|e| e as f32)) {
|
||||
Some(rpos) => rpos,
|
||||
None => continue,
|
||||
};
|
||||
let title = &poi.name;
|
||||
match poi.kind {
|
||||
PoiKind::Peak(alt) => {
|
||||
let height = format!("{} m", alt);
|
||||
if show_peaks && zoom > 3.0 {
|
||||
Text::new(title)
|
||||
.x_y_position_relative_to(
|
||||
state.ids.map_layers[0],
|
||||
position::Relative::Scalar(rpos.x as f64),
|
||||
position::Relative::Scalar(rpos.y as f64 + zoom * 3.0),
|
||||
)
|
||||
.font_size(self.fonts.cyri.scale((zoom * 2.0) as u32))
|
||||
.font_id(self.fonts.cyri.conrod_id)
|
||||
.graphics_for(state.ids.map_layers[0])
|
||||
.color(TEXT_BG)
|
||||
.set(state.ids.mmap_poi_title_bgs[i], ui);
|
||||
Text::new(title)
|
||||
.bottom_left_with_margins_on(state.ids.mmap_poi_title_bgs[i], 1.0, 1.0)
|
||||
.font_size(self.fonts.cyri.scale((zoom * 2.0) as u32))
|
||||
.font_id(self.fonts.cyri.conrod_id)
|
||||
//.graphics_for(state.ids.map_layers[0])
|
||||
.color(TEXT_COLOR)
|
||||
.set(state.ids.mmap_poi_titles[i], ui);
|
||||
// Show peak altitude
|
||||
if ui
|
||||
.widget_input(state.ids.mmap_poi_titles[i])
|
||||
.mouse()
|
||||
.map_or(false, |m| m.is_over())
|
||||
{
|
||||
Text::new(&height)
|
||||
.mid_bottom_with_margin_on(
|
||||
state.ids.mmap_poi_title_bgs[i],
|
||||
-zoom * 2.5,
|
||||
)
|
||||
.font_size(self.fonts.cyri.scale((zoom * 2.0) as u32))
|
||||
.font_id(self.fonts.cyri.conrod_id)
|
||||
.graphics_for(state.ids.map_layers[0])
|
||||
.color(TEXT_BG)
|
||||
.set(state.ids.peaks_txt_bg, ui);
|
||||
Text::new(&height)
|
||||
.bottom_left_with_margins_on(state.ids.peaks_txt_bg, 1.0, 1.0)
|
||||
.font_size(self.fonts.cyri.scale((zoom * 2.0) as u32))
|
||||
.font_id(self.fonts.cyri.conrod_id)
|
||||
.graphics_for(state.ids.map_layers[0])
|
||||
.color(TEXT_COLOR)
|
||||
.set(state.ids.peaks_txt, ui);
|
||||
}
|
||||
}
|
||||
},
|
||||
PoiKind::Lake(size) => {
|
||||
if zoom.powi(2) * size as f64 > 37.0 {
|
||||
let font_scale_factor = if size > 20 {
|
||||
size as f64 / 25.0
|
||||
} else if size > 10 {
|
||||
size as f64 / 10.0
|
||||
} else if size > 5 {
|
||||
size as f64 / 6.0
|
||||
} else {
|
||||
size as f64 / 2.5
|
||||
};
|
||||
Text::new(&format!("{}", title))
|
||||
.x_y_position_relative_to(
|
||||
state.ids.map_layers[0],
|
||||
position::Relative::Scalar(rpos.x as f64),
|
||||
position::Relative::Scalar(rpos.y as f64),
|
||||
)
|
||||
.font_size(
|
||||
self.fonts.cyri.scale(
|
||||
(2.0 + font_scale_factor * zoom).min(18.0).max(10.0) as u32,
|
||||
),
|
||||
)
|
||||
.font_id(self.fonts.cyri.conrod_id)
|
||||
.graphics_for(state.ids.map_layers[0])
|
||||
.color(TEXT_BLUE_COLOR)
|
||||
.set(state.ids.mmap_poi_icons[i], ui);
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
// Group member indicators
|
||||
let client_state = self.client.state();
|
||||
let stats = client_state.ecs().read_storage::<common::comp::Stats>();
|
||||
|
@ -105,6 +105,7 @@ use vek::*;
|
||||
|
||||
const TEXT_COLOR: Color = Color::Rgba(1.0, 1.0, 1.0, 1.0);
|
||||
const TEXT_VELORITE: Color = Color::Rgba(0.0, 0.66, 0.66, 1.0);
|
||||
const TEXT_BLUE_COLOR: Color = Color::Rgba(0.8, 0.9, 1.0, 1.0);
|
||||
const TEXT_GRAY_COLOR: Color = Color::Rgba(0.5, 0.5, 0.5, 1.0);
|
||||
const TEXT_DULL_RED_COLOR: Color = Color::Rgba(0.56, 0.2, 0.2, 1.0);
|
||||
const TEXT_BG: Color = Color::Rgba(0.0, 0.0, 0.0, 1.0);
|
||||
|
@ -111,6 +111,7 @@ pub enum Interface {
|
||||
MapShowCastles(bool),
|
||||
MapShowCaves(bool),
|
||||
MapShowTrees(bool),
|
||||
MapShowPeaks(bool),
|
||||
|
||||
ResetInterfaceSettings,
|
||||
}
|
||||
@ -454,6 +455,9 @@ impl SettingsChange {
|
||||
Interface::MapShowTrees(map_show_trees) => {
|
||||
settings.interface.map_show_trees = map_show_trees;
|
||||
},
|
||||
Interface::MapShowPeaks(map_show_peaks) => {
|
||||
settings.interface.map_show_peaks = map_show_peaks;
|
||||
},
|
||||
Interface::ResetInterfaceSettings => {
|
||||
// Reset Interface Settings
|
||||
let tmp = settings.interface.intro_show;
|
||||
|
@ -35,6 +35,7 @@ pub struct InterfaceSettings {
|
||||
pub loading_tips: bool,
|
||||
pub map_show_caves: bool,
|
||||
pub map_show_trees: bool,
|
||||
pub map_show_peaks: bool,
|
||||
pub minimap_show: bool,
|
||||
pub minimap_face_north: bool,
|
||||
pub minimap_zoom: f64,
|
||||
@ -65,10 +66,11 @@ impl Default for InterfaceSettings {
|
||||
map_show_difficulty: true,
|
||||
map_show_towns: true,
|
||||
map_show_dungeons: true,
|
||||
map_show_castles: true,
|
||||
map_show_castles: false,
|
||||
loading_tips: true,
|
||||
map_show_caves: true,
|
||||
map_show_trees: true,
|
||||
map_show_trees: false,
|
||||
map_show_peaks: false,
|
||||
minimap_show: true,
|
||||
minimap_face_north: false,
|
||||
minimap_zoom: 10.0,
|
||||
|
@ -4,10 +4,10 @@ mod econ;
|
||||
|
||||
use crate::{
|
||||
config::CONFIG,
|
||||
sim::WorldSim,
|
||||
sim::{RiverKind, WorldSim},
|
||||
site::{namegen::NameGen, Castle, Dungeon, Settlement, Site as WorldSite, Tree},
|
||||
site2,
|
||||
util::{attempt, seed_expan, NEIGHBORS},
|
||||
util::{attempt, seed_expan, CARDINALS, NEIGHBORS},
|
||||
Index, Land,
|
||||
};
|
||||
use common::{
|
||||
@ -15,12 +15,12 @@ use common::{
|
||||
path::Path,
|
||||
spiral::Spiral2d,
|
||||
store::{Id, Store},
|
||||
terrain::{uniform_idx_as_vec2, MapSizeLg, TerrainChunkSize},
|
||||
terrain::{uniform_idx_as_vec2, vec2_as_uniform_idx, MapSizeLg, TerrainChunkSize},
|
||||
vol::RectVolSize,
|
||||
};
|
||||
use core::{fmt, hash::BuildHasherDefault, ops::Range};
|
||||
use fxhash::FxHasher64;
|
||||
use hashbrown::HashMap;
|
||||
use hashbrown::{HashMap, HashSet};
|
||||
use rand::prelude::*;
|
||||
use rand_chacha::ChaChaRng;
|
||||
use tracing::{debug, info, warn};
|
||||
@ -44,6 +44,7 @@ pub struct CaveInfo {
|
||||
pub struct Civs {
|
||||
pub civs: Store<Civ>,
|
||||
pub places: Store<Place>,
|
||||
pub pois: Store<PointOfInterest>,
|
||||
|
||||
pub tracks: Store<Track>,
|
||||
/// We use this hasher (FxHasher64) because
|
||||
@ -62,6 +63,7 @@ pub struct Civs {
|
||||
|
||||
// Change this to get rid of particularly horrid seeds
|
||||
const SEED_SKIP: u8 = 5;
|
||||
const POI_THINNING_DIST_SQRD: i32 = 300;
|
||||
|
||||
pub struct GenCtx<'a, R: Rng> {
|
||||
sim: &'a mut WorldSim,
|
||||
@ -85,6 +87,10 @@ impl Civs {
|
||||
let rng = ChaChaRng::from_seed(seed_expan::rng_state(seed));
|
||||
let initial_civ_count = initial_civ_count(sim.map_size_lg());
|
||||
let mut ctx = GenCtx { sim, rng };
|
||||
info!("starting peak naming");
|
||||
this.name_peaks(&mut ctx);
|
||||
info!("starting lake naming");
|
||||
this.name_lakes(&mut ctx);
|
||||
|
||||
for _ in 0..ctx.sim.get_size().product() / 10_000 {
|
||||
this.generate_cave(&mut ctx);
|
||||
@ -456,6 +462,191 @@ impl Civs {
|
||||
self.places.insert(Place { center: loc })
|
||||
}
|
||||
|
||||
/// Adds lake POIs and names them
|
||||
fn name_lakes(&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 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),
|
||||
}
|
||||
}
|
||||
},
|
||||
// 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,
|
||||
};
|
||||
num_lakes += 1;
|
||||
self.pois.insert(lake);
|
||||
}
|
||||
info!(?num_lakes, "all lakes named");
|
||||
}
|
||||
|
||||
/// Adds mountain POIs and name them
|
||||
fn name_peaks(&mut self, ctx: &mut GenCtx<impl Rng>) {
|
||||
let map_size_lg = ctx.sim.map_size_lg();
|
||||
const MIN_MOUNTAIN_ALT: f32 = 600.0;
|
||||
const MIN_MOUNTAIN_CHAOS: f32 = 0.35;
|
||||
let rng = &mut ctx.rng;
|
||||
let sim_chunks = &ctx.sim.chunks;
|
||||
let peaks = sim_chunks
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter(|(posi, chunk)| {
|
||||
let neighbor_alts_max = common::terrain::neighbors(map_size_lg, *posi)
|
||||
.map(|i| sim_chunks[i].alt as u32)
|
||||
.max();
|
||||
chunk.alt > MIN_MOUNTAIN_ALT
|
||||
&& chunk.chaos > MIN_MOUNTAIN_CHAOS
|
||||
&& neighbor_alts_max.map_or(false, |n_alt| chunk.alt as u32 > n_alt)
|
||||
})
|
||||
.map(|(posi, chunk)| {
|
||||
(
|
||||
posi,
|
||||
uniform_idx_as_vec2(map_size_lg, posi),
|
||||
(chunk.alt - CONFIG.sea_level) as u32,
|
||||
)
|
||||
})
|
||||
.collect::<Vec<(usize, Vec2<i32>, u32)>>();
|
||||
let mut num_peaks = 0;
|
||||
let mut removals = vec![false; peaks.len()];
|
||||
for (i, peak) in peaks.iter().enumerate() {
|
||||
for (k, n_peak) in peaks.iter().enumerate() {
|
||||
// If the difference in position of this peak and another is
|
||||
// below a threshold and this peak's altitude is lower, remove the
|
||||
// peak from the list
|
||||
if i != k
|
||||
&& (peak.1).distance_squared(n_peak.1) < POI_THINNING_DIST_SQRD
|
||||
&& peak.2 <= n_peak.2
|
||||
{
|
||||
// Remove this peak
|
||||
// This cannot panic as `removals` is the same length as `peaks`
|
||||
// i is the index in `peaks`
|
||||
removals[i] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
peaks
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter(|&(i, _)| !removals[i])
|
||||
.for_each(|(_, (_, loc, alt))| {
|
||||
num_peaks += 1;
|
||||
self.pois.insert(PointOfInterest {
|
||||
name: {
|
||||
let name = NameGen::location(rng).generate();
|
||||
if *alt < 1000 {
|
||||
match rng.gen_range(0..6) {
|
||||
0 => format!("{} Bluff", name),
|
||||
1 => format!("{} Crag", name),
|
||||
_ => format!("{} Hill", name),
|
||||
}
|
||||
} else {
|
||||
match rng.gen_range(0..8) {
|
||||
0 => format!("{}'s Peak", name),
|
||||
1 => format!("{} Peak", name),
|
||||
2 => format!("{} Summit", name),
|
||||
_ => format!("Mount {}", name),
|
||||
}
|
||||
}
|
||||
},
|
||||
kind: PoiKind::Peak(*alt),
|
||||
loc: *loc,
|
||||
});
|
||||
});
|
||||
info!(?num_peaks, "all peaks named");
|
||||
}
|
||||
|
||||
fn establish_site(
|
||||
&mut self,
|
||||
ctx: &mut GenCtx<impl Rng>,
|
||||
@ -726,3 +917,18 @@ impl Site {
|
||||
|
||||
pub fn is_castle(&self) -> bool { matches!(self.kind, SiteKind::Castle) }
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Debug, Clone)]
|
||||
pub struct PointOfInterest {
|
||||
pub name: String,
|
||||
pub kind: PoiKind,
|
||||
pub loc: Vec2<i32>,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Debug, Clone)]
|
||||
pub enum PoiKind {
|
||||
/// Peak stores the altitude
|
||||
Peak(u32),
|
||||
/// Lake stores a metric relating to size
|
||||
Lake(u32),
|
||||
}
|
||||
|
@ -112,6 +112,16 @@ impl World {
|
||||
let num_sites = self.civs().sites().count() as u64;
|
||||
let num_caves = self.civs().caves.values().count() as u64;
|
||||
WorldMapMsg {
|
||||
pois: self.civs().pois.iter().map(|(_, poi)| {
|
||||
world_msg::PoiInfo {
|
||||
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),
|
||||
},
|
||||
wpos: poi.loc * TerrainChunkSize::RECT_SIZE.map(|e| e as i32),
|
||||
}
|
||||
}).collect(),
|
||||
sites: self
|
||||
.civs()
|
||||
.sites
|
||||
|
@ -1514,6 +1514,7 @@ impl WorldSim {
|
||||
alt: Grid::from_raw(self.get_size().map(|e| e as i32), alts),
|
||||
horizons,
|
||||
sites: Vec::new(), // Will be substituted later
|
||||
pois: Vec::new(), // Will be substituted later
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user