Initial work

This commit is contained in:
Sam 2023-03-07 22:22:54 -05:00
parent d6ca65a44c
commit 89aa934c3c
67 changed files with 352 additions and 523 deletions

View File

@ -8,10 +8,7 @@ ItemDef(
kind: Potion,
data: (
strength: 100.0,
duration: Some((
secs: 1,
nanos: 0,
)),
duration: Some(1),
),
cat_ids: [Natural],
)),
@ -19,8 +16,8 @@ ItemDef(
kind: PotionSickness,
data: (
strength: 0.33,
duration: Some(( secs: 45, nanos: 0, )),
delay: Some(( secs: 1, nanos: 0, ))
duration: Some(45),
delay: Some(1)
),
cat_ids: [Natural],
)),

View File

@ -8,10 +8,7 @@ ItemDef(
kind: Potion,
data: (
strength: 100.0,
duration: Some((
secs: 1,
nanos: 0,
)),
duration: Some(1),
),
cat_ids: [Natural],
)),
@ -19,8 +16,8 @@ ItemDef(
kind: PotionSickness,
data: (
strength: 0.33,
duration: Some(( secs: 45, nanos: 0, )),
delay: Some(( secs: 1, nanos: 0, ))
duration: Some(45),
delay: Some(1)
),
cat_ids: [Natural],
)),

View File

@ -8,10 +8,7 @@ ItemDef(
kind: Potion,
data: (
strength: 75.0,
duration: Some((
secs: 1,
nanos: 0,
)),
duration: Some(1),
),
cat_ids: [Natural],
)),
@ -19,8 +16,8 @@ ItemDef(
kind: PotionSickness,
data: (
strength: 0.33,
duration: Some(( secs: 45, nanos: 0, )),
delay: Some(( secs: 1, nanos: 0, ))
duration: Some(45),
delay: Some(1)
),
cat_ids: [Natural],
)),

View File

@ -8,10 +8,7 @@ ItemDef(
kind: Potion,
data: (
strength: 50.0,
duration: Some((
secs: 1,
nanos: 0,
)),
duration: Some(1),
),
cat_ids: [Natural],
)),
@ -19,8 +16,8 @@ ItemDef(
kind: PotionSickness,
data: (
strength: 0.33,
duration: Some(( secs: 45, nanos: 0, )),
delay: Some(( secs: 1, nanos: 0, ))
duration: Some(45),
delay: Some(1)
),
cat_ids: [Natural],
)),

View File

@ -8,10 +8,7 @@ ItemDef(
kind: Regeneration,
data: (
strength: 1000,
duration: Some((
secs: 999,
nanos: 0,
)),
duration: Some(999),
),
cat_ids: [Natural],
)),
@ -19,10 +16,7 @@ ItemDef(
kind: EnergyRegen,
data: (
strength: 1000,
duration: Some((
secs: 999,
nanos: 0,
)),
duration: Some(999),
),
cat_ids: [Natural],
)),
@ -30,10 +24,7 @@ ItemDef(
kind: IncreaseMaxHealth,
data: (
strength: 50000,
duration: Some((
secs: 999,
nanos: 0,
)),
duration: Some(999),
),
cat_ids: [Natural],
)),
@ -41,10 +32,7 @@ ItemDef(
kind: IncreaseMaxEnergy,
data: (
strength: 50000,
duration: Some((
secs: 999,
nanos: 0,
)),
duration: Some(999),
),
cat_ids: [Natural],
)),

View File

@ -8,10 +8,7 @@ ItemDef(
kind: Saturation,
data: (
strength: 2.0,
duration: Some((
secs: 5,
nanos: 0,
)),
duration: Some(5),
),
cat_ids: [Natural],
)),

View File

@ -8,10 +8,7 @@ ItemDef(
kind: Saturation,
data: (
strength: 10.0,
duration: Some((
secs: 5,
nanos: 0,
)),
duration: Some(5),
),
cat_ids: [Natural],
)),
@ -19,10 +16,7 @@ ItemDef(
kind: Regeneration,
data: (
strength: 1.0,
duration: Some((
secs: 70,
nanos: 0,
)),
duration: Some(70),
),
cat_ids: [Natural],
)),

View File

@ -8,10 +8,7 @@ ItemDef(
kind: Saturation,
data: (
strength: 5.0,
duration: Some((
secs: 5,
nanos: 0,
)),
duration: Some(5),
),
cat_ids: [Natural],
)),

View File

@ -8,10 +8,7 @@ ItemDef(
kind: Saturation,
data: (
strength: 3.0,
duration: Some((
secs: 20,
nanos: 0,
)),
duration: Some(20),
),
cat_ids: [Natural],
)),

View File

@ -8,10 +8,7 @@ ItemDef(
kind: Saturation,
data: (
strength: 2.0,
duration: Some((
secs: 5,
nanos: 0,
)),
duration: Some(5),
),
cat_ids: [Natural],
)),

View File

@ -8,10 +8,7 @@ ItemDef(
kind: Saturation,
data: (
strength: 1.0,
duration: Some((
secs: 5,
nanos: 0,
)),
duration: Some(5),
),
cat_ids: [Natural],
)),

View File

@ -8,10 +8,7 @@ ItemDef(
kind: Saturation,
data: (
strength: 1.5,
duration: Some((
secs: 10,
nanos: 0,
)),
duration: Some(10),
),
cat_ids: [Natural],
)),

View File

@ -8,10 +8,7 @@ ItemDef(
kind: Saturation,
data: (
strength: 4.0,
duration: Some((
secs: 5,
nanos: 0,
)),
duration: Some(5),
),
cat_ids: [Natural],
)),

View File

@ -8,10 +8,7 @@ ItemDef(
kind: Saturation,
data: (
strength: 1.0,
duration: Some((
secs: 5,
nanos: 0,
)),
duration: Some(5),
),
cat_ids: [Natural],
)),

View File

@ -8,10 +8,7 @@ ItemDef(
kind: Saturation,
data: (
strength: 1.0,
duration: Some((
secs: 5,
nanos: 0,
)),
duration: Some(5),
),
cat_ids: [Natural],
)),

View File

@ -8,10 +8,7 @@ ItemDef(
kind: Saturation,
data: (
strength: 1.0,
duration: Some((
secs: 5,
nanos: 0,
)),
duration: Some(5),
),
cat_ids: [Natural],
)),

View File

@ -8,10 +8,7 @@ ItemDef(
kind: Saturation,
data: (
strength: 4.0,
duration: Some((
secs: 5,
nanos: 0,
)),
duration: Some(5),
),
cat_ids: [Natural],
)),

View File

@ -8,10 +8,7 @@ ItemDef(
kind: Saturation,
data: (
strength: 1.0,
duration: Some((
secs: 5,
nanos: 0,
)),
duration: Some(5),
),
cat_ids: [Natural],
)),

View File

@ -8,10 +8,7 @@ ItemDef(
kind: Saturation,
data: (
strength: 1.0,
duration: Some((
secs: 5,
nanos: 0,
)),
duration: Some(5),
),
cat_ids: [Natural],
)),

View File

@ -8,10 +8,7 @@ ItemDef(
kind: Saturation,
data: (
strength: 2.5,
duration: Some((
secs: 10,
nanos: 0,
)),
duration: Some(10),
),
cat_ids: [Natural],
)),

View File

@ -8,10 +8,7 @@ ItemDef(
kind: Saturation,
data: (
strength: .45,
duration: Some((
secs: 10,
nanos: 0,
)),
duration: Some(10),
),
cat_ids: [Natural],
)),

View File

@ -8,10 +8,7 @@ ItemDef(
kind: Saturation,
data: (
strength: 1.5,
duration: Some((
secs: 10,
nanos: 0,
)),
duration: Some(10),
),
cat_ids: [Natural],
)),

View File

@ -8,10 +8,7 @@ ItemDef(
kind: Saturation,
data: (
strength: .25,
duration: Some((
secs: 10,
nanos: 0,
)),
duration: Some(10),
),
cat_ids: [Natural],
)),

View File

@ -8,10 +8,7 @@ ItemDef(
kind: Saturation,
data: (
strength: 2.5,
duration: Some((
secs: 10,
nanos: 0,
)),
duration: Some(10),
),
cat_ids: [Natural],
)),

View File

