Merge branch 'pfau/assets' into 'master'

Animated UI elements (map, healthbars...), Asset updates

See merge request veloren/veloren!697
This commit is contained in:
Monty Marz 2019-12-30 12:16:36 +00:00
commit c2b3ce72f6
54 changed files with 403 additions and 104 deletions

View File

@ -23,6 +23,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Added ability to jump while underwater
- Added proper SFX system
- Added changelog
- Added animated Map and Minimap position indicator
- Added visuals to indicate strength compared to the player
### Changed
@ -44,6 +46,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Fixed cloud performance
- Fixed region display name
- Fixed the bow fire rate
- Healthbars now flash on critical health
### Removed

View File

@ -14,7 +14,7 @@ members = [
# default profile for devs, fast to compile, okay enough to run, no debug information
[profile.dev]
opt-level = 2
overflow-checks = true
overflow-checks = false
debug-assertions = true
panic = "abort"
debug = false

View File

@ -0,0 +1,8 @@
Item(
name: "Crude Mallet",
description: "Breaks bones like sticks and stones.",
kind: Tool(
kind: Hammer,
power: 20,
),
)

View File

@ -0,0 +1,8 @@
Item(
name: "Humble Stick",
description: "Walking stick with a sharpened end.",
kind: Tool(
kind: Hammer,
power: 6,
),
)

View File

@ -3,6 +3,6 @@ Item(
description: "Every dent tells the story of a chopped tree.",
kind: Tool(
kind: Axe,
power: 10,
power: 15,
),
)

View File

@ -3,6 +3,6 @@ Item(
description: "Someone carved his initials into it...",
kind: Tool(
kind: Bow,
power: 10,
power: 15,
),
)

View File

@ -3,6 +3,6 @@ Item(
description: "Great for cutting meat.",
kind: Tool(
kind: Dagger,
power: 10,
power: 15,
),
)

View File

@ -3,6 +3,6 @@ Item(
description: "'Property of...' The rest is missing. ",
kind: Tool(
kind: Hammer,
power: 10,
power: 15,
),
)

View File

@ -3,6 +3,6 @@ Item(
description: "Smells like resin and magic.",
kind: Tool(
kind: Staff,
power: 10,
power: 20,
),
)

View File

@ -3,6 +3,6 @@ Item(
description: "Held together by Rust and hope.",
kind: Tool(
kind: Sword,
power: 10,
power: 15,
),
)

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,69 @@
use rand::{seq::SliceRandom, thread_rng};
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct Body {
pub head: Head,
pub shoulder: Shoulder,
pub chest: Chest,
pub hand: Hand
pub belt: Belt,
pub pants: Pants,
pub foot: Foot,
}
impl Body {
pub fn random() -> Self {
let mut rng = thread_rng();
Self {
head: *(&ALL_HEADS).choose(&mut rng).unwrap(),
shoulder: *(&ALL_SHOULDERS).choose(&mut rng).unwrap(),
chest: *(&ALL_CHESTS).choose(&mut rng).unwrap(),
hand: *(&ALL_HANDS).choose(&mut rng).unwrap(),
belt: *(&ALL_BELTS).choose(&mut rng).unwrap(),
pants: *(&ALL_PANTS).choose(&mut rng).unwrap(),
foot: *(&ALL_FEET).choose(&mut rng).unwrap(),
}
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum Head {
Default,
}
const ALL_HEADS: [Head; 1] = [Head::Default];
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum Shoulder {
Default,
}
const ALL_SHOULDERS: [Shoulder; 1] = [Shoulder::Default];
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum Chest {
Default,
}
const ALL_CHESTS: [Chest; 1] = [Chest::Default];
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum Hand {
Default,
}
const ALL_HANDS: [Hand; 1] = [Hand::Default];
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum Belt {
Default,
}
const ALL_BELTS: [Belt; 1] = [Belt::Default];
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum Pants {
Default,
}
const ALL_FEET: [Foot; 1] = [Foot::Default];
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum Foot {
Default,
}

View File

@ -109,6 +109,7 @@ impl<'a> System<'a> for Sys {
client.error_state(RequestStateError::Already)
}
ClientState::Spectator | ClientState::Character | ClientState::Dead => {
// TODO: remove position etc here
client.allow_state(ClientState::Registered)
}
ClientState::Pending => {}

View File

@ -98,10 +98,8 @@ impl<'a> System<'a> for Sys {
for npc in supplement.npcs {
let (mut stats, mut body) = if rand::random() {
let stats = comp::Stats::new(
"Humanoid".to_string(),
Some(assets::load_expect_cloned(
"common.items.weapons.starter_sword",
)),
"Traveler".to_string(),
Some(assets::load_expect_cloned("common.items.weapons.staff_1")),
);
let body = comp::Body::Humanoid(comp::humanoid::Body::random());
(stats, body)
@ -113,20 +111,18 @@ impl<'a> System<'a> for Sys {
let mut scale = 1.0;
// TODO: Remove this and implement scaling or level depending on stuff like species instead
stats.level.set_level(rand::thread_rng().gen_range(1, 3));
stats.level.set_level(rand::thread_rng().gen_range(1, 10));
if npc.boss {
if rand::random::<f32>() < 0.8 {
stats = comp::Stats::new(
"Humanoid".to_string(),
Some(assets::load_expect_cloned(
"common.items.weapons.starter_sword",
)),
"Fearless Wanderer".to_string(),
Some(assets::load_expect_cloned("common.items.weapons.hammer_1")),
);
body = comp::Body::Humanoid(comp::humanoid::Body::random());
}
stats.level.set_level(rand::thread_rng().gen_range(10, 50));
scale = 2.5 + rand::random::<f32>();
stats.level.set_level(rand::thread_rng().gen_range(20, 50));
scale = 2.0 + rand::random::<f32>();
}
stats.update_max_hp();

View File

@ -118,10 +118,14 @@ image_ids! {
grass: "voxygen.element.icons.item_grass",
apple: "voxygen.element.icons.item_apple",
mushroom: "voxygen.element.icons.item_mushroom",
skull: "voxygen.element.icons.skull",
skull_2: "voxygen.element.icons.skull_2",
// Map
map_indicator: "voxygen.element.buttons.map_indicator",
indicator_mmap: "voxygen.element.buttons.indicator_mmap",
indicator_mmap_2: "voxygen.element.buttons.indicator_mmap_2",
indicator_mmap_3: "voxygen.element.buttons.indicator_mmap_3",
// Crosshair
@ -154,6 +158,12 @@ image_ids! {
mmap_open: "voxygen.element.buttons.button_mmap_open",
mmap_open_hover: "voxygen.element.buttons.button_mmap_open_hover",
mmap_open_press: "voxygen.element.buttons.button_mmap_open_press",
mmap_plus: "voxygen.element.buttons.min_plus.mmap_button-plus",
mmap_plus_hover: "voxygen.element.buttons.min_plus.mmap_button-plus_hover",
mmap_plus_press: "voxygen.element.buttons.min_plus.mmap_button-plus_hover",
mmap_minus: "voxygen.element.buttons.min_plus.mmap_button-min",
mmap_minus_hover: "voxygen.element.buttons.min_plus.mmap_button-min_hover",
mmap_minus_press: "voxygen.element.buttons.min_plus.mmap_button-min_press",
// Grid
grid: "voxygen.element.buttons.grid",
@ -221,6 +231,10 @@ image_ids! {
key: "voxygen.voxel.object.key",
key_gold: "voxygen.voxel.object.key_gold",
// Enemy Healthbar
enemy_health: "voxygen.element.frames.enemybar",
//////////////////////////////////////////////////////////////////////////////////////////////////////

View File

@ -5,7 +5,7 @@ use conrod_core::{
color,
image::Id,
widget::{self, Button, Image, Rectangle, Text},
widget_ids, Colorable, Positionable, Sizeable, Widget, WidgetCommon,
widget_ids, Color, Colorable, Positionable, Sizeable, Widget, WidgetCommon,
};
use specs::WorldExt;
use vek::*;
@ -31,12 +31,12 @@ widget_ids! {
pub struct Map<'a> {
_show: &'a Show,
client: &'a Client,
_world_map: Id,
imgs: &'a Imgs,
fonts: &'a Fonts,
#[conrod(common_builder)]
common: widget::CommonBuilder,
pulse: f32,
}
impl<'a> Map<'a> {
pub fn new(
@ -45,6 +45,7 @@ impl<'a> Map<'a> {
imgs: &'a Imgs,
world_map: Id,
fonts: &'a Fonts,
pulse: f32,
) -> Self {
Self {
_show: show,
@ -53,6 +54,7 @@ impl<'a> Map<'a> {
client,
fonts: fonts,
common: widget::CommonBuilder::default(),
pulse,
}
}
}
@ -129,9 +131,10 @@ impl<'a> Widget for Map<'a> {
// Location Name
match self.client.current_chunk() {
Some(chunk) => Text::new(chunk.meta().name())
.mid_top_with_margin_on(state.ids.map_bg, 70.0)
.font_size(20)
.mid_top_with_margin_on(state.ids.map_bg, 55.0)
.font_size(60)
.color(TEXT_COLOR)
.font_id(self.fonts.alkhemi)
.parent(state.ids.map_frame_r)
.set(state.ids.location_name, ui),
None => Text::new(" ")
@ -157,15 +160,33 @@ impl<'a> Widget for Map<'a> {
.map_or(Vec3::zero(), |pos| pos.0);
let worldsize = 32768.0; // TODO This has to get the actual world size and not be hardcoded
let x = player_pos.x as f64 / worldsize * 700.0;
let x = player_pos.x as f64 / worldsize * 700.0/*= x-Size of the map image*/;
let y = (/*1.0 -*/player_pos.y as f64 / worldsize) * 700.0;
let indic_ani = (self.pulse * 6.0/*animation speed*/).cos()/*starts at 1.0*/ * 0.5 + 0.50; // changes the animation frame
let indic_scale = 1.2;
// Indicator
Image::new(self.imgs.map_indicator)
.bottom_left_with_margins_on(state.ids.grid, y, x - (12.0 * 1.4) / 2.0)
.w_h(12.0 * 1.4, 21.0 * 1.4)
.floating(true)
.parent(ui.window)
.set(state.ids.indicator, ui);
Image::new(if indic_ani <= 0.3 {
self.imgs.indicator_mmap
} else if indic_ani <= 0.6 {
self.imgs.indicator_mmap_2
} else {
self.imgs.indicator_mmap_3
})
.bottom_left_with_margins_on(state.ids.grid, y, x - (20.0 * 1.2) / 2.0)
.w_h(
22.0 * 1.2,
if indic_ani <= 0.3 {
16.0 * indic_scale
} else if indic_ani <= 0.6 {
23.0 * indic_scale
} else {
34.0 * indic_scale
},
)
.color(Some(Color::Rgba(1.0, 1.0, 1.0, 1.0)))
.floating(true)
.parent(ui.window)
.set(state.ids.indicator, ui);
None
}

View File

@ -17,6 +17,8 @@ widget_ids! {
mmap_frame_bg,
mmap_location,
mmap_button,
mmap_plus,
mmap_minus,
zone_display_bg,
zone_display,
grid,
@ -35,6 +37,8 @@ pub struct MiniMap<'a> {
fonts: &'a Fonts,
#[conrod(common_builder)]
common: widget::CommonBuilder,
pulse: f32,
zoom: f32,
}
impl<'a> MiniMap<'a> {
@ -44,6 +48,8 @@ impl<'a> MiniMap<'a> {
imgs: &'a Imgs,
world_map: Id,
fonts: &'a Fonts,
pulse: f32,
zoom: f32,
) -> Self {
Self {
show,
@ -52,6 +58,8 @@ impl<'a> MiniMap<'a> {
_world_map: world_map,
fonts: fonts,
common: widget::CommonBuilder::default(),
pulse,
zoom,
}
}
}
@ -87,20 +95,49 @@ impl<'a> Widget for MiniMap<'a> {
fn update(self, args: widget::UpdateArgs<Self>) -> Self::Event {
let widget::UpdateArgs { state, ui, .. } = args;
let zoom = self.zoom as f64;
if self.show.mini_map {
Image::new(self.imgs.mmap_frame)
.w_h(100.0 * 4.0, 100.0 * 4.0)
.w_h(100.0 * 4.0 * zoom, 100.0 * 4.0 * zoom)
.top_right_with_margins_on(ui.window, 5.0, 5.0)
.set(state.ids.mmap_frame, ui);
Rectangle::fill_with([92.0 * 4.0, 82.0 * 4.0], color::TRANSPARENT)
.mid_top_with_margin_on(state.ids.mmap_frame, 13.0 * 4.0 + 4.0)
.mid_top_with_margin_on(state.ids.mmap_frame, 13.0 * 4.0 + 4.0 * zoom)
.set(state.ids.mmap_frame_bg, ui);
// Zoom Buttons
// TODO: Add zoomable minimap
/*if Button::image(self.imgs.mmap_plus)
.w_h(100.0 * 0.2 * zoom, 100.0 * 0.2 * zoom)
.hover_image(self.imgs.mmap_plus_hover)
.press_image(self.imgs.mmap_plus_press)
.top_left_with_margins_on(state.ids.mmap_frame, 0.0, 0.0)
.set(state.ids.mmap_plus, ui)
.was_clicked()
{
if zoom > 0.0 {
zoom = zoom + 1.0
} else if zoom == 5.0 {
}
}
if Button::image(self.imgs.mmap_minus)
.w_h(100.0 * 0.2 * zoom, 100.0 * 0.2 * zoom)
.hover_image(self.imgs.mmap_minus_hover)
.press_image(self.imgs.mmap_minus_press)
.down_from(state.ids.mmap_plus, 0.0)
.set(state.ids.mmap_minus, ui)
.was_clicked()
{
if zoom < 6.0 {
zoom = zoom - 1.0
} else if zoom == 0.0 {
}
}*/
// Map Image
Image::new(/*self.world_map*/ self.imgs.map_placeholder)
.middle_of(state.ids.mmap_frame_bg)
.w_h(92.0 * 4.0, 82.0 * 4.0)
.w_h(92.0 * 4.0 * zoom, 82.0 * 4.0 * zoom)
.parent(state.ids.mmap_frame_bg)
.set(state.ids.grid, ui);
// Coordinates
@ -115,13 +152,28 @@ impl<'a> Widget for MiniMap<'a> {
let worldsize = 32768.0; // TODO This has to get the actual world size and not be hardcoded
let x = player_pos.x as f64 / worldsize * 92.0 * 4.0;
let y = (/*1.0X-*/player_pos.y as f64 / worldsize) * 82.0 * 4.0;
let indic_ani = (self.pulse * 6.0).cos() * 0.5 + 0.5; //Animation timer
let indic_scale = 0.8;
// Indicator
Image::new(self.imgs.indicator_mmap)
.bottom_left_with_margins_on(state.ids.grid, y, x - 5.0)
.w_h(10.0, 10.0)
.floating(true)
.parent(ui.window)
.set(state.ids.indicator, ui);
Image::new(if indic_ani <= 0.5 {
self.imgs.indicator_mmap
} else {
self.imgs.indicator_mmap_2
})
.bottom_left_with_margins_on(state.ids.grid, y, x - 5.0)
.w_h(
// Animation frames depening on timer value from 0.0 to 1.0
22.0 * 0.8,
if indic_ani <= 0.5 {
18.0 * indic_scale
} else {
23.0 * indic_scale
},
)
.color(Some(Color::Rgba(1.0, 1.0, 1.0, 1.0)))
.floating(true)
.parent(ui.window)
.set(state.ids.indicator, ui);
} else {
Image::new(self.imgs.mmap_frame_closed)
.w_h(100.0 * 2.0, 11.0 * 2.0)
@ -137,7 +189,7 @@ impl<'a> Widget for MiniMap<'a> {
.wh(if self.show.mini_map {
[100.0 * 0.4; 2]
} else {
[100.0 * 0.2; 2]
[100.0 * 0.2 * zoom; 2]
})
.hover_image(if self.show.mini_map {
self.imgs.mmap_open_hover

View File

@ -14,7 +14,9 @@ mod social;
mod spell;
use crate::hud::img_ids::ImgsRot;
//use rand::Rng;
pub use settings_window::ScaleChange;
use std::time::Duration;
use bag::Bag;
use buttons::Buttons;
@ -64,7 +66,7 @@ const TEXT_COLOR_3: Color = Color::Rgba(1.0, 1.0, 1.0, 0.1);
//const BG_COLOR: Color = Color::Rgba(1.0, 1.0, 1.0, 0.8);
const HP_COLOR: Color = Color::Rgba(0.33, 0.63, 0.0, 1.0);
const LOW_HP_COLOR: Color = Color::Rgba(0.93, 0.59, 0.03, 1.0);
const CRITICAL_HP_COLOR: Color = Color::Rgba(1.0, 0.0, 0.0, 1.0);
const CRITICAL_HP_COLOR: Color = Color::Rgba(0.79, 0.19, 0.17, 1.0);
const MANA_COLOR: Color = Color::Rgba(0.47, 0.55, 1.0, 0.9);
//const FOCUS_COLOR: Color = Color::Rgba(1.0, 0.56, 0.04, 1.0);
//const RAGE_COLOR: Color = Color::Rgba(0.5, 0.04, 0.13, 1.0);
@ -86,8 +88,12 @@ widget_ids! {
// Character Names
name_tags[],
levels[],
levels_skull[],
// Health Bars
health_bars[],
mana_bars[],
health_bar_fronts[],
health_bar_backs[],
// Intro Text
@ -410,6 +416,8 @@ pub struct Hud {
force_ungrab: bool,
force_chat_input: Option<String>,
force_chat_cursor: Option<Index>,
pulse: f32,
zoom: f32,
}
impl Hud {
@ -469,6 +477,8 @@ impl Hud {
force_ungrab: false,
force_chat_input: None,
force_chat_cursor: None,
pulse: 0.0,
zoom: 1.0,
}
}
@ -477,9 +487,12 @@ impl Hud {
client: &Client,
global_state: &GlobalState,
debug_info: DebugInfo,
dt: Duration,
) -> Vec<Event> {
let mut events = Vec::new();
let (ref mut ui_widgets, ref mut tooltip_manager) = self.ui.set_widgets();
// pulse time for pulsating elements
self.pulse = self.pulse + dt.as_secs_f32();
let version = format!(
"{}-{}",
@ -522,6 +535,10 @@ impl Hud {
let entities = ecs.entities();
let me = client.entity();
let view_distance = client.view_distance().unwrap_or(1);
let own_level = stats
.get(client.entity())
.map_or(0, |stats| stats.level.level());
// Get player position.
let player_pos = client
.state()
@ -530,8 +547,95 @@ impl Hud {
.get(client.entity())
.map_or(Vec3::zero(), |pos| pos.0);
let mut name_id_walker = self.ids.name_tags.walk();
let mut level_id_walker = self.ids.levels.walk();
let mut level_skull_id_walker = self.ids.levels_skull.walk();
let mut health_id_walker = self.ids.health_bars.walk();
let mut mana_id_walker = self.ids.mana_bars.walk();
let mut health_back_id_walker = self.ids.health_bar_backs.walk();
let mut health_front_id_walker = self.ids.health_bar_fronts.walk();
// Render Health Bars
for (_entity, pos, stats, scale) in (&entities, &pos, &stats, scales.maybe())
.join()
.filter(|(entity, _, stats, _)| {
*entity != me && !stats.is_dead
//&& stats.health.current() != stats.health.maximum()
})
// Don't process health bars outside the vd (visibility further limited by ui backend)
.filter(|(_, pos, _, _)| {
Vec2::from(pos.0 - player_pos)
.map2(TerrainChunk::RECT_SIZE, |d: f32, sz| {
d.abs() as f32 / sz as f32
})
.magnitude()
< view_distance as f32
})
{
let scale = scale.map(|s| s.0).unwrap_or(1.0);
let back_id = health_back_id_walker.next(
&mut self.ids.health_bar_backs,
&mut ui_widgets.widget_id_generator(),
);
let health_bar_id = health_id_walker.next(
&mut self.ids.health_bars,
&mut ui_widgets.widget_id_generator(),
);
let mana_bar_id = mana_id_walker.next(
&mut self.ids.mana_bars,
&mut ui_widgets.widget_id_generator(),
);
let front_id = health_front_id_walker.next(
&mut self.ids.health_bar_fronts,
&mut ui_widgets.widget_id_generator(),
);
let hp_percentage =
stats.health.current() as f64 / stats.health.maximum() as f64 * 100.0;
let hp_ani = (self.pulse * 4.0/*speed factor*/).cos() * 0.5 + 1.0; //Animation timer
let crit_hp_color: Color = Color::Rgba(0.79, 0.19, 0.17, hp_ani);
// Background
Rectangle::fill_with([82.0, 8.0], Color::Rgba(0.3, 0.3, 0.3, 0.5))
.x_y(0.0, -25.0)
.position_ingame(pos.0 + Vec3::new(0.0, 0.0, 1.5 * scale + 1.5))
.resolution(100.0)
.set(back_id, ui_widgets);
// % HP Filling
Image::new(self.imgs.bar_content)
.w_h(72.9 * (hp_percentage / 100.0), 5.9)
.x_y(4.5, -24.0)
.color(Some(if hp_percentage <= 25.0 {
crit_hp_color
} else if hp_percentage <= 50.0 {
LOW_HP_COLOR
} else {
HP_COLOR
}))
.position_ingame(pos.0 + Vec3::new(0.0, 0.0, 1.5 * scale + 1.5))
.resolution(100.0)
.set(health_bar_id, ui_widgets);
// % Mana Filling
Rectangle::fill_with(
[
73.0 * (stats.energy.current() as f64 / stats.energy.maximum() as f64),
1.5,
],
MANA_COLOR,
)
.x_y(4.5, -28.0)
.position_ingame(pos.0 + Vec3::new(0.0, 0.0, 1.5 * scale + 1.5))
.resolution(100.0)
.set(mana_bar_id, ui_widgets);
// Foreground
Image::new(self.imgs.enemy_health)
.w_h(84.0, 10.0)
.x_y(0.0, -25.0)
.color(Some(Color::Rgba(1.0, 1.0, 1.0, 0.99)))
.position_ingame(pos.0 + Vec3::new(0.0, 0.0, 1.5 * scale + 1.5))
.resolution(100.0)
.set(front_id, ui_widgets);
}
// Render Name Tags
for (pos, name, level, scale) in
@ -558,69 +662,71 @@ impl Hud {
(pos.0, name, stats.level, scale)
})
{
let info = format!("{} Level {}", name, level.level());
let name = format!("{}", name);
let scale = scale.map(|s| s.0).unwrap_or(1.0);
let id = name_id_walker.next(
let name_id = name_id_walker.next(
&mut self.ids.name_tags,
&mut ui_widgets.widget_id_generator(),
);
Text::new(&info)
let level_id = level_id_walker
.next(&mut self.ids.levels, &mut ui_widgets.widget_id_generator());
let level_skull_id = level_skull_id_walker.next(
&mut self.ids.levels_skull,
&mut ui_widgets.widget_id_generator(),
);
// Name
Text::new(&name)
.font_size(20)
.color(Color::Rgba(0.61, 0.61, 0.89, 1.0))
.x_y(0.0, 0.0)
.position_ingame(pos + Vec3::new(0.0, 0.0, 1.5 * scale + 1.5))
.resolution(100.0)
.set(id, ui_widgets);
}
.set(name_id, ui_widgets);
// Render Health Bars
for (_entity, pos, stats, scale) in (&entities, &pos, &stats, scales.maybe())
.join()
.filter(|(entity, _, stats, _)| {
*entity != me
&& !stats.is_dead
&& stats.health.current() != stats.health.maximum()
})
// Don't process health bars outside the vd (visibility further limited by ui backend)
.filter(|(_, pos, _, _)| {
Vec2::from(pos.0 - player_pos)
.map2(TerrainChunk::RECT_SIZE, |d: f32, sz| {
d.abs() as f32 / sz as f32
})
.magnitude()
< view_distance as f32
})
{
let scale = scale.map(|s| s.0).unwrap_or(1.0);
let back_id = health_back_id_walker.next(
&mut self.ids.health_bar_backs,
&mut ui_widgets.widget_id_generator(),
);
let bar_id = health_id_walker.next(
&mut self.ids.health_bars,
&mut ui_widgets.widget_id_generator(),
);
// Background
Rectangle::fill_with([120.0, 8.0], Color::Rgba(0.3, 0.3, 0.3, 0.5))
.x_y(0.0, -25.0)
.position_ingame(pos.0 + Vec3::new(0.0, 0.0, 1.5 * scale + 1.5))
// Level
const LOW: Color = Color::Rgba(0.54, 0.81, 0.94, 0.4);
const HIGH: Color = Color::Rgba(1.0, 0.0, 0.0, 1.0);
const EQUAL: Color = Color::Rgba(1.0, 1.0, 1.0, 1.0);
let op_level = level.level();
let level_str = format!("{}", op_level);
// Change visuals of the level display depending on the player level/opponent level
let level_comp = op_level as i64 - own_level as i64;
// + 10 level above player -> skull
// + 5-10 levels above player -> high
// -5 - +5 levels around player level -> equal
// - 5 levels below player -> low
Text::new(if level_comp < 10 { &level_str } else { "?" })
.font_size(if op_level > 9 && level_comp < 10 {
7
} else {
8
})
.color(if level_comp > 4 {
HIGH
} else if level_comp < -5 {
LOW
} else {
EQUAL
})
.x_y(-37.0, -24.0)
.position_ingame(pos + Vec3::new(0.0, 0.0, 1.5 * scale + 1.5))
.resolution(100.0)
.set(back_id, ui_widgets);
// % HP Filling
Rectangle::fill_with(
[
120.0 * (stats.health.current() as f64 / stats.health.maximum() as f64),
8.0,
],
HP_COLOR,
)
.x_y(0.0, -25.0)
.position_ingame(pos.0 + Vec3::new(0.0, 0.0, 1.5 * scale + 1.5))
.resolution(100.0)
.set(bar_id, ui_widgets);
.set(level_id, ui_widgets);
if level_comp > 9 {
let skull_ani = ((self.pulse * 0.7/*speed factor*/).cos() * 0.5 + 0.5) * 10.0; //Animation timer
Image::new(if skull_ani as i32 == 1 && rand::random::<f32>() < 0.9 {
self.imgs.skull_2
} else {
self.imgs.skull
})
.w_h(30.0, 30.0)
.x_y(0.0, 24.0)
.color(Some(Color::Rgba(1.0, 1.0, 1.0, 0.8)))
.position_ingame(pos + Vec3::new(0.0, 0.0, 1.5 * scale + 1.5))
.resolution(100.0)
.set(level_skull_id, ui_widgets);
}
}
}
// Introduction Text
@ -915,7 +1021,7 @@ impl Hud {
if self.show.help && !self.show.map && !self.show.esc_menu {
Image::new(self.imgs.help)
.middle_of(ui_widgets.window)
.w_h(1260.0, 519.0)
.w_h(1260.0 * 1.2, 519.0 * 1.2)
.set(self.ids.help, ui_widgets);
// Show tips
if Button::image(self.imgs.button)
@ -969,8 +1075,16 @@ impl Hud {
}
// MiniMap
match MiniMap::new(&self.show, client, &self.imgs, self.world_map, &self.fonts)
.set(self.ids.minimap, ui_widgets)
match MiniMap::new(
&self.show,
client,
&self.imgs,
self.world_map,
&self.fonts,
self.pulse,
self.zoom,
)
.set(self.ids.minimap, ui_widgets)
{
Some(minimap::Event::Toggle) => self.show.toggle_mini_map(),
None => {}
@ -1005,7 +1119,7 @@ impl Hud {
.read_storage::<comp::Stats>()
.get(client.entity())
{
Skillbar::new(global_state, &self.imgs, &self.fonts, stats)
Skillbar::new(global_state, &self.imgs, &self.fonts, &stats, self.pulse)
.set(self.ids.skillbar, ui_widgets);
}
@ -1169,8 +1283,15 @@ impl Hud {
}
// Map
if self.show.map {
match Map::new(&self.show, client, &self.imgs, self.world_map, &self.fonts)
.set(self.ids.map, ui_widgets)
match Map::new(
&self.show,
client,
&self.imgs,
self.world_map,
&self.fonts,
self.pulse,
)
.set(self.ids.map, ui_widgets)
{
Some(map::Event::Close) => {
self.show.map(false);
@ -1354,11 +1475,12 @@ impl Hud {
global_state: &mut GlobalState,
debug_info: DebugInfo,
camera: &Camera,
dt: Duration,
) -> Vec<Event> {
if let Some(maybe_id) = self.to_focus.take() {
self.ui.focus_widget(maybe_id);
}
let events = self.update_layout(client, global_state, debug_info);
let events = self.update_layout(client, global_state, debug_info, dt);
let (view_mat, _, _) = camera.compute_dependents(client);
let fov = camera.get_fov();
self.ui.maintain(

View File

@ -451,7 +451,7 @@ impl<'a> Widget for SettingsWindow<'a> {
if let Some(new_val) = ImageSlider::continuous(
scale.log(2.0),
0.5f64.log(2.0),
1.2f64.log(2.0),
1.0f64.log(2.0),
self.imgs.slider_indicator,
self.imgs.slider,
)
@ -857,7 +857,7 @@ impl<'a> Widget for SettingsWindow<'a> {
{
events.push(Event::ChatTransp(new_val));
}
Rectangle::fill_with([40.0 * 4.0, 1.0 * 4.0], color::TRANSPARENT)
Rectangle::fill_with([60.0 * 4.0, 1.0 * 4.0], color::TRANSPARENT)
.down_from(state.ids.chat_transp_title, 30.0)
.set(state.ids.placeholder, ui);
}

View File

@ -92,6 +92,7 @@ pub struct Skillbar<'a> {
imgs: &'a Imgs,
fonts: &'a Fonts,
stats: &'a Stats,
pulse: f32,
#[conrod(common_builder)]
common: widget::CommonBuilder,
current_resource: ResourceType,
@ -103,6 +104,7 @@ impl<'a> Skillbar<'a> {
imgs: &'a Imgs,
fonts: &'a Fonts,
stats: &'a Stats,
pulse: f32,
) -> Self {
Self {
imgs,
@ -111,6 +113,7 @@ impl<'a> Skillbar<'a> {
global_state,
current_resource: ResourceType::Mana,
common: widget::CommonBuilder::default(),
pulse,
}
}
}
@ -164,6 +167,8 @@ impl<'a> Widget for Skillbar<'a> {
const BG_COLOR: Color = Color::Rgba(1.0, 1.0, 1.0, 0.8);
const BG_COLOR_2: Color = Color::Rgba(0.0, 0.0, 0.0, 0.99);
let hp_ani = (self.pulse * 4.0/*speed factor*/).cos() * 0.5 + 0.8; //Animation timer
let crit_hp_color: Color = Color::Rgba(0.79, 0.19, 0.17, hp_ani);
// Stamina Wheel
/*
@ -300,7 +305,6 @@ impl<'a> Widget for Skillbar<'a> {
.top_left_with_margins_on(state.ids.xp_bar_left, 2.0 * scale, 10.0 * scale)
.set(state.ids.xp_bar_filling, ui);
// Level Display
if self.stats.level.level() < 10 {
Text::new(&level)
.bottom_left_with_margins_on(
@ -777,7 +781,7 @@ impl<'a> Widget for Skillbar<'a> {
Image::new(self.imgs.bar_content)
.w_h(97.0 * scale * hp_percentage / 100.0, 16.0 * scale)
.color(Some(if hp_percentage <= 20.0 {
CRITICAL_HP_COLOR
crit_hp_color
} else if hp_percentage <= 40.0 {
LOW_HP_COLOR
} else {

View File

@ -389,6 +389,7 @@ impl PlayState for SessionState {
.cloned(),
},
&self.scene.camera(),
clock.get_last_delta(),
);
// Maintain the UI.