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 2a618467cd
54 changed files with 498 additions and 118 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.

BIN
assets/voxygen/element/buttons/indicator_mmap_2.vox (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/element/buttons/indicator_mmap_3.vox (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/element/buttons/min_plus/mmap_button-min.vox (Stored with Git LFS) Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
assets/voxygen/element/buttons/min_plus/mmap_button-plus.vox (Stored with Git LFS) Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
assets/voxygen/element/frames/enemybar.vox (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/element/help.png (Stored with Git LFS)

Binary file not shown.

BIN
assets/voxygen/element/icons/skull.vox (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/element/icons/skull_2.vox (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/voxel/armor/back/short-0.vox (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/voxel/npc/oger/belt.vox (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/voxel/npc/oger/chest.vox (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/voxel/npc/oger/foot.vox (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/voxel/npc/oger/hand-l.vox (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/voxel/npc/oger/hand-r.vox (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/voxel/npc/oger/head.vox (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/voxel/npc/oger/legs.vox (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/voxel/npc/oger/shoulder.vox (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/voxel/weapon/hammer/hammer_1_2h.vox (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/voxel/weapon/staff/wood-simple.vox (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/world/tree/acacia/1.vox (Stored with Git LFS)

Binary file not shown.

BIN
assets/world/tree/acacia/2.vox (Stored with Git LFS)

Binary file not shown.

BIN
assets/world/tree/acacia/3.vox (Stored with Git LFS)

Binary file not shown.

BIN
assets/world/tree/acacia/4.vox (Stored with Git LFS)

Binary file not shown.

BIN
assets/world/tree/acacia/5.vox (Stored with Git LFS)

Binary file not shown.

BIN
assets/world/tree/acacia_savannah/1.vox (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/world/tree/acacia_savannah/2.vox (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/world/tree/acacia_savannah/3.vox (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/world/tree/acacia_savannah/4.vox (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/world/tree/acacia_savannah/5.vox (Stored with Git LFS) Normal file

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.