@ -8,10 +8,7 @@ ItemDef(
kind: Saturation,
data: (
strength: 5.5,
duration: Some((
secs: 15,
nanos: 0,
)),
duration: Some(15),
),
cat_ids: [Natural],
)),

View File

@ -8,10 +8,7 @@ ItemDef(
kind: Saturation,
data: (
strength: .9,
duration: Some((
secs: 10,
nanos: 0,
)),
duration: Some(10),
),
cat_ids: [Natural],
)),

View File

@ -8,10 +8,7 @@ ItemDef(
kind: Saturation,
data: (
strength: .45,
duration: Some((
secs: 10,
nanos: 0,
)),
duration: Some(10),
),
cat_ids: [Natural],
)),

View File

@ -8,10 +8,7 @@ ItemDef(
kind: Saturation,
data: (
strength: 2.5,
duration: Some((
secs: 10,
nanos: 0,
)),
duration: Some(10),
),
cat_ids: [Natural],
)),

View File

@ -8,10 +8,7 @@ ItemDef(
kind: Saturation,
data: (
strength: .45,
duration: Some((
secs: 10,
nanos: 0,
)),
duration: Some(10),
),
cat_ids: [Natural],
)),

View File

@ -8,10 +8,7 @@ ItemDef(
kind: Saturation,
data: (
strength: 2.0,
duration: Some((
secs: 10,
nanos: 0,
)),
duration: Some(10),
),
cat_ids: [Natural],
)),

View File

@ -8,10 +8,7 @@ ItemDef(
kind: Saturation,
data: (
strength: .36,
duration: Some((
secs: 10,
nanos: 0,
)),
duration: Some(10),
),
cat_ids: [Natural],
)),

View File

@ -8,10 +8,7 @@ ItemDef(
kind: Saturation,
data: (
strength: 1.0,
duration: Some((
secs: 5,
nanos: 0,
)),
duration: Some(5),
),
cat_ids: [Natural],
)),

View File

@ -8,10 +8,7 @@ ItemDef(
kind: Saturation,
data: (
strength: 4.0,
duration: Some((
secs: 5,
nanos: 0,
)),
duration: Some(5),
),
cat_ids: [Natural],
)),

View File

@ -8,10 +8,7 @@ ItemDef(
kind: Saturation,
data: (
strength: 1.0,
duration: Some((
secs: 5,
nanos: 0,
)),
duration: Some(5),
),
cat_ids: [Natural],
)),

View File

@ -8,10 +8,7 @@ ItemDef(
kind: Saturation,
data: (
strength: 2.0,
duration: Some((
secs: 5,
nanos: 0,
)),
duration: Some(5),
),
cat_ids: [Natural],
)),

View File

@ -8,10 +8,7 @@ ItemDef(
kind: Saturation,
data: (
strength: 2.0,
duration: Some((
secs: 10,
nanos: 0,
)),
duration: Some(10),
),
cat_ids: [Natural],
)),

View File

@ -8,10 +8,7 @@ ItemDef(
kind: Saturation,
data: (
strength: 1.0,
duration: Some((
secs: 5,
nanos: 0,
)),
duration: Some(5),
),
cat_ids: [Natural],
)),

View File

@ -8,10 +8,7 @@ ItemDef(
kind: Frenzied,
data: (
strength: 0.4,
duration: Some((
secs: 60,
nanos: 0,
)),
duration: Some(60),
),
cat_ids: [Natural],
)),
@ -19,10 +16,7 @@ ItemDef(
kind: Cursed,
data: (
strength: 0.35,
duration: Some((
secs: 60,
nanos: 0,
)),
duration: Some(60),
),
cat_ids: [Natural],
)),

View File

@ -8,10 +8,7 @@ ItemDef(
kind: Saturation,
data: (
strength: 2.0,
duration: Some((
secs: 10,
nanos: 0,
)),
duration: Some(10),
),
cat_ids: [Natural],
)),

View File

@ -8,10 +8,7 @@ ItemDef(
kind: Saturation,
data: (
strength: 1.0,
duration: Some((
secs: 5,
nanos: 0,
)),
duration: Some(5),
),
cat_ids: [Natural],
)),

View File

@ -8,10 +8,7 @@ ItemDef(
kind: Saturation,
data: (
strength: 3.0,
duration: Some((
secs: 5,
nanos: 0,
)),
duration: Some(5),
),
cat_ids: [Natural],
)),

View File

