mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Cleaned up UI code. Removed stuff added for testing. Added 10% for melee attacks to inflict a bleeding debuff. Renamed BuffId to BuffKind. Fixed memory leak. Set event emission to false when timer is decremented.
This commit is contained in:
parent
fdf8decb18
commit
337cf6e137
@ -25,6 +25,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- Gave the axe a third attack
|
||||
- A new secondary charged melee attack for the hammer
|
||||
- Added Dutch translations
|
||||
- Buff system
|
||||
|
||||
### Changed
|
||||
|
||||
|
2
Cargo.lock
generated
2
Cargo.lock
generated
@ -4957,7 +4957,6 @@ dependencies = [
|
||||
"guillotiere",
|
||||
"hashbrown 0.7.2",
|
||||
"image",
|
||||
"inline_tweak",
|
||||
"itertools",
|
||||
"native-dialog",
|
||||
"num 0.2.1",
|
||||
@ -4989,7 +4988,6 @@ name = "veloren-voxygen-anim"
|
||||
version = "0.7.0"
|
||||
dependencies = [
|
||||
"find_folder",
|
||||
"inline_tweak",
|
||||
"lazy_static",
|
||||
"libloading 0.6.3",
|
||||
"notify",
|
||||
|
@ -37,7 +37,7 @@ use common::{
|
||||
terrain::{block::Block, neighbors, TerrainChunk, TerrainChunkSize},
|
||||
vol::RectVolSize,
|
||||
};
|
||||
use comp::BuffId;
|
||||
use comp::BuffKind;
|
||||
use futures_executor::block_on;
|
||||
use futures_timer::Delay;
|
||||
use futures_util::{select, FutureExt};
|
||||
@ -632,7 +632,7 @@ impl Client {
|
||||
self.send_msg(ClientGeneral::ControlEvent(ControlEvent::DisableLantern));
|
||||
}
|
||||
|
||||
pub fn remove_buff(&mut self, buff_id: BuffId) {
|
||||
pub fn remove_buff(&mut self, buff_id: BuffKind) {
|
||||
self.send_msg(ClientGeneral::ControlEvent(ControlEvent::RemoveBuff(
|
||||
buff_id,
|
||||
)));
|
||||
@ -971,6 +971,11 @@ impl Client {
|
||||
|
||||
// 4) Tick the client's LocalState
|
||||
self.state.tick(dt, add_foreign_systems, true);
|
||||
// TODO: avoid emitting these in the first place
|
||||
self.state
|
||||
.ecs()
|
||||
.fetch::<EventBus<common::event::ServerEvent>>()
|
||||
.recv_all();
|
||||
|
||||
// 5) Terrain
|
||||
let pos = self
|
||||
|
@ -4,17 +4,12 @@ use specs::{Component, FlaggedStorage};
|
||||
use specs_idvs::IdvStorage;
|
||||
use std::time::Duration;
|
||||
|
||||
/// De/buff ID.
|
||||
/// ID can be independant of an actual type/config of a `BuffEffect`.
|
||||
/// Therefore, information provided by `BuffId` can be incomplete/incorrect.
|
||||
///
|
||||
/// For example, there could be two regeneration buffs, each with
|
||||
/// different strength, but they could use the same `BuffId`,
|
||||
/// making it harder to recognize which is which.
|
||||
///
|
||||
/// Also, this should be dehardcoded eventually.
|
||||
/// De/buff Kind.
|
||||
/// This is used to determine what effects a buff will have, as well as
|
||||
/// determine the strength and duration of the buff effects using the internal
|
||||
/// values
|
||||
#[derive(Clone, Copy, PartialEq, Debug, Serialize, Deserialize)]
|
||||
pub enum BuffId {
|
||||
pub enum BuffKind {
|
||||
/// Restores health/time for some period
|
||||
Regeneration {
|
||||
strength: f32,
|
||||
@ -31,22 +26,21 @@ pub enum BuffId {
|
||||
}
|
||||
|
||||
/// De/buff category ID.
|
||||
/// Similar to `BuffId`, but to mark a category (for more generic usage, like
|
||||
/// Similar to `BuffKind`, but to mark a category (for more generic usage, like
|
||||
/// positive/negative buffs).
|
||||
#[derive(Clone, Copy, Eq, PartialEq, Debug, Serialize, Deserialize)]
|
||||
pub enum BuffCategoryId {
|
||||
// Buff and debuff get added in builder function based off of the buff kind
|
||||
Debuff,
|
||||
Buff,
|
||||
Natural,
|
||||
Physical,
|
||||
Magical,
|
||||
Divine,
|
||||
Debuff,
|
||||
Buff,
|
||||
PersistOnDeath,
|
||||
}
|
||||
|
||||
/// Data indicating and configuring behaviour of a de/buff.
|
||||
///
|
||||
/// NOTE: Contents of this enum are WIP/Placeholder
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub enum BuffEffect {
|
||||
/// Periodically damages or heals entity
|
||||
@ -58,23 +52,16 @@ pub enum BuffEffect {
|
||||
/// Actual de/buff.
|
||||
/// Buff can timeout after some time if `time` is Some. If `time` is None,
|
||||
/// Buff will last indefinitely, until removed manually (by some action, like
|
||||
/// uncursing). The `time` field might be moved into the `Buffs` component
|
||||
/// (so that `Buff` does not own this information).
|
||||
/// uncursing).
|
||||
///
|
||||
/// Buff has an id and data, which can be independent on each other.
|
||||
/// This makes it hard to create buff stacking "helpers", as the system
|
||||
/// does not assume that the same id is always the same behaviour (data).
|
||||
/// Therefore id=behaviour relationship has to be enforced elsewhere (if
|
||||
/// desired).
|
||||
/// Buff has a kind, which is used to determine the effects in a builder
|
||||
/// function.
|
||||
///
|
||||
/// To provide more classification info when needed,
|
||||
/// buff can be in one or more buff category.
|
||||
///
|
||||
/// `data` is separate, to make this system more flexible
|
||||
/// (at the cost of the fact that id=behaviour relationship might not apply).
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct Buff {
|
||||
pub id: BuffId,
|
||||
pub kind: BuffKind,
|
||||
pub cat_ids: Vec<BuffCategoryId>,
|
||||
pub time: Option<Duration>,
|
||||
pub effects: Vec<BuffEffect>,
|
||||
@ -88,10 +75,12 @@ pub enum BuffChange {
|
||||
/// Adds this buff.
|
||||
Add(Buff),
|
||||
/// Removes all buffs with this ID.
|
||||
RemoveById(BuffId),
|
||||
RemoveByKind(BuffKind),
|
||||
/// Removes all buffs with this ID, but not debuffs.
|
||||
RemoveFromClient(BuffKind),
|
||||
/// Removes buffs of these indices (first vec is for active buffs, second is
|
||||
/// for inactive buffs)
|
||||
RemoveByIndex(Vec<usize>, Vec<usize>),
|
||||
/// for inactive buffs), should only be called when buffs expire
|
||||
RemoveExpiredByIndex(Vec<usize>, Vec<usize>),
|
||||
/// Removes buffs of these categories (first vec is of categories of which
|
||||
/// all are required, second vec is of categories of which at least one is
|
||||
/// required, third vec is of categories that will not be removed)
|
||||
@ -139,37 +128,40 @@ pub struct Buffs {
|
||||
pub inactive_buffs: Vec<Buff>,
|
||||
}
|
||||
|
||||
impl Buffs {
|
||||
/// This is a primitive check if a specific buff is present and active.
|
||||
/// (for purposes like blocking usage of abilities or something like this).
|
||||
pub fn has_buff_id(&self, id: &BuffId) -> bool {
|
||||
self.active_buffs.iter().any(|buff| buff.id == *id)
|
||||
}
|
||||
}
|
||||
|
||||
impl Buff {
|
||||
pub fn new(id: BuffId, cat_ids: Vec<BuffCategoryId>, source: BuffSource) -> Self {
|
||||
let (effects, time) = match id {
|
||||
BuffId::Bleeding { strength, duration } => (
|
||||
vec![BuffEffect::HealthChangeOverTime {
|
||||
rate: -strength,
|
||||
accumulated: 0.0,
|
||||
}],
|
||||
duration,
|
||||
),
|
||||
BuffId::Regeneration { strength, duration } => (
|
||||
vec![BuffEffect::HealthChangeOverTime {
|
||||
rate: strength,
|
||||
accumulated: 0.0,
|
||||
}],
|
||||
duration,
|
||||
),
|
||||
BuffId::Cursed { duration } => (
|
||||
vec![BuffEffect::NameChange {
|
||||
prefix: String::from("Cursed "),
|
||||
}],
|
||||
duration,
|
||||
),
|
||||
/// Builder function for buffs
|
||||
pub fn new(kind: BuffKind, cat_ids: Vec<BuffCategoryId>, source: BuffSource) -> Self {
|
||||
let mut cat_ids = cat_ids;
|
||||
let (effects, time) = match kind {
|
||||
BuffKind::Bleeding { strength, duration } => {
|
||||
cat_ids.push(BuffCategoryId::Debuff);
|
||||
(
|
||||
vec![BuffEffect::HealthChangeOverTime {
|
||||
rate: -strength,
|
||||
accumulated: 0.0,
|
||||
}],
|
||||
duration,
|
||||
)
|
||||
},
|
||||
BuffKind::Regeneration { strength, duration } => {
|
||||
cat_ids.push(BuffCategoryId::Buff);
|
||||
(
|
||||
vec![BuffEffect::HealthChangeOverTime {
|
||||
rate: strength,
|
||||
accumulated: 0.0,
|
||||
}],
|
||||
duration,
|
||||
)
|
||||
},
|
||||
BuffKind::Cursed { duration } => {
|
||||
cat_ids.push(BuffCategoryId::Debuff);
|
||||
(
|
||||
vec![BuffEffect::NameChange {
|
||||
prefix: String::from("Cursed "),
|
||||
}],
|
||||
duration,
|
||||
)
|
||||
},
|
||||
};
|
||||
assert_eq!(
|
||||
cat_ids
|
||||
@ -179,7 +171,7 @@ impl Buff {
|
||||
"Buff must have either buff or debuff category."
|
||||
);
|
||||
Buff {
|
||||
id,
|
||||
kind,
|
||||
cat_ids,
|
||||
time,
|
||||
effects,
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::{
|
||||
comp::{inventory::slot::Slot, BuffId},
|
||||
comp::{inventory::slot::Slot, BuffKind},
|
||||
sync::Uid,
|
||||
util::Dir,
|
||||
};
|
||||
@ -41,7 +41,7 @@ pub enum ControlEvent {
|
||||
Unmount,
|
||||
InventoryManip(InventoryManip),
|
||||
GroupManip(GroupManip),
|
||||
RemoveBuff(BuffId),
|
||||
RemoveBuff(BuffKind),
|
||||
Respawn,
|
||||
}
|
||||
|
||||
|
@ -32,7 +32,7 @@ pub use body::{
|
||||
biped_large, bird_medium, bird_small, dragon, fish_medium, fish_small, golem, humanoid, object,
|
||||
quadruped_low, quadruped_medium, quadruped_small, theropod, AllBodies, Body, BodyData,
|
||||
};
|
||||
pub use buff::{Buff, BuffCategoryId, BuffChange, BuffEffect, BuffId, BuffSource, Buffs};
|
||||
pub use buff::{Buff, BuffCategoryId, BuffChange, BuffEffect, BuffKind, BuffSource, Buffs};
|
||||
pub use character_state::{Attacking, CharacterState, StateUpdate};
|
||||
pub use chat::{
|
||||
ChatMode, ChatMsg, ChatType, Faction, SpeechBubble, SpeechBubbleType, UnresolvedChatMsg,
|
||||
|
@ -684,7 +684,7 @@ impl<'a> System<'a> for Sys {
|
||||
for (_invite, /*alignment,*/ agent, controller) in
|
||||
(&invites, /*&alignments,*/ &mut agents, &mut controllers).join()
|
||||
{
|
||||
let accept = true; // set back to "matches!(alignment, Alignment::Npc)" when we got better NPC recruitment mechanics
|
||||
let accept = false; // set back to "matches!(alignment, Alignment::Npc)" when we got better NPC recruitment mechanics
|
||||
if accept {
|
||||
// Clear agent comp
|
||||
*agent = Agent::default();
|
||||
|
@ -23,33 +23,62 @@ impl<'a> System<'a> for Sys {
|
||||
|
||||
fn run(&mut self, (dt, server_bus, uids, stats, mut buffs): Self::SystemData) {
|
||||
let mut server_emitter = server_bus.emitter();
|
||||
for (uid, stat, mut buffs) in (&uids, &stats, &mut buffs.restrict_mut()).join() {
|
||||
let buff_comp = buffs.get_mut_unchecked();
|
||||
// Set to false to avoid spamming server
|
||||
buffs.set_event_emission(false);
|
||||
for (buff_comp, uid) in (&mut buffs, &uids).join() {
|
||||
let (mut active_buff_indices_for_removal, mut inactive_buff_indices_for_removal) =
|
||||
(Vec::<usize>::new(), Vec::<usize>::new());
|
||||
// Tick all de/buffs on a Buffs component.
|
||||
for (i, active_buff) in buff_comp.active_buffs.iter_mut().enumerate() {
|
||||
// First, tick the buff and subtract delta from it
|
||||
// and return how much "real" time the buff took (for tick independence).
|
||||
let buff_delta = if let Some(remaining_time) = &mut active_buff.time {
|
||||
let pre_tick = remaining_time.as_secs_f32();
|
||||
// Tick the buff and subtract delta from it
|
||||
if let Some(remaining_time) = &mut active_buff.time {
|
||||
let new_duration = remaining_time.checked_sub(Duration::from_secs_f32(dt.0));
|
||||
let post_tick = if let Some(dur) = new_duration {
|
||||
if new_duration.is_some() {
|
||||
// The buff still continues.
|
||||
*remaining_time -= Duration::from_secs_f32(dt.0);
|
||||
dur.as_secs_f32()
|
||||
} else {
|
||||
// The buff has expired.
|
||||
// Remove it.
|
||||
active_buff_indices_for_removal.push(i);
|
||||
0.0
|
||||
active_buff.time = Some(Duration::default());
|
||||
};
|
||||
pre_tick - post_tick
|
||||
} else {
|
||||
// The buff is indefinite, and it takes full tick (delta).
|
||||
dt.0
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
for (i, inactive_buff) in buff_comp.inactive_buffs.iter_mut().enumerate() {
|
||||
// Tick the buff and subtract delta from it
|
||||
if let Some(remaining_time) = &mut inactive_buff.time {
|
||||
let new_duration = remaining_time.checked_sub(Duration::from_secs_f32(dt.0));
|
||||
if new_duration.is_some() {
|
||||
// The buff still continues.
|
||||
*remaining_time -= Duration::from_secs_f32(dt.0);
|
||||
} else {
|
||||
// The buff has expired.
|
||||
// Remove it.
|
||||
inactive_buff_indices_for_removal.push(i);
|
||||
inactive_buff.time = Some(Duration::default());
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if !active_buff_indices_for_removal.is_empty()
|
||||
|| !inactive_buff_indices_for_removal.is_empty()
|
||||
{
|
||||
server_emitter.emit(ServerEvent::Buff {
|
||||
uid: *uid,
|
||||
buff_change: BuffChange::RemoveExpiredByIndex(
|
||||
active_buff_indices_for_removal,
|
||||
inactive_buff_indices_for_removal,
|
||||
),
|
||||
});
|
||||
}
|
||||
}
|
||||
// Set back to true after timer decrement
|
||||
buffs.set_event_emission(true);
|
||||
for (uid, stat, mut buffs) in (&uids, &stats, &mut buffs.restrict_mut()).join() {
|
||||
let buff_comp = buffs.get_mut_unchecked();
|
||||
// Tick all de/buffs on a Buffs component.
|
||||
for active_buff in buff_comp.active_buffs.iter_mut() {
|
||||
// Get buff owner
|
||||
let buff_owner = if let BuffSource::Character { by: owner } = active_buff.source {
|
||||
Some(owner)
|
||||
} else {
|
||||
@ -60,12 +89,13 @@ impl<'a> System<'a> for Sys {
|
||||
match effect {
|
||||
// Only add an effect here if it is continuous or it is not immediate
|
||||
BuffEffect::HealthChangeOverTime { rate, accumulated } => {
|
||||
*accumulated += *rate * buff_delta;
|
||||
// Apply damage only once a second (with a minimum of 1 damage), or when a buff is removed
|
||||
*accumulated += *rate * dt.0;
|
||||
// Apply damage only once a second (with a minimum of 1 damage), or when
|
||||
// a buff is removed
|
||||
if accumulated.abs() > rate.abs().max(10.0)
|
||||
|| active_buff_indices_for_removal
|
||||
.iter()
|
||||
.any(|index| *index == i)
|
||||
|| active_buff
|
||||
.time
|
||||
.map_or(false, |dur| dur == Duration::default())
|
||||
{
|
||||
let cause = if *accumulated > 0.0 {
|
||||
HealthSource::Healing { by: buff_owner }
|
||||
@ -87,31 +117,6 @@ impl<'a> System<'a> for Sys {
|
||||
}
|
||||
}
|
||||
|
||||
for (i, inactive_buff) in buff_comp.inactive_buffs.iter_mut().enumerate() {
|
||||
// First, tick the buff and subtract delta from it
|
||||
// and return how much "real" time the buff took (for tick independence).
|
||||
// TODO: handle delta for "indefinite" buffs, i.e. time since they got removed.
|
||||
if let Some(remaining_time) = &mut inactive_buff.time {
|
||||
let new_duration = remaining_time.checked_sub(Duration::from_secs_f32(dt.0));
|
||||
if new_duration.is_some() {
|
||||
// The buff still continues.
|
||||
*remaining_time -= Duration::from_secs_f32(dt.0);
|
||||
} else {
|
||||
// The buff has expired.
|
||||
// Remove it.
|
||||
inactive_buff_indices_for_removal.push(i);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
server_emitter.emit(ServerEvent::Buff {
|
||||
uid: *uid,
|
||||
buff_change: BuffChange::RemoveByIndex(
|
||||
active_buff_indices_for_removal,
|
||||
inactive_buff_indices_for_removal,
|
||||
),
|
||||
});
|
||||
|
||||
if stat.is_dead {
|
||||
server_emitter.emit(ServerEvent::Buff {
|
||||
uid: *uid,
|
||||
|
@ -9,6 +9,7 @@ use crate::{
|
||||
sync::Uid,
|
||||
util::Dir,
|
||||
};
|
||||
use rand::{thread_rng, Rng};
|
||||
use specs::{Entities, Join, Read, ReadExpect, ReadStorage, System, WriteStorage};
|
||||
use std::time::Duration;
|
||||
use vek::*;
|
||||
@ -151,37 +152,21 @@ impl<'a> System<'a> for Sys {
|
||||
cause,
|
||||
},
|
||||
});
|
||||
// Test for server event of buff, remove before merging
|
||||
server_emitter.emit(ServerEvent::Buff {
|
||||
uid: *uid_b,
|
||||
buff_change: buff::BuffChange::Add(buff::Buff::new(
|
||||
buff::BuffId::Bleeding {
|
||||
strength: attack.base_damage as f32,
|
||||
duration: Some(Duration::from_secs(30)),
|
||||
},
|
||||
vec![buff::BuffCategoryId::Physical, buff::BuffCategoryId::Debuff],
|
||||
buff::BuffSource::Character { by: *uid },
|
||||
)),
|
||||
});
|
||||
server_emitter.emit(ServerEvent::Buff {
|
||||
uid: *uid_b,
|
||||
buff_change: buff::BuffChange::Add(buff::Buff::new(
|
||||
buff::BuffId::Regeneration {
|
||||
strength: 1.0,
|
||||
duration: Some(Duration::from_secs(60)),
|
||||
},
|
||||
vec![buff::BuffCategoryId::Physical, buff::BuffCategoryId::Buff],
|
||||
buff::BuffSource::Character { by: *uid },
|
||||
)),
|
||||
});
|
||||
server_emitter.emit(ServerEvent::Buff {
|
||||
uid: *uid_b,
|
||||
buff_change: buff::BuffChange::Add(buff::Buff::new(
|
||||
buff::BuffId::Cursed { duration: None },
|
||||
vec![buff::BuffCategoryId::Physical, buff::BuffCategoryId::Debuff],
|
||||
buff::BuffSource::Character { by: *uid },
|
||||
)),
|
||||
});
|
||||
// Apply bleeding buff on melee hits with 10% chance
|
||||
// TODO: Don't have buff uniformly applied on all melee attacks
|
||||
if thread_rng().gen::<f32>() < 0.1 {
|
||||
server_emitter.emit(ServerEvent::Buff {
|
||||
uid: *uid_b,
|
||||
buff_change: buff::BuffChange::Add(buff::Buff::new(
|
||||
buff::BuffKind::Bleeding {
|
||||
strength: attack.base_damage as f32 / 10.0,
|
||||
duration: Some(Duration::from_secs(10)),
|
||||
},
|
||||
vec![buff::BuffCategoryId::Physical],
|
||||
buff::BuffSource::Character { by: *uid },
|
||||
)),
|
||||
});
|
||||
}
|
||||
attack.hit_count += 1;
|
||||
}
|
||||
if attack.knockback != 0.0 && damage.healthchange != 0.0 {
|
||||
|
@ -86,7 +86,7 @@ impl<'a> System<'a> for Sys {
|
||||
ControlEvent::RemoveBuff(buff_id) => {
|
||||
server_emitter.emit(ServerEvent::Buff {
|
||||
uid: *uid,
|
||||
buff_change: BuffChange::RemoveById(buff_id),
|
||||
buff_change: BuffChange::RemoveFromClient(buff_id),
|
||||
});
|
||||
},
|
||||
ControlEvent::Unmount => server_emitter.emit(ServerEvent::Unmount(entity)),
|
||||
|
@ -724,7 +724,7 @@ pub fn handle_buff(server: &mut Server, uid: Uid, buff_change: buff::BuffChange)
|
||||
// inactive buffs, or move active buff to
|
||||
// inactive buffs and add new buff to active
|
||||
// buffs.
|
||||
if discriminant(&active_buff.id) == discriminant(&new_buff.id) {
|
||||
if discriminant(&active_buff.kind) == discriminant(&new_buff.kind) {
|
||||
duplicate_existed = true;
|
||||
// Determines if active buff is weaker than newer buff
|
||||
if determine_replace_active_buff(
|
||||
@ -761,19 +761,40 @@ pub fn handle_buff(server: &mut Server, uid: Uid, buff_change: buff::BuffChange)
|
||||
}
|
||||
}
|
||||
},
|
||||
BuffChange::RemoveByIndex(active_indices, inactive_indices) => {
|
||||
BuffChange::RemoveExpiredByIndex(active_indices, inactive_indices) => {
|
||||
active_buff_indices_for_removal = active_indices;
|
||||
inactive_buff_indices_for_removal = inactive_indices;
|
||||
},
|
||||
BuffChange::RemoveById(id) => {
|
||||
let some_predicate = |current_id: &buff::BuffId| *current_id == id;
|
||||
BuffChange::RemoveByKind(kind) => {
|
||||
for (i, buff) in buffs.active_buffs.iter().enumerate() {
|
||||
if some_predicate(&buff.id) {
|
||||
if discriminant(&kind) == discriminant(&buff.kind) {
|
||||
active_buff_indices_for_removal.push(i);
|
||||
}
|
||||
}
|
||||
for (i, buff) in buffs.inactive_buffs.iter().enumerate() {
|
||||
if some_predicate(&buff.id) {
|
||||
if discriminant(&kind) == discriminant(&buff.kind) {
|
||||
inactive_buff_indices_for_removal.push(i);
|
||||
}
|
||||
}
|
||||
},
|
||||
BuffChange::RemoveFromClient(kind) => {
|
||||
for (i, buff) in buffs.active_buffs.iter().enumerate() {
|
||||
if discriminant(&kind) == discriminant(&buff.kind)
|
||||
&& buff
|
||||
.cat_ids
|
||||
.iter()
|
||||
.any(|cat| *cat == buff::BuffCategoryId::Buff)
|
||||
{
|
||||
active_buff_indices_for_removal.push(i);
|
||||
}
|
||||
}
|
||||
for (i, buff) in buffs.inactive_buffs.iter().enumerate() {
|
||||
if discriminant(&kind) == discriminant(&buff.kind)
|
||||
&& buff
|
||||
.cat_ids
|
||||
.iter()
|
||||
.any(|cat| *cat == buff::BuffCategoryId::Buff)
|
||||
{
|
||||
inactive_buff_indices_for_removal.push(i);
|
||||
}
|
||||
}
|
||||
@ -830,11 +851,11 @@ pub fn handle_buff(server: &mut Server, uid: Uid, buff_change: buff::BuffChange)
|
||||
}
|
||||
},
|
||||
}
|
||||
let mut removed_active_buff_ids = Vec::new();
|
||||
let mut removed_active_buff_kinds = Vec::new();
|
||||
while !active_buff_indices_for_removal.is_empty() {
|
||||
if let Some(i) = active_buff_indices_for_removal.pop() {
|
||||
let buff = buffs.active_buffs.remove(i);
|
||||
removed_active_buff_ids.push(buff.id);
|
||||
removed_active_buff_kinds.push(buff.kind);
|
||||
remove_buff_effects(buff, stats.get_mut(entity));
|
||||
}
|
||||
}
|
||||
@ -845,19 +866,19 @@ pub fn handle_buff(server: &mut Server, uid: Uid, buff_change: buff::BuffChange)
|
||||
}
|
||||
// Checks after buffs are removed so that it doesn't grab incorrect
|
||||
// index
|
||||
for buff_id in removed_active_buff_ids {
|
||||
for buff_kind in removed_active_buff_kinds {
|
||||
// Checks to verify that there are no active buffs with the same id
|
||||
if buffs
|
||||
.active_buffs
|
||||
.iter()
|
||||
.any(|buff| discriminant(&buff.id) == discriminant(&buff_id))
|
||||
.any(|buff| discriminant(&buff.kind) == discriminant(&buff_kind))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
let mut new_active_buff = None::<buff::Buff>;
|
||||
let mut replacement_buff_index = 0;
|
||||
for (i, inactive_buff) in buffs.inactive_buffs.iter().enumerate() {
|
||||
if discriminant(&buff_id) == discriminant(&inactive_buff.id) {
|
||||
if discriminant(&buff_kind) == discriminant(&inactive_buff.kind) {
|
||||
if let Some(ref buff) = new_active_buff {
|
||||
if determine_replace_active_buff(buff.clone(), inactive_buff.clone()) {
|
||||
new_active_buff = Some(inactive_buff.clone());
|
||||
@ -880,16 +901,16 @@ pub fn handle_buff(server: &mut Server, uid: Uid, buff_change: buff::BuffChange)
|
||||
}
|
||||
|
||||
fn determine_replace_active_buff(active_buff: buff::Buff, new_buff: buff::Buff) -> bool {
|
||||
use buff::BuffId;
|
||||
match new_buff.id {
|
||||
BuffId::Bleeding {
|
||||
use buff::BuffKind;
|
||||
match new_buff.kind {
|
||||
BuffKind::Bleeding {
|
||||
strength: new_strength,
|
||||
duration: new_duration,
|
||||
} => {
|
||||
if let BuffId::Bleeding {
|
||||
if let BuffKind::Bleeding {
|
||||
strength: active_strength,
|
||||
duration: _,
|
||||
} = active_buff.id
|
||||
} = active_buff.kind
|
||||
{
|
||||
new_strength > active_strength
|
||||
|| (new_strength >= active_strength
|
||||
@ -900,14 +921,14 @@ fn determine_replace_active_buff(active_buff: buff::Buff, new_buff: buff::Buff)
|
||||
false
|
||||
}
|
||||
},
|
||||
BuffId::Regeneration {
|
||||
BuffKind::Regeneration {
|
||||
strength: new_strength,
|
||||
duration: new_duration,
|
||||
} => {
|
||||
if let BuffId::Regeneration {
|
||||
if let BuffKind::Regeneration {
|
||||
strength: active_strength,
|
||||
duration: _,
|
||||
} = active_buff.id
|
||||
} = active_buff.kind
|
||||
{
|
||||
new_strength > active_strength
|
||||
|| (new_strength >= active_strength
|
||||
@ -918,7 +939,7 @@ fn determine_replace_active_buff(active_buff: buff::Buff, new_buff: buff::Buff)
|
||||
false
|
||||
}
|
||||
},
|
||||
BuffId::Cursed {
|
||||
BuffKind::Cursed {
|
||||
duration: new_duration,
|
||||
} => new_duration.map_or(true, |new_dur| {
|
||||
active_buff.time.map_or(false, |act_dur| new_dur > act_dur)
|
||||
|
@ -74,7 +74,6 @@ treeculler = "0.1.0"
|
||||
uvth = "3.1.1"
|
||||
# vec_map = { version = "0.8.2" }
|
||||
const-tweaker = {version = "0.3.1", optional = true}
|
||||
inline_tweak = "1.0.2"
|
||||
itertools = "0.9.0"
|
||||
|
||||
# Logging
|
||||
|
@ -20,7 +20,6 @@ default = ["be-dyn-lib", "simd"]
|
||||
[dependencies]
|
||||
common = {package = "veloren-common", path = "../../../common"}
|
||||
find_folder = {version = "0.3.0", optional = true}
|
||||
inline_tweak = "1.0.2"
|
||||
lazy_static = {version = "1.4.0", optional = true}
|
||||
libloading = {version = "0.6.2", optional = true}
|
||||
notify = {version = "5.0.0-pre.2", optional = true}
|
||||
|
@ -9,14 +9,13 @@ use crate::{
|
||||
GlobalState,
|
||||
};
|
||||
|
||||
use common::comp::{BuffId, Buffs};
|
||||
use common::comp::{BuffKind, Buffs};
|
||||
use conrod_core::{
|
||||
color,
|
||||
widget::{self, Button, Image, Rectangle, Text},
|
||||
widget_ids, Color, Colorable, Positionable, Sizeable, Widget, WidgetCommon,
|
||||
};
|
||||
use inline_tweak::*;
|
||||
use std::time::Duration;
|
||||
|
||||
widget_ids! {
|
||||
struct Ids {
|
||||
align,
|
||||
@ -77,7 +76,7 @@ pub struct State {
|
||||
}
|
||||
|
||||
pub enum Event {
|
||||
RemoveBuff(BuffId),
|
||||
RemoveBuff(BuffKind),
|
||||
}
|
||||
|
||||
impl<'a> Widget for BuffsBar<'a> {
|
||||
@ -123,7 +122,7 @@ impl<'a> Widget for BuffsBar<'a> {
|
||||
if let BuffPosition::Bar = buff_position {
|
||||
// Alignment
|
||||
Rectangle::fill_with([484.0, 100.0], color::TRANSPARENT)
|
||||
.mid_bottom_with_margin_on(ui.window, tweak!(92.0))
|
||||
.mid_bottom_with_margin_on(ui.window, 92.0)
|
||||
.set(state.ids.align, ui);
|
||||
Rectangle::fill_with([484.0 / 2.0, 90.0], color::TRANSPARENT)
|
||||
.bottom_left_with_margins_on(state.ids.align, 0.0, 0.0)
|
||||
@ -177,14 +176,18 @@ impl<'a> Widget for BuffsBar<'a> {
|
||||
)
|
||||
.enumerate()
|
||||
.for_each(|(i, ((id, timer_id), buff))| {
|
||||
let max_duration = match buff.id {
|
||||
BuffId::Regeneration { duration, .. } => duration.unwrap().as_secs_f32(),
|
||||
_ => 10.0,
|
||||
let max_duration = match buff.kind {
|
||||
BuffKind::Bleeding { duration, .. } => duration,
|
||||
BuffKind::Regeneration { duration, .. } => duration,
|
||||
BuffKind::Cursed { duration } => duration,
|
||||
};
|
||||
let current_duration = buff.dur;
|
||||
let duration_percentage = (current_duration / max_duration * 1000.0) as u32; // Percentage to determine which frame of the timer overlay is displayed
|
||||
let buff_img = match buff.id {
|
||||
BuffId::Regeneration { .. } => self.imgs.buff_plus_0,
|
||||
let duration_percentage = current_duration.map_or(1000.0, |cur| {
|
||||
max_duration
|
||||
.map_or(1000.0, |max| cur.as_secs_f32() / max.as_secs_f32() * 1000.0)
|
||||
}) as u32; // Percentage to determine which frame of the timer overlay is displayed
|
||||
let buff_img = match buff.kind {
|
||||
BuffKind::Regeneration { .. } => self.imgs.buff_plus_0,
|
||||
_ => self.imgs.missing_icon,
|
||||
};
|
||||
let buff_widget = Image::new(buff_img).w_h(20.0, 20.0);
|
||||
@ -197,30 +200,32 @@ impl<'a> Widget for BuffsBar<'a> {
|
||||
0.0 + x as f64 * (21.0),
|
||||
);
|
||||
buff_widget
|
||||
.color(if current_duration < 10.0 {
|
||||
Some(pulsating_col)
|
||||
} else {
|
||||
Some(norm_col)
|
||||
})
|
||||
.color(
|
||||
if current_duration.map_or(false, |cur| cur.as_secs_f32() < 10.0) {
|
||||
Some(pulsating_col)
|
||||
} else {
|
||||
Some(norm_col)
|
||||
},
|
||||
)
|
||||
.set(id, ui);
|
||||
// Create Buff tooltip
|
||||
let title = match buff.id {
|
||||
BuffId::Regeneration { .. } => {
|
||||
*&localized_strings.get("buff.title.heal_test")
|
||||
let title = match buff.kind {
|
||||
BuffKind::Regeneration { .. } => {
|
||||
localized_strings.get("buff.title.heal_test")
|
||||
},
|
||||
_ => *&localized_strings.get("buff.title.missing"),
|
||||
_ => localized_strings.get("buff.title.missing"),
|
||||
};
|
||||
let remaining_time = if current_duration == 10e6 as f32 {
|
||||
let remaining_time = if current_duration.is_none() {
|
||||
"Permanent".to_string()
|
||||
} else {
|
||||
format!("Remaining: {:.0}s", current_duration)
|
||||
format!("Remaining: {:.0}s", current_duration.unwrap().as_secs_f32())
|
||||
};
|
||||
let click_to_remove = format!("<{}>", &localized_strings.get("buff.remove"));
|
||||
let desc_txt = match buff.id {
|
||||
BuffId::Regeneration { .. } => {
|
||||
*&localized_strings.get("buff.desc.heal_test")
|
||||
let desc_txt = match buff.kind {
|
||||
BuffKind::Regeneration { .. } => {
|
||||
localized_strings.get("buff.desc.heal_test")
|
||||
},
|
||||
_ => *&localized_strings.get("buff.desc.missing"),
|
||||
_ => localized_strings.get("buff.desc.missing"),
|
||||
};
|
||||
let desc = format!("{}\n\n{}\n\n{}", desc_txt, remaining_time, click_to_remove);
|
||||
// Timer overlay
|
||||
@ -247,7 +252,7 @@ impl<'a> Widget for BuffsBar<'a> {
|
||||
.set(timer_id, ui)
|
||||
.was_clicked()
|
||||
{
|
||||
event.push(Event::RemoveBuff(buff.id));
|
||||
event.push(Event::RemoveBuff(buff.kind));
|
||||
};
|
||||
});
|
||||
// Create Debuff Widgets
|
||||
@ -266,21 +271,19 @@ impl<'a> Widget for BuffsBar<'a> {
|
||||
)
|
||||
.enumerate()
|
||||
.for_each(|(i, ((id, timer_id), debuff))| {
|
||||
let max_duration = match debuff.id {
|
||||
BuffId::Bleeding { duration, .. } => {
|
||||
duration.unwrap_or(Duration::from_secs(60)).as_secs_f32()
|
||||
},
|
||||
BuffId::Cursed { duration, .. } => {
|
||||
duration.unwrap_or(Duration::from_secs(60)).as_secs_f32()
|
||||
},
|
||||
|
||||
_ => 10.0,
|
||||
let max_duration = match debuff.kind {
|
||||
BuffKind::Bleeding { duration, .. } => duration,
|
||||
BuffKind::Regeneration { duration, .. } => duration,
|
||||
BuffKind::Cursed { duration } => duration,
|
||||
};
|
||||
let current_duration = debuff.dur;
|
||||
let duration_percentage = current_duration / max_duration * 1000.0; // Percentage to determine which frame of the timer overlay is displayed
|
||||
let debuff_img = match debuff.id {
|
||||
BuffId::Bleeding { .. } => self.imgs.debuff_bleed_0,
|
||||
BuffId::Cursed { .. } => self.imgs.debuff_skull_0,
|
||||
let duration_percentage = current_duration.map_or(1000.0, |cur| {
|
||||
max_duration
|
||||
.map_or(1000.0, |max| cur.as_secs_f32() / max.as_secs_f32() * 1000.0)
|
||||
}) as u32; // Percentage to determine which frame of the timer overlay is displayed
|
||||
let debuff_img = match debuff.kind {
|
||||
BuffKind::Bleeding { .. } => self.imgs.debuff_bleed_0,
|
||||
BuffKind::Cursed { .. } => self.imgs.debuff_skull_0,
|
||||
_ => self.imgs.missing_icon,
|
||||
};
|
||||
let debuff_widget = Image::new(debuff_img).w_h(20.0, 20.0);
|
||||
@ -294,29 +297,31 @@ impl<'a> Widget for BuffsBar<'a> {
|
||||
);
|
||||
|
||||
debuff_widget
|
||||
.color(if current_duration < 10.0 {
|
||||
Some(pulsating_col)
|
||||
} else {
|
||||
Some(norm_col)
|
||||
})
|
||||
.color(
|
||||
if current_duration.map_or(false, |cur| cur.as_secs_f32() < 10.0) {
|
||||
Some(pulsating_col)
|
||||
} else {
|
||||
Some(norm_col)
|
||||
},
|
||||
)
|
||||
.set(id, ui);
|
||||
// Create Debuff tooltip
|
||||
let title = match debuff.id {
|
||||
BuffId::Bleeding { .. } => {
|
||||
*&localized_strings.get("debuff.title.bleed_test")
|
||||
let title = match debuff.kind {
|
||||
BuffKind::Bleeding { .. } => {
|
||||
localized_strings.get("debuff.title.bleed_test")
|
||||
},
|
||||
_ => *&localized_strings.get("buff.title.missing"),
|
||||
_ => localized_strings.get("buff.title.missing"),
|
||||
};
|
||||
let remaining_time = if current_duration == 10e6 as f32 {
|
||||
let remaining_time = if current_duration.is_none() {
|
||||
"Permanent".to_string()
|
||||
} else {
|
||||
format!("Remaining: {:.0}s", current_duration)
|
||||
format!("Remaining: {:.0}s", current_duration.unwrap().as_secs_f32())
|
||||
};
|
||||
let desc_txt = match debuff.id {
|
||||
BuffId::Bleeding { .. } => {
|
||||
*&localized_strings.get("debuff.desc.bleed_test")
|
||||
let desc_txt = match debuff.kind {
|
||||
BuffKind::Bleeding { .. } => {
|
||||
localized_strings.get("debuff.desc.bleed_test")
|
||||
},
|
||||
_ => *&localized_strings.get("debuff.desc.missing"),
|
||||
_ => localized_strings.get("debuff.desc.missing"),
|
||||
};
|
||||
let desc = format!("{}\n\n{}", desc_txt, remaining_time);
|
||||
Image::new(match duration_percentage as u64 {
|
||||
@ -346,7 +351,7 @@ impl<'a> Widget for BuffsBar<'a> {
|
||||
if let BuffPosition::Map = buff_position {
|
||||
// Alignment
|
||||
Rectangle::fill_with([210.0, 210.0], color::TRANSPARENT)
|
||||
.top_right_with_margins_on(ui.window, 5.0, tweak!(270.0))
|
||||
.top_right_with_margins_on(ui.window, 5.0, 270.0)
|
||||
.set(state.ids.align, ui);
|
||||
|
||||
// Buffs and Debuffs
|
||||
@ -376,18 +381,21 @@ impl<'a> Widget for BuffsBar<'a> {
|
||||
.zip(buffs.active_buffs.iter().map(get_buff_info))
|
||||
.enumerate()
|
||||
.for_each(|(i, (((id, timer_id), txt_id), buff))| {
|
||||
let max_duration = match buff.id {
|
||||
BuffId::Regeneration { duration, .. } => duration.unwrap().as_secs_f32(),
|
||||
BuffId::Bleeding { duration, .. } => duration.unwrap().as_secs_f32(),
|
||||
_ => 10e6,
|
||||
let max_duration = match buff.kind {
|
||||
BuffKind::Bleeding { duration, .. } => duration,
|
||||
BuffKind::Regeneration { duration, .. } => duration,
|
||||
BuffKind::Cursed { duration } => duration,
|
||||
};
|
||||
let current_duration = buff.dur;
|
||||
// Percentage to determine which frame of the timer overlay is displayed
|
||||
let duration_percentage = (current_duration / max_duration * 1000.0) as u32;
|
||||
let buff_img = match buff.id {
|
||||
BuffId::Regeneration { .. } => self.imgs.buff_plus_0,
|
||||
BuffId::Bleeding { .. } => self.imgs.debuff_bleed_0,
|
||||
BuffId::Cursed { .. } => self.imgs.debuff_skull_0,
|
||||
let duration_percentage = current_duration.map_or(1000.0, |cur| {
|
||||
max_duration
|
||||
.map_or(1000.0, |max| cur.as_secs_f32() / max.as_secs_f32() * 1000.0)
|
||||
}) as u32;
|
||||
let buff_img = match buff.kind {
|
||||
BuffKind::Regeneration { .. } => self.imgs.buff_plus_0,
|
||||
BuffKind::Bleeding { .. } => self.imgs.debuff_bleed_0,
|
||||
BuffKind::Cursed { .. } => self.imgs.debuff_skull_0,
|
||||
};
|
||||
let buff_widget = Image::new(buff_img).w_h(40.0, 40.0);
|
||||
// Sort buffs into rows of 6 slots
|
||||
@ -399,41 +407,43 @@ impl<'a> Widget for BuffsBar<'a> {
|
||||
0.0 + x as f64 * (42.0),
|
||||
);
|
||||
buff_widget
|
||||
.color(if current_duration < 10.0 {
|
||||
Some(pulsating_col)
|
||||
} else {
|
||||
Some(norm_col)
|
||||
})
|
||||
.color(
|
||||
if current_duration.map_or(false, |cur| cur.as_secs_f32() < 10.0) {
|
||||
Some(pulsating_col)
|
||||
} else {
|
||||
Some(norm_col)
|
||||
},
|
||||
)
|
||||
.set(id, ui);
|
||||
// Create Buff tooltip
|
||||
let title = match buff.id {
|
||||
BuffId::Regeneration { .. } => {
|
||||
*&localized_strings.get("buff.title.heal_test")
|
||||
let title = match buff.kind {
|
||||
BuffKind::Regeneration { .. } => {
|
||||
localized_strings.get("buff.title.heal_test")
|
||||
},
|
||||
BuffId::Bleeding { .. } => {
|
||||
*&localized_strings.get("debuff.title.bleed_test")
|
||||
BuffKind::Bleeding { .. } => {
|
||||
localized_strings.get("debuff.title.bleed_test")
|
||||
},
|
||||
_ => *&localized_strings.get("buff.title.missing"),
|
||||
_ => localized_strings.get("buff.title.missing"),
|
||||
};
|
||||
let remaining_time = if current_duration == 10e6 as f32 {
|
||||
let remaining_time = if current_duration.is_none() {
|
||||
"".to_string()
|
||||
} else {
|
||||
format!("{:.0}s", current_duration)
|
||||
format!("{:.0}s", current_duration.unwrap().as_secs_f32())
|
||||
};
|
||||
let click_to_remove = format!("<{}>", &localized_strings.get("buff.remove"));
|
||||
let desc_txt = match buff.id {
|
||||
BuffId::Regeneration { .. } => {
|
||||
*&localized_strings.get("buff.desc.heal_test")
|
||||
let desc_txt = match buff.kind {
|
||||
BuffKind::Regeneration { .. } => {
|
||||
localized_strings.get("buff.desc.heal_test")
|
||||
},
|
||||
BuffId::Bleeding { .. } => {
|
||||
*&localized_strings.get("debuff.desc.bleed_test")
|
||||
BuffKind::Bleeding { .. } => {
|
||||
localized_strings.get("debuff.desc.bleed_test")
|
||||
},
|
||||
_ => *&localized_strings.get("buff.desc.missing"),
|
||||
_ => localized_strings.get("buff.desc.missing"),
|
||||
};
|
||||
let desc = if buff.is_buff {
|
||||
format!("{}\n\n{}", desc_txt, click_to_remove)
|
||||
} else {
|
||||
format!("{}", desc_txt)
|
||||
desc_txt.to_string()
|
||||
};
|
||||
// Timer overlay
|
||||
if Button::image(match duration_percentage as u64 {
|
||||
@ -442,7 +452,7 @@ impl<'a> Widget for BuffsBar<'a> {
|
||||
625..=749 => self.imgs.buff_1, // 6/8
|
||||
500..=624 => self.imgs.buff_2, // 5/8
|
||||
375..=499 => self.imgs.buff_3, // 4/8
|
||||
250..=374 => self.imgs.buff_4, //3/8
|
||||
250..=374 => self.imgs.buff_4, // 3/8
|
||||
125..=249 => self.imgs.buff_5, // 2/8
|
||||
0..=124 => self.imgs.buff_6, // 1/8
|
||||
_ => self.imgs.nothing,
|
||||
@ -463,13 +473,11 @@ impl<'a> Widget for BuffsBar<'a> {
|
||||
.set(timer_id, ui)
|
||||
.was_clicked()
|
||||
{
|
||||
if buff.is_buff {
|
||||
event.push(Event::RemoveBuff(buff.id));
|
||||
}
|
||||
event.push(Event::RemoveBuff(buff.kind));
|
||||
}
|
||||
Text::new(&remaining_time)
|
||||
.down_from(timer_id, tweak!(1.0))
|
||||
.font_size(self.fonts.cyri.scale(tweak!(10)))
|
||||
.down_from(timer_id, 1.0)
|
||||
.font_size(self.fonts.cyri.scale(10))
|
||||
.font_id(self.fonts.cyri.conrod_id)
|
||||
.graphics_for(timer_id)
|
||||
.color(TEXT_COLOR)
|
||||
|
@ -14,7 +14,7 @@ use crate::{
|
||||
};
|
||||
use client::{self, Client};
|
||||
use common::{
|
||||
comp::{group::Role, BuffId, Stats},
|
||||
comp::{group::Role, BuffKind, Stats},
|
||||
sync::{Uid, WorldSyncExt},
|
||||
};
|
||||
use conrod_core::{
|
||||
@ -23,7 +23,6 @@ use conrod_core::{
|
||||
widget::{self, Button, Image, Rectangle, Scrollbar, Text},
|
||||
widget_ids, Color, Colorable, Labelable, Positionable, Sizeable, Widget, WidgetCommon,
|
||||
};
|
||||
use inline_tweak::*;
|
||||
use specs::{saveload::MarkerAllocator, WorldExt};
|
||||
widget_ids! {
|
||||
pub struct Ids {
|
||||
@ -329,7 +328,7 @@ impl<'a> Widget for Group<'a> {
|
||||
.ecs()
|
||||
.read_resource::<common::sync::UidAllocator>();
|
||||
let offset = if self.global_state.settings.gameplay.toggle_debug {
|
||||
tweak!(320.0)
|
||||
320.0
|
||||
} else {
|
||||
110.0
|
||||
};
|
||||
@ -467,21 +466,23 @@ impl<'a> Widget for Group<'a> {
|
||||
.skip(total_buff_count - buff_count)
|
||||
.zip(buffs.active_buffs.iter().map(get_buff_info))
|
||||
.for_each(|((id, timer_id), buff)| {
|
||||
let max_duration = match buff.id {
|
||||
BuffId::Regeneration { duration, .. } => {
|
||||
duration.unwrap().as_secs_f32()
|
||||
},
|
||||
_ => 10.0,
|
||||
let max_duration = match buff.kind {
|
||||
BuffKind::Bleeding { duration, .. } => duration,
|
||||
BuffKind::Regeneration { duration, .. } => duration,
|
||||
BuffKind::Cursed { duration } => duration,
|
||||
};
|
||||
let pulsating_col = Color::Rgba(1.0, 1.0, 1.0, buff_ani);
|
||||
let norm_col = Color::Rgba(1.0, 1.0, 1.0, 1.0);
|
||||
let current_duration = buff.dur;
|
||||
let duration_percentage =
|
||||
(current_duration / max_duration * 1000.0) as u32; // Percentage to determine which frame of the timer overlay is displayed
|
||||
let buff_img = match buff.id {
|
||||
BuffId::Regeneration { .. } => self.imgs.buff_plus_0,
|
||||
BuffId::Bleeding { .. } => self.imgs.debuff_bleed_0,
|
||||
BuffId::Cursed { .. } => self.imgs.debuff_skull_0,
|
||||
let duration_percentage = current_duration.map_or(1000.0, |cur| {
|
||||
max_duration.map_or(1000.0, |max| {
|
||||
cur.as_secs_f32() / max.as_secs_f32() * 1000.0
|
||||
})
|
||||
}) as u32; // Percentage to determine which frame of the timer overlay is displayed
|
||||
let buff_img = match buff.kind {
|
||||
BuffKind::Regeneration { .. } => self.imgs.buff_plus_0,
|
||||
BuffKind::Bleeding { .. } => self.imgs.debuff_bleed_0,
|
||||
BuffKind::Cursed { .. } => self.imgs.debuff_skull_0,
|
||||
};
|
||||
let buff_widget = Image::new(buff_img).w_h(15.0, 15.0);
|
||||
let buff_widget = if let Some(id) = prev_id {
|
||||
@ -495,35 +496,42 @@ impl<'a> Widget for Group<'a> {
|
||||
};
|
||||
prev_id = Some(id);
|
||||
buff_widget
|
||||
.color(if current_duration < 10.0 {
|
||||
Some(pulsating_col)
|
||||
} else {
|
||||
Some(norm_col)
|
||||
})
|
||||
.color(
|
||||
if current_duration
|
||||
.map_or(false, |cur| cur.as_secs_f32() < 10.0)
|
||||
{
|
||||
Some(pulsating_col)
|
||||
} else {
|
||||
Some(norm_col)
|
||||
},
|
||||
)
|
||||
.set(id, ui);
|
||||
// Create Buff tooltip
|
||||
let title = match buff.id {
|
||||
BuffId::Regeneration { .. } => {
|
||||
*&localized_strings.get("buff.title.heal_test")
|
||||
let title = match buff.kind {
|
||||
BuffKind::Regeneration { .. } => {
|
||||
localized_strings.get("buff.title.heal_test")
|
||||
},
|
||||
BuffId::Bleeding { .. } => {
|
||||
*&localized_strings.get("debuff.title.bleed_test")
|
||||
BuffKind::Bleeding { .. } => {
|
||||
localized_strings.get("debuff.title.bleed_test")
|
||||
},
|
||||
_ => *&localized_strings.get("buff.title.missing"),
|
||||
_ => localized_strings.get("buff.title.missing"),
|
||||
};
|
||||
let remaining_time = if current_duration == 10e6 as f32 {
|
||||
let remaining_time = if current_duration.is_none() {
|
||||
"Permanent".to_string()
|
||||
} else {
|
||||
format!("Remaining: {:.0}s", current_duration)
|
||||
format!(
|
||||
"Remaining: {:.0}s",
|
||||
current_duration.unwrap().as_secs_f32()
|
||||
)
|
||||
};
|
||||
let desc_txt = match buff.id {
|
||||
BuffId::Regeneration { .. } => {
|
||||
*&localized_strings.get("buff.desc.heal_test")
|
||||
let desc_txt = match buff.kind {
|
||||
BuffKind::Regeneration { .. } => {
|
||||
localized_strings.get("buff.desc.heal_test")
|
||||
},
|
||||
BuffId::Bleeding { .. } => {
|
||||
*&localized_strings.get("debuff.desc.bleed_test")
|
||||
BuffKind::Bleeding { .. } => {
|
||||
localized_strings.get("debuff.desc.bleed_test")
|
||||
},
|
||||
_ => *&localized_strings.get("buff.desc.missing"),
|
||||
_ => localized_strings.get("buff.desc.missing"),
|
||||
};
|
||||
let desc = format!("{}\n\n{}", desc_txt, remaining_time);
|
||||
Image::new(match duration_percentage as u64 {
|
||||
|
@ -62,7 +62,7 @@ use common::{
|
||||
comp,
|
||||
comp::{
|
||||
item::{ItemDesc, Quality},
|
||||
BuffId,
|
||||
BuffKind,
|
||||
},
|
||||
span,
|
||||
sync::Uid,
|
||||
@ -274,9 +274,9 @@ widget_ids! {
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct BuffInfo {
|
||||
id: comp::BuffId,
|
||||
kind: comp::BuffKind,
|
||||
is_buff: bool,
|
||||
dur: f32,
|
||||
dur: Option<Duration>,
|
||||
}
|
||||
|
||||
pub struct DebugInfo {
|
||||
@ -364,7 +364,7 @@ pub enum Event {
|
||||
KickMember(common::sync::Uid),
|
||||
LeaveGroup,
|
||||
AssignLeader(common::sync::Uid),
|
||||
RemoveBuff(BuffId),
|
||||
RemoveBuff(BuffKind),
|
||||
}
|
||||
|
||||
// TODO: Are these the possible layouts we want?
|
||||
@ -1145,7 +1145,7 @@ impl Hud {
|
||||
let speech_bubbles = &self.speech_bubbles;
|
||||
|
||||
// Render overhead name tags and health bars
|
||||
for (pos, info, bubble, stats, buffs, height_offset, hpfl, in_group) in (
|
||||
for (pos, info, bubble, stats, _, height_offset, hpfl, in_group) in (
|
||||
&entities,
|
||||
&pos,
|
||||
interpolated.maybe(),
|
||||
@ -2728,14 +2728,11 @@ pub fn get_quality_col<I: ItemDesc>(item: &I) -> Color {
|
||||
// Get info about applied buffs
|
||||
fn get_buff_info(buff: &comp::Buff) -> BuffInfo {
|
||||
BuffInfo {
|
||||
id: buff.id,
|
||||
kind: buff.kind,
|
||||
is_buff: buff
|
||||
.cat_ids
|
||||
.iter()
|
||||
.any(|cat| *cat == comp::BuffCategoryId::Buff),
|
||||
dur: buff
|
||||
.time
|
||||
.map(|dur| dur.as_secs_f32())
|
||||
.unwrap_or(10e6 as f32),
|
||||
dur: buff.time,
|
||||
}
|
||||
}
|
||||
|
@ -8,14 +8,13 @@ use crate::{
|
||||
settings::GameplaySettings,
|
||||
ui::{fonts::ConrodVoxygenFonts, Ingameable},
|
||||
};
|
||||
use common::comp::{BuffId, Buffs, Energy, SpeechBubble, SpeechBubbleType, Stats};
|
||||
use common::comp::{BuffKind, Buffs, Energy, SpeechBubble, SpeechBubbleType, Stats};
|
||||
use conrod_core::{
|
||||
color,
|
||||
position::Align,
|
||||
widget::{self, Image, Rectangle, Text},
|
||||
widget_ids, Color, Colorable, Positionable, Sizeable, Widget, WidgetCommon,
|
||||
};
|
||||
use inline_tweak::*;
|
||||
const MAX_BUBBLE_WIDTH: f64 = 250.0;
|
||||
|
||||
widget_ids! {
|
||||
@ -55,11 +54,6 @@ widget_ids! {
|
||||
}
|
||||
}
|
||||
|
||||
/*pub struct BuffInfo {
|
||||
id: comp::BuffId,
|
||||
dur: f32,
|
||||
}*/
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct Info<'a> {
|
||||
pub name: &'a str,
|
||||
@ -211,8 +205,8 @@ impl<'a> Widget for Overhead<'a> {
|
||||
// Buffs
|
||||
// Alignment
|
||||
let buff_count = buffs.active_buffs.len().min(11);
|
||||
Rectangle::fill_with([tweak!(168.0), tweak!(100.0)], color::TRANSPARENT)
|
||||
.x_y(-1.0, name_y + tweak!(60.0))
|
||||
Rectangle::fill_with([168.0, 100.0], color::TRANSPARENT)
|
||||
.x_y(-1.0, name_y + 60.0)
|
||||
.parent(id)
|
||||
.set(state.ids.buffs_align, ui);
|
||||
|
||||
@ -239,18 +233,21 @@ impl<'a> Widget for Overhead<'a> {
|
||||
.enumerate()
|
||||
.for_each(|(i, ((id, timer_id), buff))| {
|
||||
// Limit displayed buffs
|
||||
let max_duration = match buff.id {
|
||||
BuffId::Regeneration { duration, .. } => {
|
||||
duration.unwrap().as_secs_f32()
|
||||
},
|
||||
_ => 10.0,
|
||||
let max_duration = match buff.kind {
|
||||
BuffKind::Bleeding { duration, .. } => duration,
|
||||
BuffKind::Regeneration { duration, .. } => duration,
|
||||
BuffKind::Cursed { duration } => duration,
|
||||
};
|
||||
let current_duration = buff.dur;
|
||||
let duration_percentage = (current_duration / max_duration * 1000.0) as u32; // Percentage to determine which frame of the timer overlay is displayed
|
||||
let buff_img = match buff.id {
|
||||
BuffId::Regeneration { .. } => self.imgs.buff_plus_0,
|
||||
BuffId::Bleeding { .. } => self.imgs.debuff_bleed_0,
|
||||
BuffId::Cursed { .. } => self.imgs.debuff_skull_0,
|
||||
let duration_percentage = current_duration.map_or(1000.0, |cur| {
|
||||
max_duration.map_or(1000.0, |max| {
|
||||
cur.as_secs_f32() / max.as_secs_f32() * 1000.0
|
||||
})
|
||||
}) as u32; // Percentage to determine which frame of the timer overlay is displayed
|
||||
let buff_img = match buff.kind {
|
||||
BuffKind::Regeneration { .. } => self.imgs.buff_plus_0,
|
||||
BuffKind::Bleeding { .. } => self.imgs.debuff_bleed_0,
|
||||
BuffKind::Cursed { .. } => self.imgs.debuff_skull_0,
|
||||
};
|
||||
let buff_widget = Image::new(buff_img).w_h(20.0, 20.0);
|
||||
// Sort buffs into rows of 5 slots
|
||||
@ -262,11 +259,13 @@ impl<'a> Widget for Overhead<'a> {
|
||||
0.0 + x as f64 * (21.0),
|
||||
);
|
||||
buff_widget
|
||||
.color(if current_duration < 10.0 {
|
||||
Some(pulsating_col)
|
||||
} else {
|
||||
Some(norm_col)
|
||||
})
|
||||
.color(
|
||||
if current_duration.map_or(false, |cur| cur.as_secs_f32() < 10.0) {
|
||||
Some(pulsating_col)
|
||||
} else {
|
||||
Some(norm_col)
|
||||
},
|
||||
)
|
||||
.set(id, ui);
|
||||
|
||||
Image::new(match duration_percentage as u64 {
|
||||
@ -275,7 +274,7 @@ impl<'a> Widget for Overhead<'a> {
|
||||
625..=749 => self.imgs.buff_1, // 6/8
|
||||
500..=624 => self.imgs.buff_2, // 5/8
|
||||
375..=499 => self.imgs.buff_3, // 4/8
|
||||
250..=374 => self.imgs.buff_4, //3/8
|
||||
250..=374 => self.imgs.buff_4, // 3/8
|
||||
125..=249 => self.imgs.buff_5, // 2/8
|
||||
0..=124 => self.imgs.buff_6, // 1/8
|
||||
_ => self.imgs.nothing,
|
||||
|
@ -20,7 +20,6 @@ use conrod_core::{
|
||||
};
|
||||
use core::convert::TryFrom;
|
||||
|
||||
use inline_tweak::*;
|
||||
use itertools::Itertools;
|
||||
use std::iter::once;
|
||||
use winit::monitor::VideoMode;
|
||||
@ -2709,8 +2708,8 @@ impl<'a> Widget for SettingsWindow<'a> {
|
||||
});
|
||||
};
|
||||
for (i, language) in language_list.iter().enumerate() {
|
||||
let button_w = tweak!(400.0);
|
||||
let button_h = tweak!(50.0);
|
||||
let button_w = 400.0;
|
||||
let button_h = 50.0;
|
||||
let button = Button::image(if selected_language == &language.language_identifier {
|
||||
self.imgs.selection
|
||||
} else {
|
||||
@ -2727,7 +2726,7 @@ impl<'a> Widget for SettingsWindow<'a> {
|
||||
.hover_image(self.imgs.selection_hover)
|
||||
.press_image(self.imgs.selection_press)
|
||||
.label_color(TEXT_COLOR)
|
||||
.label_font_size(self.fonts.cyri.scale(tweak!(22)))
|
||||
.label_font_size(self.fonts.cyri.scale(22))
|
||||
.label_font_id(self.fonts.cyri.conrod_id)
|
||||
.label_y(conrod_core::position::Relative::Scalar(2.0))
|
||||
.set(state.ids.language_list[i], ui)
|
||||
|
@ -27,7 +27,6 @@ use conrod_core::{
|
||||
widget::{self, Button, Image, Rectangle, Text},
|
||||
widget_ids, Color, Colorable, Positionable, Sizeable, Widget, WidgetCommon,
|
||||
};
|
||||
use inline_tweak::*;
|
||||
use std::time::{Duration, Instant};
|
||||
use vek::*;
|
||||
|
||||
@ -347,9 +346,9 @@ impl<'a> Widget for Skillbar<'a> {
|
||||
.set(state.ids.bg, ui);
|
||||
// Level
|
||||
let lvl_size = match self.stats.level.level() {
|
||||
11..=99 => tweak!(13),
|
||||
100..=999 => tweak!(10),
|
||||
_ => tweak!(14),
|
||||
11..=99 => 13,
|
||||
100..=999 => 10,
|
||||
_ => 14,
|
||||
};
|
||||
Text::new(&level)
|
||||
.mid_top_with_margin_on(state.ids.bg, 3.0)
|
||||
@ -1009,11 +1008,11 @@ impl<'a> Widget for Skillbar<'a> {
|
||||
// TODO Don't show this if key bindings are changed
|
||||
Image::new(self.imgs.m1_ico)
|
||||
.w_h(16.0, 18.0)
|
||||
.mid_bottom_with_margin_on(state.ids.m1_content, tweak!(-11.0))
|
||||
.mid_bottom_with_margin_on(state.ids.m1_content, -11.0)
|
||||
.set(state.ids.m1_ico, ui);
|
||||
Image::new(self.imgs.m2_ico)
|
||||
.w_h(16.0, 18.0)
|
||||
.mid_bottom_with_margin_on(state.ids.m2_content, tweak!(-11.0))
|
||||
.mid_bottom_with_margin_on(state.ids.m2_content, -11.0)
|
||||
.set(state.ids.m2_ico, ui);
|
||||
|
||||
// Buffs
|
||||
|
@ -18,7 +18,6 @@ use conrod_core::{
|
||||
widget_ids, Borderable, Color, Colorable, Labelable, Positionable, Sizeable, Widget,
|
||||
};
|
||||
use image::DynamicImage;
|
||||
//use inline_tweak::*;
|
||||
use rand::{seq::SliceRandom, thread_rng, Rng};
|
||||
use std::time::Duration;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user