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.
|
- Specific music tracks can now play exclusively in towns.
|
||||||
- Custom map markers can be placed now
|
- Custom map markers can be placed now
|
||||||
- Fundamentals/prototype for wiring system
|
- Fundamentals/prototype for wiring system
|
||||||
|
- Mountain peak and lake markers on the map
|
||||||
|
|
||||||
### Changed
|
### 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.
|
- Skillbar buttons correctly account for skill points when checking if player has enough stamina for the ability.
|
||||||
- Burning Debuff icon is now displayed correctly.
|
- Burning Debuff icon is now displayed correctly.
|
||||||
- Villagers in safezones no longer spam messages upon seeing an enemy
|
- 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
|
## [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.dungeons": "Dungeons",
|
||||||
"hud.map.caves": "Caves",
|
"hud.map.caves": "Caves",
|
||||||
"hud.map.cave": "Cave",
|
"hud.map.cave": "Cave",
|
||||||
|
"hud.map.peaks": "Mountains",
|
||||||
"hud.map.trees": "Giant Trees",
|
"hud.map.trees": "Giant Trees",
|
||||||
"hud.map.tree": "Giant Tree",
|
"hud.map.tree": "Giant Tree",
|
||||||
"hud.map.town": "Town",
|
"hud.map.town": "Town",
|
||||||
|
@ -47,7 +47,7 @@ use common_base::span;
|
|||||||
use common_net::{
|
use common_net::{
|
||||||
msg::{
|
msg::{
|
||||||
self, validate_chat_msg,
|
self, validate_chat_msg,
|
||||||
world_msg::{EconomyInfo, SiteId, SiteInfo},
|
world_msg::{EconomyInfo, PoiInfo, SiteId, SiteInfo},
|
||||||
ChatMsgValidationError, ClientGeneral, ClientMsg, ClientRegister, ClientType,
|
ChatMsgValidationError, ClientGeneral, ClientMsg, ClientRegister, ClientType,
|
||||||
DisconnectReason, InviteAnswer, Notification, PingMsg, PlayerInfo, PlayerListUpdate,
|
DisconnectReason, InviteAnswer, Notification, PingMsg, PlayerInfo, PlayerListUpdate,
|
||||||
PresenceKind, RegisterError, ServerGeneral, ServerInit, ServerRegisterAnswer,
|
PresenceKind, RegisterError, ServerGeneral, ServerInit, ServerRegisterAnswer,
|
||||||
@ -152,6 +152,7 @@ pub struct Client {
|
|||||||
player_list: HashMap<Uid, PlayerInfo>,
|
player_list: HashMap<Uid, PlayerInfo>,
|
||||||
character_list: CharacterList,
|
character_list: CharacterList,
|
||||||
sites: HashMap<SiteId, SiteInfoRich>,
|
sites: HashMap<SiteId, SiteInfoRich>,
|
||||||
|
pois: Vec<PoiInfo>,
|
||||||
pub chat_mode: ChatMode,
|
pub chat_mode: ChatMode,
|
||||||
recipe_book: RecipeBook,
|
recipe_book: RecipeBook,
|
||||||
available_recipes: HashMap<String, Option<SpriteKind>>,
|
available_recipes: HashMap<String, Option<SpriteKind>>,
|
||||||
@ -266,6 +267,7 @@ impl Client {
|
|||||||
lod_horizon,
|
lod_horizon,
|
||||||
world_map,
|
world_map,
|
||||||
sites,
|
sites,
|
||||||
|
pois,
|
||||||
recipe_book,
|
recipe_book,
|
||||||
max_group_size,
|
max_group_size,
|
||||||
client_timeout,
|
client_timeout,
|
||||||
@ -628,6 +630,7 @@ impl Client {
|
|||||||
Grid::from_raw(map_size.map(|e| e as i32), lod_horizon),
|
Grid::from_raw(map_size.map(|e| e as i32), lod_horizon),
|
||||||
(world_map_layers, map_size, map_bounds),
|
(world_map_layers, map_size, map_bounds),
|
||||||
world_map.sites,
|
world_map.sites,
|
||||||
|
world_map.pois,
|
||||||
recipe_book,
|
recipe_book,
|
||||||
max_group_size,
|
max_group_size,
|
||||||
client_timeout,
|
client_timeout,
|
||||||
@ -661,6 +664,7 @@ impl Client {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
.collect(),
|
.collect(),
|
||||||
|
pois,
|
||||||
recipe_book,
|
recipe_book,
|
||||||
available_recipes: HashMap::default(),
|
available_recipes: HashMap::default(),
|
||||||
chat_mode: ChatMode::default(),
|
chat_mode: ChatMode::default(),
|
||||||
@ -1036,6 +1040,9 @@ impl Client {
|
|||||||
/// Unstable, likely to be removed in a future release
|
/// Unstable, likely to be removed in a future release
|
||||||
pub fn sites(&self) -> &HashMap<SiteId, SiteInfoRich> { &self.sites }
|
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 sites_mut(&mut self) -> &mut HashMap<SiteId, SiteInfoRich> { &mut self.sites }
|
||||||
|
|
||||||
pub fn enable_lantern(&mut self) {
|
pub fn enable_lantern(&mut self) {
|
||||||
|
@ -123,6 +123,7 @@ pub struct WorldMapMsg {
|
|||||||
/// (256 possible angles).
|
/// (256 possible angles).
|
||||||
pub horizons: [(Vec<u8>, Vec<u8>); 2],
|
pub horizons: [(Vec<u8>, Vec<u8>); 2],
|
||||||
pub sites: Vec<SiteInfo>,
|
pub sites: Vec<SiteInfo>,
|
||||||
|
pub pois: Vec<PoiInfo>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type SiteId = common::trade::SiteId;
|
pub type SiteId = common::trade::SiteId;
|
||||||
@ -156,3 +157,17 @@ pub struct EconomyInfo {
|
|||||||
pub last_exports: HashMap<Good, f32>,
|
pub last_exports: HashMap<Good, f32>,
|
||||||
pub resources: 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_top_r: usize,
|
||||||
created_btns_bot_l: usize,
|
created_btns_bot_l: usize,
|
||||||
created_btns_bot_r: usize,
|
created_btns_bot_r: usize,
|
||||||
hovering_exp_bar: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Diary<'a> {
|
impl<'a> Diary<'a> {
|
||||||
@ -222,7 +221,6 @@ impl<'a> Diary<'a> {
|
|||||||
created_btns_top_r: 0,
|
created_btns_top_r: 0,
|
||||||
created_btns_bot_l: 0,
|
created_btns_bot_l: 0,
|
||||||
created_btns_bot_r: 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)
|
.middle_of(state.exp_bar_bg)
|
||||||
.set(state.exp_bar_frame, ui);
|
.set(state.exp_bar_frame, ui);
|
||||||
// Show EXP bar text on hover
|
// Show EXP bar text on hover
|
||||||
self.hovering_exp_bar = ui
|
if ui
|
||||||
.widget_input(state.exp_bar_frame)
|
.widget_input(state.exp_bar_frame)
|
||||||
.mouse()
|
.mouse()
|
||||||
.map_or(false, |m| m.is_over());
|
.map_or(false, |m| m.is_over())
|
||||||
if self.hovering_exp_bar {
|
{
|
||||||
Text::new(&exp_txt)
|
Text::new(&exp_txt)
|
||||||
.mid_top_with_margin_on(state.exp_bar_frame, 47.0)
|
.mid_top_with_margin_on(state.exp_bar_frame, 47.0)
|
||||||
.font_id(self.fonts.cyri.conrod_id)
|
.font_id(self.fonts.cyri.conrod_id)
|
||||||
|
@ -382,6 +382,8 @@ image_ids! {
|
|||||||
mmap_site_excl: "voxygen.element.ui.map.buttons.excl",
|
mmap_site_excl: "voxygen.element.ui.map.buttons.excl",
|
||||||
mmap_site_tree: "voxygen.element.ui.map.buttons.tree",
|
mmap_site_tree: "voxygen.element.ui.map.buttons.tree",
|
||||||
mmap_site_tree_hover: "voxygen.element.ui.map.buttons.tree_hover",
|
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 Parts
|
||||||
window_3: "voxygen.element.ui.generic.frames.window_3",
|
window_3: "voxygen.element.ui.generic.frames.window_3",
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use super::{
|
use super::{
|
||||||
img_ids::{Imgs, ImgsRot},
|
img_ids::{Imgs, ImgsRot},
|
||||||
Show, QUALITY_COMMON, QUALITY_DEBUG, QUALITY_EPIC, QUALITY_HIGH, QUALITY_LOW, QUALITY_MODERATE,
|
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::{
|
use crate::{
|
||||||
i18n::Localization,
|
i18n::Localization,
|
||||||
@ -11,7 +11,7 @@ use crate::{
|
|||||||
};
|
};
|
||||||
use client::{self, Client, SiteInfoRich};
|
use client::{self, Client, SiteInfoRich};
|
||||||
use common::{comp, comp::group::Role, terrain::TerrainChunkSize, trade::Good, vol::RectVolSize};
|
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::{
|
use conrod_core::{
|
||||||
color, position,
|
color, position,
|
||||||
widget::{self, Button, Image, Rectangle, Text},
|
widget::{self, Button, Image, Rectangle, Text},
|
||||||
@ -37,6 +37,11 @@ widget_ids! {
|
|||||||
qlog_title,
|
qlog_title,
|
||||||
zoom_slider,
|
zoom_slider,
|
||||||
mmap_site_icons[],
|
mmap_site_icons[],
|
||||||
|
mmap_poi_icons[],
|
||||||
|
mmap_poi_title_bgs[],
|
||||||
|
mmap_poi_titles[],
|
||||||
|
peaks_txt,
|
||||||
|
peaks_txt_bg,
|
||||||
site_difs[],
|
site_difs[],
|
||||||
member_indicators[],
|
member_indicators[],
|
||||||
member_height_indicators[],
|
member_height_indicators[],
|
||||||
@ -57,6 +62,9 @@ widget_ids! {
|
|||||||
show_trees_img,
|
show_trees_img,
|
||||||
show_trees_box,
|
show_trees_box,
|
||||||
show_trees_text,
|
show_trees_text,
|
||||||
|
show_peaks_img,
|
||||||
|
show_peaks_box,
|
||||||
|
show_peaks_text,
|
||||||
show_difficulty_img,
|
show_difficulty_img,
|
||||||
show_difficulty_box,
|
show_difficulty_box,
|
||||||
show_difficulty_text,
|
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_castles = self.global_state.settings.interface.map_show_castles;
|
||||||
let show_caves = self.global_state.settings.interface.map_show_caves;
|
let show_caves = self.global_state.settings.interface.map_show_caves;
|
||||||
let show_trees = self.global_state.settings.interface.map_show_trees;
|
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 show_topo_map = self.global_state.settings.interface.map_show_topo_map;
|
||||||
let mut events = Vec::new();
|
let mut events = Vec::new();
|
||||||
let i18n = &self.localized_strings;
|
let i18n = &self.localized_strings;
|
||||||
@ -371,6 +380,9 @@ impl<'a> Widget for Map<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Handle zooming with the mousewheel
|
// Handle zooming with the mousewheel
|
||||||
|
|
||||||
|
// TODO: Experiment with zooming around cursor position instead of map center
|
||||||
|
// (issue #1111)
|
||||||
let scrolled: f64 = ui
|
let scrolled: f64 = ui
|
||||||
.widget_input(state.ids.map_layers[0])
|
.widget_input(state.ids.map_layers[0])
|
||||||
.scrolls()
|
.scrolls()
|
||||||
@ -590,7 +602,61 @@ impl<'a> Widget for Map<'a> {
|
|||||||
.graphics_for(state.ids.show_trees_box)
|
.graphics_for(state.ids.show_trees_box)
|
||||||
.color(TEXT_COLOR)
|
.color(TEXT_COLOR)
|
||||||
.set(state.ids.show_trees_text, ui);
|
.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
|
// 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() {
|
if state.ids.mmap_site_icons.len() < self.client.sites().len() {
|
||||||
state.update(|state| {
|
state.update(|state| {
|
||||||
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
|
// Group member indicators
|
||||||
let client_state = self.client.state();
|
let client_state = self.client.state();
|
||||||
let stats = client_state.ecs().read_storage::<common::comp::Stats>();
|
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_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_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_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_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);
|
const TEXT_BG: Color = Color::Rgba(0.0, 0.0, 0.0, 1.0);
|
||||||
|
@ -111,6 +111,7 @@ pub enum Interface {
|
|||||||
MapShowCastles(bool),
|
MapShowCastles(bool),
|
||||||
MapShowCaves(bool),
|
MapShowCaves(bool),
|
||||||
MapShowTrees(bool),
|
MapShowTrees(bool),
|
||||||
|
MapShowPeaks(bool),
|
||||||
|
|
||||||
ResetInterfaceSettings,
|
ResetInterfaceSettings,
|
||||||
}
|
}
|
||||||
@ -454,6 +455,9 @@ impl SettingsChange {
|
|||||||
Interface::MapShowTrees(map_show_trees) => {
|
Interface::MapShowTrees(map_show_trees) => {
|
||||||
settings.interface.map_show_trees = 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 => {
|
Interface::ResetInterfaceSettings => {
|
||||||
// Reset Interface Settings
|
// Reset Interface Settings
|
||||||
let tmp = settings.interface.intro_show;
|
let tmp = settings.interface.intro_show;
|
||||||
|
@ -35,6 +35,7 @@ pub struct InterfaceSettings {
|
|||||||
pub loading_tips: bool,
|
pub loading_tips: bool,
|
||||||
pub map_show_caves: bool,
|
pub map_show_caves: bool,
|
||||||
pub map_show_trees: bool,
|
pub map_show_trees: bool,
|
||||||
|
pub map_show_peaks: bool,
|
||||||
pub minimap_show: bool,
|
pub minimap_show: bool,
|
||||||
pub minimap_face_north: bool,
|
pub minimap_face_north: bool,
|
||||||
pub minimap_zoom: f64,
|
pub minimap_zoom: f64,
|
||||||
@ -65,10 +66,11 @@ impl Default for InterfaceSettings {
|
|||||||
map_show_difficulty: true,
|
map_show_difficulty: true,
|
||||||
map_show_towns: true,
|
map_show_towns: true,
|
||||||
map_show_dungeons: true,
|
map_show_dungeons: true,
|
||||||
map_show_castles: true,
|
map_show_castles: false,
|
||||||
loading_tips: true,
|
loading_tips: true,
|
||||||
map_show_caves: true,
|
map_show_caves: true,
|
||||||
map_show_trees: true,
|
map_show_trees: false,
|
||||||
|
map_show_peaks: false,
|
||||||
minimap_show: true,
|
minimap_show: true,
|
||||||
minimap_face_north: false,
|
minimap_face_north: false,
|
||||||
minimap_zoom: 10.0,
|
minimap_zoom: 10.0,
|
||||||
|
@ -4,10 +4,10 @@ mod econ;
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
config::CONFIG,
|
config::CONFIG,
|
||||||
sim::WorldSim,
|
sim::{RiverKind, WorldSim},
|
||||||
site::{namegen::NameGen, Castle, Dungeon, Settlement, Site as WorldSite, Tree},
|
site::{namegen::NameGen, Castle, Dungeon, Settlement, Site as WorldSite, Tree},
|
||||||
site2,
|
site2,
|
||||||
util::{attempt, seed_expan, NEIGHBORS},
|
util::{attempt, seed_expan, CARDINALS, NEIGHBORS},
|
||||||
Index, Land,
|
Index, Land,
|
||||||
};
|
};
|
||||||
use common::{
|
use common::{
|
||||||
@ -15,12 +15,12 @@ use common::{
|
|||||||
path::Path,
|
path::Path,
|
||||||
spiral::Spiral2d,
|
spiral::Spiral2d,
|
||||||
store::{Id, Store},
|
store::{Id, Store},
|
||||||
terrain::{uniform_idx_as_vec2, MapSizeLg, TerrainChunkSize},
|
terrain::{uniform_idx_as_vec2, vec2_as_uniform_idx, MapSizeLg, TerrainChunkSize},
|
||||||
vol::RectVolSize,
|
vol::RectVolSize,
|
||||||
};
|
};
|
||||||
use core::{fmt, hash::BuildHasherDefault, ops::Range};
|
use core::{fmt, hash::BuildHasherDefault, ops::Range};
|
||||||
use fxhash::FxHasher64;
|
use fxhash::FxHasher64;
|
||||||
use hashbrown::HashMap;
|
use hashbrown::{HashMap, HashSet};
|
||||||
use rand::prelude::*;
|
use rand::prelude::*;
|
||||||
use rand_chacha::ChaChaRng;
|
use rand_chacha::ChaChaRng;
|
||||||
use tracing::{debug, info, warn};
|
use tracing::{debug, info, warn};
|
||||||
@ -44,6 +44,7 @@ pub struct CaveInfo {
|
|||||||
pub struct Civs {
|
pub struct Civs {
|
||||||
pub civs: Store<Civ>,
|
pub civs: Store<Civ>,
|
||||||
pub places: Store<Place>,
|
pub places: Store<Place>,
|
||||||
|
pub pois: Store<PointOfInterest>,
|
||||||
|
|
||||||
pub tracks: Store<Track>,
|
pub tracks: Store<Track>,
|
||||||
/// We use this hasher (FxHasher64) because
|
/// We use this hasher (FxHasher64) because
|
||||||
@ -62,6 +63,7 @@ pub struct Civs {
|
|||||||
|
|
||||||
// Change this to get rid of particularly horrid seeds
|
// Change this to get rid of particularly horrid seeds
|
||||||
const SEED_SKIP: u8 = 5;
|
const SEED_SKIP: u8 = 5;
|
||||||
|
const POI_THINNING_DIST_SQRD: i32 = 300;
|
||||||
|
|
||||||
pub struct GenCtx<'a, R: Rng> {
|
pub struct GenCtx<'a, R: Rng> {
|
||||||
sim: &'a mut WorldSim,
|
sim: &'a mut WorldSim,
|
||||||
@ -85,6 +87,10 @@ impl Civs {
|
|||||||
let rng = ChaChaRng::from_seed(seed_expan::rng_state(seed));
|
let rng = ChaChaRng::from_seed(seed_expan::rng_state(seed));
|
||||||
let initial_civ_count = initial_civ_count(sim.map_size_lg());
|
let initial_civ_count = initial_civ_count(sim.map_size_lg());
|
||||||
let mut ctx = GenCtx { sim, rng };
|
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 {
|
for _ in 0..ctx.sim.get_size().product() / 10_000 {
|
||||||
this.generate_cave(&mut ctx);
|
this.generate_cave(&mut ctx);
|
||||||
@ -456,6 +462,191 @@ impl Civs {
|
|||||||
self.places.insert(Place { center: loc })
|
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(
|
fn establish_site(
|
||||||
&mut self,
|
&mut self,
|
||||||
ctx: &mut GenCtx<impl Rng>,
|
ctx: &mut GenCtx<impl Rng>,
|
||||||
@ -726,3 +917,18 @@ impl Site {
|
|||||||
|
|
||||||
pub fn is_castle(&self) -> bool { matches!(self.kind, SiteKind::Castle) }
|
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_sites = self.civs().sites().count() as u64;
|
||||||
let num_caves = self.civs().caves.values().count() as u64;
|
let num_caves = self.civs().caves.values().count() as u64;
|
||||||
WorldMapMsg {
|
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
|
sites: self
|
||||||
.civs()
|
.civs()
|
||||||
.sites
|
.sites
|
||||||
|
@ -1514,6 +1514,7 @@ impl WorldSim {
|
|||||||
alt: Grid::from_raw(self.get_size().map(|e| e as i32), alts),
|
alt: Grid::from_raw(self.get_size().map(|e| e as i32), alts),
|
||||||
horizons,
|
horizons,
|
||||||
sites: Vec::new(), // Will be substituted later
|
sites: Vec::new(), // Will be substituted later
|
||||||
|
pois: Vec::new(), // Will be substituted later
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user