@ -2135,9 +2135,10 @@ impl Client {
return Err(Error::Other("Failed to find entity from uid.".into()));
}
},
ServerGeneral::TimeOfDay(time_of_day, calendar) => {
ServerGeneral::TimeOfDay(time_of_day, calendar, time) => {
self.target_time_of_day = Some(time_of_day);
*self.state.ecs_mut().write_resource() = calendar;
*self.state.ecs_mut().write_resource() = time;
},
ServerGeneral::EntitySync(entity_sync_package) => {
self.state

View File

@ -11,7 +11,7 @@ use common::{
lod,
outcome::Outcome,
recipe::{ComponentRecipeBook, RecipeBook},
resources::TimeOfDay,
resources::{Time, TimeOfDay},
terrain::{Block, TerrainChunk, TerrainChunkMeta, TerrainChunkSize},
trade::{PendingTrade, SitePrices, TradeId, TradeResult},
uid::Uid,
@ -192,7 +192,7 @@ pub enum ServerGeneral {
ChatMsg(comp::ChatMsg),
ChatMode(comp::ChatMode),
SetPlayerEntity(Uid),
TimeOfDay(TimeOfDay, Calendar),
TimeOfDay(TimeOfDay, Calendar, Time),
EntitySync(sync::EntitySyncPackage),
CompSync(sync::CompSyncPackage<EcsCompPacket>, u64),
CreateEntity(sync::EntityPackage<EcsCompPacket>),
@ -340,7 +340,7 @@ impl ServerMsg {
| ServerGeneral::ChatMsg(_)
| ServerGeneral::ChatMode(_)
| ServerGeneral::SetPlayerEntity(_)
| ServerGeneral::TimeOfDay(_, _)
| ServerGeneral::TimeOfDay(_, _, _)
| ServerGeneral::EntitySync(_)
| ServerGeneral::CompSync(_, _)
| ServerGeneral::CreateEntity(_)

View File

@ -27,7 +27,7 @@ use crate::{comp::Group, resources::Time};
#[cfg(not(target_arch = "wasm32"))]
use specs::{saveload::MarkerAllocator, Entity as EcsEntity, ReadStorage};
#[cfg(not(target_arch = "wasm32"))]
use std::{ops::MulAssign, time::Duration};
use std::ops::MulAssign;
#[cfg(not(target_arch = "wasm32"))] use vek::*;
#[cfg(not(target_arch = "wasm32"))]
@ -363,6 +363,7 @@ impl Attack {
emit(ServerEvent::Buff {
entity: target.entity,
buff_change: BuffChange::Add(b.to_buff(
time,
attacker.map(|a| a.uid),
applied_damage,
strength_modifier,
@ -529,6 +530,7 @@ impl Attack {
emit(ServerEvent::Buff {
entity: target.entity,
buff_change: BuffChange::Add(b.to_buff(
time,
attacker.map(|a| a.uid),
accumulated_damage,
strength_modifier,
@ -1027,7 +1029,7 @@ impl MulAssign<f32> for CombatBuffStrength {
#[cfg(not(target_arch = "wasm32"))]
impl CombatBuff {
fn to_buff(self, uid: Option<Uid>, damage: f32, strength_modifier: f32) -> Buff {
fn to_buff(self, time: Time, uid: Option<Uid>, damage: f32, strength_modifier: f32) -> Buff {
// TODO: Generate BufCategoryId vec (probably requires damage overhaul?)
let source = if let Some(uid) = uid {
BuffSource::Character { by: uid }
@ -1038,11 +1040,12 @@ impl CombatBuff {
self.kind,
BuffData::new(
self.strength.to_strength(damage, strength_modifier),
Some(Duration::from_secs_f32(self.dur_secs)),
Some(self.dur_secs as f64),
None,
),
Vec::new(),
source,
time,
)
}
}

View File

@ -728,7 +728,7 @@ pub enum CharacterAbility {
recover_duration: f32,
buff_kind: buff::BuffKind,
buff_strength: f32,
buff_duration: Option<f32>,
buff_duration: Option<f64>,
energy_cost: f32,
#[serde(default)]
meta: AbilityMeta,
@ -2051,7 +2051,7 @@ impl CharacterAbility {
if let Ok(level) = skillset.skill_level(Sceptre(HDuration)) {
auras.iter_mut().for_each(|ref mut aura| {
if let Some(ref mut duration) = aura.duration {
*duration *= modifiers.duration.powi(level.into());
*duration *= modifiers.duration.powi(level.into()) as f64;
}
});
}
@ -2078,7 +2078,7 @@ impl CharacterAbility {
if let Ok(level) = skillset.skill_level(Sceptre(ADuration)) {
auras.iter_mut().for_each(|ref mut aura| {
if let Some(ref mut duration) = aura.duration {
*duration *= modifiers.duration.powi(level.into());
*duration *= modifiers.duration.powi(level.into()) as f64;
}
});
}
@ -2667,7 +2667,7 @@ impl From<(&CharacterAbility, AbilityInfo, &JoinData<'_>)> for CharacterState {
recover_duration: Duration::from_secs_f32(*recover_duration),
buff_kind: *buff_kind,
buff_strength: *buff_strength,
buff_duration: buff_duration.map(Duration::from_secs_f32),
buff_duration: *buff_duration,
ability_info,
},
timer: Duration::default(),

View File

@ -142,7 +142,7 @@ impl Auras {
pub struct AuraBuffConstructor {
pub kind: BuffKind,
pub strength: f32,
pub duration: Option<f32>,
pub duration: Option<f64>,
pub category: BuffCategory,
}
@ -158,7 +158,7 @@ impl AuraBuffConstructor {
kind: self.kind,
data: BuffData {
strength: self.strength,
duration: self.duration.map(Duration::from_secs_f32),
duration: self.duration,
delay: None,
},
category: self.category,

View File

@ -1,6 +1,6 @@
#![allow(clippy::nonstandard_macro_braces)] //tmp as of false positive !?
use crate::uid::Uid;
use core::{cmp::Ordering, time::Duration};
use crate::{resources::Time, uid::Uid};
use core::cmp::Ordering;
#[cfg(not(target_arch = "wasm32"))]
use hashbrown::HashMap;
use itertools::Either;
@ -143,13 +143,13 @@ impl BuffKind {
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
pub struct BuffData {
pub strength: f32,
pub duration: Option<Duration>,
pub delay: Option<Duration>,
pub duration: Option<f64>,
pub delay: Option<f64>,
}
#[cfg(not(target_arch = "wasm32"))]
impl BuffData {
pub fn new(strength: f32, duration: Option<Duration>, delay: Option<Duration>) -> Self {
pub fn new(strength: f32, duration: Option<f64>, delay: Option<f64>) -> Self {
Self {
strength,
duration,
@ -233,8 +233,8 @@ pub struct Buff {
pub kind: BuffKind,
pub data: BuffData,
pub cat_ids: Vec<BuffCategory>,
pub time: Option<Duration>,
pub delay: Option<Duration>,
pub end_time: Option<Time>,
pub start_time: Time,
pub effects: Vec<BuffEffect>,
pub source: BuffSource,
}
@ -270,179 +270,125 @@ impl Buff {
data: BuffData,
cat_ids: Vec<BuffCategory>,
source: BuffSource,
time: Time,
) -> Self {
// Normalized nonlinear scaling
let nn_scaling = |a| a / (a + 0.5);
let instance = rand::random();
let (effects, time) = match kind {
BuffKind::Bleeding => (
vec![BuffEffect::HealthChangeOverTime {
rate: -data.strength,
accumulated: 0.0,
kind: ModifierKind::Additive,
instance,
}],
data.duration,
),
BuffKind::Regeneration | BuffKind::Saturation | BuffKind::Potion => (
let effects = match kind {
BuffKind::Bleeding => vec![BuffEffect::HealthChangeOverTime {
rate: -data.strength,
accumulated: 0.0,
kind: ModifierKind::Additive,
instance,
}],
BuffKind::Regeneration | BuffKind::Saturation | BuffKind::Potion => {
vec![BuffEffect::HealthChangeOverTime {
rate: data.strength,
accumulated: 0.0,
kind: ModifierKind::Additive,
instance,
}],
data.duration,
),
BuffKind::CampfireHeal => (
vec![BuffEffect::HealthChangeOverTime {
rate: data.strength,
accumulated: 0.0,
kind: ModifierKind::Fractional,
instance,
}],
data.duration,
),
BuffKind::Cursed => (
vec![
BuffEffect::MaxHealthChangeOverTime {
rate: -1.0,
kind: ModifierKind::Additive,
target_fraction: 1.0 - data.strength,
achieved_fraction: None,
},
BuffEffect::HealthChangeOverTime {
rate: -1.0,
accumulated: 0.0,
kind: ModifierKind::Additive,
instance,
},
],
data.duration,
),
BuffKind::EnergyRegen => (
vec![BuffEffect::EnergyChangeOverTime {
rate: data.strength,
accumulated: 0.0,
}]
},
BuffKind::CampfireHeal => vec![BuffEffect::HealthChangeOverTime {
rate: data.strength,
accumulated: 0.0,
kind: ModifierKind::Fractional,
instance,
}],
BuffKind::Cursed => vec![
BuffEffect::MaxHealthChangeOverTime {
rate: -1.0,
kind: ModifierKind::Additive,
}],
data.duration,
),
BuffKind::IncreaseMaxEnergy => (
vec![BuffEffect::MaxEnergyModifier {
value: data.strength,
kind: ModifierKind::Additive,
}],
data.duration,
),
BuffKind::IncreaseMaxHealth => (
vec![BuffEffect::MaxHealthModifier {
value: data.strength,
kind: ModifierKind::Additive,
}],
data.duration,
),
BuffKind::Invulnerability => (vec![BuffEffect::DamageReduction(1.0)], data.duration),
BuffKind::ProtectingWard => (
vec![BuffEffect::DamageReduction(
// Causes non-linearity in effect strength, but necessary
// to allow for tool power and other things to affect the
// strength. 0.5 also still provides 50% damage reduction.
nn_scaling(data.strength),
)],
data.duration,
),
BuffKind::Burning => (
vec![BuffEffect::HealthChangeOverTime {
rate: -data.strength,
target_fraction: 1.0 - data.strength,
achieved_fraction: None,
},
BuffEffect::HealthChangeOverTime {
rate: -1.0,
accumulated: 0.0,
kind: ModifierKind::Additive,
instance,
}],
data.duration,
),
BuffKind::Poisoned => (
vec![BuffEffect::EnergyChangeOverTime {
rate: -data.strength,
},
],
BuffKind::EnergyRegen => vec![BuffEffect::EnergyChangeOverTime {
rate: data.strength,
accumulated: 0.0,
kind: ModifierKind::Additive,
}],
BuffKind::IncreaseMaxEnergy => vec![BuffEffect::MaxEnergyModifier {
value: data.strength,
kind: ModifierKind::Additive,
}],
BuffKind::IncreaseMaxHealth => vec![BuffEffect::MaxHealthModifier {
value: data.strength,
kind: ModifierKind::Additive,
}],
BuffKind::Invulnerability => vec![BuffEffect::DamageReduction(1.0)],
BuffKind::ProtectingWard => vec![BuffEffect::DamageReduction(
// Causes non-linearity in effect strength, but necessary
// to allow for tool power and other things to affect the
// strength. 0.5 also still provides 50% damage reduction.
nn_scaling(data.strength),
)],
BuffKind::Burning => vec![BuffEffect::HealthChangeOverTime {
rate: -data.strength,
accumulated: 0.0,
kind: ModifierKind::Additive,
instance,
}],
BuffKind::Poisoned => vec![BuffEffect::EnergyChangeOverTime {
rate: -data.strength,
accumulated: 0.0,
kind: ModifierKind::Additive,
}],
BuffKind::Crippled => vec![
BuffEffect::MovementSpeed(1.0 - nn_scaling(data.strength)),
BuffEffect::HealthChangeOverTime {
rate: -data.strength * 4.0,
accumulated: 0.0,
kind: ModifierKind::Additive,
}],
data.duration,
),
BuffKind::Crippled => (
vec![
BuffEffect::MovementSpeed(1.0 - nn_scaling(data.strength)),
BuffEffect::HealthChangeOverTime {
rate: -data.strength * 4.0,
accumulated: 0.0,
kind: ModifierKind::Additive,
instance,
},
],
data.duration,
),
BuffKind::Frenzied => (
vec![
BuffEffect::MovementSpeed(1.0 + data.strength),
BuffEffect::HealthChangeOverTime {
rate: data.strength * 10.0,
accumulated: 0.0,
kind: ModifierKind::Additive,
instance,
},
],
data.duration,
),
BuffKind::Frozen => (
vec![
BuffEffect::MovementSpeed(f32::powf(1.0 - nn_scaling(data.strength), 1.1)),
BuffEffect::AttackSpeed(1.0 - nn_scaling(data.strength)),
],
data.duration,
),
BuffKind::Wet => (
vec![BuffEffect::GroundFriction(1.0 - nn_scaling(data.strength))],
data.duration,
),
BuffKind::Ensnared => (
vec![BuffEffect::MovementSpeed(1.0 - nn_scaling(data.strength))],
data.duration,
),
BuffKind::Hastened => (
vec![
BuffEffect::MovementSpeed(1.0 + data.strength),
BuffEffect::AttackSpeed(1.0 + data.strength),
],
data.duration,
),
BuffKind::Fortitude => (
vec![BuffEffect::PoiseReduction(data.strength)],
data.duration,
),
BuffKind::Parried => (vec![BuffEffect::AttackSpeed(0.5)], data.duration),
BuffKind::PotionSickness => (
vec![BuffEffect::HealReduction {
rate: data.strength,
}],
data.duration,
),
instance,
},
],
BuffKind::Frenzied => vec![
BuffEffect::MovementSpeed(1.0 + data.strength),
BuffEffect::HealthChangeOverTime {
rate: data.strength * 10.0,
accumulated: 0.0,
kind: ModifierKind::Additive,
instance,
},
],
BuffKind::Frozen => vec![
BuffEffect::MovementSpeed(f32::powf(1.0 - nn_scaling(data.strength), 1.1)),
BuffEffect::AttackSpeed(1.0 - nn_scaling(data.strength)),
],
BuffKind::Wet => vec![BuffEffect::GroundFriction(1.0 - nn_scaling(data.strength))],
BuffKind::Ensnared => vec![BuffEffect::MovementSpeed(1.0 - nn_scaling(data.strength))],
BuffKind::Hastened => vec![
BuffEffect::MovementSpeed(1.0 + data.strength),
BuffEffect::AttackSpeed(1.0 + data.strength),
],
BuffKind::Fortitude => vec![BuffEffect::PoiseReduction(data.strength)],
BuffKind::Parried => vec![BuffEffect::AttackSpeed(0.5)],
BuffKind::PotionSickness => vec![BuffEffect::HealReduction {
rate: data.strength,
}],
};
let start_time = Time(time.0 + data.delay.unwrap_or(0.0));
Buff {
kind,
data,
cat_ids,
time,
delay: data.delay,
start_time,
end_time: data.duration.map(|dur| Time(start_time.0 + dur)),
effects,
source,
}
}
/// Calculate how much time has elapsed since the buff was applied (if the
/// buff has a finite duration, otherwise insufficient information
/// exists to track that)
pub fn elapsed(&self) -> Option<Duration> {
self.data.duration.zip_with(self.time, |x, y| x - y)
}
/// Calculate how much time has elapsed since the buff was applied
pub fn elapsed(&self, time: Time) -> f64 { time.0 - self.start_time.0 }
}
#[cfg(not(target_arch = "wasm32"))]
@ -454,9 +400,9 @@ impl PartialOrd for Buff {
Some(Ordering::Greater)
} else if self.data.strength < other.data.strength {
Some(Ordering::Less)
} else if compare_duration(self.time, other.time) {
} else if compare_end_time(self.end_time, other.end_time) {
Some(Ordering::Greater)
} else if compare_duration(other.time, self.time) {
} else if compare_end_time(other.end_time, self.end_time) {
Some(Ordering::Less)
} else {
None
@ -465,14 +411,14 @@ impl PartialOrd for Buff {
}
#[cfg(not(target_arch = "wasm32"))]
fn compare_duration(a: Option<Duration>, b: Option<Duration>) -> bool {
a.map_or(true, |dur_a| b.map_or(false, |dur_b| dur_a > dur_b))
fn compare_end_time(a: Option<Time>, b: Option<Time>) -> bool {
a.map_or(true, |time_a| b.map_or(false, |time_b| time_a.0 > time_b.0))
}
#[cfg(not(target_arch = "wasm32"))]
impl PartialEq for Buff {
fn eq(&self, other: &Self) -> bool {
self.data.strength == other.data.strength && self.time == other.time
self.data.strength == other.data.strength && self.end_time == other.end_time
}
}
@ -540,11 +486,12 @@ impl Buffs {
}
}
pub fn force_insert(&mut self, id: BuffId, buff: Buff) -> BuffId {
fn force_insert(&mut self, id: BuffId, buff: Buff) -> BuffId {
let kind = buff.kind;
self.kinds.entry(kind).or_default().push(id);
self.buffs.insert(id, buff);
self.sort_kind(kind);
self.delay_queueable_buffs(kind);
id
}
@ -579,7 +526,6 @@ impl Buffs {
}
// Gets most powerful buff of a given kind
// pub fn get_active_kind(&self, kind: BuffKind) -> Buff
pub fn remove(&mut self, buff_id: BuffId) {
if let Some(kind) = self.buffs.remove(&buff_id) {
let kind = kind.kind;
@ -595,6 +541,29 @@ impl Buffs {
pub fn parts(&mut self) -> (&HashMap<BuffKind, Vec<BuffId>>, &mut HashMap<BuffId, Buff>) {
(&self.kinds, &mut self.buffs)
}
pub fn delay_queueable_buffs(&mut self, kind: BuffKind) {
let mut next_start_time: Option<Time> = None;
if let Some(buffs) = self.kinds.get(&kind) {
buffs.iter().for_each(|id| {
if let Some(buff) = self.buffs.get_mut(id) {
if let Some(next_start_time) = next_start_time {
// Delays buff to have a start time after the previous buff of this kind has
// ended.
// End time offset by the difference in time between old and new start time.
// Will technically cause buffs to "end early" if they have a weaker
// strength than a buff with an infinite duration, but this is fine since
// those buffs wouldn't matter anyways
buff.end_time = buff
.end_time
.map(|end| Time(end.0 + next_start_time.0 - buff.start_time.0));
buff.start_time = next_start_time;
}
next_start_time = buff.end_time;
}
})
}
}
}
pub type BuffId = u64;

View File

@ -9,7 +9,7 @@ use crate::{
},
link::Is,
mounting::Rider,
resources::DeltaTime,
resources::{DeltaTime, Time},
terrain::TerrainGrid,
uid::Uid,
};
@ -125,6 +125,7 @@ pub struct JoinData<'a> {
pub mass: &'a Mass,
pub density: &'a Density,
pub dt: &'a DeltaTime,
pub time: &'a Time,
pub controller: &'a Controller,
pub inputs: &'a ControllerInputs,
pub health: Option<&'a Health>,
@ -176,6 +177,7 @@ impl<'a> JoinData<'a> {
j: &'a JoinStruct<'a>,
updater: &'a LazyUpdate,
dt: &'a DeltaTime,
time: &'a Time,
msm: &'a MaterialStatManifest,
ability_map: &'a AbilityMap,
) -> Self {
@ -200,6 +202,7 @@ impl<'a> JoinData<'a> {
skill_set: j.skill_set,
updater,
dt,
time,
msm,
ability_map,
combo: j.combo,

View File

@ -27,7 +27,7 @@ pub struct StaticData {
/// Strength of the created buff
pub buff_strength: f32,
/// How long buff lasts
pub buff_duration: Option<Duration>,
pub buff_duration: Option<f64>,
/// What key is used to press ability
pub ability_info: AbilityInfo,
}
@ -69,6 +69,7 @@ impl CharacterBehavior for Data {
},
Vec::new(),
BuffSource::Character { by: *data.uid },
*data.time,
);
output_events.emit_server(ServerEvent::Buff {
entity: data.entity,

View File

@ -252,9 +252,9 @@ impl State {
ecs.insert(TimeOfDay(0.0));
ecs.insert(Calendar::default());
ecs.insert(WeatherGrid::new(Vec2::zero()));
ecs.insert(Time(0.0));
// Register unsynced resources used by the ECS.
ecs.insert(Time(0.0));
ecs.insert(DeltaTime(0.0));
ecs.insert(PlayerEntity(None));
ecs.insert(TerrainGrid::new(map_size_lg, default_chunk).unwrap());
@ -591,7 +591,12 @@ impl State {
span!(_guard, "tick", "State::tick");
// Change the time accordingly.
self.ecs.write_resource::<TimeOfDay>().0 += dt.as_secs_f64() * DAY_CYCLE_FACTOR;
self.ecs.write_resource::<Time>().0 += dt.as_secs_f64();
// TODO: This feels really hacky, no idea best way to handle this. Incrementing
// time by dt on client causes crashes sometimes, and this bool is supposedly
// only true on client
if !update_terrain_and_regions {
self.ecs.write_resource::<Time>().0 += dt.as_secs_f64();
}
// Update delta time.
// Beyond a delta time of MAX_DELTA_TIME, start lagging to avoid skipping

View File

@ -7,7 +7,7 @@ use common::{
Alignment, Aura, Auras, BuffKind, Buffs, CharacterState, Health, Player, Pos,
},
event::{Emitter, EventBus, ServerEvent},
resources::DeltaTime,
resources::{DeltaTime, Time},
uid::{Uid, UidAllocator},
};
use common_ecs::{Job, Origin, Phase, System};
@ -22,6 +22,7 @@ pub struct ReadData<'a> {
entities: Entities<'a>,
players: ReadStorage<'a, Player>,
dt: Read<'a, DeltaTime>,
time: Read<'a, Time>,
server_bus: Read<'a, EventBus<ServerEvent>>,
uid_allocator: Read<'a, UidAllocator>,
cached_spatial_grid: Read<'a, common::CachedSpatialGrid>,
@ -240,8 +241,9 @@ fn activate_aura(
.any(|cat_id| matches!(cat_id, BuffCategory::FromAura(_)))
&& buff.kind == kind
&& buff.data.strength >= data.strength
&& buff.time.map_or(true, |dur| {
data.duration.map_or(false, |dur_2| dur >= dur_2)
&& buff.end_time.map_or(true, |end| {
data.duration
.map_or(false, |dur| end.0 >= read_data.time.0 + dur)
})
});
if emit_buff {
@ -252,6 +254,7 @@ fn activate_aura(
data,
vec![category, BuffCategory::FromAura(true)],
source,
*read_data.time,
)),
});
}

View File

@ -19,13 +19,11 @@ use common::{
};
use common_base::prof_span;
use common_ecs::{Job, Origin, ParMode, Phase, System};
use hashbrown::HashMap;
use rayon::iter::ParallelIterator;
use specs::{
saveload::MarkerAllocator, shred::ResourceId, Entities, Entity, Join, ParJoin, Read,
ReadExpect, ReadStorage, SystemData, World, WriteStorage,
};
use std::time::Duration;
#[derive(SystemData)]
pub struct ReadData<'a> {
@ -146,9 +144,10 @@ impl<'a> System<'a> for Sys {
entity,
buff_change: BuffChange::Add(Buff::new(
BuffKind::Ensnared,
BuffData::new(1.0, Some(Duration::from_secs_f32(1.0)), None),
BuffData::new(1.0, Some(1.0), None),
Vec::new(),
BuffSource::World,
*read_data.time,
)),
});
}
@ -161,9 +160,10 @@ impl<'a> System<'a> for Sys {
entity,
buff_change: BuffChange::Add(Buff::new(
BuffKind::Bleeding,
BuffData::new(1.0, Some(Duration::from_secs_f32(6.0)), None),
BuffData::new(1.0, Some(6.0), None),
Vec::new(),
BuffSource::World,
*read_data.time,
)),
});
}
@ -176,9 +176,10 @@ impl<'a> System<'a> for Sys {
entity,
buff_change: BuffChange::Add(Buff::new(
BuffKind::Bleeding,
BuffData::new(15.0, Some(Duration::from_secs_f32(0.1)), None),
BuffData::new(15.0, Some(0.1), None),
Vec::new(),
BuffSource::World,
*read_data.time,
)),
});
// When standing on IceSpike also apply Frozen
@ -186,9 +187,10 @@ impl<'a> System<'a> for Sys {
entity,
buff_change: BuffChange::Add(Buff::new(
BuffKind::Frozen,
BuffData::new(0.2, Some(Duration::from_secs_f32(1.0)), None),
BuffData::new(0.2, Some(1.0), None),
Vec::new(),
BuffSource::World,
*read_data.time,
)),
});
}
@ -207,6 +209,7 @@ impl<'a> System<'a> for Sys {
BuffData::new(20.0, None, None),
vec![BuffCategory::Natural],
BuffSource::World,
*read_data.time,
)),
});
} else if matches!(
@ -225,31 +228,12 @@ impl<'a> System<'a> for Sys {
}
}
let (buff_comp_kinds, buff_comp_buffs): (
&HashMap<BuffKind, Vec<BuffId>>,
&mut HashMap<BuffId, Buff>,
) = buff_comp.parts();
let mut expired_buffs = Vec::<BuffId>::new();
// For each buff kind present on entity, if the buff kind queues, only ticks
// duration of strongest buff of that kind, else it ticks durations of all buffs
// of that kind. Any buffs whose durations expire are marked expired.
for (kind, ids) in buff_comp_kinds.iter() {
if kind.queues() {
if let Some((Some(buff), id)) =
ids.first().map(|id| (buff_comp_buffs.get_mut(id), id))
{
tick_buff(*id, buff, dt, |id| expired_buffs.push(id));
}
} else {
for (id, buff) in buff_comp_buffs
.iter_mut()
.filter(|(i, _)| ids.iter().any(|id| id == *i))
{
tick_buff(*id, buff, dt, |id| expired_buffs.push(id));
}
buff_comp.buffs.iter().for_each(|(id, buff)| {
if buff.end_time.map_or(false, |end| end.0 < read_data.time.0) {
expired_buffs.push(*id)
}
}
});
let damage_reduction = Damage::compute_damage_reduction(
None,
@ -288,7 +272,7 @@ impl<'a> System<'a> for Sys {
for buff_id in active_buff_ids.into_iter() {
if let Some(buff) = buff_comp.buffs.get_mut(&buff_id) {
// Skip the effect of buffs whose start delay hasn't expired.
if buff.delay.is_some() {
if buff.start_time.0 > read_data.time.0 {
continue;
}
// Get buff owner?
@ -303,7 +287,7 @@ impl<'a> System<'a> for Sys {
execute_effect(
effect,
buff.kind,
buff.time,
buff.end_time,
&read_data,
&mut stat,
health,
@ -312,6 +296,7 @@ impl<'a> System<'a> for Sys {
buff_owner,
&mut server_emitter,
dt,
*read_data.time,
);
}
}
@ -347,7 +332,7 @@ impl<'a> System<'a> for Sys {
fn execute_effect(
effect: &mut BuffEffect,
buff_kind: BuffKind,
buff_time: Option<std::time::Duration>,
buff_end_time: Option<Time>,
read_data: &ReadData,
stat: &mut Stats,
health: &Health,
@ -356,6 +341,7 @@ fn execute_effect(
buff_owner: Option<Uid>,
server_emitter: &mut Emitter<ServerEvent>,
dt: f32,
time: Time,
) {
match effect {
BuffEffect::HealthChangeOverTime {
@ -368,7 +354,7 @@ fn execute_effect(
// Apply health change only once per second, per health, or
// when a buff is removed
if accumulated.abs() > rate.abs().min(1.0)
|| buff_time.map_or(false, |dur| dur == Duration::default())
|| buff_end_time.map_or(false, |end| end.0 < time.0)
{
let (cause, by) = if *accumulated != 0.0 {
(Some(DamageSource::Buff(buff_kind)), buff_owner)
@ -413,7 +399,7 @@ fn execute_effect(
// Apply energy change only once per second, per energy, or
// when a buff is removed
if accumulated.abs() > rate.abs().min(10.0)
|| buff_time.map_or(false, |dur| dur == Duration::default())
|| buff_end_time.map_or(false, |end| end.0 < time.0)
{
let amount = match *kind {
ModifierKind::Additive => *accumulated,
@ -523,30 +509,3 @@ fn execute_effect(
},
};
}
fn tick_buff(id: u64, buff: &mut Buff, dt: f32, mut expire_buff: impl FnMut(u64)) {
// If a buff is recently applied from an aura, do not tick duration
if buff
.cat_ids
.iter()
.any(|cat_id| matches!(cat_id, BuffCategory::FromAura(true)))
{
return;
}
if let Some(remaining_delay) = buff.delay {
buff.delay = remaining_delay.checked_sub(Duration::from_secs_f32(dt));
}
if let Some(remaining_time) = &mut buff.time {
if let Some(new_duration) = remaining_time.checked_sub(Duration::from_secs_f32(dt)) {
// The buff still continues.
*remaining_time = new_duration;
} else {
// checked_sub returns None when remaining time
// went below 0, so set to 0
*remaining_time = Duration::default();
// The buff has expired.
// Remove it.
expire_buff(id);
}
}
}

View File

@ -207,6 +207,7 @@ impl<'a> System<'a> for Sys {
&join_struct,
&read_data.lazy_update,
&read_data.dt,
&read_data.time,
&read_data.msm,
&read_data.ability_map,
);
@ -226,6 +227,7 @@ impl<'a> System<'a> for Sys {
&join_struct,
&read_data.lazy_update,
&read_data.dt,
&read_data.time,
&read_data.msm,
&read_data.ability_map,
);

View File

@ -558,8 +558,7 @@ impl<'a> AgentData<'a> {
Effect::Buff(BuffEffect { kind, data, .. })
if matches!(kind, Regeneration | Saturation | Potion) =>
{
value += data.strength
* data.duration.map_or(0.0, |d| d.as_secs() as f32);
value += data.strength * data.duration.map_or(0.0, |d| d as f32);
},
Effect::Buff(BuffEffect { kind, .. })
if matches!(kind, PotionSickness) =>

View File

@ -203,7 +203,7 @@ impl Client {
| ServerGeneral::ChatMsg(_)
| ServerGeneral::ChatMode(_)
| ServerGeneral::SetPlayerEntity(_)
| ServerGeneral::TimeOfDay(_, _)
| ServerGeneral::TimeOfDay(_, _, _)
| ServerGeneral::EntitySync(_)
| ServerGeneral::CompSync(_, _)
| ServerGeneral::CreateEntity(_)

View File

@ -53,7 +53,7 @@ use common_net::{
sync::WorldSyncExt,
};
use common_state::{BuildAreaError, BuildAreas};
use core::{cmp::Ordering, convert::TryFrom, time::Duration};
use core::{cmp::Ordering, convert::TryFrom};
use hashbrown::{HashMap, HashSet};
use humantime::Duration as HumanDuration;
use rand::{thread_rng, Rng};
@ -1045,6 +1045,7 @@ fn handle_time(
};
server.state.mut_resource::<TimeOfDay>().0 = new_time;
let time = server.state.ecs().read_resource::<Time>();
// Update all clients with the new TimeOfDay (without this they would have to
// wait for the next 100th tick to receive the update).
@ -1056,6 +1057,7 @@ fn handle_time(
client.prepare(ServerGeneral::TimeOfDay(
TimeOfDay(new_time),
(*calendar).clone(),
*time,
))
});
let _ = client.send_prepared(&msg);
@ -1476,7 +1478,7 @@ fn handle_spawn_campfire(
Aura::new(
AuraKind::Buff {
kind: BuffKind::CampfireHeal,
data: BuffData::new(0.02, Some(Duration::from_secs(1)), None),
data: BuffData::new(0.02, Some(1.0), None),
category: BuffCategory::Natural,
source: BuffSource::World,
},
@ -1487,7 +1489,7 @@ fn handle_spawn_campfire(
Aura::new(
AuraKind::Buff {
kind: BuffKind::Burning,
data: BuffData::new(2.0, Some(Duration::from_secs(10)), None),
data: BuffData::new(2.0, Some(10.0), None),
category: BuffCategory::Natural,
source: BuffSource::World,
},
@ -3516,7 +3518,7 @@ fn handle_buff(
) -> CmdResult<()> {
if let (Some(buff), strength, duration) = parse_cmd_args!(args, String, f32, f64) {
let strength = strength.unwrap_or(0.01);
let duration = Duration::from_secs_f64(duration.unwrap_or(1.0));
let duration = duration.unwrap_or(1.0);
let buffdata = BuffData::new(strength, Some(duration), None);
if buff != "all" {
cast_buff(&buff, buffdata, server, target)
@ -3535,8 +3537,15 @@ fn cast_buff(kind: &str, data: BuffData, server: &mut Server, target: EcsEntity)
if let Some(buffkind) = parse_buffkind(kind) {
let ecs = &server.state.ecs();
let mut buffs_all = ecs.write_storage::<comp::Buffs>();
let time = ecs.read_resource::<Time>();
if let Some(mut buffs) = buffs_all.get_mut(target) {
buffs.insert(Buff::new(buffkind, data, vec![], BuffSource::Command));
buffs.insert(Buff::new(
buffkind,
data,
vec![],
BuffSource::Command,
*time,
));
}
Ok(())
} else {

View File

@ -21,7 +21,6 @@ use common::{
};
use common_net::{msg::ServerGeneral, sync::WorldSyncExt};
use specs::{Builder, Entity as EcsEntity, WorldExt};
use std::time::Duration;
use vek::{Rgb, Vec3};
use super::group_manip::update_map_markers;
@ -285,7 +284,7 @@ pub fn handle_create_waypoint(server: &mut Server, pos: Vec3<f32>) {
Aura::new(
AuraKind::Buff {
kind: BuffKind::CampfireHeal,
data: BuffData::new(0.02, Some(Duration::from_secs(1)), None),
data: BuffData::new(0.02, Some(1.0), None),
category: BuffCategory::Natural,
source: BuffSource::World,
},
@ -296,7 +295,7 @@ pub fn handle_create_waypoint(server: &mut Server, pos: Vec3<f32>) {
Aura::new(
AuraKind::Buff {
kind: BuffKind::Burning,
data: BuffData::new(2.0, Some(Duration::from_secs(10)), None),
data: BuffData::new(2.0, Some(10.0), None),
category: BuffCategory::Natural,
source: BuffSource::World,
},

View File

@ -1310,17 +1310,19 @@ pub fn handle_parry_hook(server: &Server, defender: EcsEntity, attacker: Option<
.map_or(0.5, |dur| dur.as_secs_f32())
.max(0.5)
.mul(2.0);
let data = buff::BuffData::new(1.0, Some(Duration::from_secs_f32(duration)), None);
let data = buff::BuffData::new(1.0, Some(duration as f64), None);
let source = if let Some(uid) = ecs.read_storage::<Uid>().get(defender) {
BuffSource::Character { by: *uid }
} else {
BuffSource::World
};
let time = ecs.read_resource::<Time>();
let buff = buff::Buff::new(
BuffKind::Parried,
data,
vec![buff::BuffCategory::Physical],
source,
*time,
);
server_eventbus.emit_now(ServerEvent::Buff {
entity: attacker,

View File

@ -38,7 +38,7 @@ use specs::{
saveload::MarkerAllocator, Builder, Entity as EcsEntity, EntityBuilder as EcsEntityBuilder,
Join, WorldExt,
};
use std::time::{Duration, Instant};
use std::time::Instant;
use tracing::{trace, warn};
use vek::*;
@ -222,6 +222,7 @@ impl StateExt for State {
}
},
Effect::Buff(buff) => {
let time = self.ecs().read_resource::<Time>();
self.ecs()
.write_storage::<comp::Buffs>()
.get_mut(entity)
@ -231,6 +232,7 @@ impl StateExt for State {
buff.data,
buff.cat_ids,
comp::BuffSource::Item,
*time,
))
});
},
@ -431,7 +433,7 @@ impl StateExt for State {
.with(Auras::new(vec![Aura::new(
AuraKind::Buff {
kind: BuffKind::Invulnerability,
data: BuffData::new(1.0, Some(Duration::from_secs(1)), None),
data: BuffData::new(1.0, Some(1.0), None),
category: BuffCategory::Natural,
source: BuffSource::World,
},

View File

@ -10,7 +10,7 @@ use common::{
event::EventBus,
outcome::Outcome,
region::{Event as RegionEvent, RegionMap},
resources::{PlayerPhysicsSettings, TimeOfDay},
resources::{PlayerPhysicsSettings, Time, TimeOfDay},
terrain::TerrainChunkSize,
uid::Uid,
vol::RectVolSize,
@ -33,6 +33,7 @@ impl<'a> System<'a> for Sys {
Read<'a, PlayerPhysicsSettings>,
TrackedStorages<'a>,
ReadExpect<'a, TimeOfDay>,
ReadExpect<'a, Time>,
ReadExpect<'a, Calendar>,
ReadExpect<'a, RegionMap>,
ReadExpect<'a, UpdateTrackers>,
@ -64,6 +65,7 @@ impl<'a> System<'a> for Sys {
player_physics_settings,
tracked_storages,
time_of_day,
time,
calendar,
region_map,
trackers,
@ -416,7 +418,11 @@ impl<'a> System<'a> for Sys {
let mut tod_lazymsg = None;
for client in (&clients).join() {
let msg = tod_lazymsg.unwrap_or_else(|| {
client.prepare(ServerGeneral::TimeOfDay(*time_of_day, (*calendar).clone()))
client.prepare(ServerGeneral::TimeOfDay(
*time_of_day,
(*calendar).clone(),
*time,
))
});
// We don't care much about stream errors here since they could just represent
// network disconnection, which is handled elsewhere.

View File

@ -645,8 +645,8 @@ fn selected_entity_window(
buffs.buffs.iter().for_each(|(_, v)| {
ui.label(format!("{:?}", v.kind));
ui.label(
v.time.map_or("-".to_string(), |time| {
format!("{:?}", time)
v.end_time.map_or("-".to_string(), |end| {
format!("{:?}", end.0 - v.start_time.0)
}),
);
ui.label(format!("{:?}", v.source));

View File

@ -9,7 +9,10 @@ use crate::{
};
use i18n::Localization;
use common::comp::{BuffKind, Buffs, CharacterState, Energy, Health};
use common::{
comp::{BuffKind, Buffs, CharacterState, Energy, Health},
resources::Time,
};
use conrod_core::{
color,
image::Id,
@ -49,6 +52,7 @@ pub struct BuffsBar<'a> {
global_state: &'a GlobalState,
health: &'a Health,
energy: &'a Energy,
time: &'a Time,
}
impl<'a> BuffsBar<'a> {
@ -64,6 +68,7 @@ impl<'a> BuffsBar<'a> {
global_state: &'a GlobalState,
health: &'a Health,
energy: &'a Energy,
time: &'a Time,
) -> Self {
Self {
imgs,
@ -78,6 +83,7 @@ impl<'a> BuffsBar<'a> {
global_state,
health,
energy,
time,
}
}
}
@ -219,10 +225,9 @@ impl<'a> Widget for BuffsBar<'a> {
.enumerate()
.for_each(|(i, (((id, timer_id), mult_id), buff))| {
let max_duration = buff.kind.max_duration();
let current_duration = buff.dur;
let current_duration = buff.end_time.map(|end| end - self.time.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)
max_duration.map_or(1000.0, |max| cur / max * 1000.0)
}) as u32; // Percentage to determine which frame of the timer overlay is displayed
let buff_img = buff.kind.image(self.imgs);
let buff_widget = Image::new(buff_img).w_h(40.0, 40.0);
@ -236,13 +241,11 @@ impl<'a> Widget for BuffsBar<'a> {
);
buff_widget
.color(
if current_duration.map_or(false, |cur| cur.as_secs_f32() < 10.0) {
Some(pulsating_col)
} else {
Some(norm_col)
},
)
.color(if current_duration.map_or(false, |cur| cur < 10.0) {
Some(pulsating_col)
} else {
Some(norm_col)
})
.set(*id, ui);
if buff.multiplicity() > 1 {
Rectangle::fill_with([0.0, 0.0], MULTIPLICITY_COLOR.plain_contrast())
@ -260,7 +263,7 @@ impl<'a> Widget for BuffsBar<'a> {
}
// Create Buff tooltip
let (title, desc_txt) = buff.kind.title_description(localized_strings);
let remaining_time = buff.get_buff_time();
let remaining_time = buff.get_buff_time(*self.time);
let click_to_remove =
format!("<{}>", &localized_strings.get_msg("buff-remove"));
let desc = format!("{}\n\n{}\n\n{}", desc_txt, remaining_time, click_to_remove);
@ -304,10 +307,9 @@ impl<'a> Widget for BuffsBar<'a> {
.enumerate()
.for_each(|(i, (((id, timer_id), mult_id), debuff))| {
let max_duration = debuff.kind.max_duration();
let current_duration = debuff.dur;
let current_duration = debuff.end_time.map(|end| end - self.time.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)
max_duration.map_or(1000.0, |max| cur / max * 1000.0)
}) as u32; // Percentage to determine which frame of the timer overlay is displayed
let debuff_img = debuff.kind.image(self.imgs);
let debuff_widget = Image::new(debuff_img).w_h(40.0, 40.0);
@ -321,13 +323,11 @@ impl<'a> Widget for BuffsBar<'a> {
);
debuff_widget
.color(
if current_duration.map_or(false, |cur| cur.as_secs_f32() < 10.0) {
Some(pulsating_col)
} else {
Some(norm_col)
},
)
.color(if current_duration.map_or(false, |cur| cur < 10.0) {
Some(pulsating_col)
} else {
Some(norm_col)
})
.set(*id, ui);
if debuff.multiplicity() > 1 {
Rectangle::fill_with([0.0, 0.0], MULTIPLICITY_COLOR.plain_contrast())
@ -345,7 +345,7 @@ impl<'a> Widget for BuffsBar<'a> {
}
// Create Debuff tooltip
let (title, desc_txt) = debuff.kind.title_description(localized_strings);
let remaining_time = debuff.get_buff_time();
let remaining_time = debuff.get_buff_time(*self.time);
let desc = format!("{}\n\n{}", desc_txt, remaining_time);
Image::new(self.get_duration_image(duration_percentage))
.w_h(40.0, 40.0)
@ -405,11 +405,10 @@ impl<'a> Widget for BuffsBar<'a> {
buff_vec.iter().enumerate().for_each(
|(i, ((((id, timer_id), txt_id), mult_id), buff))| {
let max_duration = buff.kind.max_duration();
let current_duration = buff.dur;
let current_duration = buff.end_time.map(|end| end - self.time.0);
// Percentage to determine which frame of the timer overlay is displayed
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)
max_duration.map_or(1000.0, |max| cur / max * 1000.0)
}) as u32;
let buff_img = buff.kind.image(self.imgs);
let buff_widget = Image::new(buff_img).w_h(40.0, 40.0);
@ -422,13 +421,11 @@ impl<'a> Widget for BuffsBar<'a> {
0.0 + x as f64 * (42.0),
);
buff_widget
.color(
if current_duration.map_or(false, |cur| cur.as_secs_f32() < 10.0) {
Some(pulsating_col)
} else {
Some(norm_col)
},
)
.color(if current_duration.map_or(false, |cur| cur < 10.0) {
Some(pulsating_col)
} else {
Some(norm_col)
})
.set(*id, ui);
if buff.multiplicity() > 1 {
Rectangle::fill_with([0.0, 0.0], MULTIPLICITY_COLOR.plain_contrast())
@ -446,7 +443,7 @@ impl<'a> Widget for BuffsBar<'a> {
}
// Create Buff tooltip
let (title, desc_txt) = buff.kind.title_description(localized_strings);
let remaining_time = buff.get_buff_time();
let remaining_time = buff.get_buff_time(*self.time);
let click_to_remove =
format!("<{}>", &localized_strings.get_msg("buff-remove"));
let desc = if buff.is_buff {

View File

@ -17,6 +17,7 @@ use client::{self, Client};
use common::{
combat,
comp::{group::Role, inventory::item::MaterialStatManifest, invite::InviteKind, Stats},
resources::Time,
uid::{Uid, UidAllocator},
};
use common_net::sync::WorldSyncExt;
@ -83,6 +84,7 @@ pub struct Group<'a> {
global_state: &'a GlobalState,
tooltip_manager: &'a mut TooltipManager,
msm: &'a MaterialStatManifest,
time: &'a Time,
#[conrod(common_builder)]
common: widget::CommonBuilder,
@ -101,6 +103,7 @@ impl<'a> Group<'a> {
global_state: &'a GlobalState,
tooltip_manager: &'a mut TooltipManager,
msm: &'a MaterialStatManifest,
time: &'a Time,
) -> Self {
Self {
show,
@ -114,6 +117,7 @@ impl<'a> Group<'a> {
global_state,
tooltip_manager,
msm,
time,
common: widget::CommonBuilder::default(),
}
}
@ -532,11 +536,9 @@ impl<'a> Widget for Group<'a> {
let max_duration = buff.kind.max_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 current_duration = buff.end_time.map(|end| end - self.time.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
})
max_duration.map_or(1000.0, |max| cur / max * 1000.0)
}) as u32; // Percentage to determine which frame of the timer overlay is displayed
let buff_img = buff.kind.image(self.imgs);
let buff_widget = Image::new(buff_img).w_h(15.0, 15.0);
@ -551,20 +553,16 @@ impl<'a> Widget for Group<'a> {
};
prev_id = Some(id);
buff_widget
.color(
if current_duration
.map_or(false, |cur| cur.as_secs_f32() < 10.0)
{
Some(pulsating_col)
} else {
Some(norm_col)
},
)
.color(if current_duration.map_or(false, |cur| cur < 10.0) {
Some(pulsating_col)
} else {
Some(norm_col)
})
.set(id, ui);
// Create Buff tooltip
let (title, desc_txt) =
buff.kind.title_description(localized_strings);
let remaining_time = buff.get_buff_time();
let remaining_time = buff.get_buff_time(*self.time);
let desc = format!("{}\n\n{}", desc_txt, remaining_time);
Image::new(match duration_percentage as u64 {
875..=1000 => self.imgs.nothing, // 8/8

View File

@ -104,6 +104,7 @@ use common::{
link::Is,
mounting::Mount,
outcome::Outcome,
resources::Time,
slowjob::SlowJobPool,
terrain::{SpriteKind, TerrainChunk, UnlockKind},
trade::{ReducedInventory, TradeAction},
@ -481,7 +482,7 @@ impl<'a> BuffIconKind<'a> {
}
}
pub fn max_duration(&self) -> Option<Duration> {
pub fn max_duration(&self) -> Option<f64> {
match self {
Self::Buff { data, .. } => data.duration,
Self::Ability { .. } => None,
@ -563,7 +564,7 @@ impl<'a> Eq for BuffIconKind<'a> {}
pub struct BuffIcon<'a> {
kind: BuffIconKind<'a>,
is_buff: bool,
dur: Option<Duration>,
end_time: Option<f64>,
}
impl<'a> BuffIcon<'a> {
@ -574,9 +575,9 @@ impl<'a> BuffIcon<'a> {
}
}
pub fn get_buff_time(&self) -> String {
if let Some(dur) = self.dur {
format!("{:.0}s", dur.as_secs_f32())
pub fn get_buff_time(&self, time: Time) -> String {
if let Some(end) = self.end_time {
format!("{:.0}s", end - time.0)
} else {
"".to_string()
}
@ -600,7 +601,7 @@ impl<'a> BuffIcon<'a> {
Some(BuffIcon {
kind: BuffIconKind::Ability { ability_id: id },
is_buff: true,
dur: None,
end_time: None,
})
} else {
None
@ -619,7 +620,7 @@ impl<'a> BuffIcon<'a> {
multiplicity: count,
},
is_buff: buff.kind.is_buff(),
dur: buff.time,
end_time: buff.end_time.map(|end| end.0),
})
}
}
@ -1491,6 +1492,7 @@ impl Hud {
let alignments = ecs.read_storage::<comp::Alignment>();
let is_mount = ecs.read_storage::<Is<Mount>>();
let char_states = ecs.read_storage::<comp::CharacterState>();
let time = ecs.read_resource::<Time>();
// Check if there was a persistence load error of the skillset, and if so
// display a dialog prompt
@ -2335,6 +2337,7 @@ impl Hud {
},
_ => Vec::new(),
},
&time,
)
.x_y(0.0, 100.0)
.position_ingame(ingame_pos)
@ -2814,6 +2817,7 @@ impl Hud {
let buffs = ecs.read_storage::<comp::Buffs>();
let char_states = ecs.read_storage::<comp::CharacterState>();
let msm = ecs.read_resource::<MaterialStatManifest>();
let time = ecs.read_resource::<Time>();
match Buttons::new(
&self.imgs,
@ -2845,6 +2849,7 @@ impl Hud {
global_state,
tooltip_manager,
&msm,
&time,
)
.set(self.ids.group_window, ui_widgets)
{
@ -2928,6 +2933,7 @@ impl Hud {
let bodies = ecs.read_storage::<comp::Body>();
let poises = ecs.read_storage::<comp::Poise>();
let combos = ecs.read_storage::<comp::Combo>();
let time = ecs.read_resource::<Time>();
// Combo floater stuffs
self.floaters.combo_floater = self.floaters.combo_floater.map(|mut f| {
f.timer -= dt.as_secs_f64();
@ -3116,6 +3122,7 @@ impl Hud {
global_state,
health,
energy,
&time,
)
.set(self.ids.buffs, ui_widgets)
{

View File

@ -9,7 +9,10 @@ use crate::{
settings::{ControlSettings, InterfaceSettings},
ui::{fonts::Fonts, Ingameable},
};
use common::comp::{Buffs, CharacterState, Energy, Health, SpeechBubble, SpeechBubbleType};
use common::{
comp::{Buffs, CharacterState, Energy, Health, SpeechBubble, SpeechBubbleType},
resources::Time,
};
use conrod_core::{
color,
position::Align,
@ -96,6 +99,7 @@ pub struct Overhead<'a> {
fonts: &'a Fonts,
key_layout: &'a Option<KeyLayout>,
interaction_options: Vec<(GameInput, String)>,
time: &'a Time,
#[conrod(common_builder)]
common: widget::CommonBuilder,
@ -115,6 +119,7 @@ impl<'a> Overhead<'a> {
fonts: &'a Fonts,
key_layout: &'a Option<KeyLayout>,
interaction_options: Vec<(GameInput, String)>,
time: &'a Time,
) -> Self {
Self {
info,
@ -128,6 +133,7 @@ impl<'a> Overhead<'a> {
fonts,
key_layout,
interaction_options,
time,
common: widget::CommonBuilder::default(),
}
}
@ -264,11 +270,9 @@ impl<'a> Widget for Overhead<'a> {
.for_each(|(i, ((id, timer_id), buff))| {
// Limit displayed buffs
let max_duration = buff.kind.max_duration();
let current_duration = buff.dur;
let current_duration = buff.end_time.map(|end| end - self.time.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
})
max_duration.map_or(1000.0, |max| cur / max * 1000.0)
}) as u32; // Percentage to determine which frame of the timer overlay is displayed
let buff_img = buff.kind.image(self.imgs);
let buff_widget = Image::new(buff_img).w_h(20.0, 20.0);
@ -281,13 +285,11 @@ impl<'a> Widget for Overhead<'a> {
0.0 + x as f64 * (21.0),
);
buff_widget
.color(
if current_duration.map_or(false, |cur| cur.as_secs_f32() < 10.0) {
Some(pulsating_col)
} else {
Some(norm_col)
},
)
.color(if current_duration.map_or(false, |cur| cur < 10.0) {
Some(pulsating_col)
} else {
Some(norm_col)
})
.set(id, ui);
Image::new(match duration_percentage as u64 {

View File

@ -146,7 +146,7 @@ pub fn consumable_desc(effects: &[Effect], i18n: &Localization) -> Vec<String> {
let mut description = String::new();
if let Effect::Buff(buff) = effect {
let strength = buff.data.strength;
let dur_secs = buff.data.duration.map(|d| d.as_secs_f32());
let dur_secs = buff.data.duration.map(|d| d as f32);
let str_total = dur_secs.map_or(strength, |secs| strength * secs);
let format_float =

View File

@ -16,7 +16,7 @@ use common::{
},
figure::Segment,
outcome::Outcome,
resources::DeltaTime,
resources::{DeltaTime, Time},
spiral::Spiral2d,
states::{self, utils::StageSection},
terrain::{Block, SpriteKind, TerrainChunk, TerrainGrid},
@ -1301,7 +1301,7 @@ impl ParticleMgr {
.iter()
.filter_map(|id| buffs.buffs.get(id))
.any(|buff| {
matches!(buff.elapsed(), Some(dur) if Duration::from_secs(1) <= dur && dur <= Duration::from_secs_f32(1.5))
matches!(buff.elapsed(Time(time)), dur if (1.0..=1.5).contains(&dur))
})
{
multiplicity = 1;