veloren/voxygen/src/hud/minimap.rs

307 lines
10 KiB
Rust
Raw Normal View History

use super::{
img_ids::{Imgs, ImgsRot},
2020-05-14 16:56:10 +00:00
Show, TEXT_COLOR, UI_HIGHLIGHT_0, UI_MAIN,
};
use crate::ui::{fonts::ConrodVoxygenFonts, img_ids};
2019-10-02 10:05:17 +00:00
use client::{self, Client};
use common::{comp, terrain::TerrainChunkSize, vol::RectVolSize};
use conrod_core::{
color, position,
widget::{self, Button, Image, Rectangle, Text},
2020-07-10 14:00:20 +00:00
widget_ids, Color, Colorable, Positionable, Sizeable, Widget, WidgetCommon,
};
2019-11-30 06:41:20 +00:00
use specs::WorldExt;
2019-10-02 10:05:17 +00:00
use vek::*;
2019-06-19 14:55:26 +00:00
widget_ids! {
struct Ids {
mmap_frame,
mmap_frame_2,
mmap_frame_bg,
mmap_location,
mmap_button,
mmap_plus,
mmap_minus,
2019-10-02 10:05:17 +00:00
grid,
2020-07-10 14:00:20 +00:00
indicator,
mmap_north,
mmap_east,
mmap_south,
mmap_west,
}
}
#[derive(WidgetCommon)]
pub struct MiniMap<'a> {
show: &'a Show,
2019-06-19 14:55:26 +00:00
client: &'a Client,
imgs: &'a Imgs,
rot_imgs: &'a ImgsRot,
world_map: &'a (img_ids::Rotations, Vec2<u32>),
fonts: &'a ConrodVoxygenFonts,
#[conrod(common_builder)]
common: widget::CommonBuilder,
2020-07-10 14:00:20 +00:00
ori: Vec3<f32>,
}
impl<'a> MiniMap<'a> {
pub fn new(
show: &'a Show,
client: &'a Client,
imgs: &'a Imgs,
rot_imgs: &'a ImgsRot,
world_map: &'a (img_ids::Rotations, Vec2<u32>),
fonts: &'a ConrodVoxygenFonts,
2020-07-10 14:00:20 +00:00
ori: Vec3<f32>,
) -> Self {
Self {
show,
2019-06-19 14:55:26 +00:00
client,
imgs,
rot_imgs,
world_map,
fonts,
common: widget::CommonBuilder::default(),
2020-07-10 14:00:20 +00:00
ori,
}
}
}
pub struct State {
ids: Ids,
zoom: f64,
}
pub enum Event {
Toggle,
}
impl<'a> Widget for MiniMap<'a> {
type Event = Option<Event>;
type State = State;
type Style = ();
fn init_state(&self, id_gen: widget::id::Generator) -> Self::State {
State {
ids: Ids::new(id_gen),
zoom: {
let min_world_dim = self.world_map.1.reduce_partial_min() as f64;
min_world_dim.min(
min_world_dim
* (TerrainChunkSize::RECT_SIZE.reduce_partial_max() as f64 / 32.0)
* (16.0 / 1024.0),
)
},
}
}
#[allow(clippy::unused_unit)] // TODO: Pending review in #587
fn style(&self) -> Self::Style { () }
fn update(self, args: widget::UpdateArgs<Self>) -> Self::Event {
let widget::UpdateArgs { state, ui, .. } = args;
let zoom = state.zoom;
2020-03-15 13:21:04 +00:00
const SCALE: f64 = 1.5;
if self.show.mini_map {
Image::new(self.imgs.mmap_frame)
2020-03-14 13:07:20 +00:00
.w_h(174.0 * SCALE, 190.0 * SCALE)
2020-03-15 13:21:04 +00:00
.top_right_with_margins_on(ui.window, 0.0, 5.0)
.color(Some(UI_MAIN))
.set(state.ids.mmap_frame, ui);
Image::new(self.imgs.mmap_frame_2)
.w_h(174.0 * SCALE, 190.0 * SCALE)
.middle_of(state.ids.mmap_frame)
.color(Some(UI_HIGHLIGHT_0))
.set(state.ids.mmap_frame_2, ui);
2020-03-14 13:07:20 +00:00
Rectangle::fill_with([170.0 * SCALE, 170.0 * SCALE], color::TRANSPARENT)
.mid_top_with_margin_on(state.ids.mmap_frame_2, 18.0 * SCALE)
.set(state.ids.mmap_frame_bg, ui);
// Map size
let (world_map, worldsize) = self.world_map;
let worldsize = worldsize.map2(TerrainChunkSize::RECT_SIZE, |e, f| e as f64 * f as f64);
// Zoom Buttons
// Pressing + multiplies, and - divides, zoom by ZOOM_FACTOR.
const ZOOM_FACTOR: f64 = 2.0;
// TODO: Either prevent zooming all the way in, *or* see if we can interpolate
// somehow if you zoom in too far. Or both.
let min_zoom = 1.0;
let max_zoom = (worldsize / TerrainChunkSize::RECT_SIZE.map(|e| e as f64))
.reduce_partial_min()/*.min(f64::MAX)*/;
// NOTE: Not sure if a button can be clicked while disabled, but we still double
// check for both kinds of zoom to make sure that not only was the
// button clicked, it is also okay to perform the zoom action.
// Note that since `Button::image` has side effects, we must perform
// the `can_zoom_in` and `can_zoom_out` checks after the `&&` to avoid
// undesired early termination.
let can_zoom_in = zoom < max_zoom;
let can_zoom_out = zoom > min_zoom;
if Button::image(self.imgs.mmap_minus)
2020-03-14 13:07:20 +00:00
.w_h(16.0 * SCALE, 18.0 * SCALE)
.hover_image(self.imgs.mmap_minus_hover)
.press_image(self.imgs.mmap_minus_press)
.top_left_with_margins_on(state.ids.mmap_frame, 0.0, 0.0)
.image_color(UI_HIGHLIGHT_0)
.enabled(can_zoom_out)
.set(state.ids.mmap_minus, ui)
.was_clicked()
&& can_zoom_out
{
// Set the image dimensions here, rather than recomputing each time.
let zoom = min_zoom.max(zoom / ZOOM_FACTOR);
state.update(|s| s.zoom = zoom);
// set_image_dims(zoom);
}
if Button::image(self.imgs.mmap_plus)
2020-03-14 13:07:20 +00:00
.w_h(18.0 * SCALE, 18.0 * SCALE)
.hover_image(self.imgs.mmap_plus_hover)
.press_image(self.imgs.mmap_plus_press)
2020-03-14 13:07:20 +00:00
.right_from(state.ids.mmap_minus, 0.0)
.image_color(UI_HIGHLIGHT_0)
.enabled(can_zoom_in)
.set(state.ids.mmap_plus, ui)
.was_clicked()
&& can_zoom_in
{
let zoom = max_zoom.min(zoom * ZOOM_FACTOR);
state.update(|s| s.zoom = zoom);
// set_image_dims(zoom);
}
// Reload zoom in case it changed.
let zoom = state.zoom;
2019-10-02 10:05:17 +00:00
// Coordinates
let player_pos = self
.client
.state()
.ecs()
.read_storage::<comp::Pos>()
.get(self.client.entity())
.map_or(Vec3::zero(), |pos| pos.0);
// Get map image source rectangle dimensons.
let w_src = worldsize.x / TerrainChunkSize::RECT_SIZE.x as f64 / zoom;
let h_src = worldsize.y / TerrainChunkSize::RECT_SIZE.y as f64 / zoom;
// Set map image to be centered around player coordinates.
let rect_src = position::Rect::from_xy_dim(
[
player_pos.x as f64 / TerrainChunkSize::RECT_SIZE.x as f64,
(worldsize.y - player_pos.y as f64) / TerrainChunkSize::RECT_SIZE.y as f64,
],
[w_src, h_src],
);
2020-07-10 14:00:20 +00:00
let map_size = Vec2::new(170.0, 170.0);
// Map Image
Image::new(world_map.source_north)
.middle_of(state.ids.mmap_frame_bg)
2020-07-10 14:00:20 +00:00
.w_h(map_size.x * SCALE, map_size.y * SCALE)
.parent(state.ids.mmap_frame_bg)
.source_rectangle(rect_src)
.set(state.ids.grid, ui);
2019-10-02 10:05:17 +00:00
// Indicator
let ind_scale = 0.4;
Image::new(self.rot_imgs.indicator_mmap_small.none)
.middle_of(state.ids.grid)
.w_h(32.0 * ind_scale, 37.0 * ind_scale)
2020-03-27 03:05:04 +00:00
.color(Some(UI_HIGHLIGHT_0))
.floating(true)
.parent(ui.window)
.set(state.ids.indicator, ui);
2020-07-10 14:00:20 +00:00
// Compass directions
let dirs = [
(Vec2::new(0.0, 1.0), state.ids.mmap_north, "N", true),
(Vec2::new(1.0, 0.0), state.ids.mmap_east, "E", false),
(Vec2::new(0.0, -1.0), state.ids.mmap_south, "S", false),
(Vec2::new(-1.0, 0.0), state.ids.mmap_west, "W", false),
];
for (dir, id, name, bold) in dirs.iter() {
let cardinal_dir = Vec2::unit_x().rotated_z(self.ori.x as f64) * dir.x
+ Vec2::unit_y().rotated_z(self.ori.x as f64) * dir.y;
let clamped = (cardinal_dir * 3.0)
/ (cardinal_dir * 3.0).map(|e| e.abs()).reduce_partial_max();
let pos = clamped * (map_size * 0.75 - 10.0);
Text::new(name)
.x_y_position_relative_to(
state.ids.grid,
position::Relative::Scalar(pos.x),
position::Relative::Scalar(pos.y),
)
.font_size(self.fonts.cyri.scale(18))
.font_id(self.fonts.cyri.conrod_id)
.color(if *bold {
Color::Rgba(0.7, 0.3, 0.3, 1.0)
} else {
TEXT_COLOR
})
.floating(true)
.parent(ui.window)
.set(*id, ui);
}
} else {
Image::new(self.imgs.mmap_frame_closed)
2020-03-14 13:07:20 +00:00
.w_h(174.0 * SCALE, 18.0 * SCALE)
.color(Some(UI_MAIN))
2020-03-15 16:27:56 +00:00
.top_right_with_margins_on(ui.window, 0.0, 5.0)
.set(state.ids.mmap_frame, ui);
}
if Button::image(if self.show.mini_map {
self.imgs.mmap_open
} else {
self.imgs.mmap_closed
})
2020-03-14 13:07:20 +00:00
.w_h(18.0 * SCALE, 18.0 * SCALE)
.hover_image(if self.show.mini_map {
self.imgs.mmap_open_hover
} else {
self.imgs.mmap_closed_hover
})
.press_image(if self.show.mini_map {
self.imgs.mmap_open_press
} else {
self.imgs.mmap_closed_press
})
.top_right_with_margins_on(state.ids.mmap_frame, 0.0, 0.0)
.image_color(UI_HIGHLIGHT_0)
.set(state.ids.mmap_button, ui)
.was_clicked()
{
return Some(Event::Toggle);
}
// TODO: Subregion name display
// Title
2019-06-19 14:55:26 +00:00
match self.client.current_chunk() {
Some(chunk) => Text::new(chunk.meta().name())
2020-03-15 13:21:04 +00:00
.mid_top_with_margin_on(state.ids.mmap_frame, 2.0)
.font_size(self.fonts.cyri.scale(18))
.font_id(self.fonts.cyri.conrod_id)
2019-06-19 14:55:26 +00:00
.color(TEXT_COLOR)
.set(state.ids.mmap_location, ui),
2019-06-22 14:30:53 +00:00
None => Text::new(" ")
2019-10-04 18:27:12 +00:00
.mid_top_with_margin_on(state.ids.mmap_frame, 0.0)
.font_size(self.fonts.cyri.scale(18))
2019-06-19 14:55:26 +00:00
.color(TEXT_COLOR)
.set(state.ids.mmap_location, ui),
}
None
}
}