mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Scrolling Combat Text (SCT)
This commit is contained in:
parent
4e7c955490
commit
851d7858e6
1
.gitignore
vendored
1
.gitignore
vendored
@ -38,3 +38,4 @@ todo.txt
|
|||||||
|
|
||||||
# direnv
|
# direnv
|
||||||
/.envrc
|
/.envrc
|
||||||
|
*.bat
|
||||||
|
@ -25,6 +25,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
- Added changelog
|
- Added changelog
|
||||||
- Added animated Map and Minimap position indicator
|
- Added animated Map and Minimap position indicator
|
||||||
- Added visuals to indicate strength compared to the player
|
- Added visuals to indicate strength compared to the player
|
||||||
|
- Added Scrolling Combat Text (SCT) & Settings for it
|
||||||
|
- Added a Death Screen and Hurt Screen
|
||||||
|
- Added randomly selected Loading Screen background images
|
||||||
|
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
@ -50,6 +54,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
- Fixed ghosts when going back to character screen
|
- Fixed ghosts when going back to character screen
|
||||||
- Fixed not being able to unmount
|
- Fixed not being able to unmount
|
||||||
- Fixed non-humanoids being able to climb and glide
|
- Fixed non-humanoids being able to climb and glide
|
||||||
|
- Made shadows and lights use interpolated positions
|
||||||
|
|
||||||
### Removed
|
### Removed
|
||||||
|
|
||||||
|
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -3544,6 +3544,7 @@ dependencies = [
|
|||||||
"serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)",
|
"serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"serde_derive 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)",
|
"serde_derive 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"specs 0.15.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"specs 0.15.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"specs-idvs 0.1.0 (git+https://gitlab.com/veloren/specs-idvs.git)",
|
||||||
"treeculler 0.1.0 (git+https://gitlab.com/yusdacra/treeculler.git)",
|
"treeculler 0.1.0 (git+https://gitlab.com/yusdacra/treeculler.git)",
|
||||||
"vek 0.9.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
"vek 0.9.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"veloren-client 0.4.0",
|
"veloren-client 0.4.0",
|
||||||
|
BIN
assets/voxygen/background/bg_1.png
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/background/bg_1.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/background/bg_3.png
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/background/bg_3.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/background/bg_4.png
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/background/bg_4.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/background/bg_5.png
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/background/bg_5.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/background/bg_6.png
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/background/bg_6.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/background/bg_7.png
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/background/bg_7.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/background/bg_8.png
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/background/bg_8.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/background/death.png
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/background/death.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/background/hurt.png
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/background/hurt.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/background/map.png
(Stored with Git LFS)
BIN
assets/voxygen/background/map.png
(Stored with Git LFS)
Binary file not shown.
BIN
assets/voxygen/element/frames/enemybar.png
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/element/frames/enemybar.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/element/icons/skull.vox
(Stored with Git LFS)
BIN
assets/voxygen/element/icons/skull.vox
(Stored with Git LFS)
Binary file not shown.
BIN
assets/voxygen/element/icons/skull_2.vox
(Stored with Git LFS)
BIN
assets/voxygen/element/icons/skull_2.vox
(Stored with Git LFS)
Binary file not shown.
BIN
assets/voxygen/element/skillbar/enemy_bar_content.png
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/element/skillbar/enemy_bar_content.png
(Stored with Git LFS)
Normal file
Binary file not shown.
@ -25,6 +25,10 @@ void main() {
|
|||||||
if (w_pos.w == 1.0) {
|
if (w_pos.w == 1.0) {
|
||||||
// In-game element
|
// In-game element
|
||||||
gl_Position = proj_mat * (view_mat * w_pos + vec4(v_pos, 0.0, 0.0));
|
gl_Position = proj_mat * (view_mat * w_pos + vec4(v_pos, 0.0, 0.0));
|
||||||
|
} else if (w_pos.w == -1.0 ) {
|
||||||
|
// Fixed scale In-game element
|
||||||
|
vec4 projected_pos = proj_mat * view_mat * vec4(w_pos.xyz, 1.0);
|
||||||
|
gl_Position = vec4(projected_pos.xy / projected_pos.w + v_pos, 0.0, 1.0);
|
||||||
} else {
|
} else {
|
||||||
// Interface element
|
// Interface element
|
||||||
gl_Position = vec4(v_pos, 0.0, 1.0);
|
gl_Position = vec4(v_pos, 0.0, 1.0);
|
||||||
|
@ -65,7 +65,11 @@ fn main() {
|
|||||||
client.send_chat(msg)
|
client.send_chat(msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
let events = match client.tick(comp::ControllerInputs::default(), clock.get_last_delta()) {
|
let events = match client.tick(
|
||||||
|
comp::ControllerInputs::default(),
|
||||||
|
clock.get_last_delta(),
|
||||||
|
|_| {},
|
||||||
|
) {
|
||||||
Ok(events) => events,
|
Ok(events) => events,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
error!("Error: {:?}", err);
|
error!("Error: {:?}", err);
|
||||||
|
@ -8,7 +8,7 @@ pub use crate::error::Error;
|
|||||||
pub use specs::{
|
pub use specs::{
|
||||||
join::Join,
|
join::Join,
|
||||||
saveload::{Marker, MarkerAllocator},
|
saveload::{Marker, MarkerAllocator},
|
||||||
Builder, Entity as EcsEntity, ReadStorage, WorldExt,
|
Builder, DispatcherBuilder, Entity as EcsEntity, ReadStorage, WorldExt,
|
||||||
};
|
};
|
||||||
|
|
||||||
use common::{
|
use common::{
|
||||||
@ -316,7 +316,12 @@ impl Client {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Execute a single client tick, handle input and update the game state by the given duration.
|
/// Execute a single client tick, handle input and update the game state by the given duration.
|
||||||
pub fn tick(&mut self, inputs: ControllerInputs, dt: Duration) -> Result<Vec<Event>, Error> {
|
pub fn tick(
|
||||||
|
&mut self,
|
||||||
|
inputs: ControllerInputs,
|
||||||
|
dt: Duration,
|
||||||
|
add_foreign_systems: impl Fn(&mut DispatcherBuilder),
|
||||||
|
) -> Result<Vec<Event>, Error> {
|
||||||
// This tick function is the centre of the Veloren universe. Most client-side things are
|
// This tick function is the centre of the Veloren universe. Most client-side things are
|
||||||
// managed from here, and as such it's important that it stays organised. Please consult
|
// managed from here, and as such it's important that it stays organised. Please consult
|
||||||
// the core developers before making significant changes to this code. Here is the
|
// the core developers before making significant changes to this code. Here is the
|
||||||
@ -374,7 +379,7 @@ impl Client {
|
|||||||
// 3) Update client local data
|
// 3) Update client local data
|
||||||
|
|
||||||
// 4) Tick the client's LocalState
|
// 4) Tick the client's LocalState
|
||||||
self.state.tick(dt, |_| {});
|
self.state.tick(dt, add_foreign_systems);
|
||||||
|
|
||||||
// 5) Terrain
|
// 5) Terrain
|
||||||
let pos = self
|
let pos = self
|
||||||
|
@ -12,15 +12,15 @@ mod stats;
|
|||||||
use specs::DispatcherBuilder;
|
use specs::DispatcherBuilder;
|
||||||
|
|
||||||
// System names
|
// System names
|
||||||
const AGENT_SYS: &str = "agent_sys";
|
pub const AGENT_SYS: &str = "agent_sys";
|
||||||
const CONTROLLER_SYS: &str = "controller_sys";
|
pub const CONTROLLER_SYS: &str = "controller_sys";
|
||||||
const MOUNT_SYS: &str = "mount_sys";
|
pub const MOUNT_SYS: &str = "mount_sys";
|
||||||
const PHYS_SYS: &str = "phys_sys";
|
pub const PHYS_SYS: &str = "phys_sys";
|
||||||
const MOVEMENT_SYS: &str = "movement_sys";
|
pub const MOVEMENT_SYS: &str = "movement_sys";
|
||||||
const PROJECTILE_SYS: &str = "projectile_sys";
|
pub const PROJECTILE_SYS: &str = "projectile_sys";
|
||||||
const COMBAT_SYS: &str = "combat_sys";
|
pub const COMBAT_SYS: &str = "combat_sys";
|
||||||
const STATS_SYS: &str = "stats_sys";
|
pub const STATS_SYS: &str = "stats_sys";
|
||||||
const CLEANUP_SYS: &str = "cleanup_sys";
|
pub const CLEANUP_SYS: &str = "cleanup_sys";
|
||||||
|
|
||||||
pub fn add_local_systems(dispatch_builder: &mut DispatcherBuilder) {
|
pub fn add_local_systems(dispatch_builder: &mut DispatcherBuilder) {
|
||||||
dispatch_builder.add(agent::Sys, AGENT_SYS, &[]);
|
dispatch_builder.add(agent::Sys, AGENT_SYS, &[]);
|
||||||
|
2
server-cli/.gitignore
vendored
2
server-cli/.gitignore
vendored
@ -1 +1 @@
|
|||||||
settings.ron
|
|
||||||
|
@ -87,9 +87,9 @@ lazy_static! {
|
|||||||
/// Static list of chat commands available to the server.
|
/// Static list of chat commands available to the server.
|
||||||
pub static ref CHAT_COMMANDS: Vec<ChatCommand> = vec![
|
pub static ref CHAT_COMMANDS: Vec<ChatCommand> = vec![
|
||||||
ChatCommand::new(
|
ChatCommand::new(
|
||||||
"giveitem",
|
"give_item",
|
||||||
"{d}",
|
"{d}",
|
||||||
"/giveitem <path to item>\n\
|
"/give_item <path to item>\n\
|
||||||
Example: common/items/debug/boost",
|
Example: common/items/debug/boost",
|
||||||
true,
|
true,
|
||||||
handle_give,),
|
handle_give,),
|
||||||
|
@ -22,7 +22,7 @@ impl Default for ServerSettings {
|
|||||||
Self {
|
Self {
|
||||||
gameserver_address: SocketAddr::from(([0; 4], 14004)),
|
gameserver_address: SocketAddr::from(([0; 4], 14004)),
|
||||||
metrics_address: SocketAddr::from(([0; 4], 14005)),
|
metrics_address: SocketAddr::from(([0; 4], 14005)),
|
||||||
world_seed: 1337,
|
world_seed: 5284,
|
||||||
server_name: "Veloren Alpha".to_owned(),
|
server_name: "Veloren Alpha".to_owned(),
|
||||||
server_description: "This is the best Veloren server.".to_owned(),
|
server_description: "This is the best Veloren server.".to_owned(),
|
||||||
max_players: 100,
|
max_players: 100,
|
||||||
|
@ -24,8 +24,12 @@ const TERRAIN_SYS: &str = "server_terrain_sys";
|
|||||||
|
|
||||||
pub fn add_server_systems(dispatch_builder: &mut DispatcherBuilder) {
|
pub fn add_server_systems(dispatch_builder: &mut DispatcherBuilder) {
|
||||||
// TODO: makes some of these dependent on systems in common like the phys system
|
// TODO: makes some of these dependent on systems in common like the phys system
|
||||||
dispatch_builder.add(sentinel::Sys, SENTINEL_SYS, &[]);
|
dispatch_builder.add(sentinel::Sys, SENTINEL_SYS, &[common::sys::PHYS_SYS]);
|
||||||
dispatch_builder.add(subscription::Sys, SUBSCRIPTION_SYS, &[]);
|
dispatch_builder.add(
|
||||||
|
subscription::Sys,
|
||||||
|
SUBSCRIPTION_SYS,
|
||||||
|
&[common::sys::PHYS_SYS],
|
||||||
|
);
|
||||||
dispatch_builder.add(
|
dispatch_builder.add(
|
||||||
entity_sync::Sys,
|
entity_sync::Sys,
|
||||||
ENTITY_SYNC_SYS,
|
ENTITY_SYNC_SYS,
|
||||||
|
@ -28,6 +28,7 @@ euc = "0.3.0"
|
|||||||
|
|
||||||
# ECS
|
# ECS
|
||||||
specs = "0.15.1"
|
specs = "0.15.1"
|
||||||
|
specs-idvs = { git = "https://gitlab.com/veloren/specs-idvs.git" }
|
||||||
|
|
||||||
# Mathematics
|
# Mathematics
|
||||||
vek = { version = "0.9.9", features = ["serde"] }
|
vek = { version = "0.9.9", features = ["serde"] }
|
||||||
|
34
voxygen/src/ecs/comp.rs
Normal file
34
voxygen/src/ecs/comp.rs
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
use specs::Component;
|
||||||
|
use specs_idvs::IDVStorage;
|
||||||
|
use vek::*;
|
||||||
|
|
||||||
|
// Floats over entity that has had a health change, rising up over time until it vanishes
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
pub struct HpFloater {
|
||||||
|
pub timer: f32,
|
||||||
|
// Numbers of times significant damage has been dealt
|
||||||
|
pub hp_change: i32,
|
||||||
|
// Used for randomly offseting
|
||||||
|
pub rand: f32,
|
||||||
|
}
|
||||||
|
#[derive(Clone, Debug, Default)]
|
||||||
|
pub struct HpFloaterList {
|
||||||
|
// Order oldest to newest
|
||||||
|
pub floaters: Vec<HpFloater>,
|
||||||
|
// Keep from spawning more floaters from same hp change
|
||||||
|
// Note: this can't detect a change if equivalent healing and damage take place simultaneously
|
||||||
|
pub last_hp: u32,
|
||||||
|
}
|
||||||
|
impl Component for HpFloaterList {
|
||||||
|
type Storage = IDVStorage<Self>;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Used for smooth interpolation of visual elements that are tied to entity position
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
pub struct Interpolated {
|
||||||
|
pub pos: Vec3<f32>,
|
||||||
|
pub ori: Vec3<f32>,
|
||||||
|
}
|
||||||
|
impl Component for Interpolated {
|
||||||
|
type Storage = IDVStorage<Self>;
|
||||||
|
}
|
28
voxygen/src/ecs/mod.rs
Normal file
28
voxygen/src/ecs/mod.rs
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
pub mod comp;
|
||||||
|
pub mod sys;
|
||||||
|
|
||||||
|
use specs::{Entity, World, WorldExt};
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
pub struct MyEntity(pub Entity);
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
pub struct ExpFloater {
|
||||||
|
pub exp_change: i32, // Maybe you can loose exp :p
|
||||||
|
pub timer: f32,
|
||||||
|
// Used to randomly offset position
|
||||||
|
pub rand: (f32, f32),
|
||||||
|
}
|
||||||
|
#[derive(Clone, Debug, Default)]
|
||||||
|
pub struct MyExpFloaterList {
|
||||||
|
pub floaters: Vec<ExpFloater>,
|
||||||
|
pub last_exp: u32,
|
||||||
|
pub last_level: u32,
|
||||||
|
pub last_exp_max: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn init(world: &mut World) {
|
||||||
|
world.register::<comp::HpFloaterList>();
|
||||||
|
world.register::<comp::Interpolated>();
|
||||||
|
world.insert(MyExpFloaterList::default());
|
||||||
|
}
|
17
voxygen/src/ecs/sys.rs
Normal file
17
voxygen/src/ecs/sys.rs
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
pub mod floater;
|
||||||
|
mod interpolation;
|
||||||
|
|
||||||
|
use specs::DispatcherBuilder;
|
||||||
|
|
||||||
|
// System names
|
||||||
|
const FLOATER_SYS: &str = "floater_voxygen_sys";
|
||||||
|
const INTERPOLATION_SYS: &str = "interpolation_voxygen_sys";
|
||||||
|
|
||||||
|
pub fn add_local_systems(dispatch_builder: &mut DispatcherBuilder) {
|
||||||
|
dispatch_builder.add(
|
||||||
|
interpolation::Sys,
|
||||||
|
INTERPOLATION_SYS,
|
||||||
|
&[common::sys::PHYS_SYS],
|
||||||
|
);
|
||||||
|
dispatch_builder.add(floater::Sys, FLOATER_SYS, &[INTERPOLATION_SYS]);
|
||||||
|
}
|
181
voxygen/src/ecs/sys/floater.rs
Normal file
181
voxygen/src/ecs/sys/floater.rs
Normal file
@ -0,0 +1,181 @@
|
|||||||
|
use crate::ecs::{
|
||||||
|
comp::{HpFloater, HpFloaterList},
|
||||||
|
ExpFloater, MyEntity, MyExpFloaterList,
|
||||||
|
};
|
||||||
|
use common::{
|
||||||
|
comp::{HealthSource, Pos, Stats},
|
||||||
|
state::DeltaTime,
|
||||||
|
sync::Uid,
|
||||||
|
};
|
||||||
|
use specs::{Entities, Join, Read, ReadExpect, ReadStorage, System, Write, WriteStorage};
|
||||||
|
|
||||||
|
// How long floaters last (in seconds)
|
||||||
|
pub const HP_SHOWTIME: f32 = 3.0;
|
||||||
|
pub const MY_HP_SHOWTIME: f32 = 2.5;
|
||||||
|
pub const MY_EXP_SHOWTIME: f32 = 4.0;
|
||||||
|
|
||||||
|
pub struct Sys;
|
||||||
|
impl<'a> System<'a> for Sys {
|
||||||
|
type SystemData = (
|
||||||
|
Entities<'a>,
|
||||||
|
ReadExpect<'a, MyEntity>,
|
||||||
|
Read<'a, DeltaTime>,
|
||||||
|
Write<'a, MyExpFloaterList>,
|
||||||
|
ReadStorage<'a, Uid>,
|
||||||
|
ReadStorage<'a, Pos>,
|
||||||
|
ReadStorage<'a, Stats>,
|
||||||
|
WriteStorage<'a, HpFloaterList>,
|
||||||
|
);
|
||||||
|
|
||||||
|
fn run(
|
||||||
|
&mut self,
|
||||||
|
(entities, my_entity, dt, mut my_exp_floater_list, uids, pos, stats, mut hp_floater_lists): Self::SystemData,
|
||||||
|
) {
|
||||||
|
// Add hp floater lists to all entities with stats and a postion
|
||||||
|
// Note: neccessary in order to know last_hp
|
||||||
|
for (entity, last_hp) in (&entities, &stats, &pos, !&hp_floater_lists)
|
||||||
|
.join()
|
||||||
|
.map(|(e, s, _, _)| (e, s.health.current()))
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
{
|
||||||
|
let _ = hp_floater_lists.insert(
|
||||||
|
entity,
|
||||||
|
HpFloaterList {
|
||||||
|
floaters: Vec::new(),
|
||||||
|
last_hp,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add hp floaters to all entities that have been damaged
|
||||||
|
let my_uid = uids.get(my_entity.0);
|
||||||
|
for (entity, health, hp_floater_list) in (&entities, &stats, &mut hp_floater_lists)
|
||||||
|
.join()
|
||||||
|
.map(|(e, s, fl)| (e, s.health, fl))
|
||||||
|
{
|
||||||
|
// Check if health has changed (won't work if damaged and then healed with
|
||||||
|
// equivalently in the same frame)
|
||||||
|
if hp_floater_list.last_hp != health.current() {
|
||||||
|
hp_floater_list.last_hp = health.current();
|
||||||
|
// TODO: What if multiple health changes occured since last check here
|
||||||
|
// Also, If we make stats store a vec of the last_changes (from say the last frame),
|
||||||
|
// what if the client recieves the stats component from two different server ticks at
|
||||||
|
// once, then one will be lost (tbf this is probably a rare occurance and the results
|
||||||
|
// would just be a transient glitch in the display of these damage numbers) (maybe
|
||||||
|
// health changes could be sent to the client as a list of events)
|
||||||
|
if match health.last_change.1.cause {
|
||||||
|
HealthSource::Attack { by } => {
|
||||||
|
my_entity.0 == entity || my_uid.map_or(false, |&uid| by == uid)
|
||||||
|
}
|
||||||
|
HealthSource::Suicide => my_entity.0 == entity,
|
||||||
|
HealthSource::World => my_entity.0 == entity,
|
||||||
|
HealthSource::Revive => false,
|
||||||
|
HealthSource::Command => true,
|
||||||
|
HealthSource::LevelUp => my_entity.0 == entity,
|
||||||
|
HealthSource::Item => true,
|
||||||
|
HealthSource::Unknown => false,
|
||||||
|
} {
|
||||||
|
hp_floater_list.floaters.push(HpFloater {
|
||||||
|
timer: 0.0,
|
||||||
|
hp_change: health.last_change.1.amount,
|
||||||
|
rand: rand::random(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove floater lists on entities without stats or without posistion
|
||||||
|
for entity in (&entities, !&stats, &hp_floater_lists)
|
||||||
|
.join()
|
||||||
|
.map(|(e, _, _)| e)
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
{
|
||||||
|
hp_floater_lists.remove(entity);
|
||||||
|
}
|
||||||
|
for entity in (&entities, !&pos, &hp_floater_lists)
|
||||||
|
.join()
|
||||||
|
.map(|(e, _, _)| e)
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
{
|
||||||
|
hp_floater_lists.remove(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Maintain existing floaters
|
||||||
|
for (
|
||||||
|
entity,
|
||||||
|
HpFloaterList {
|
||||||
|
ref mut floaters,
|
||||||
|
ref last_hp,
|
||||||
|
},
|
||||||
|
) in (&entities, &mut hp_floater_lists).join()
|
||||||
|
{
|
||||||
|
for mut floater in floaters.iter_mut() {
|
||||||
|
// Increment timer
|
||||||
|
floater.timer += dt.0;
|
||||||
|
}
|
||||||
|
// Clear floaters if newest floater is past show time or health runs out
|
||||||
|
if floaters.last().map_or(false, |f| {
|
||||||
|
f.timer
|
||||||
|
> if entity != my_entity.0 {
|
||||||
|
HP_SHOWTIME
|
||||||
|
} else {
|
||||||
|
MY_HP_SHOWTIME
|
||||||
|
}
|
||||||
|
|| *last_hp == 0
|
||||||
|
}) {
|
||||||
|
floaters.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update MyExpFloaterList
|
||||||
|
if let Some(stats) = stats.get(my_entity.0) {
|
||||||
|
let mut fl = my_exp_floater_list;
|
||||||
|
// Add a floater if exp changed
|
||||||
|
// TODO: can't handle if you level up more than once (maybe store total exp in stats)
|
||||||
|
let exp_change = if stats.level.level() != fl.last_level {
|
||||||
|
if stats.level.level() > fl.last_level {
|
||||||
|
stats.exp.current() as i32 + fl.last_exp_max as i32 - fl.last_exp as i32
|
||||||
|
} else {
|
||||||
|
// Level down
|
||||||
|
stats.exp.current() as i32 - stats.exp.maximum() as i32 - fl.last_exp as i32
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
stats.exp.current() as i32 - fl.last_exp as i32
|
||||||
|
};
|
||||||
|
|
||||||
|
if exp_change != 0 {
|
||||||
|
fl.floaters.push(ExpFloater {
|
||||||
|
timer: 0.0,
|
||||||
|
exp_change,
|
||||||
|
rand: (rand::random(), rand::random()),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Increment timers
|
||||||
|
for mut floater in &mut fl.floaters {
|
||||||
|
floater.timer += dt.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear if the newest is past show time
|
||||||
|
if fl
|
||||||
|
.floaters
|
||||||
|
.last()
|
||||||
|
.map_or(false, |f| f.timer > MY_EXP_SHOWTIME)
|
||||||
|
{
|
||||||
|
fl.floaters.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update stored values
|
||||||
|
fl.last_exp = stats.exp.current();
|
||||||
|
fl.last_exp_max = stats.exp.maximum();
|
||||||
|
fl.last_level = stats.level.level();
|
||||||
|
} else {
|
||||||
|
// Clear if stats component doesn't exist
|
||||||
|
my_exp_floater_list.floaters.clear();
|
||||||
|
// Clear stored values
|
||||||
|
my_exp_floater_list.last_exp = 0;
|
||||||
|
my_exp_floater_list.last_exp_max = 0;
|
||||||
|
my_exp_floater_list.last_level = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
80
voxygen/src/ecs/sys/interpolation.rs
Normal file
80
voxygen/src/ecs/sys/interpolation.rs
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
use crate::ecs::comp::Interpolated;
|
||||||
|
use common::{
|
||||||
|
comp::{Ori, Pos, Vel},
|
||||||
|
state::DeltaTime,
|
||||||
|
};
|
||||||
|
use log::warn;
|
||||||
|
use specs::{Entities, Join, Read, ReadStorage, System, WriteStorage};
|
||||||
|
use vek::*;
|
||||||
|
|
||||||
|
/// This system will allow NPCs to modify their controller
|
||||||
|
pub struct Sys;
|
||||||
|
impl<'a> System<'a> for Sys {
|
||||||
|
type SystemData = (
|
||||||
|
Entities<'a>,
|
||||||
|
Read<'a, DeltaTime>,
|
||||||
|
ReadStorage<'a, Pos>,
|
||||||
|
ReadStorage<'a, Ori>,
|
||||||
|
ReadStorage<'a, Vel>,
|
||||||
|
WriteStorage<'a, Interpolated>,
|
||||||
|
);
|
||||||
|
|
||||||
|
fn run(
|
||||||
|
&mut self,
|
||||||
|
(entities, dt, positions, orientations, velocities, mut interpolated): Self::SystemData,
|
||||||
|
) {
|
||||||
|
// Update interpolated positions and orientations
|
||||||
|
for (pos, ori, i, vel) in (&positions, &orientations, &mut interpolated, &velocities).join()
|
||||||
|
{
|
||||||
|
// Update interpolation values
|
||||||
|
if i.pos.distance_squared(pos.0) < 64.0 * 64.0 {
|
||||||
|
i.pos = Lerp::lerp(i.pos, pos.0 + vel.0 * 0.03, 10.0 * dt.0);
|
||||||
|
let ori_interp = Slerp::slerp(i.ori, ori.0, 5.0 * dt.0);
|
||||||
|
// Check for NaNs
|
||||||
|
// TODO: why are we getting NaNs here! Zero-length ori vectors?
|
||||||
|
i.ori = if !ori_interp.map(|e| e.is_nan()).reduce_or() {
|
||||||
|
ori_interp
|
||||||
|
} else {
|
||||||
|
ori.0
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
i.pos = pos.0;
|
||||||
|
i.ori = ori.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Insert interpolation components for entities which don't have them
|
||||||
|
for (entity, pos, ori) in (&entities, &positions, &orientations, !&interpolated)
|
||||||
|
.join()
|
||||||
|
.map(|(e, p, o, _)| (e, p.0, o.0))
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
{
|
||||||
|
interpolated
|
||||||
|
.insert(entity, Interpolated { pos, ori })
|
||||||
|
.err()
|
||||||
|
.map(|err| warn!("Error inserting Interpolated component: {}", err));
|
||||||
|
}
|
||||||
|
// Remove Interpolated component from entities which don't have a position or an
|
||||||
|
// orientation or a velocity
|
||||||
|
for entity in (&entities, !&positions, &interpolated)
|
||||||
|
.join()
|
||||||
|
.map(|(e, _, _)| e)
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
{
|
||||||
|
interpolated.remove(entity);
|
||||||
|
}
|
||||||
|
for entity in (&entities, !&orientations, &interpolated)
|
||||||
|
.join()
|
||||||
|
.map(|(e, _, _)| e)
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
{
|
||||||
|
interpolated.remove(entity);
|
||||||
|
}
|
||||||
|
for entity in (&entities, !&velocities, &interpolated)
|
||||||
|
.join()
|
||||||
|
.map(|(e, _, _)| e)
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
{
|
||||||
|
interpolated.remove(entity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -231,8 +231,7 @@ image_ids! {
|
|||||||
key: "voxygen.voxel.object.key",
|
key: "voxygen.voxel.object.key",
|
||||||
key_gold: "voxygen.voxel.object.key_gold",
|
key_gold: "voxygen.voxel.object.key_gold",
|
||||||
|
|
||||||
// Enemy Healthbar
|
|
||||||
enemy_health: "voxygen.element.frames.enemybar",
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -247,6 +246,13 @@ image_ids! {
|
|||||||
charwindow_gradient:"voxygen.element.misc_bg.charwindow",
|
charwindow_gradient:"voxygen.element.misc_bg.charwindow",
|
||||||
map_placeholder: "voxygen.background.map",
|
map_placeholder: "voxygen.background.map",
|
||||||
|
|
||||||
|
death_bg: "voxygen.background.death",
|
||||||
|
hurt_bg: "voxygen.background.hurt",
|
||||||
|
|
||||||
|
// Enemy Healthbar
|
||||||
|
enemy_health: "voxygen.element.frames.enemybar",
|
||||||
|
// Enemy Bar Content:
|
||||||
|
enemy_bar: "voxygen.element.skillbar.enemy_bar_content",
|
||||||
// Spell Book Window
|
// Spell Book Window
|
||||||
spellbook_icon: "voxygen.element.icons.spellbook",
|
spellbook_icon: "voxygen.element.icons.spellbook",
|
||||||
// Bag
|
// Bag
|
||||||
|
@ -9,7 +9,6 @@ use conrod_core::{
|
|||||||
};
|
};
|
||||||
use specs::WorldExt;
|
use specs::WorldExt;
|
||||||
use vek::*;
|
use vek::*;
|
||||||
|
|
||||||
widget_ids! {
|
widget_ids! {
|
||||||
struct Ids {
|
struct Ids {
|
||||||
map_frame,
|
map_frame,
|
||||||
@ -37,6 +36,7 @@ pub struct Map<'a> {
|
|||||||
#[conrod(common_builder)]
|
#[conrod(common_builder)]
|
||||||
common: widget::CommonBuilder,
|
common: widget::CommonBuilder,
|
||||||
pulse: f32,
|
pulse: f32,
|
||||||
|
velocity: f32,
|
||||||
}
|
}
|
||||||
impl<'a> Map<'a> {
|
impl<'a> Map<'a> {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
@ -46,6 +46,7 @@ impl<'a> Map<'a> {
|
|||||||
world_map: Id,
|
world_map: Id,
|
||||||
fonts: &'a Fonts,
|
fonts: &'a Fonts,
|
||||||
pulse: f32,
|
pulse: f32,
|
||||||
|
velocity: f32,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
_show: show,
|
_show: show,
|
||||||
@ -55,6 +56,7 @@ impl<'a> Map<'a> {
|
|||||||
fonts: fonts,
|
fonts: fonts,
|
||||||
common: widget::CommonBuilder::default(),
|
common: widget::CommonBuilder::default(),
|
||||||
pulse,
|
pulse,
|
||||||
|
velocity,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -84,6 +86,15 @@ impl<'a> Widget for Map<'a> {
|
|||||||
|
|
||||||
fn update(self, args: widget::UpdateArgs<Self>) -> Self::Event {
|
fn update(self, args: widget::UpdateArgs<Self>) -> Self::Event {
|
||||||
let widget::UpdateArgs { state, ui, .. } = args;
|
let widget::UpdateArgs { state, ui, .. } = args;
|
||||||
|
// Set map transparency to 0.5 when player is moving
|
||||||
|
/*let vel = match self.velocity {
|
||||||
|
Some(velocity) => velocity.0.magnitude(),
|
||||||
|
None => 0.0,
|
||||||
|
};*/
|
||||||
|
let mut fade = 1.0;
|
||||||
|
if self.velocity > 7.0 {
|
||||||
|
fade = 0.7
|
||||||
|
};
|
||||||
|
|
||||||
// BG
|
// BG
|
||||||
Rectangle::fill_with([824.0, 976.0], color::TRANSPARENT)
|
Rectangle::fill_with([824.0, 976.0], color::TRANSPARENT)
|
||||||
@ -96,24 +107,29 @@ impl<'a> Widget for Map<'a> {
|
|||||||
Image::new(self.imgs.map_frame_l)
|
Image::new(self.imgs.map_frame_l)
|
||||||
.top_left_with_margins_on(state.ids.map_bg, 0.0, 0.0)
|
.top_left_with_margins_on(state.ids.map_bg, 0.0, 0.0)
|
||||||
.w_h(412.0, 488.0)
|
.w_h(412.0, 488.0)
|
||||||
|
.color(Some(Color::Rgba(1.0, 1.0, 1.0, fade)))
|
||||||
.set(state.ids.map_frame_l, ui);
|
.set(state.ids.map_frame_l, ui);
|
||||||
Image::new(self.imgs.map_frame_r)
|
Image::new(self.imgs.map_frame_r)
|
||||||
.right_from(state.ids.map_frame_l, 0.0)
|
.right_from(state.ids.map_frame_l, 0.0)
|
||||||
|
.color(Some(Color::Rgba(1.0, 1.0, 1.0, fade)))
|
||||||
.w_h(412.0, 488.0)
|
.w_h(412.0, 488.0)
|
||||||
.set(state.ids.map_frame_r, ui);
|
.set(state.ids.map_frame_r, ui);
|
||||||
Image::new(self.imgs.map_frame_br)
|
Image::new(self.imgs.map_frame_br)
|
||||||
.down_from(state.ids.map_frame_r, 0.0)
|
.down_from(state.ids.map_frame_r, 0.0)
|
||||||
.w_h(412.0, 488.0)
|
.w_h(412.0, 488.0)
|
||||||
|
.color(Some(Color::Rgba(1.0, 1.0, 1.0, fade)))
|
||||||
.set(state.ids.map_frame_br, ui);
|
.set(state.ids.map_frame_br, ui);
|
||||||
Image::new(self.imgs.map_frame_bl)
|
Image::new(self.imgs.map_frame_bl)
|
||||||
.down_from(state.ids.map_frame_l, 0.0)
|
.down_from(state.ids.map_frame_l, 0.0)
|
||||||
.w_h(412.0, 488.0)
|
.w_h(412.0, 488.0)
|
||||||
|
.color(Some(Color::Rgba(1.0, 1.0, 1.0, fade)))
|
||||||
.set(state.ids.map_frame_bl, ui);
|
.set(state.ids.map_frame_bl, ui);
|
||||||
|
|
||||||
// Icon
|
// Icon
|
||||||
Image::new(self.imgs.map_icon)
|
Image::new(self.imgs.map_icon)
|
||||||
.w_h(224.0 / 3.0, 224.0 / 3.0)
|
.w_h(224.0 / 3.0, 224.0 / 3.0)
|
||||||
.top_left_with_margins_on(state.ids.map_frame, -10.0, -10.0)
|
.top_left_with_margins_on(state.ids.map_frame, -10.0, -10.0)
|
||||||
|
.color(Some(Color::Rgba(1.0, 1.0, 1.0, fade)))
|
||||||
.set(state.ids.map_icon, ui);
|
.set(state.ids.map_icon, ui);
|
||||||
|
|
||||||
// X-Button
|
// X-Button
|
||||||
@ -121,6 +137,7 @@ impl<'a> Widget for Map<'a> {
|
|||||||
.w_h(28.0, 28.0)
|
.w_h(28.0, 28.0)
|
||||||
.hover_image(self.imgs.close_button_hover)
|
.hover_image(self.imgs.close_button_hover)
|
||||||
.press_image(self.imgs.close_button_press)
|
.press_image(self.imgs.close_button_press)
|
||||||
|
.color(Color::Rgba(1.0, 1.0, 1.0, fade - 0.5))
|
||||||
.top_right_with_margins_on(state.ids.map_frame_r, 0.0, 0.0)
|
.top_right_with_margins_on(state.ids.map_frame_r, 0.0, 0.0)
|
||||||
.set(state.ids.map_close, ui)
|
.set(state.ids.map_close, ui)
|
||||||
.was_clicked()
|
.was_clicked()
|
||||||
@ -144,9 +161,11 @@ impl<'a> Widget for Map<'a> {
|
|||||||
.color(TEXT_COLOR)
|
.color(TEXT_COLOR)
|
||||||
.set(state.ids.location_name, ui),
|
.set(state.ids.location_name, ui),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Map Image
|
// Map Image
|
||||||
Image::new(/*self.world_map*/ self.imgs.map_placeholder)
|
Image::new(/*self.world_map*/ self.imgs.map_placeholder)
|
||||||
.middle_of(state.ids.map_bg)
|
.middle_of(state.ids.map_bg)
|
||||||
|
.color(Some(Color::Rgba(1.0, 1.0, 1.0, fade)))
|
||||||
.w_h(700.0, 700.0)
|
.w_h(700.0, 700.0)
|
||||||
.parent(state.ids.map_bg)
|
.parent(state.ids.map_bg)
|
||||||
.set(state.ids.grid, ui);
|
.set(state.ids.grid, ui);
|
||||||
@ -183,7 +202,7 @@ impl<'a> Widget for Map<'a> {
|
|||||||
34.0 * indic_scale
|
34.0 * indic_scale
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.color(Some(Color::Rgba(1.0, 1.0, 1.0, 1.0)))
|
.color(Some(Color::Rgba(1.0, 1.0, 1.0, fade + 0.2)))
|
||||||
.floating(true)
|
.floating(true)
|
||||||
.parent(ui.window)
|
.parent(ui.window)
|
||||||
.set(state.ids.indicator, ui);
|
.set(state.ids.indicator, ui);
|
||||||
|
@ -13,8 +13,7 @@ mod skillbar;
|
|||||||
mod social;
|
mod social;
|
||||||
mod spell;
|
mod spell;
|
||||||
|
|
||||||
use crate::hud::img_ids::ImgsRot;
|
use crate::{ecs::comp::HpFloaterList, hud::img_ids::ImgsRot};
|
||||||
//use rand::Rng;
|
|
||||||
pub use settings_window::ScaleChange;
|
pub use settings_window::ScaleChange;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
@ -36,6 +35,7 @@ use social::{Social, SocialTab};
|
|||||||
use spell::Spell;
|
use spell::Spell;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
ecs::comp as vcomp,
|
||||||
render::{AaMode, Consts, Globals, Renderer},
|
render::{AaMode, Consts, Globals, Renderer},
|
||||||
scene::camera::Camera,
|
scene::camera::Camera,
|
||||||
//settings::ControlSettings,
|
//settings::ControlSettings,
|
||||||
@ -60,6 +60,7 @@ use crate::{discord, discord::DiscordUpdate};
|
|||||||
|
|
||||||
const XP_COLOR: Color = Color::Rgba(0.59, 0.41, 0.67, 1.0);
|
const XP_COLOR: Color = Color::Rgba(0.59, 0.41, 0.67, 1.0);
|
||||||
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_COLOR_GREY: Color = Color::Rgba(1.0, 1.0, 1.0, 0.5);
|
||||||
const MENU_BG: Color = Color::Rgba(0.0, 0.0, 0.0, 0.4);
|
const MENU_BG: Color = Color::Rgba(0.0, 0.0, 0.0, 0.4);
|
||||||
//const TEXT_COLOR_2: Color = Color::Rgba(0.0, 0.0, 0.0, 1.0);
|
//const TEXT_COLOR_2: Color = Color::Rgba(0.0, 0.0, 0.0, 1.0);
|
||||||
const TEXT_COLOR_3: Color = Color::Rgba(1.0, 1.0, 1.0, 0.1);
|
const TEXT_COLOR_3: Color = Color::Rgba(1.0, 1.0, 1.0, 0.1);
|
||||||
@ -88,6 +89,7 @@ widget_ids! {
|
|||||||
|
|
||||||
// Character Names
|
// Character Names
|
||||||
name_tags[],
|
name_tags[],
|
||||||
|
name_tags_bgs[],
|
||||||
levels[],
|
levels[],
|
||||||
levels_skull[],
|
levels_skull[],
|
||||||
// Health Bars
|
// Health Bars
|
||||||
@ -96,6 +98,18 @@ widget_ids! {
|
|||||||
health_bar_fronts[],
|
health_bar_fronts[],
|
||||||
health_bar_backs[],
|
health_bar_backs[],
|
||||||
|
|
||||||
|
// SCT
|
||||||
|
player_scts[],
|
||||||
|
player_sct_bgs[],
|
||||||
|
sct_exp_bgs[],
|
||||||
|
sct_exps[],
|
||||||
|
sct_lvl_bg,
|
||||||
|
sct_lvl,
|
||||||
|
hurt_bg,
|
||||||
|
death_bg,
|
||||||
|
sct_bgs[],
|
||||||
|
scts[],
|
||||||
|
|
||||||
// Intro Text
|
// Intro Text
|
||||||
intro_bg,
|
intro_bg,
|
||||||
intro_text,
|
intro_text,
|
||||||
@ -128,6 +142,7 @@ widget_ids! {
|
|||||||
// Help
|
// Help
|
||||||
help,
|
help,
|
||||||
help_info,
|
help_info,
|
||||||
|
debug_info,
|
||||||
|
|
||||||
// Window Frames
|
// Window Frames
|
||||||
window_frame_0,
|
window_frame_0,
|
||||||
@ -200,6 +215,10 @@ pub enum Event {
|
|||||||
Intro(Intro),
|
Intro(Intro),
|
||||||
ToggleBarNumbers(BarNumbers),
|
ToggleBarNumbers(BarNumbers),
|
||||||
ToggleShortcutNumbers(ShortcutNumbers),
|
ToggleShortcutNumbers(ShortcutNumbers),
|
||||||
|
Sct(bool),
|
||||||
|
SctPlayerBatch(bool),
|
||||||
|
SctDamageBatch(bool),
|
||||||
|
ToggleDebug(bool),
|
||||||
UiScale(ScaleChange),
|
UiScale(ScaleChange),
|
||||||
CharacterSelection,
|
CharacterSelection,
|
||||||
UseInventorySlot(usize),
|
UseInventorySlot(usize),
|
||||||
@ -265,7 +284,6 @@ pub struct Show {
|
|||||||
ingame: bool,
|
ingame: bool,
|
||||||
settings_tab: SettingsTab,
|
settings_tab: SettingsTab,
|
||||||
social_tab: SocialTab,
|
social_tab: SocialTab,
|
||||||
|
|
||||||
want_grab: bool,
|
want_grab: bool,
|
||||||
}
|
}
|
||||||
impl Show {
|
impl Show {
|
||||||
@ -424,6 +442,7 @@ pub struct Hud {
|
|||||||
force_chat_cursor: Option<Index>,
|
force_chat_cursor: Option<Index>,
|
||||||
pulse: f32,
|
pulse: f32,
|
||||||
zoom: f32,
|
zoom: f32,
|
||||||
|
velocity: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Hud {
|
impl Hud {
|
||||||
@ -461,7 +480,7 @@ impl Hud {
|
|||||||
show: Show {
|
show: Show {
|
||||||
help: false,
|
help: false,
|
||||||
intro: true,
|
intro: true,
|
||||||
debug: true,
|
debug: false,
|
||||||
bag: false,
|
bag: false,
|
||||||
esc_menu: false,
|
esc_menu: false,
|
||||||
open_windows: Windows::None,
|
open_windows: Windows::None,
|
||||||
@ -485,6 +504,7 @@ impl Hud {
|
|||||||
force_chat_cursor: None,
|
force_chat_cursor: None,
|
||||||
pulse: 0.0,
|
pulse: 0.0,
|
||||||
zoom: 1.0,
|
zoom: 1.0,
|
||||||
|
velocity: 0.0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -499,43 +519,23 @@ impl Hud {
|
|||||||
let (ref mut ui_widgets, ref mut tooltip_manager) = self.ui.set_widgets();
|
let (ref mut ui_widgets, ref mut tooltip_manager) = self.ui.set_widgets();
|
||||||
// pulse time for pulsating elements
|
// pulse time for pulsating elements
|
||||||
self.pulse = self.pulse + dt.as_secs_f32();
|
self.pulse = self.pulse + dt.as_secs_f32();
|
||||||
|
self.velocity = match debug_info.velocity {
|
||||||
|
Some(velocity) => velocity.0.magnitude(),
|
||||||
|
None => 0.0,
|
||||||
|
};
|
||||||
|
|
||||||
let version = format!(
|
let version = format!(
|
||||||
"{}-{}",
|
"{}-{}",
|
||||||
env!("CARGO_PKG_VERSION"),
|
env!("CARGO_PKG_VERSION"),
|
||||||
common::util::GIT_VERSION.to_string()
|
common::util::GIT_VERSION.to_string()
|
||||||
);
|
);
|
||||||
if self.show.ingame {
|
|
||||||
// Crosshair
|
|
||||||
if !self.show.help {
|
|
||||||
Image::new(
|
|
||||||
// TODO: Do we want to match on this every frame?
|
|
||||||
match global_state.settings.gameplay.crosshair_type {
|
|
||||||
CrosshairType::Round => self.imgs.crosshair_outer_round,
|
|
||||||
CrosshairType::RoundEdges => self.imgs.crosshair_outer_round_edges,
|
|
||||||
CrosshairType::Edges => self.imgs.crosshair_outer_edges,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.w_h(21.0 * 1.5, 21.0 * 1.5)
|
|
||||||
.middle_of(ui_widgets.window)
|
|
||||||
.color(Some(Color::Rgba(
|
|
||||||
1.0,
|
|
||||||
1.0,
|
|
||||||
1.0,
|
|
||||||
global_state.settings.gameplay.crosshair_transp,
|
|
||||||
)))
|
|
||||||
.set(self.ids.crosshair_outer, ui_widgets);
|
|
||||||
Image::new(self.imgs.crosshair_inner)
|
|
||||||
.w_h(21.0 * 2.0, 21.0 * 2.0)
|
|
||||||
.middle_of(self.ids.crosshair_outer)
|
|
||||||
.color(Some(Color::Rgba(1.0, 1.0, 1.0, 0.6)))
|
|
||||||
.set(self.ids.crosshair_inner, ui_widgets);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Nametags and healthbars
|
if self.show.ingame {
|
||||||
let ecs = client.state().ecs();
|
let ecs = client.state().ecs();
|
||||||
let pos = ecs.read_storage::<comp::Pos>();
|
let pos = ecs.read_storage::<comp::Pos>();
|
||||||
let stats = ecs.read_storage::<comp::Stats>();
|
let stats = ecs.read_storage::<comp::Stats>();
|
||||||
|
let hp_floater_lists = ecs.read_storage::<vcomp::HpFloaterList>();
|
||||||
|
let interpolated = ecs.read_storage::<vcomp::Interpolated>();
|
||||||
let players = ecs.read_storage::<comp::Player>();
|
let players = ecs.read_storage::<comp::Player>();
|
||||||
let scales = ecs.read_storage::<comp::Scale>();
|
let scales = ecs.read_storage::<comp::Scale>();
|
||||||
let entities = ecs.entities();
|
let entities = ecs.entities();
|
||||||
@ -545,6 +545,62 @@ impl Hud {
|
|||||||
.get(client.entity())
|
.get(client.entity())
|
||||||
.map_or(0, |stats| stats.level.level());
|
.map_or(0, |stats| stats.level.level());
|
||||||
|
|
||||||
|
if let Some(stats) = stats.get(me) {
|
||||||
|
// Hurt Frame
|
||||||
|
let hp_percentage =
|
||||||
|
stats.health.current() as f32 / stats.health.maximum() as f32 * 100.0;
|
||||||
|
if hp_percentage < 10.0 && !stats.is_dead {
|
||||||
|
let hurt_fade =
|
||||||
|
(self.pulse * (10.0 - hp_percentage as f32) * 0.1/*speed factor*/).sin()
|
||||||
|
* 0.5
|
||||||
|
+ 0.6; //Animation timer
|
||||||
|
Image::new(self.imgs.hurt_bg)
|
||||||
|
.wh_of(ui_widgets.window)
|
||||||
|
.middle_of(ui_widgets.window)
|
||||||
|
.graphics_for(ui_widgets.window)
|
||||||
|
.color(Some(Color::Rgba(1.0, 1.0, 1.0, hurt_fade)))
|
||||||
|
.set(self.ids.hurt_bg, ui_widgets);
|
||||||
|
}
|
||||||
|
// Death Frame
|
||||||
|
if stats.is_dead {
|
||||||
|
Image::new(self.imgs.death_bg)
|
||||||
|
.wh_of(ui_widgets.window)
|
||||||
|
.middle_of(ui_widgets.window)
|
||||||
|
.graphics_for(ui_widgets.window)
|
||||||
|
.color(Some(Color::Rgba(0.0, 0.0, 0.0, 1.0)))
|
||||||
|
.set(self.ids.death_bg, ui_widgets);
|
||||||
|
}
|
||||||
|
// Crosshair
|
||||||
|
if !self.show.help && !stats.is_dead {
|
||||||
|
Image::new(
|
||||||
|
// TODO: Do we want to match on this every frame?
|
||||||
|
match global_state.settings.gameplay.crosshair_type {
|
||||||
|
CrosshairType::Round => self.imgs.crosshair_outer_round,
|
||||||
|
CrosshairType::RoundEdges => self.imgs.crosshair_outer_round_edges,
|
||||||
|
CrosshairType::Edges => self.imgs.crosshair_outer_edges,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.w_h(21.0 * 1.5, 21.0 * 1.5)
|
||||||
|
.middle_of(ui_widgets.window)
|
||||||
|
.color(Some(Color::Rgba(
|
||||||
|
1.0,
|
||||||
|
1.0,
|
||||||
|
1.0,
|
||||||
|
global_state.settings.gameplay.crosshair_transp,
|
||||||
|
)))
|
||||||
|
.set(self.ids.crosshair_outer, ui_widgets);
|
||||||
|
Image::new(self.imgs.crosshair_inner)
|
||||||
|
.w_h(21.0 * 2.0, 21.0 * 2.0)
|
||||||
|
.middle_of(self.ids.crosshair_outer)
|
||||||
|
.color(Some(Color::Rgba(1.0, 1.0, 1.0, 0.6)))
|
||||||
|
.set(self.ids.crosshair_inner, ui_widgets);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Nametags and healthbars
|
||||||
|
|
||||||
|
// Max amount the sct font size increases when "flashing"
|
||||||
|
const FLASH_MAX: f32 = 25.0;
|
||||||
// Get player position.
|
// Get player position.
|
||||||
let player_pos = client
|
let player_pos = client
|
||||||
.state()
|
.state()
|
||||||
@ -553,21 +609,31 @@ impl Hud {
|
|||||||
.get(client.entity())
|
.get(client.entity())
|
||||||
.map_or(Vec3::zero(), |pos| pos.0);
|
.map_or(Vec3::zero(), |pos| pos.0);
|
||||||
let mut name_id_walker = self.ids.name_tags.walk();
|
let mut name_id_walker = self.ids.name_tags.walk();
|
||||||
|
let mut name_id_bg_walker = self.ids.name_tags_bgs.walk();
|
||||||
let mut level_id_walker = self.ids.levels.walk();
|
let mut level_id_walker = self.ids.levels.walk();
|
||||||
let mut level_skull_id_walker = self.ids.levels_skull.walk();
|
let mut level_skull_id_walker = self.ids.levels_skull.walk();
|
||||||
let mut health_id_walker = self.ids.health_bars.walk();
|
let mut health_id_walker = self.ids.health_bars.walk();
|
||||||
let mut mana_id_walker = self.ids.mana_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_back_id_walker = self.ids.health_bar_backs.walk();
|
||||||
let mut health_front_id_walker = self.ids.health_bar_fronts.walk();
|
let mut health_front_id_walker = self.ids.health_bar_fronts.walk();
|
||||||
|
let mut sct_bg_id_walker = self.ids.sct_bgs.walk();
|
||||||
|
let mut sct_id_walker = self.ids.scts.walk();
|
||||||
// Render Health Bars
|
// Render Health Bars
|
||||||
for (_entity, pos, stats, scale) in (&entities, &pos, &stats, scales.maybe())
|
for (pos, stats, scale, hp_floater_list) in (
|
||||||
|
&entities,
|
||||||
|
&pos,
|
||||||
|
interpolated.maybe(),
|
||||||
|
&stats,
|
||||||
|
scales.maybe(),
|
||||||
|
hp_floater_lists.maybe(), // Potentially move this to its own loop
|
||||||
|
)
|
||||||
.join()
|
.join()
|
||||||
.filter(|(entity, _, stats, _)| {
|
.filter(|(entity, _, _, stats, _, _)| {
|
||||||
*entity != me && !stats.is_dead
|
*entity != me && !stats.is_dead
|
||||||
//&& stats.health.current() != stats.health.maximum()
|
//&& stats.health.current() != stats.health.maximum()
|
||||||
})
|
})
|
||||||
// Don't process health bars outside the vd (visibility further limited by ui backend)
|
// Don't process health bars outside the vd (visibility further limited by ui backend)
|
||||||
.filter(|(_, pos, _, _)| {
|
.filter(|(_, pos, _, _, _, _)| {
|
||||||
Vec2::from(pos.0 - player_pos)
|
Vec2::from(pos.0 - player_pos)
|
||||||
.map2(TerrainChunk::RECT_SIZE, |d: f32, sz| {
|
.map2(TerrainChunk::RECT_SIZE, |d: f32, sz| {
|
||||||
d.abs() as f32 / sz as f32
|
d.abs() as f32 / sz as f32
|
||||||
@ -575,9 +641,15 @@ impl Hud {
|
|||||||
.magnitude()
|
.magnitude()
|
||||||
< view_distance as f32
|
< view_distance as f32
|
||||||
})
|
})
|
||||||
|
.map(|(_, pos, interpolated, stats, scale, f)| {
|
||||||
|
(
|
||||||
|
interpolated.map_or(pos.0, |i| i.pos),
|
||||||
|
stats,
|
||||||
|
scale.map_or(1.0, |s| s.0),
|
||||||
|
f,
|
||||||
|
)
|
||||||
|
})
|
||||||
{
|
{
|
||||||
let scale = scale.map(|s| s.0).unwrap_or(1.0);
|
|
||||||
|
|
||||||
let back_id = health_back_id_walker.next(
|
let back_id = health_back_id_walker.next(
|
||||||
&mut self.ids.health_bar_backs,
|
&mut self.ids.health_bar_backs,
|
||||||
&mut ui_widgets.widget_id_generator(),
|
&mut ui_widgets.widget_id_generator(),
|
||||||
@ -596,20 +668,22 @@ impl Hud {
|
|||||||
);
|
);
|
||||||
let hp_percentage =
|
let hp_percentage =
|
||||||
stats.health.current() as f64 / stats.health.maximum() as f64 * 100.0;
|
stats.health.current() as f64 / stats.health.maximum() as f64 * 100.0;
|
||||||
|
let energy_percentage =
|
||||||
|
stats.energy.current() as f64 / stats.energy.maximum() as f64 * 100.0;
|
||||||
let hp_ani = (self.pulse * 4.0/*speed factor*/).cos() * 0.5 + 1.0; //Animation timer
|
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);
|
let crit_hp_color: Color = Color::Rgba(0.79, 0.19, 0.17, hp_ani);
|
||||||
|
|
||||||
// Background
|
// Background
|
||||||
Rectangle::fill_with([82.0, 8.0], Color::Rgba(0.3, 0.3, 0.3, 0.5))
|
Rectangle::fill_with([82.0, 8.0], Color::Rgba(0.3, 0.3, 0.3, 0.5))
|
||||||
.x_y(0.0, -25.0)
|
.x_y(0.0, -25.0)
|
||||||
.position_ingame(pos.0 + Vec3::new(0.0, 0.0, 1.5 * scale + 1.5))
|
.position_ingame(pos + Vec3::new(0.0, 0.0, 1.5 * scale + 1.5))
|
||||||
.resolution(100.0)
|
.resolution(100.0)
|
||||||
.set(back_id, ui_widgets);
|
.set(back_id, ui_widgets);
|
||||||
|
|
||||||
// % HP Filling
|
// % HP Filling
|
||||||
Image::new(self.imgs.bar_content)
|
Image::new(self.imgs.enemy_bar)
|
||||||
.w_h(72.9 * (hp_percentage / 100.0), 5.9)
|
.w_h(72.9 * (hp_percentage / 100.0), 5.9)
|
||||||
.x_y(4.5, -24.0)
|
.x_y(4.5 + (hp_percentage / 100.0 * 36.45) - 36.45, -24.0)
|
||||||
.color(Some(if hp_percentage <= 25.0 {
|
.color(Some(if hp_percentage <= 25.0 {
|
||||||
crit_hp_color
|
crit_hp_color
|
||||||
} else if hp_percentage <= 50.0 {
|
} else if hp_percentage <= 50.0 {
|
||||||
@ -617,7 +691,7 @@ impl Hud {
|
|||||||
} else {
|
} else {
|
||||||
HP_COLOR
|
HP_COLOR
|
||||||
}))
|
}))
|
||||||
.position_ingame(pos.0 + Vec3::new(0.0, 0.0, 1.5 * scale + 1.5))
|
.position_ingame(pos + Vec3::new(0.0, 0.0, 1.5 * scale + 1.5))
|
||||||
.resolution(100.0)
|
.resolution(100.0)
|
||||||
.set(health_bar_id, ui_widgets);
|
.set(health_bar_id, ui_widgets);
|
||||||
// % Mana Filling
|
// % Mana Filling
|
||||||
@ -628,8 +702,8 @@ impl Hud {
|
|||||||
],
|
],
|
||||||
MANA_COLOR,
|
MANA_COLOR,
|
||||||
)
|
)
|
||||||
.x_y(4.5, -28.0)
|
.x_y(4.5 + (energy_percentage / 100.0 * 36.5) - 36.45, -28.0)
|
||||||
.position_ingame(pos.0 + Vec3::new(0.0, 0.0, 1.5 * scale + 1.5))
|
.position_ingame(pos + Vec3::new(0.0, 0.0, 1.5 * scale + 1.5))
|
||||||
.resolution(100.0)
|
.resolution(100.0)
|
||||||
.set(mana_bar_id, ui_widgets);
|
.set(mana_bar_id, ui_widgets);
|
||||||
|
|
||||||
@ -638,42 +712,426 @@ impl Hud {
|
|||||||
.w_h(84.0, 10.0)
|
.w_h(84.0, 10.0)
|
||||||
.x_y(0.0, -25.0)
|
.x_y(0.0, -25.0)
|
||||||
.color(Some(Color::Rgba(1.0, 1.0, 1.0, 0.99)))
|
.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))
|
.position_ingame(pos + Vec3::new(0.0, 0.0, 1.5 * scale + 1.5))
|
||||||
.resolution(100.0)
|
.resolution(100.0)
|
||||||
.set(front_id, ui_widgets);
|
.set(front_id, ui_widgets);
|
||||||
|
|
||||||
|
// Enemy SCT
|
||||||
|
if let Some(HpFloaterList { floaters, .. }) = hp_floater_list
|
||||||
|
.filter(|fl| !fl.floaters.is_empty() && global_state.settings.gameplay.sct)
|
||||||
|
{
|
||||||
|
// Colors
|
||||||
|
const WHITE: Rgb<f32> = Rgb::new(1.0, 0.9, 0.8);
|
||||||
|
const LIGHT_OR: Rgb<f32> = Rgb::new(1.0, 0.925, 0.749);
|
||||||
|
const LIGHT_MED_OR: Rgb<f32> = Rgb::new(1.0, 0.85, 0.498);
|
||||||
|
const MED_OR: Rgb<f32> = Rgb::new(1.0, 0.776, 0.247);
|
||||||
|
const DARK_ORANGE: Rgb<f32> = Rgb::new(1.0, 0.7, 0.0);
|
||||||
|
const RED_ORANGE: Rgb<f32> = Rgb::new(1.0, 0.349, 0.0);
|
||||||
|
const DAMAGE_COLORS: [Rgb<f32>; 6] = [
|
||||||
|
WHITE,
|
||||||
|
LIGHT_OR,
|
||||||
|
LIGHT_MED_OR,
|
||||||
|
MED_OR,
|
||||||
|
DARK_ORANGE,
|
||||||
|
RED_ORANGE,
|
||||||
|
];
|
||||||
|
// Largest value that select the first color is 40, then it shifts colors
|
||||||
|
// every 5
|
||||||
|
let font_col = |font_size: u32| {
|
||||||
|
DAMAGE_COLORS[(font_size.saturating_sub(36) / 5).min(5) as usize]
|
||||||
|
};
|
||||||
|
|
||||||
|
if global_state.settings.gameplay.sct_damage_batch {
|
||||||
|
let number_speed = 50.0; // Damage number speed
|
||||||
|
let sct_bg_id = sct_bg_id_walker
|
||||||
|
.next(&mut self.ids.sct_bgs, &mut ui_widgets.widget_id_generator());
|
||||||
|
let sct_id = sct_id_walker
|
||||||
|
.next(&mut self.ids.scts, &mut ui_widgets.widget_id_generator());
|
||||||
|
// Calculate total change
|
||||||
|
// Ignores healing
|
||||||
|
let hp_damage = floaters.iter().fold(0, |acc, f| {
|
||||||
|
if f.hp_change < 0 {
|
||||||
|
acc + f.hp_change
|
||||||
|
} else {
|
||||||
|
acc
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let max_hp_frac = hp_damage.abs() as f32 / stats.health.maximum() as f32;
|
||||||
|
let timer = floaters
|
||||||
|
.last()
|
||||||
|
.expect("There must be at least one floater")
|
||||||
|
.timer;
|
||||||
|
// Increase font size based on fraction of maximum health
|
||||||
|
// "flashes" by having a larger size in the first 100ms
|
||||||
|
let font_size = 30
|
||||||
|
+ (max_hp_frac * 30.0) as u32
|
||||||
|
+ if timer < 0.1 {
|
||||||
|
(FLASH_MAX * (1.0 - timer / 0.1)) as u32
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
};
|
||||||
|
let font_col = font_col(font_size);
|
||||||
|
// Timer sets the widget offset
|
||||||
|
let y = (timer as f64 / crate::ecs::sys::floater::HP_SHOWTIME as f64
|
||||||
|
* number_speed)
|
||||||
|
+ 30.0;
|
||||||
|
// Timer sets text transparency
|
||||||
|
let fade = ((crate::ecs::sys::floater::HP_SHOWTIME - timer) * 0.25) + 0.2;
|
||||||
|
|
||||||
|
Text::new(&format!("{}", (hp_damage).abs()))
|
||||||
|
.font_size(font_size)
|
||||||
|
.color(Color::Rgba(0.0, 0.0, 0.0, fade))
|
||||||
|
.x_y(0.0, y - 3.0)
|
||||||
|
.position_ingame(pos + Vec3::new(0.0, 0.0, 1.5 * scale as f32 + 1.8))
|
||||||
|
.fixed_scale()
|
||||||
|
.resolution(100.0)
|
||||||
|
.set(sct_bg_id, ui_widgets);
|
||||||
|
Text::new(&format!("{}", hp_damage.abs()))
|
||||||
|
.font_size(font_size)
|
||||||
|
.x_y(0.0, y)
|
||||||
|
.color(if hp_damage < 0 {
|
||||||
|
Color::Rgba(font_col.r, font_col.g, font_col.b, fade)
|
||||||
|
} else {
|
||||||
|
Color::Rgba(0.1, 1.0, 0.1, fade)
|
||||||
|
})
|
||||||
|
.position_ingame(pos + Vec3::new(0.0, 0.0, 1.5 * scale as f32 + 1.8))
|
||||||
|
.fixed_scale()
|
||||||
|
.resolution(100.0)
|
||||||
|
.set(sct_id, ui_widgets);
|
||||||
|
} else {
|
||||||
|
for floater in floaters {
|
||||||
|
let number_speed = 250.0; // Single Numbers Speed
|
||||||
|
let sct_bg_id = sct_bg_id_walker
|
||||||
|
.next(&mut self.ids.sct_bgs, &mut ui_widgets.widget_id_generator());
|
||||||
|
let sct_id = sct_id_walker
|
||||||
|
.next(&mut self.ids.scts, &mut ui_widgets.widget_id_generator());
|
||||||
|
// Calculate total change
|
||||||
|
let max_hp_frac =
|
||||||
|
floater.hp_change.abs() as f32 / stats.health.maximum() as f32;
|
||||||
|
// Increase font size based on fraction of maximum health
|
||||||
|
// "flashes" by having a larger size in the first 100ms
|
||||||
|
let font_size = 30
|
||||||
|
+ (max_hp_frac * 30.0) as u32
|
||||||
|
+ if floater.timer < 0.1 {
|
||||||
|
(FLASH_MAX * (1.0 - floater.timer / 0.1)) as u32
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
};
|
||||||
|
let font_col = font_col(font_size);
|
||||||
|
// Timer sets the widget offset
|
||||||
|
let y = (floater.timer as f64
|
||||||
|
/ crate::ecs::sys::floater::HP_SHOWTIME as f64
|
||||||
|
* number_speed)
|
||||||
|
+ 30.0;
|
||||||
|
// Timer sets text transparency
|
||||||
|
let fade = ((crate::ecs::sys::floater::HP_SHOWTIME - floater.timer)
|
||||||
|
* 0.25)
|
||||||
|
+ 0.2;
|
||||||
|
|
||||||
|
Text::new(&format!("{}", (floater.hp_change).abs()))
|
||||||
|
.font_size(font_size)
|
||||||
|
.color(if floater.hp_change < 0 {
|
||||||
|
Color::Rgba(0.0, 0.0, 0.0, fade)
|
||||||
|
} else {
|
||||||
|
Color::Rgba(0.1, 1.0, 0.1, 0.0)
|
||||||
|
})
|
||||||
|
.x_y(0.0, y - 3.0)
|
||||||
|
.position_ingame(
|
||||||
|
pos + Vec3::new(0.0, 0.0, 1.5 * scale as f32 + 1.8),
|
||||||
|
)
|
||||||
|
.fixed_scale()
|
||||||
|
.resolution(100.0)
|
||||||
|
.set(sct_bg_id, ui_widgets);
|
||||||
|
Text::new(&format!("{}", (floater.hp_change).abs()))
|
||||||
|
.font_size(font_size)
|
||||||
|
.x_y(0.0, y)
|
||||||
|
.color(if floater.hp_change < 0 {
|
||||||
|
Color::Rgba(font_col.r, font_col.g, font_col.b, fade)
|
||||||
|
} else {
|
||||||
|
Color::Rgba(0.1, 1.0, 0.1, 0.0)
|
||||||
|
})
|
||||||
|
.position_ingame(
|
||||||
|
pos + Vec3::new(0.0, 0.0, 1.5 * scale as f32 + 1.8),
|
||||||
|
)
|
||||||
|
.fixed_scale()
|
||||||
|
.resolution(100.0)
|
||||||
|
.set(sct_id, ui_widgets);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if global_state.settings.gameplay.sct {
|
||||||
|
// Render Player SCT numbers
|
||||||
|
let mut player_sct_bg_id_walker = self.ids.player_sct_bgs.walk();
|
||||||
|
let mut player_sct_id_walker = self.ids.player_scts.walk();
|
||||||
|
if let (Some(HpFloaterList { floaters, .. }), Some(stats)) = (
|
||||||
|
hp_floater_lists
|
||||||
|
.get(me)
|
||||||
|
.filter(|fl| !fl.floaters.is_empty()),
|
||||||
|
stats.get(me),
|
||||||
|
) {
|
||||||
|
if global_state.settings.gameplay.sct_player_batch {
|
||||||
|
let number_speed = 100.0; // Player Batched Numbers Speed
|
||||||
|
let player_sct_bg_id = player_sct_bg_id_walker.next(
|
||||||
|
&mut self.ids.player_sct_bgs,
|
||||||
|
&mut ui_widgets.widget_id_generator(),
|
||||||
|
);
|
||||||
|
let player_sct_id = player_sct_id_walker.next(
|
||||||
|
&mut self.ids.player_scts,
|
||||||
|
&mut ui_widgets.widget_id_generator(),
|
||||||
|
);
|
||||||
|
// Calculate total change
|
||||||
|
// Ignores healing
|
||||||
|
let hp_damage = floaters.iter().fold(0, |acc, f| f.hp_change.min(0) + acc);
|
||||||
|
let max_hp_frac = hp_damage.abs() as f32 / stats.health.maximum() as f32;
|
||||||
|
let timer = floaters
|
||||||
|
.last()
|
||||||
|
.expect("There must be at least one floater")
|
||||||
|
.timer;
|
||||||
|
// Increase font size based on fraction of maximum health
|
||||||
|
// "flashes" by having a larger size in the first 100ms
|
||||||
|
let font_size = 30
|
||||||
|
+ (max_hp_frac * 30.0) as u32
|
||||||
|
+ if timer < 0.1 {
|
||||||
|
(FLASH_MAX * (1.0 - timer / 0.1)) as u32
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
};
|
||||||
|
// Timer sets the widget offset
|
||||||
|
let y = timer as f64 * number_speed * -1.0;
|
||||||
|
// Timer sets text transparency
|
||||||
|
let hp_fade =
|
||||||
|
((crate::ecs::sys::floater::MY_HP_SHOWTIME - timer) * 0.25) + 0.2;
|
||||||
|
Text::new(&format!("{}", (hp_damage).abs()))
|
||||||
|
.font_size(font_size)
|
||||||
|
.color(Color::Rgba(0.0, 0.0, 0.0, hp_fade))
|
||||||
|
.mid_bottom_with_margin_on(ui_widgets.window, 297.0 + y)
|
||||||
|
.set(player_sct_bg_id, ui_widgets);
|
||||||
|
Text::new(&format!("{}", (hp_damage).abs()))
|
||||||
|
.font_size(font_size)
|
||||||
|
.color(if hp_damage < 0 {
|
||||||
|
Color::Rgba(1.0, 0.1, 0.0, hp_fade)
|
||||||
|
} else {
|
||||||
|
Color::Rgba(0.1, 1.0, 0.1, 0.0)
|
||||||
|
})
|
||||||
|
.mid_bottom_with_margin_on(ui_widgets.window, 300.0 + y)
|
||||||
|
.set(player_sct_id, ui_widgets);
|
||||||
|
};
|
||||||
|
for floater in floaters {
|
||||||
|
// Healing always single numbers so just skip damage when in batch mode
|
||||||
|
|
||||||
|
if global_state.settings.gameplay.sct_player_batch && floater.hp_change < 0
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let number_speed = 50.0; // Player Heal Speed
|
||||||
|
let player_sct_bg_id = player_sct_bg_id_walker.next(
|
||||||
|
&mut self.ids.player_sct_bgs,
|
||||||
|
&mut ui_widgets.widget_id_generator(),
|
||||||
|
);
|
||||||
|
let player_sct_id = player_sct_id_walker.next(
|
||||||
|
&mut self.ids.player_scts,
|
||||||
|
&mut ui_widgets.widget_id_generator(),
|
||||||
|
);
|
||||||
|
let max_hp_frac =
|
||||||
|
floater.hp_change.abs() as f32 / stats.health.maximum() as f32;
|
||||||
|
// Increase font size based on fraction of maximum health
|
||||||
|
// "flashes" by having a larger size in the first 100ms
|
||||||
|
let font_size = 30
|
||||||
|
+ (max_hp_frac * 30.0) as u32
|
||||||
|
+ if floater.timer < 0.1 {
|
||||||
|
(FLASH_MAX * (1.0 - floater.timer / 0.1)) as u32
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
};
|
||||||
|
// Timer sets the widget offset
|
||||||
|
let y = if floater.hp_change < 0 {
|
||||||
|
floater.timer as f64
|
||||||
|
* number_speed
|
||||||
|
* floater.hp_change.signum() as f64
|
||||||
|
//* -1.0
|
||||||
|
+ 300.0
|
||||||
|
- ui_widgets.win_h * 0.5
|
||||||
|
} else {
|
||||||
|
floater.timer as f64
|
||||||
|
* number_speed
|
||||||
|
* floater.hp_change.signum() as f64
|
||||||
|
* -1.0
|
||||||
|
+ 300.0
|
||||||
|
- ui_widgets.win_h * 0.5
|
||||||
|
};
|
||||||
|
// Healing is offset randomly
|
||||||
|
let x = if floater.hp_change < 0 {
|
||||||
|
0.0
|
||||||
|
} else {
|
||||||
|
(floater.rand as f64 - 0.5) * 0.2 * ui_widgets.win_w
|
||||||
|
};
|
||||||
|
// Timer sets text transparency
|
||||||
|
let hp_fade = ((crate::ecs::sys::floater::MY_HP_SHOWTIME - floater.timer)
|
||||||
|
* 0.25)
|
||||||
|
+ 0.2;
|
||||||
|
Text::new(&format!("{}", (floater.hp_change).abs()))
|
||||||
|
.font_size(font_size)
|
||||||
|
.color(Color::Rgba(0.0, 0.0, 0.0, hp_fade))
|
||||||
|
.x_y(x, y - 3.0)
|
||||||
|
.set(player_sct_bg_id, ui_widgets);
|
||||||
|
Text::new(&format!("{}", (floater.hp_change).abs()))
|
||||||
|
.font_size(font_size)
|
||||||
|
.color(if floater.hp_change < 0 {
|
||||||
|
Color::Rgba(1.0, 0.1, 0.0, hp_fade)
|
||||||
|
} else {
|
||||||
|
Color::Rgba(0.1, 1.0, 0.1, hp_fade)
|
||||||
|
})
|
||||||
|
.x_y(x, y)
|
||||||
|
.set(player_sct_id, ui_widgets);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// EXP Numbers
|
||||||
|
if let (Some(floaters), Some(stats)) = (
|
||||||
|
Some(&*ecs.read_resource::<crate::ecs::MyExpFloaterList>())
|
||||||
|
.map(|l| &l.floaters)
|
||||||
|
.filter(|f| !f.is_empty()),
|
||||||
|
stats.get(me),
|
||||||
|
) {
|
||||||
|
// TODO replace with setting
|
||||||
|
let batched_sct = false;
|
||||||
|
if batched_sct {
|
||||||
|
let number_speed = 50.0; // Number Speed for Cumulated EXP
|
||||||
|
let player_sct_bg_id = player_sct_bg_id_walker.next(
|
||||||
|
&mut self.ids.player_sct_bgs,
|
||||||
|
&mut ui_widgets.widget_id_generator(),
|
||||||
|
);
|
||||||
|
let player_sct_id = player_sct_id_walker.next(
|
||||||
|
&mut self.ids.player_scts,
|
||||||
|
&mut ui_widgets.widget_id_generator(),
|
||||||
|
);
|
||||||
|
// Sum xp change
|
||||||
|
let exp_change = floaters.iter().fold(0, |acc, f| f.exp_change + acc);
|
||||||
|
// Can't fail since we filtered out empty lists above
|
||||||
|
let (timer, rand) = floaters
|
||||||
|
.last()
|
||||||
|
.map(|f| (f.timer, f.rand))
|
||||||
|
.expect("Impossible");
|
||||||
|
// Increase font size based on fraction of maximum health
|
||||||
|
// "flashes" by having a larger size in the first 100ms
|
||||||
|
let font_size_xp = 30
|
||||||
|
+ (exp_change.abs() as f32 / stats.exp.maximum() as f32 * 50.0) as u32
|
||||||
|
+ if timer < 0.1 {
|
||||||
|
(FLASH_MAX * (1.0 - timer / 0.1)) as u32
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
};
|
||||||
|
|
||||||
|
let y = timer as f64 * number_speed; // Timer sets the widget offset
|
||||||
|
let fade = ((4.0 - timer as f32) * 0.25) + 0.2; // Timer sets text transparency
|
||||||
|
|
||||||
|
Text::new(&format!("{} Exp", exp_change))
|
||||||
|
.font_size(font_size_xp)
|
||||||
|
.color(Color::Rgba(0.0, 0.0, 0.0, fade))
|
||||||
|
.x_y(
|
||||||
|
ui_widgets.win_w * (0.5 * rand.0 as f64 - 0.25),
|
||||||
|
ui_widgets.win_h * (0.15 * rand.1 as f64) + y - 3.0,
|
||||||
|
)
|
||||||
|
.set(player_sct_bg_id, ui_widgets);
|
||||||
|
Text::new(&format!("{} Exp", exp_change))
|
||||||
|
.font_size(font_size_xp)
|
||||||
|
.color(Color::Rgba(0.59, 0.41, 0.67, fade))
|
||||||
|
.x_y(
|
||||||
|
ui_widgets.win_w * (0.5 * rand.0 as f64 - 0.25),
|
||||||
|
ui_widgets.win_h * (0.15 * rand.1 as f64) + y,
|
||||||
|
)
|
||||||
|
.set(player_sct_id, ui_widgets);
|
||||||
|
} else {
|
||||||
|
for floater in floaters {
|
||||||
|
let number_speed = 50.0; // Number Speed for Single EXP
|
||||||
|
let player_sct_bg_id = player_sct_bg_id_walker.next(
|
||||||
|
&mut self.ids.player_sct_bgs,
|
||||||
|
&mut ui_widgets.widget_id_generator(),
|
||||||
|
);
|
||||||
|
let player_sct_id = player_sct_id_walker.next(
|
||||||
|
&mut self.ids.player_scts,
|
||||||
|
&mut ui_widgets.widget_id_generator(),
|
||||||
|
);
|
||||||
|
// Increase font size based on fraction of maximum health
|
||||||
|
// "flashes" by having a larger size in the first 100ms
|
||||||
|
let font_size_xp = 30
|
||||||
|
+ (floater.exp_change.abs() as f32 / stats.exp.maximum() as f32
|
||||||
|
* 50.0) as u32
|
||||||
|
+ if floater.timer < 0.1 {
|
||||||
|
(FLASH_MAX * (1.0 - floater.timer / 0.1)) as u32
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
};
|
||||||
|
|
||||||
|
let y = floater.timer as f64 * number_speed; // Timer sets the widget offset
|
||||||
|
let fade = ((4.0 - floater.timer as f32) * 0.25) + 0.2; // Timer sets text transparency
|
||||||
|
|
||||||
|
Text::new(&format!("{} Exp", floater.exp_change))
|
||||||
|
.font_size(font_size_xp)
|
||||||
|
.color(Color::Rgba(0.0, 0.0, 0.0, fade))
|
||||||
|
.x_y(
|
||||||
|
ui_widgets.win_w * (0.5 * floater.rand.0 as f64 - 0.25),
|
||||||
|
ui_widgets.win_h * (0.15 * floater.rand.1 as f64) + y - 3.0,
|
||||||
|
)
|
||||||
|
.set(player_sct_bg_id, ui_widgets);
|
||||||
|
Text::new(&format!("{} Exp", floater.exp_change))
|
||||||
|
.font_size(font_size_xp)
|
||||||
|
.color(Color::Rgba(0.59, 0.41, 0.67, fade))
|
||||||
|
.x_y(
|
||||||
|
ui_widgets.win_w * (0.5 * floater.rand.0 as f64 - 0.25),
|
||||||
|
ui_widgets.win_h * (0.15 * floater.rand.1 as f64) + y,
|
||||||
|
)
|
||||||
|
.set(player_sct_id, ui_widgets);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Render Name Tags
|
// Render Name Tags
|
||||||
for (pos, name, level, scale) in
|
for (pos, name, level, scale) in (
|
||||||
(&entities, &pos, &stats, players.maybe(), scales.maybe())
|
&entities,
|
||||||
.join()
|
&pos,
|
||||||
.filter(|(entity, _, stats, _, _)| *entity != me && !stats.is_dead)
|
interpolated.maybe(),
|
||||||
// Don't process nametags outside the vd (visibility further limited by ui backend)
|
&stats,
|
||||||
.filter(|(_, pos, _, _, _)| {
|
players.maybe(),
|
||||||
Vec2::from(pos.0 - player_pos)
|
scales.maybe(),
|
||||||
.map2(TerrainChunk::RECT_SIZE, |d: f32, sz| {
|
)
|
||||||
d.abs() as f32 / sz as f32
|
.join()
|
||||||
})
|
.filter(|(entity, _, _, stats, _, _)| *entity != me && !stats.is_dead)
|
||||||
.magnitude()
|
// Don't process nametags outside the vd (visibility further limited by ui backend)
|
||||||
< view_distance as f32
|
.filter(|(_, pos, _, _, _, _)| {
|
||||||
})
|
Vec2::from(pos.0 - player_pos)
|
||||||
.map(|(_, pos, stats, player, scale)| {
|
.map2(TerrainChunk::RECT_SIZE, |d: f32, sz| {
|
||||||
// TODO: This is temporary
|
d.abs() as f32 / sz as f32
|
||||||
// If the player used the default character name display their name instead
|
})
|
||||||
let name = if stats.name == "Character Name" {
|
.magnitude()
|
||||||
player.map_or(&stats.name, |p| &p.alias)
|
< view_distance as f32
|
||||||
} else {
|
})
|
||||||
&stats.name
|
.map(|(_, pos, interpolated, stats, player, scale)| {
|
||||||
};
|
// TODO: This is temporary
|
||||||
(pos.0, name, stats.level, scale)
|
// If the player used the default character name display their name instead
|
||||||
})
|
let name = if stats.name == "Character Name" {
|
||||||
|
player.map_or(&stats.name, |p| &p.alias)
|
||||||
|
} else {
|
||||||
|
&stats.name
|
||||||
|
};
|
||||||
|
(
|
||||||
|
interpolated.map_or(pos.0, |i| i.pos),
|
||||||
|
format!("{}", name),
|
||||||
|
stats.level,
|
||||||
|
scale.map_or(1.0, |s| s.0),
|
||||||
|
)
|
||||||
|
})
|
||||||
{
|
{
|
||||||
let name = format!("{}", name);
|
|
||||||
let scale = scale.map(|s| s.0).unwrap_or(1.0);
|
|
||||||
let name_id = name_id_walker.next(
|
let name_id = name_id_walker.next(
|
||||||
&mut self.ids.name_tags,
|
&mut self.ids.name_tags,
|
||||||
&mut ui_widgets.widget_id_generator(),
|
&mut ui_widgets.widget_id_generator(),
|
||||||
);
|
);
|
||||||
|
let name_bg_id = name_id_bg_walker.next(
|
||||||
|
&mut self.ids.name_tags_bgs,
|
||||||
|
&mut ui_widgets.widget_id_generator(),
|
||||||
|
);
|
||||||
let level_id = level_id_walker
|
let level_id = level_id_walker
|
||||||
.next(&mut self.ids.levels, &mut ui_widgets.widget_id_generator());
|
.next(&mut self.ids.levels, &mut ui_widgets.widget_id_generator());
|
||||||
let level_skull_id = level_skull_id_walker.next(
|
let level_skull_id = level_skull_id_walker.next(
|
||||||
@ -682,6 +1140,13 @@ impl Hud {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Name
|
// Name
|
||||||
|
Text::new(&name)
|
||||||
|
.font_size(20)
|
||||||
|
.color(Color::Rgba(0.0, 0.0, 0.0, 1.0))
|
||||||
|
.x_y(-1.0, -1.0)
|
||||||
|
.position_ingame(pos + Vec3::new(0.0, 0.0, 1.5 * scale + 1.5))
|
||||||
|
.resolution(100.0)
|
||||||
|
.set(name_bg_id, ui_widgets);
|
||||||
Text::new(&name)
|
Text::new(&name)
|
||||||
.font_size(20)
|
.font_size(20)
|
||||||
.color(Color::Rgba(0.61, 0.61, 0.89, 1.0))
|
.color(Color::Rgba(0.61, 0.61, 0.89, 1.0))
|
||||||
@ -726,15 +1191,16 @@ impl Hud {
|
|||||||
} else {
|
} else {
|
||||||
self.imgs.skull
|
self.imgs.skull
|
||||||
})
|
})
|
||||||
.w_h(30.0, 30.0)
|
.w_h(18.0, 18.0)
|
||||||
.x_y(0.0, 24.0)
|
.x_y(-39.0, -25.0)
|
||||||
.color(Some(Color::Rgba(1.0, 1.0, 1.0, 0.8)))
|
.color(Some(Color::Rgba(1.0, 1.0, 1.0, 1.0)))
|
||||||
.position_ingame(pos + Vec3::new(0.0, 0.0, 1.5 * scale + 1.5))
|
.position_ingame(pos + Vec3::new(0.0, 0.0, 1.5 * scale + 1.5))
|
||||||
.resolution(100.0)
|
.resolution(100.0)
|
||||||
.set(level_skull_id, ui_widgets);
|
.set(level_skull_id, ui_widgets);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Introduction Text
|
// Introduction Text
|
||||||
let intro_text: &'static str =
|
let intro_text: &'static str =
|
||||||
"Welcome to the Veloren Alpha!\n\
|
"Welcome to the Veloren Alpha!\n\
|
||||||
@ -906,7 +1372,7 @@ impl Hud {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Display debug window.
|
// Display debug window.
|
||||||
if self.show.debug {
|
if global_state.settings.gameplay.toggle_debug {
|
||||||
// Alpha Version
|
// Alpha Version
|
||||||
Text::new(&version)
|
Text::new(&version)
|
||||||
.top_left_with_margins_on(ui_widgets.window, 5.0, 5.0)
|
.top_left_with_margins_on(ui_widgets.window, 5.0, 5.0)
|
||||||
@ -1018,12 +1484,46 @@ impl Hud {
|
|||||||
.set(self.ids.num_figures, ui_widgets);
|
.set(self.ids.num_figures, ui_widgets);
|
||||||
|
|
||||||
// Help Window
|
// Help Window
|
||||||
Text::new("Press 'F1' to show Keybindings")
|
Text::new(&format!(
|
||||||
.color(TEXT_COLOR)
|
"Press {:?} to show keybindings",
|
||||||
.down_from(self.ids.num_figures, 5.0)
|
global_state.settings.controls.help
|
||||||
.font_id(self.fonts.cyri)
|
))
|
||||||
.font_size(14)
|
.color(TEXT_COLOR)
|
||||||
.set(self.ids.help_info, ui_widgets);
|
.down_from(self.ids.num_figures, 5.0)
|
||||||
|
.font_id(self.fonts.cyri)
|
||||||
|
.font_size(14)
|
||||||
|
.set(self.ids.help_info, ui_widgets);
|
||||||
|
// Info about Debug Shortcut
|
||||||
|
Text::new(&format!(
|
||||||
|
"Press {:?} to toggle debug info",
|
||||||
|
global_state.settings.controls.toggle_debug
|
||||||
|
))
|
||||||
|
.color(TEXT_COLOR)
|
||||||
|
.down_from(self.ids.help_info, 5.0)
|
||||||
|
.font_id(self.fonts.cyri)
|
||||||
|
.font_size(14)
|
||||||
|
.set(self.ids.debug_info, ui_widgets);
|
||||||
|
} else {
|
||||||
|
// Help Window
|
||||||
|
Text::new(&format!(
|
||||||
|
"Press {:?} to show keybindings",
|
||||||
|
global_state.settings.controls.help
|
||||||
|
))
|
||||||
|
.color(TEXT_COLOR)
|
||||||
|
.top_left_with_margins_on(ui_widgets.window, 5.0, 5.0)
|
||||||
|
.font_id(self.fonts.cyri)
|
||||||
|
.font_size(16)
|
||||||
|
.set(self.ids.help_info, ui_widgets);
|
||||||
|
// Info about Debug Shortcut
|
||||||
|
Text::new(&format!(
|
||||||
|
"Press {:?} to toggle debug info",
|
||||||
|
global_state.settings.controls.toggle_debug
|
||||||
|
))
|
||||||
|
.color(TEXT_COLOR)
|
||||||
|
.down_from(self.ids.help_info, 5.0)
|
||||||
|
.font_id(self.fonts.cyri)
|
||||||
|
.font_size(12)
|
||||||
|
.set(self.ids.debug_info, ui_widgets);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add Bag-Space Button.
|
// Add Bag-Space Button.
|
||||||
@ -1185,6 +1685,15 @@ impl Hud {
|
|||||||
.set(self.ids.settings_window, ui_widgets)
|
.set(self.ids.settings_window, ui_widgets)
|
||||||
{
|
{
|
||||||
match event {
|
match event {
|
||||||
|
settings_window::Event::Sct(sct) => {
|
||||||
|
events.push(Event::Sct(sct));
|
||||||
|
}
|
||||||
|
settings_window::Event::SctPlayerBatch(sct_player_batch) => {
|
||||||
|
events.push(Event::SctPlayerBatch(sct_player_batch));
|
||||||
|
}
|
||||||
|
settings_window::Event::SctDamageBatch(sct_damage_batch) => {
|
||||||
|
events.push(Event::SctDamageBatch(sct_damage_batch));
|
||||||
|
}
|
||||||
settings_window::Event::ToggleHelp => self.show.help = !self.show.help,
|
settings_window::Event::ToggleHelp => self.show.help = !self.show.help,
|
||||||
settings_window::Event::ToggleDebug => self.show.debug = !self.show.debug,
|
settings_window::Event::ToggleDebug => self.show.debug = !self.show.debug,
|
||||||
settings_window::Event::ChangeTab(tab) => self.show.open_setting_tab(tab),
|
settings_window::Event::ChangeTab(tab) => self.show.open_setting_tab(tab),
|
||||||
@ -1319,6 +1828,7 @@ impl Hud {
|
|||||||
self.world_map,
|
self.world_map,
|
||||||
&self.fonts,
|
&self.fonts,
|
||||||
self.pulse,
|
self.pulse,
|
||||||
|
self.velocity,
|
||||||
)
|
)
|
||||||
.set(self.ids.map, ui_widgets)
|
.set(self.ids.map, ui_widgets)
|
||||||
{
|
{
|
||||||
@ -1471,7 +1981,8 @@ impl Hud {
|
|||||||
true
|
true
|
||||||
}
|
}
|
||||||
GameInput::ToggleDebug => {
|
GameInput::ToggleDebug => {
|
||||||
self.show.debug = !self.show.debug;
|
global_state.settings.gameplay.toggle_debug =
|
||||||
|
!global_state.settings.gameplay.toggle_debug;
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
GameInput::ToggleIngameUi => {
|
GameInput::ToggleIngameUi => {
|
||||||
|
@ -18,6 +18,7 @@ const FPS_CHOICES: [u32; 11] = [15, 30, 40, 50, 60, 90, 120, 144, 240, 300, 500]
|
|||||||
widget_ids! {
|
widget_ids! {
|
||||||
struct Ids {
|
struct Ids {
|
||||||
settings_content,
|
settings_content,
|
||||||
|
settings_content_r,
|
||||||
settings_icon,
|
settings_icon,
|
||||||
settings_button_mo,
|
settings_button_mo,
|
||||||
settings_close,
|
settings_close,
|
||||||
@ -109,7 +110,24 @@ widget_ids! {
|
|||||||
placeholder,
|
placeholder,
|
||||||
chat_transp_title,
|
chat_transp_title,
|
||||||
chat_transp_text,
|
chat_transp_text,
|
||||||
chat_transp_slider
|
chat_transp_slider,
|
||||||
|
sct_title,
|
||||||
|
sct_show_text,
|
||||||
|
sct_show_radio,
|
||||||
|
sct_single_dmg_text,
|
||||||
|
sct_single_dmg_radio,
|
||||||
|
sct_show_batch_text,
|
||||||
|
sct_show_batch_radio,
|
||||||
|
sct_batched_dmg_text,
|
||||||
|
sct_batched_dmg_radio,
|
||||||
|
sct_inc_dmg_text,
|
||||||
|
sct_inc_dmg_radio,
|
||||||
|
sct_batch_inc_text,
|
||||||
|
sct_batch_inc_radio,
|
||||||
|
sct_num_dur_text,
|
||||||
|
sct_num_dur_slider,
|
||||||
|
sct_num_dur_value,
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -178,6 +196,9 @@ pub enum Event {
|
|||||||
CrosshairType(CrosshairType),
|
CrosshairType(CrosshairType),
|
||||||
UiScale(ScaleChange),
|
UiScale(ScaleChange),
|
||||||
ChatTransp(f32),
|
ChatTransp(f32),
|
||||||
|
Sct(bool),
|
||||||
|
SctPlayerBatch(bool),
|
||||||
|
SctDamageBatch(bool),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum ScaleChange {
|
pub enum ScaleChange {
|
||||||
@ -229,6 +250,10 @@ impl<'a> Widget for SettingsWindow<'a> {
|
|||||||
.scroll_kids()
|
.scroll_kids()
|
||||||
.scroll_kids_vertically()
|
.scroll_kids_vertically()
|
||||||
.set(state.ids.settings_content, ui);
|
.set(state.ids.settings_content, ui);
|
||||||
|
Rectangle::fill_with([198.0 * 4.0 * 0.5, 97.0 * 4.0], color::TRANSPARENT)
|
||||||
|
.top_right_with_margins_on(state.ids.settings_content, 0.0, 0.0)
|
||||||
|
.parent(state.ids.settings_content)
|
||||||
|
.set(state.ids.settings_content_r, ui);
|
||||||
Scrollbar::y_axis(state.ids.settings_content)
|
Scrollbar::y_axis(state.ids.settings_content)
|
||||||
.thickness(5.0)
|
.thickness(5.0)
|
||||||
.rgba(0.33, 0.33, 0.33, 1.0)
|
.rgba(0.33, 0.33, 0.33, 1.0)
|
||||||
@ -279,7 +304,7 @@ impl<'a> Widget for SettingsWindow<'a> {
|
|||||||
events.push(Event::ChangeTab(SettingsTab::Interface));
|
events.push(Event::ChangeTab(SettingsTab::Interface));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Contents
|
// Contents Left Side
|
||||||
if let SettingsTab::Interface = self.show.settings_tab {
|
if let SettingsTab::Interface = self.show.settings_tab {
|
||||||
let crosshair_transp = self.global_state.settings.gameplay.crosshair_transp;
|
let crosshair_transp = self.global_state.settings.gameplay.crosshair_transp;
|
||||||
let crosshair_type = self.global_state.settings.gameplay.crosshair_type;
|
let crosshair_type = self.global_state.settings.gameplay.crosshair_type;
|
||||||
@ -309,7 +334,7 @@ impl<'a> Widget for SettingsWindow<'a> {
|
|||||||
events.push(Event::ToggleHelp);
|
events.push(Event::ToggleHelp);
|
||||||
}
|
}
|
||||||
|
|
||||||
Text::new("Show Help Window")
|
Text::new("Help Window")
|
||||||
.right_from(state.ids.button_help, 10.0)
|
.right_from(state.ids.button_help, 10.0)
|
||||||
.font_size(14)
|
.font_size(14)
|
||||||
.font_id(self.fonts.cyri)
|
.font_id(self.fonts.cyri)
|
||||||
@ -333,7 +358,7 @@ impl<'a> Widget for SettingsWindow<'a> {
|
|||||||
events.push(Event::ToggleDebug);
|
events.push(Event::ToggleDebug);
|
||||||
}
|
}
|
||||||
|
|
||||||
Text::new("Show Debug Info")
|
Text::new("Debug Info")
|
||||||
.right_from(state.ids.debug_button, 10.0)
|
.right_from(state.ids.debug_button, 10.0)
|
||||||
.font_size(14)
|
.font_size(14)
|
||||||
.font_id(self.fonts.cyri)
|
.font_id(self.fonts.cyri)
|
||||||
@ -360,7 +385,7 @@ impl<'a> Widget for SettingsWindow<'a> {
|
|||||||
Intro::Never => events.push(Event::Intro(Intro::Show)),
|
Intro::Never => events.push(Event::Intro(Intro::Show)),
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
Text::new("Show Tips on Startup")
|
Text::new("Tips on Startup")
|
||||||
.right_from(state.ids.tips_button, 10.0)
|
.right_from(state.ids.tips_button, 10.0)
|
||||||
.font_size(14)
|
.font_size(14)
|
||||||
.font_id(self.fonts.cyri)
|
.font_id(self.fonts.cyri)
|
||||||
@ -683,7 +708,7 @@ impl<'a> Widget for SettingsWindow<'a> {
|
|||||||
XpBar::OnGain => events.push(Event::ToggleXpBar(XpBar::Always)),
|
XpBar::OnGain => events.push(Event::ToggleXpBar(XpBar::Always)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Text::new("Always show Experience Bar")
|
Text::new("Toggle Experience Bar")
|
||||||
.right_from(state.ids.show_xpbar_button, 10.0)
|
.right_from(state.ids.show_xpbar_button, 10.0)
|
||||||
.font_size(14)
|
.font_size(14)
|
||||||
.font_id(self.fonts.cyri)
|
.font_id(self.fonts.cyri)
|
||||||
@ -717,7 +742,7 @@ impl<'a> Widget for SettingsWindow<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Text::new("Always show Shortcuts")
|
Text::new("Toggle Shortcuts")
|
||||||
.right_from(state.ids.show_shortcuts_button, 10.0)
|
.right_from(state.ids.show_shortcuts_button, 10.0)
|
||||||
.font_size(14)
|
.font_size(14)
|
||||||
.font_id(self.fonts.cyri)
|
.font_id(self.fonts.cyri)
|
||||||
@ -725,10 +750,151 @@ impl<'a> Widget for SettingsWindow<'a> {
|
|||||||
.color(TEXT_COLOR)
|
.color(TEXT_COLOR)
|
||||||
.set(state.ids.show_shortcuts_text, ui);
|
.set(state.ids.show_shortcuts_text, ui);
|
||||||
|
|
||||||
|
Rectangle::fill_with([60.0 * 4.0, 1.0 * 4.0], color::TRANSPARENT)
|
||||||
|
.down_from(state.ids.show_shortcuts_text, 30.0)
|
||||||
|
.set(state.ids.placeholder, ui);
|
||||||
|
|
||||||
|
// Content Right Side
|
||||||
|
|
||||||
|
/*Scrolling Combat text
|
||||||
|
|
||||||
|
O Show Damage Numbers
|
||||||
|
O Show single Damage Numbers
|
||||||
|
O Show batched dealt Damage
|
||||||
|
O Show incoming Damage
|
||||||
|
O Batch incoming Numbers
|
||||||
|
|
||||||
|
Number Display Duration: 1s ----I----5s
|
||||||
|
*/
|
||||||
|
// SCT/ Scrolling Combat Text
|
||||||
|
Text::new("Scrolling Combat Text")
|
||||||
|
.top_left_with_margins_on(state.ids.settings_content_r, 5.0, 5.0)
|
||||||
|
.font_size(18)
|
||||||
|
.font_id(self.fonts.cyri)
|
||||||
|
.color(TEXT_COLOR)
|
||||||
|
.set(state.ids.sct_title, ui);
|
||||||
|
// Generally toggle the SCT
|
||||||
|
let show_sct = ToggleButton::new(
|
||||||
|
self.global_state.settings.gameplay.sct,
|
||||||
|
self.imgs.checkbox,
|
||||||
|
self.imgs.checkbox_checked,
|
||||||
|
)
|
||||||
|
.w_h(18.0, 18.0)
|
||||||
|
.down_from(state.ids.sct_title, 20.0)
|
||||||
|
.hover_images(self.imgs.checkbox_mo, self.imgs.checkbox_checked_mo)
|
||||||
|
.press_images(self.imgs.checkbox_press, self.imgs.checkbox_checked)
|
||||||
|
.set(state.ids.sct_show_radio, ui);
|
||||||
|
|
||||||
|
if self.global_state.settings.gameplay.sct != show_sct {
|
||||||
|
events.push(Event::Sct(!self.global_state.settings.gameplay.sct))
|
||||||
|
}
|
||||||
|
Text::new("Scrolling Combat Text")
|
||||||
|
.right_from(state.ids.sct_show_radio, 10.0)
|
||||||
|
.font_size(14)
|
||||||
|
.font_id(self.fonts.cyri)
|
||||||
|
.graphics_for(state.ids.sct_show_radio)
|
||||||
|
.color(TEXT_COLOR)
|
||||||
|
.set(state.ids.sct_show_text, ui);
|
||||||
|
if self.global_state.settings.gameplay.sct {
|
||||||
|
// Toggle single damage numbers
|
||||||
|
let show_sct_damage_batch = !ToggleButton::new(
|
||||||
|
!self.global_state.settings.gameplay.sct_damage_batch,
|
||||||
|
self.imgs.checkbox,
|
||||||
|
self.imgs.checkbox_checked,
|
||||||
|
)
|
||||||
|
.w_h(18.0, 18.0)
|
||||||
|
.down_from(state.ids.sct_show_text, 8.0)
|
||||||
|
.hover_images(self.imgs.checkbox_mo, self.imgs.checkbox_checked_mo)
|
||||||
|
.press_images(self.imgs.checkbox_press, self.imgs.checkbox_checked)
|
||||||
|
.set(state.ids.sct_single_dmg_radio, ui);
|
||||||
|
|
||||||
|
Text::new("Single Damage Numbers")
|
||||||
|
.right_from(state.ids.sct_single_dmg_radio, 10.0)
|
||||||
|
.font_size(14)
|
||||||
|
.font_id(self.fonts.cyri)
|
||||||
|
.graphics_for(state.ids.sct_single_dmg_radio)
|
||||||
|
.color(TEXT_COLOR)
|
||||||
|
.set(state.ids.sct_single_dmg_text, ui);
|
||||||
|
// Toggle Batched Damage
|
||||||
|
let show_sct_damage_batch = ToggleButton::new(
|
||||||
|
show_sct_damage_batch,
|
||||||
|
self.imgs.checkbox,
|
||||||
|
self.imgs.checkbox_checked,
|
||||||
|
)
|
||||||
|
.w_h(18.0, 18.0)
|
||||||
|
.down_from(state.ids.sct_single_dmg_radio, 8.0)
|
||||||
|
.hover_images(self.imgs.checkbox_mo, self.imgs.checkbox_checked_mo)
|
||||||
|
.press_images(self.imgs.checkbox_press, self.imgs.checkbox_checked)
|
||||||
|
.set(state.ids.sct_show_batch_radio, ui);
|
||||||
|
|
||||||
|
if self.global_state.settings.gameplay.sct_damage_batch != show_sct_damage_batch {
|
||||||
|
events.push(Event::SctDamageBatch(
|
||||||
|
!self.global_state.settings.gameplay.sct_damage_batch,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
Text::new("Cumulated Damage")
|
||||||
|
.right_from(state.ids.sct_show_batch_radio, 10.0)
|
||||||
|
.font_size(14)
|
||||||
|
.font_id(self.fonts.cyri)
|
||||||
|
.graphics_for(state.ids.sct_batched_dmg_radio)
|
||||||
|
.color(TEXT_COLOR)
|
||||||
|
.set(state.ids.sct_show_batch_text, ui);
|
||||||
|
// Toggle Incoming Damage
|
||||||
|
let show_sct_player_batch = !ToggleButton::new(
|
||||||
|
!self.global_state.settings.gameplay.sct_player_batch,
|
||||||
|
self.imgs.checkbox,
|
||||||
|
self.imgs.checkbox_checked,
|
||||||
|
)
|
||||||
|
.w_h(18.0, 18.0)
|
||||||
|
.down_from(state.ids.sct_show_batch_radio, 8.0)
|
||||||
|
.hover_images(self.imgs.checkbox_mo, self.imgs.checkbox_checked_mo)
|
||||||
|
.press_images(self.imgs.checkbox_press, self.imgs.checkbox_checked)
|
||||||
|
.set(state.ids.sct_inc_dmg_radio, ui);
|
||||||
|
|
||||||
|
Text::new("Incoming Damage")
|
||||||
|
.right_from(state.ids.sct_inc_dmg_radio, 10.0)
|
||||||
|
.font_size(14)
|
||||||
|
.font_id(self.fonts.cyri)
|
||||||
|
.graphics_for(state.ids.sct_inc_dmg_radio)
|
||||||
|
.color(TEXT_COLOR)
|
||||||
|
.set(state.ids.sct_inc_dmg_text, ui);
|
||||||
|
// Toggle Batched Incoming Damage
|
||||||
|
let show_sct_player_batch = ToggleButton::new(
|
||||||
|
show_sct_player_batch,
|
||||||
|
self.imgs.checkbox,
|
||||||
|
self.imgs.checkbox_checked,
|
||||||
|
)
|
||||||
|
.w_h(18.0, 18.0)
|
||||||
|
.down_from(state.ids.sct_inc_dmg_radio, 8.0)
|
||||||
|
.hover_images(self.imgs.checkbox_mo, self.imgs.checkbox_checked_mo)
|
||||||
|
.press_images(self.imgs.checkbox_press, self.imgs.checkbox_checked)
|
||||||
|
.set(state.ids.sct_batch_inc_radio, ui);
|
||||||
|
|
||||||
|
if self.global_state.settings.gameplay.sct_player_batch != show_sct_player_batch {
|
||||||
|
events.push(Event::SctPlayerBatch(
|
||||||
|
!self.global_state.settings.gameplay.sct_player_batch,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
Text::new("Cumulated Incoming Damage")
|
||||||
|
.right_from(state.ids.sct_batch_inc_radio, 10.0)
|
||||||
|
.font_size(14)
|
||||||
|
.font_id(self.fonts.cyri)
|
||||||
|
.graphics_for(state.ids.sct_batch_inc_radio)
|
||||||
|
.color(TEXT_COLOR)
|
||||||
|
.set(state.ids.sct_batch_inc_text, ui);
|
||||||
|
}
|
||||||
|
|
||||||
// Energybars Numbers
|
// Energybars Numbers
|
||||||
// Hotbar text
|
// Hotbar text
|
||||||
Text::new("Energybar Numbers")
|
Text::new("Energybar Numbers")
|
||||||
.down_from(state.ids.show_shortcuts_button, 20.0)
|
.down_from(
|
||||||
|
if self.global_state.settings.gameplay.sct {
|
||||||
|
state.ids.sct_batch_inc_radio
|
||||||
|
} else {
|
||||||
|
state.ids.sct_show_radio
|
||||||
|
},
|
||||||
|
20.0,
|
||||||
|
)
|
||||||
.font_size(18)
|
.font_size(18)
|
||||||
.font_id(self.fonts.cyri)
|
.font_id(self.fonts.cyri)
|
||||||
.color(TEXT_COLOR)
|
.color(TEXT_COLOR)
|
||||||
@ -857,9 +1023,6 @@ impl<'a> Widget for SettingsWindow<'a> {
|
|||||||
{
|
{
|
||||||
events.push(Event::ChatTransp(new_val));
|
events.push(Event::ChatTransp(new_val));
|
||||||
}
|
}
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2) Gameplay Tab --------------------------------
|
// 2) Gameplay Tab --------------------------------
|
||||||
|
@ -70,14 +70,19 @@ widget_ids! {
|
|||||||
healthbar_bg,
|
healthbar_bg,
|
||||||
healthbar_filling,
|
healthbar_filling,
|
||||||
health_text,
|
health_text,
|
||||||
|
health_text_bg,
|
||||||
energybar_bg,
|
energybar_bg,
|
||||||
energybar_filling,
|
energybar_filling,
|
||||||
energy_text,
|
energy_text,
|
||||||
|
energy_text_bg,
|
||||||
level_up,
|
level_up,
|
||||||
level_down,
|
level_down,
|
||||||
level_align,
|
level_align,
|
||||||
level_message,
|
level_message,
|
||||||
|
level_message_bg,
|
||||||
stamina_wheel,
|
stamina_wheel,
|
||||||
|
death_bg,
|
||||||
|
hurt_bg,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -215,7 +220,7 @@ impl<'a> Widget for Skillbar<'a> {
|
|||||||
// Update last_value
|
// Update last_value
|
||||||
state.update(|s| s.last_level = current_level);
|
state.update(|s| s.last_level = current_level);
|
||||||
state.update(|s| s.last_update_level = Instant::now());
|
state.update(|s| s.last_update_level = Instant::now());
|
||||||
}
|
};
|
||||||
|
|
||||||
let seconds_level = state.last_update_level.elapsed().as_secs_f32();
|
let seconds_level = state.last_update_level.elapsed().as_secs_f32();
|
||||||
let fade_level = if current_level == 1 {
|
let fade_level = if current_level == 1 {
|
||||||
@ -236,6 +241,12 @@ impl<'a> Widget for Skillbar<'a> {
|
|||||||
.middle_of(state.ids.level_align)
|
.middle_of(state.ids.level_align)
|
||||||
.font_size(30)
|
.font_size(30)
|
||||||
.font_id(self.fonts.cyri)
|
.font_id(self.fonts.cyri)
|
||||||
|
.color(Color::Rgba(0.0, 0.0, 0.0, fade_level))
|
||||||
|
.set(state.ids.level_message_bg, ui);
|
||||||
|
Text::new(&level_up_text)
|
||||||
|
.bottom_left_with_margins_on(state.ids.level_message_bg, 2.0, 2.0)
|
||||||
|
.font_size(30)
|
||||||
|
.font_id(self.fonts.cyri)
|
||||||
.color(Color::Rgba(1.0, 1.0, 1.0, fade_level))
|
.color(Color::Rgba(1.0, 1.0, 1.0, fade_level))
|
||||||
.set(state.ids.level_message, ui);
|
.set(state.ids.level_message, ui);
|
||||||
Image::new(self.imgs.level_up)
|
Image::new(self.imgs.level_up)
|
||||||
@ -251,35 +262,38 @@ impl<'a> Widget for Skillbar<'a> {
|
|||||||
.graphics_for(state.ids.level_align)
|
.graphics_for(state.ids.level_align)
|
||||||
.set(state.ids.level_down, ui);
|
.set(state.ids.level_down, ui);
|
||||||
// Death message
|
// Death message
|
||||||
if hp_percentage == 0.0 {
|
if self.stats.is_dead {
|
||||||
Text::new("You Died")
|
Text::new("You Died")
|
||||||
.mid_top_with_margin_on(ui.window, 60.0)
|
.middle_of(ui.window)
|
||||||
.font_size(40)
|
.font_size(50)
|
||||||
.font_id(self.fonts.cyri)
|
.font_id(self.fonts.cyri)
|
||||||
.color(Color::Rgba(0.0, 0.0, 0.0, 1.0))
|
.color(Color::Rgba(0.0, 0.0, 0.0, 1.0))
|
||||||
.set(state.ids.death_message_1_bg, ui);
|
.set(state.ids.death_message_1_bg, ui);
|
||||||
Text::new(&format!(
|
Text::new(&format!(
|
||||||
"Press {:?} to respawn.",
|
"Press {:?} to respawn at your Waypoint.\n\
|
||||||
|
\n\
|
||||||
|
Press Enter, type in /waypoint and confirm to set it here.",
|
||||||
self.global_state.settings.controls.respawn
|
self.global_state.settings.controls.respawn
|
||||||
))
|
))
|
||||||
.mid_bottom_with_margin_on(state.ids.death_message_1, -30.0)
|
.mid_bottom_with_margin_on(state.ids.death_message_1_bg, -120.0)
|
||||||
.font_size(15)
|
.font_size(30)
|
||||||
.font_id(self.fonts.cyri)
|
.font_id(self.fonts.cyri)
|
||||||
.color(Color::Rgba(0.0, 0.0, 0.0, 1.0))
|
.color(Color::Rgba(0.0, 0.0, 0.0, 1.0))
|
||||||
.set(state.ids.death_message_2_bg, ui);
|
.set(state.ids.death_message_2_bg, ui);
|
||||||
|
|
||||||
Text::new("You Died")
|
Text::new("You Died")
|
||||||
.top_left_with_margins_on(state.ids.death_message_1_bg, -2.0, -2.0)
|
.bottom_left_with_margins_on(state.ids.death_message_1_bg, 2.0, 2.0)
|
||||||
.font_size(40)
|
.font_size(50)
|
||||||
.font_id(self.fonts.cyri)
|
.font_id(self.fonts.cyri)
|
||||||
.color(CRITICAL_HP_COLOR)
|
.color(CRITICAL_HP_COLOR)
|
||||||
.set(state.ids.death_message_1, ui);
|
.set(state.ids.death_message_1, ui);
|
||||||
Text::new(&format!(
|
Text::new(&format!(
|
||||||
"Press {:?} to respawn.",
|
"Press {:?} to respawn at your Waypoint.\n\
|
||||||
|
\n\
|
||||||
|
Press Enter, type in /waypoint and confirm to set it here.",
|
||||||
self.global_state.settings.controls.respawn
|
self.global_state.settings.controls.respawn
|
||||||
))
|
))
|
||||||
.top_left_with_margins_on(state.ids.death_message_2_bg, -1.5, -1.5)
|
.bottom_left_with_margins_on(state.ids.death_message_2_bg, 2.0, 2.0)
|
||||||
.font_size(15)
|
.font_size(30)
|
||||||
.color(CRITICAL_HP_COLOR)
|
.color(CRITICAL_HP_COLOR)
|
||||||
.set(state.ids.death_message_2, ui);
|
.set(state.ids.death_message_2, ui);
|
||||||
}
|
}
|
||||||
@ -812,7 +826,13 @@ impl<'a> Widget for Skillbar<'a> {
|
|||||||
self.stats.health.maximum() as u32
|
self.stats.health.maximum() as u32
|
||||||
);
|
);
|
||||||
Text::new(&hp_text)
|
Text::new(&hp_text)
|
||||||
.mid_top_with_margin_on(state.ids.healthbar_bg, 5.0 * scale)
|
.mid_top_with_margin_on(state.ids.healthbar_bg, 6.0 * scale)
|
||||||
|
.font_size(14)
|
||||||
|
.font_id(self.fonts.cyri)
|
||||||
|
.color(Color::Rgba(0.0, 0.0, 0.0, 1.0))
|
||||||
|
.set(state.ids.health_text_bg, ui);
|
||||||
|
Text::new(&hp_text)
|
||||||
|
.bottom_left_with_margins_on(state.ids.health_text_bg, 2.0, 2.0)
|
||||||
.font_size(14)
|
.font_size(14)
|
||||||
.font_id(self.fonts.cyri)
|
.font_id(self.fonts.cyri)
|
||||||
.color(TEXT_COLOR)
|
.color(TEXT_COLOR)
|
||||||
@ -823,7 +843,13 @@ impl<'a> Widget for Skillbar<'a> {
|
|||||||
self.stats.energy.maximum() as u32
|
self.stats.energy.maximum() as u32
|
||||||
);
|
);
|
||||||
Text::new(&energy_text)
|
Text::new(&energy_text)
|
||||||
.mid_top_with_margin_on(state.ids.energybar_bg, 5.0 * scale)
|
.mid_top_with_margin_on(state.ids.energybar_bg, 6.0 * scale)
|
||||||
|
.font_size(14)
|
||||||
|
.font_id(self.fonts.cyri)
|
||||||
|
.color(Color::Rgba(0.0, 0.0, 0.0, 1.0))
|
||||||
|
.set(state.ids.energy_text_bg, ui);
|
||||||
|
Text::new(&energy_text)
|
||||||
|
.bottom_left_with_margins_on(state.ids.energy_text_bg, 2.0, 2.0)
|
||||||
.font_size(14)
|
.font_size(14)
|
||||||
.font_id(self.fonts.cyri)
|
.font_id(self.fonts.cyri)
|
||||||
.color(TEXT_COLOR)
|
.color(TEXT_COLOR)
|
||||||
@ -833,14 +859,26 @@ impl<'a> Widget for Skillbar<'a> {
|
|||||||
if let BarNumbers::Percent = bar_values {
|
if let BarNumbers::Percent = bar_values {
|
||||||
let hp_text = format!("{}%", hp_percentage as u32);
|
let hp_text = format!("{}%", hp_percentage as u32);
|
||||||
Text::new(&hp_text)
|
Text::new(&hp_text)
|
||||||
.mid_top_with_margin_on(state.ids.healthbar_bg, 5.0 * scale)
|
.mid_top_with_margin_on(state.ids.healthbar_bg, 6.0 * scale)
|
||||||
|
.font_size(14)
|
||||||
|
.font_id(self.fonts.cyri)
|
||||||
|
.color(Color::Rgba(0.0, 0.0, 0.0, 1.0))
|
||||||
|
.set(state.ids.health_text_bg, ui);
|
||||||
|
Text::new(&hp_text)
|
||||||
|
.bottom_left_with_margins_on(state.ids.health_text_bg, 2.0, 2.0)
|
||||||
.font_size(14)
|
.font_size(14)
|
||||||
.font_id(self.fonts.cyri)
|
.font_id(self.fonts.cyri)
|
||||||
.color(TEXT_COLOR)
|
.color(TEXT_COLOR)
|
||||||
.set(state.ids.health_text, ui);
|
.set(state.ids.health_text, ui);
|
||||||
let energy_text = format!("{}%", energy_percentage as u32);
|
let energy_text = format!("{}%", energy_percentage as u32);
|
||||||
Text::new(&energy_text)
|
Text::new(&energy_text)
|
||||||
.mid_top_with_margin_on(state.ids.energybar_bg, 5.0 * scale)
|
.mid_top_with_margin_on(state.ids.energybar_bg, 6.0 * scale)
|
||||||
|
.font_size(14)
|
||||||
|
.font_id(self.fonts.cyri)
|
||||||
|
.color(Color::Rgba(0.0, 0.0, 0.0, 1.0))
|
||||||
|
.set(state.ids.energy_text_bg, ui);
|
||||||
|
Text::new(&energy_text)
|
||||||
|
.bottom_left_with_margins_on(state.ids.energy_text_bg, 2.0, 2.0)
|
||||||
.font_size(14)
|
.font_size(14)
|
||||||
.font_id(self.fonts.cyri)
|
.font_id(self.fonts.cyri)
|
||||||
.color(TEXT_COLOR)
|
.color(TEXT_COLOR)
|
||||||
|
@ -16,6 +16,7 @@ use std::sync::Mutex;
|
|||||||
pub mod ui;
|
pub mod ui;
|
||||||
pub mod anim;
|
pub mod anim;
|
||||||
pub mod audio;
|
pub mod audio;
|
||||||
|
mod ecs;
|
||||||
pub mod error;
|
pub mod error;
|
||||||
pub mod hud;
|
pub mod hud;
|
||||||
pub mod key_state;
|
pub mod key_state;
|
||||||
|
@ -108,11 +108,11 @@ impl PlayState for CharSelectionState {
|
|||||||
.render(global_state.window.renderer_mut(), self.scene.globals());
|
.render(global_state.window.renderer_mut(), self.scene.globals());
|
||||||
|
|
||||||
// Tick the client (currently only to keep the connection alive).
|
// Tick the client (currently only to keep the connection alive).
|
||||||
if let Err(err) = self
|
if let Err(err) = self.client.borrow_mut().tick(
|
||||||
.client
|
comp::ControllerInputs::default(),
|
||||||
.borrow_mut()
|
clock.get_last_delta(),
|
||||||
.tick(comp::ControllerInputs::default(), clock.get_last_delta())
|
|_| {},
|
||||||
{
|
) {
|
||||||
global_state.info_message = Some(
|
global_state.info_message = Some(
|
||||||
"Connection lost!\nDid the server restart?\nIs the client up to date?"
|
"Connection lost!\nDid the server restart?\nIs the client up to date?"
|
||||||
.to_owned(),
|
.to_owned(),
|
||||||
|
@ -69,8 +69,10 @@ impl PlayState for MainMenuState {
|
|||||||
|
|
||||||
// Poll client creation.
|
// Poll client creation.
|
||||||
match client_init.as_ref().and_then(|init| init.poll()) {
|
match client_init.as_ref().and_then(|init| init.poll()) {
|
||||||
Some(Ok(client)) => {
|
Some(Ok(mut client)) => {
|
||||||
self.main_menu_ui.connected();
|
self.main_menu_ui.connected();
|
||||||
|
// Register voxygen components / resources
|
||||||
|
crate::ecs::init(client.state_mut().ecs_mut());
|
||||||
return PlayStateResult::Push(Box::new(CharSelectionState::new(
|
return PlayStateResult::Push(Box::new(CharSelectionState::new(
|
||||||
global_state,
|
global_state,
|
||||||
std::rc::Rc::new(std::cell::RefCell::new(client)),
|
std::rc::Rc::new(std::cell::RefCell::new(client)),
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
use crate::ui::Graphic;
|
||||||
use crate::{
|
use crate::{
|
||||||
render::Renderer,
|
render::Renderer,
|
||||||
ui::{
|
ui::{
|
||||||
@ -7,6 +8,7 @@ use crate::{
|
|||||||
},
|
},
|
||||||
GlobalState,
|
GlobalState,
|
||||||
};
|
};
|
||||||
|
use common::assets::load_expect;
|
||||||
use conrod_core::{
|
use conrod_core::{
|
||||||
color,
|
color,
|
||||||
color::TRANSPARENT,
|
color::TRANSPARENT,
|
||||||
@ -14,6 +16,7 @@ use conrod_core::{
|
|||||||
widget::{text_box::Event as TextBoxEvent, Button, Image, List, Rectangle, Text, TextBox},
|
widget::{text_box::Event as TextBoxEvent, Button, Image, List, Rectangle, Text, TextBox},
|
||||||
widget_ids, Borderable, Color, Colorable, Labelable, Positionable, Sizeable, Widget,
|
widget_ids, Borderable, Color, Colorable, Labelable, Positionable, Sizeable, Widget,
|
||||||
};
|
};
|
||||||
|
use rand::{seq::SliceRandom, thread_rng};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
widget_ids! {
|
widget_ids! {
|
||||||
@ -84,7 +87,6 @@ image_ids! {
|
|||||||
|
|
||||||
<ImageGraphic>
|
<ImageGraphic>
|
||||||
bg: "voxygen.background.bg_main",
|
bg: "voxygen.background.bg_main",
|
||||||
load: "voxygen.background.bg_load",
|
|
||||||
|
|
||||||
<BlankGraphic>
|
<BlankGraphic>
|
||||||
nothing: (),
|
nothing: (),
|
||||||
@ -129,6 +131,7 @@ pub enum PopupType {
|
|||||||
Error,
|
Error,
|
||||||
ConnectionInfo,
|
ConnectionInfo,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct PopupData {
|
pub struct PopupData {
|
||||||
msg: String,
|
msg: String,
|
||||||
button_text: String,
|
button_text: String,
|
||||||
@ -150,6 +153,7 @@ pub struct MainMenuUi {
|
|||||||
show_servers: bool,
|
show_servers: bool,
|
||||||
show_disclaimer: bool,
|
show_disclaimer: bool,
|
||||||
time: f32,
|
time: f32,
|
||||||
|
bg_img_id: conrod_core::image::Id,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MainMenuUi {
|
impl MainMenuUi {
|
||||||
@ -157,6 +161,18 @@ impl MainMenuUi {
|
|||||||
let window = &mut global_state.window;
|
let window = &mut global_state.window;
|
||||||
let networking = &global_state.settings.networking;
|
let networking = &global_state.settings.networking;
|
||||||
let gameplay = &global_state.settings.gameplay;
|
let gameplay = &global_state.settings.gameplay;
|
||||||
|
// Randomly loaded background images
|
||||||
|
let bg_imgs = [
|
||||||
|
"voxygen.background.bg_1",
|
||||||
|
"voxygen.background.bg_2",
|
||||||
|
"voxygen.background.bg_3",
|
||||||
|
"voxygen.background.bg_4",
|
||||||
|
"voxygen.background.bg_5",
|
||||||
|
"voxygen.background.bg_6",
|
||||||
|
"voxygen.background.bg_7",
|
||||||
|
"voxygen.background.bg_8",
|
||||||
|
];
|
||||||
|
let mut rng = thread_rng();
|
||||||
|
|
||||||
let mut ui = Ui::new(window).unwrap();
|
let mut ui = Ui::new(window).unwrap();
|
||||||
ui.set_scaling_mode(gameplay.ui_scale);
|
ui.set_scaling_mode(gameplay.ui_scale);
|
||||||
@ -165,6 +181,9 @@ impl MainMenuUi {
|
|||||||
// Load images
|
// Load images
|
||||||
let imgs = Imgs::load(&mut ui).expect("Failed to load images");
|
let imgs = Imgs::load(&mut ui).expect("Failed to load images");
|
||||||
let rot_imgs = ImgsRot::load(&mut ui).expect("Failed to load images!");
|
let rot_imgs = ImgsRot::load(&mut ui).expect("Failed to load images!");
|
||||||
|
let bg_img_id = ui.add_graphic(Graphic::Image(load_expect(
|
||||||
|
bg_imgs.choose(&mut rng).unwrap(),
|
||||||
|
)));
|
||||||
// Load fonts
|
// Load fonts
|
||||||
let fonts = Fonts::load(&mut ui).expect("Failed to load fonts");
|
let fonts = Fonts::load(&mut ui).expect("Failed to load fonts");
|
||||||
|
|
||||||
@ -183,6 +202,7 @@ impl MainMenuUi {
|
|||||||
connect: false,
|
connect: false,
|
||||||
time: 0.0,
|
time: 0.0,
|
||||||
show_disclaimer: global_state.settings.show_disclaimer,
|
show_disclaimer: global_state.settings.show_disclaimer,
|
||||||
|
bg_img_id,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -225,14 +245,14 @@ impl MainMenuUi {
|
|||||||
.desc_text_color(TEXT_COLOR_2);
|
.desc_text_color(TEXT_COLOR_2);
|
||||||
|
|
||||||
// Background image, Veloren logo, Alpha-Version Label
|
// Background image, Veloren logo, Alpha-Version Label
|
||||||
|
|
||||||
Image::new(if self.connect {
|
Image::new(if self.connect {
|
||||||
self.imgs.load
|
self.bg_img_id
|
||||||
} else {
|
} else {
|
||||||
self.imgs.bg
|
self.imgs.bg
|
||||||
})
|
})
|
||||||
.middle_of(ui_widgets.window)
|
.middle_of(ui_widgets.window)
|
||||||
.set(self.ids.bg, ui_widgets);
|
.set(self.ids.bg, ui_widgets);
|
||||||
|
|
||||||
// Version displayed top right corner
|
// Version displayed top right corner
|
||||||
Text::new(&version)
|
Text::new(&version)
|
||||||
.color(TEXT_COLOR)
|
.color(TEXT_COLOR)
|
||||||
|
@ -43,10 +43,10 @@ impl Pipeline for UiPipeline {
|
|||||||
type Vertex = Vertex;
|
type Vertex = Vertex;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Vec3<f32>> for Locals {
|
impl From<Vec4<f32>> for Locals {
|
||||||
fn from(pos: Vec3<f32>) -> Self {
|
fn from(pos: Vec4<f32>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
pos: [pos.x, pos.y, pos.z, 1.0],
|
pos: pos.into_array(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1194,6 +1194,7 @@ impl<S: Skeleton> FigureState<S> {
|
|||||||
self.last_ori = Lerp::lerp(self.last_ori, ori, 15.0 * dt);
|
self.last_ori = Lerp::lerp(self.last_ori, ori, 15.0 * dt);
|
||||||
|
|
||||||
// Update interpolation values
|
// Update interpolation values
|
||||||
|
// TODO: use values from Interpolated component instead of recalculating
|
||||||
if self.pos.distance_squared(pos) < 64.0 * 64.0 {
|
if self.pos.distance_squared(pos) < 64.0 * 64.0 {
|
||||||
self.pos = Lerp::lerp(self.pos, pos + vel * 0.03, 10.0 * dt);
|
self.pos = Lerp::lerp(self.pos, pos + vel * 0.03, 10.0 * dt);
|
||||||
self.ori = Slerp::slerp(self.ori, ori, 5.0 * dt);
|
self.ori = Slerp::slerp(self.ori, ori, 5.0 * dt);
|
||||||
@ -1205,6 +1206,7 @@ impl<S: Skeleton> FigureState<S> {
|
|||||||
self.movement_time += (dt * movement_rate) as f64;
|
self.movement_time += (dt * movement_rate) as f64;
|
||||||
self.action_time += (dt * action_rate) as f64;
|
self.action_time += (dt * action_rate) as f64;
|
||||||
|
|
||||||
|
// TODO: what are the interpolated ori values used for if not here???
|
||||||
let mat = Mat4::<f32>::identity()
|
let mat = Mat4::<f32>::identity()
|
||||||
* Mat4::translation_3d(self.pos)
|
* Mat4::translation_3d(self.pos)
|
||||||
* Mat4::rotation_z(-ori.x.atan2(ori.y))
|
* Mat4::rotation_z(-ori.x.atan2(ori.y))
|
||||||
|
@ -215,23 +215,31 @@ impl Scene {
|
|||||||
let mut lights = (
|
let mut lights = (
|
||||||
&client.state().ecs().read_storage::<comp::Pos>(),
|
&client.state().ecs().read_storage::<comp::Pos>(),
|
||||||
client.state().ecs().read_storage::<comp::Ori>().maybe(),
|
client.state().ecs().read_storage::<comp::Ori>().maybe(),
|
||||||
|
client
|
||||||
|
.state()
|
||||||
|
.ecs()
|
||||||
|
.read_storage::<crate::ecs::comp::Interpolated>()
|
||||||
|
.maybe(),
|
||||||
&client.state().ecs().read_storage::<comp::LightEmitter>(),
|
&client.state().ecs().read_storage::<comp::LightEmitter>(),
|
||||||
)
|
)
|
||||||
.join()
|
.join()
|
||||||
.filter(|(pos, _, _)| {
|
.filter(|(pos, _, _, _)| {
|
||||||
(pos.0.distance_squared(player_pos) as f32)
|
(pos.0.distance_squared(player_pos) as f32)
|
||||||
< self.loaded_distance.powf(2.0) + LIGHT_DIST_RADIUS
|
< self.loaded_distance.powf(2.0) + LIGHT_DIST_RADIUS
|
||||||
})
|
})
|
||||||
.map(|(pos, ori, light_emitter)| {
|
.map(|(pos, ori, interpolated, light_emitter)| {
|
||||||
|
// Use interpolated values if they are available
|
||||||
|
let (pos, ori) =
|
||||||
|
interpolated.map_or((pos.0, ori.map(|o| o.0)), |i| (i.pos, Some(i.ori)));
|
||||||
let rot = {
|
let rot = {
|
||||||
if let Some(o) = ori {
|
if let Some(o) = ori {
|
||||||
Mat3::rotation_z(-o.0.x.atan2(o.0.y))
|
Mat3::rotation_z(-o.x.atan2(o.y))
|
||||||
} else {
|
} else {
|
||||||
Mat3::identity()
|
Mat3::identity()
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
Light::new(
|
Light::new(
|
||||||
pos.0 + (rot * light_emitter.offset),
|
pos + (rot * light_emitter.offset),
|
||||||
light_emitter.col,
|
light_emitter.col,
|
||||||
light_emitter.strength,
|
light_emitter.strength,
|
||||||
)
|
)
|
||||||
@ -246,17 +254,28 @@ impl Scene {
|
|||||||
// Update shadow constants
|
// Update shadow constants
|
||||||
let mut shadows = (
|
let mut shadows = (
|
||||||
&client.state().ecs().read_storage::<comp::Pos>(),
|
&client.state().ecs().read_storage::<comp::Pos>(),
|
||||||
|
client
|
||||||
|
.state()
|
||||||
|
.ecs()
|
||||||
|
.read_storage::<crate::ecs::comp::Interpolated>()
|
||||||
|
.maybe(),
|
||||||
client.state().ecs().read_storage::<comp::Scale>().maybe(),
|
client.state().ecs().read_storage::<comp::Scale>().maybe(),
|
||||||
&client.state().ecs().read_storage::<comp::Body>(),
|
&client.state().ecs().read_storage::<comp::Body>(),
|
||||||
&client.state().ecs().read_storage::<comp::Stats>(),
|
&client.state().ecs().read_storage::<comp::Stats>(),
|
||||||
)
|
)
|
||||||
.join()
|
.join()
|
||||||
.filter(|(_, _, _, stats)| !stats.is_dead)
|
.filter(|(_, _, _, _, stats)| !stats.is_dead)
|
||||||
.filter(|(pos, _, _, _)| {
|
.filter(|(pos, _, _, _, _)| {
|
||||||
(pos.0.distance_squared(player_pos) as f32)
|
(pos.0.distance_squared(player_pos) as f32)
|
||||||
< (self.loaded_distance.min(SHADOW_MAX_DIST) + SHADOW_DIST_RADIUS).powf(2.0)
|
< (self.loaded_distance.min(SHADOW_MAX_DIST) + SHADOW_DIST_RADIUS).powf(2.0)
|
||||||
})
|
})
|
||||||
.map(|(pos, scale, _, _)| Shadow::new(pos.0, scale.map(|s| s.0).unwrap_or(1.0)))
|
.map(|(pos, interpolated, scale, _, _)| {
|
||||||
|
Shadow::new(
|
||||||
|
// Use interpolated values pos if it is available
|
||||||
|
interpolated.map_or(pos.0, |i| i.pos),
|
||||||
|
scale.map_or(1.0, |s| s.0),
|
||||||
|
)
|
||||||
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
shadows.sort_by_key(|shadow| shadow.get_pos().distance_squared(player_pos) as i32);
|
shadows.sort_by_key(|shadow| shadow.get_pos().distance_squared(player_pos) as i32);
|
||||||
shadows.truncate(MAX_SHADOW_COUNT);
|
shadows.truncate(MAX_SHADOW_COUNT);
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
|
ecs::MyEntity,
|
||||||
hud::{DebugInfo, Event as HudEvent, Hud},
|
hud::{DebugInfo, Event as HudEvent, Hud},
|
||||||
key_state::KeyState,
|
key_state::KeyState,
|
||||||
render::Renderer,
|
render::Renderer,
|
||||||
@ -40,6 +41,14 @@ impl SessionState {
|
|||||||
.camera_mut()
|
.camera_mut()
|
||||||
.set_fov_deg(global_state.settings.graphics.fov);
|
.set_fov_deg(global_state.settings.graphics.fov);
|
||||||
let hud = Hud::new(global_state, &client.borrow());
|
let hud = Hud::new(global_state, &client.borrow());
|
||||||
|
{
|
||||||
|
let my_entity = client.borrow().entity();
|
||||||
|
client
|
||||||
|
.borrow_mut()
|
||||||
|
.state_mut()
|
||||||
|
.ecs_mut()
|
||||||
|
.insert(MyEntity(my_entity));
|
||||||
|
}
|
||||||
Self {
|
Self {
|
||||||
scene,
|
scene,
|
||||||
client,
|
client,
|
||||||
@ -55,7 +64,11 @@ impl SessionState {
|
|||||||
/// Tick the session (and the client attached to it).
|
/// Tick the session (and the client attached to it).
|
||||||
fn tick(&mut self, dt: Duration) -> Result<(), Error> {
|
fn tick(&mut self, dt: Duration) -> Result<(), Error> {
|
||||||
self.inputs.tick(dt);
|
self.inputs.tick(dt);
|
||||||
for event in self.client.borrow_mut().tick(self.inputs.clone(), dt)? {
|
for event in self.client.borrow_mut().tick(
|
||||||
|
self.inputs.clone(),
|
||||||
|
dt,
|
||||||
|
crate::ecs::sys::add_local_systems,
|
||||||
|
)? {
|
||||||
match event {
|
match event {
|
||||||
Chat {
|
Chat {
|
||||||
chat_type: _,
|
chat_type: _,
|
||||||
@ -423,6 +436,22 @@ impl PlayState for SessionState {
|
|||||||
global_state.settings.gameplay.zoom_inversion = zoom_inverted;
|
global_state.settings.gameplay.zoom_inversion = zoom_inverted;
|
||||||
global_state.settings.save_to_file_warn();
|
global_state.settings.save_to_file_warn();
|
||||||
}
|
}
|
||||||
|
HudEvent::Sct(sct) => {
|
||||||
|
global_state.settings.gameplay.sct = sct;
|
||||||
|
global_state.settings.save_to_file_warn();
|
||||||
|
}
|
||||||
|
HudEvent::SctPlayerBatch(sct_player_batch) => {
|
||||||
|
global_state.settings.gameplay.sct_player_batch = sct_player_batch;
|
||||||
|
global_state.settings.save_to_file_warn();
|
||||||
|
}
|
||||||
|
HudEvent::SctDamageBatch(sct_damage_batch) => {
|
||||||
|
global_state.settings.gameplay.sct_damage_batch = sct_damage_batch;
|
||||||
|
global_state.settings.save_to_file_warn();
|
||||||
|
}
|
||||||
|
HudEvent::ToggleDebug(toggle_debug) => {
|
||||||
|
global_state.settings.gameplay.toggle_debug = toggle_debug;
|
||||||
|
global_state.settings.save_to_file_warn();
|
||||||
|
}
|
||||||
HudEvent::ToggleMouseYInvert(mouse_y_inverted) => {
|
HudEvent::ToggleMouseYInvert(mouse_y_inverted) => {
|
||||||
global_state.window.mouse_y_inversion = mouse_y_inverted;
|
global_state.window.mouse_y_inversion = mouse_y_inverted;
|
||||||
global_state.settings.gameplay.mouse_y_inversion = mouse_y_inverted;
|
global_state.settings.gameplay.mouse_y_inversion = mouse_y_inverted;
|
||||||
|
@ -85,8 +85,8 @@ impl Default for ControlSettings {
|
|||||||
screenshot: KeyMouse::Key(VirtualKeyCode::F4),
|
screenshot: KeyMouse::Key(VirtualKeyCode::F4),
|
||||||
toggle_ingame_ui: KeyMouse::Key(VirtualKeyCode::F6),
|
toggle_ingame_ui: KeyMouse::Key(VirtualKeyCode::F6),
|
||||||
roll: KeyMouse::Mouse(MouseButton::Middle),
|
roll: KeyMouse::Mouse(MouseButton::Middle),
|
||||||
respawn: KeyMouse::Mouse(MouseButton::Left),
|
respawn: KeyMouse::Key(VirtualKeyCode::Space),
|
||||||
interact: KeyMouse::Key(VirtualKeyCode::E),
|
interact: KeyMouse::Mouse(MouseButton::Right),
|
||||||
toggle_wield: KeyMouse::Key(VirtualKeyCode::T),
|
toggle_wield: KeyMouse::Key(VirtualKeyCode::T),
|
||||||
charge: KeyMouse::Key(VirtualKeyCode::V),
|
charge: KeyMouse::Key(VirtualKeyCode::V),
|
||||||
}
|
}
|
||||||
@ -100,6 +100,10 @@ pub struct GameplaySettings {
|
|||||||
pub pan_sensitivity: u32,
|
pub pan_sensitivity: u32,
|
||||||
pub zoom_sensitivity: u32,
|
pub zoom_sensitivity: u32,
|
||||||
pub zoom_inversion: bool,
|
pub zoom_inversion: bool,
|
||||||
|
pub toggle_debug: bool,
|
||||||
|
pub sct: bool,
|
||||||
|
pub sct_player_batch: bool,
|
||||||
|
pub sct_damage_batch: bool,
|
||||||
pub mouse_y_inversion: bool,
|
pub mouse_y_inversion: bool,
|
||||||
pub crosshair_transp: f32,
|
pub crosshair_transp: f32,
|
||||||
pub chat_transp: f32,
|
pub chat_transp: f32,
|
||||||
@ -118,6 +122,10 @@ impl Default for GameplaySettings {
|
|||||||
zoom_sensitivity: 100,
|
zoom_sensitivity: 100,
|
||||||
zoom_inversion: false,
|
zoom_inversion: false,
|
||||||
mouse_y_inversion: false,
|
mouse_y_inversion: false,
|
||||||
|
toggle_debug: false,
|
||||||
|
sct: true,
|
||||||
|
sct_player_batch: true,
|
||||||
|
sct_damage_batch: false,
|
||||||
crosshair_transp: 0.6,
|
crosshair_transp: 0.6,
|
||||||
chat_transp: 0.4,
|
chat_transp: 0.4,
|
||||||
crosshair_type: CrosshairType::Round,
|
crosshair_type: CrosshairType::Round,
|
||||||
@ -179,9 +187,9 @@ pub struct GraphicsSettings {
|
|||||||
impl Default for GraphicsSettings {
|
impl Default for GraphicsSettings {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
view_distance: 5,
|
view_distance: 10,
|
||||||
max_fps: 60,
|
max_fps: 60,
|
||||||
fov: 75,
|
fov: 50,
|
||||||
aa_mode: AaMode::Fxaa,
|
aa_mode: AaMode::Fxaa,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -305,7 +305,7 @@ impl Ui {
|
|||||||
enum Placement {
|
enum Placement {
|
||||||
Interface,
|
Interface,
|
||||||
// Number of primitives left to render ingame and relative scaling/resolution
|
// Number of primitives left to render ingame and relative scaling/resolution
|
||||||
InWorld(usize, Option<f32>),
|
InWorld(usize, Option<(f64, f64)>),
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut placement = Placement::Interface;
|
let mut placement = Placement::Interface;
|
||||||
@ -399,7 +399,7 @@ impl Ui {
|
|||||||
|
|
||||||
// Functions for converting for conrod scalar coords to GL vertex coords (-1.0 to 1.0).
|
// Functions for converting for conrod scalar coords to GL vertex coords (-1.0 to 1.0).
|
||||||
let (ui_win_w, ui_win_h) = match placement {
|
let (ui_win_w, ui_win_h) = match placement {
|
||||||
Placement::InWorld(_, Some(res)) => (res as f64, res as f64),
|
Placement::InWorld(_, Some(res)) => res,
|
||||||
// Behind the camera or far away
|
// Behind the camera or far away
|
||||||
Placement::InWorld(_, None) => continue,
|
Placement::InWorld(_, None) => continue,
|
||||||
Placement::Interface => (self.ui.win_w, self.ui.win_h),
|
Placement::Interface => (self.ui.win_w, self.ui.win_h),
|
||||||
@ -624,10 +624,12 @@ impl Ui {
|
|||||||
.parameters;
|
.parameters;
|
||||||
|
|
||||||
let pos_in_view = view_mat * Vec4::from_point(parameters.pos);
|
let pos_in_view = view_mat * Vec4::from_point(parameters.pos);
|
||||||
|
|
||||||
let scale_factor = self.ui.win_w as f64
|
let scale_factor = self.ui.win_w as f64
|
||||||
/ (-2.0
|
/ (-2.0
|
||||||
* pos_in_view.z as f64
|
* pos_in_view.z as f64
|
||||||
* (0.5 * fov as f64).tan()
|
* (0.5 * fov as f64).tan()
|
||||||
|
// TODO: make this have no effect for fixed scale
|
||||||
* parameters.res as f64);
|
* parameters.res as f64);
|
||||||
// Don't process ingame elements behind the camera or very far away
|
// Don't process ingame elements behind the camera or very far away
|
||||||
placement = if scale_factor > 0.2 {
|
placement = if scale_factor > 0.2 {
|
||||||
@ -642,32 +644,43 @@ impl Ui {
|
|||||||
});
|
});
|
||||||
start = mesh.vertices().len();
|
start = mesh.vertices().len();
|
||||||
// Push new position command
|
// Push new position command
|
||||||
|
let mut world_pos = Vec4::from_point(parameters.pos);
|
||||||
|
if parameters.fixed_scale {
|
||||||
|
world_pos.w = -1.0
|
||||||
|
};
|
||||||
|
|
||||||
if self.ingame_locals.len() > ingame_local_index {
|
if self.ingame_locals.len() > ingame_local_index {
|
||||||
renderer
|
renderer
|
||||||
.update_consts(
|
.update_consts(
|
||||||
&mut self.ingame_locals[ingame_local_index],
|
&mut self.ingame_locals[ingame_local_index],
|
||||||
&[parameters.pos.into()],
|
&[world_pos.into()],
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
} else {
|
} else {
|
||||||
self.ingame_locals.push(
|
self.ingame_locals
|
||||||
renderer.create_consts(&[parameters.pos.into()]).unwrap(),
|
.push(renderer.create_consts(&[world_pos.into()]).unwrap());
|
||||||
);
|
|
||||||
}
|
}
|
||||||
self.draw_commands
|
self.draw_commands
|
||||||
.push(DrawCommand::WorldPos(Some(ingame_local_index)));
|
.push(DrawCommand::WorldPos(Some(ingame_local_index)));
|
||||||
ingame_local_index += 1;
|
ingame_local_index += 1;
|
||||||
|
|
||||||
p_scale_factor = ((scale_factor * 10.0).log2().round().powi(2)
|
p_scale_factor = if parameters.fixed_scale {
|
||||||
/ 10.0)
|
self.scale.scale_factor_physical()
|
||||||
.min(1.6)
|
} else {
|
||||||
.max(0.2);
|
((scale_factor * 10.0).log2().round().powi(2) / 10.0)
|
||||||
|
.min(1.6)
|
||||||
|
.max(0.2)
|
||||||
|
};
|
||||||
|
|
||||||
// Scale down ingame elements that are close to the camera
|
// Scale down ingame elements that are close to the camera
|
||||||
let res = if scale_factor > 3.2 {
|
let res = if parameters.fixed_scale {
|
||||||
parameters.res * scale_factor as f32 / 3.2
|
(self.ui.win_w, self.ui.win_h)
|
||||||
|
} else if scale_factor > 3.2 {
|
||||||
|
let res = parameters.res * scale_factor as f32 / 3.2;
|
||||||
|
(res as f64, res as f64)
|
||||||
} else {
|
} else {
|
||||||
parameters.res
|
let res = parameters.res;
|
||||||
|
(res as f64, res as f64)
|
||||||
};
|
};
|
||||||
|
|
||||||
Placement::InWorld(parameters.num, Some(res))
|
Placement::InWorld(parameters.num, Some(res))
|
||||||
|
@ -57,6 +57,9 @@ pub struct IngameParameters {
|
|||||||
// Used for widgets that are rasterized before being sent to the gpu (text & images)
|
// Used for widgets that are rasterized before being sent to the gpu (text & images)
|
||||||
// Potentially make this automatic based on distance to camera?
|
// Potentially make this automatic based on distance to camera?
|
||||||
pub res: f32,
|
pub res: f32,
|
||||||
|
// Whether the widgets should be scaled based on distance to the camera or if they should be a
|
||||||
|
// fixed size (res is ignored in that case)
|
||||||
|
pub fixed_scale: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct State {
|
pub struct State {
|
||||||
@ -74,10 +77,15 @@ impl<W: Ingameable> Ingame<W> {
|
|||||||
num: widget.prim_count(),
|
num: widget.prim_count(),
|
||||||
pos,
|
pos,
|
||||||
res: 1.0,
|
res: 1.0,
|
||||||
|
fixed_scale: false,
|
||||||
},
|
},
|
||||||
widget,
|
widget,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
pub fn fixed_scale(mut self) -> Self {
|
||||||
|
self.parameters.fixed_scale = true;
|
||||||
|
self
|
||||||
|
}
|
||||||
builder_methods! {
|
builder_methods! {
|
||||||
pub resolution { parameters.res = f32 }
|
pub resolution { parameters.res = f32 }
|
||||||
}
|
}
|
||||||
@ -147,6 +155,7 @@ impl IngameAnchor {
|
|||||||
num: 0,
|
num: 0,
|
||||||
pos,
|
pos,
|
||||||
res: 1.0,
|
res: 1.0,
|
||||||
|
fixed_scale: false,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user