diff --git a/reffect/src/addon/event.rs b/reffect/src/addon/event.rs index 321fa04..d4c7858 100644 --- a/reffect/src/addon/event.rs +++ b/reffect/src/addon/event.rs @@ -51,13 +51,14 @@ impl Addon { pub fn unload() { log::info!("Reffect v{VERSION} unload"); - { - let plugin = Self::lock(); - AddonSettings::new(&plugin.context).save(); - if plugin.context.settings.save_on_unload { - plugin.save_packs(); - } + let plugin = Self::lock(); + AddonSettings::new(&plugin.context).save(); + if plugin.context.settings.save_on_unload { + plugin.save_packs(); } + drop(plugin); + + Internal::deinit(); TextureManager::unload(); } diff --git a/reffect/src/elements/bar/progress.rs b/reffect/src/elements/bar/progress.rs index 6d0c3da..5650055 100644 --- a/reffect/src/elements/bar/progress.rs +++ b/reffect/src/elements/bar/progress.rs @@ -1,4 +1,7 @@ -use crate::{context::Context, trigger::ProgressActive}; +use crate::{ + context::Context, + trigger::{ProgressActive, ProgressValue}, +}; use serde::{Deserialize, Serialize}; use strum::{AsRefStr, EnumIter, VariantArray}; @@ -19,15 +22,18 @@ use strum::{AsRefStr, EnumIter, VariantArray}; Deserialize, )] pub enum Progress { + Intensity, + #[default] Duration, - Intensity, + + #[strum(serialize = "Secondary Duration")] + SecondaryDuration, } impl Progress { pub fn calc_progress(&self, ctx: &Context, active: &ProgressActive, max: u32) -> f32 { match self { - Self::Duration => active.progress_or_default(ctx.now), Self::Intensity => { if max > 0 { active.intensity() as f32 / max as f32 @@ -35,13 +41,18 @@ impl Progress { 0.0 } } + Self::Duration => active.progress_or_default(ProgressValue::Primary, ctx.now), + Self::SecondaryDuration => { + active.progress_or_default(ProgressValue::Secondary, ctx.now) + } } } pub fn progress_max(&self, active: &ProgressActive, max: u32) -> u32 { match self { - Self::Duration => active.max(), Self::Intensity => max, + Self::Duration => active.max(ProgressValue::Primary), + Self::SecondaryDuration => active.max(ProgressValue::Secondary), } } } diff --git a/reffect/src/elements/icon/mod.rs b/reffect/src/elements/icon/mod.rs index b28a52a..95e43d4 100644 --- a/reffect/src/elements/icon/mod.rs +++ b/reffect/src/elements/icon/mod.rs @@ -13,7 +13,7 @@ use crate::{ }, render_util::{debug_optional, draw_spinner_bg, draw_text_bg, Rect}, settings::icon::{DurationBarSettings, DurationTextSettings, StackTextSettings}, - trigger::ProgressActive, + trigger::{ProgressActive, ProgressValue}, }; use nexus::imgui::Ui; use serde::{Deserialize, Serialize}; @@ -98,7 +98,7 @@ impl Icon { // render duration bar if self.duration_bar { - if let Some(progress) = active.progress(ctx.now) { + if let Some(progress) = active.progress(ProgressValue::Secondary, ctx.now) { let DurationBarSettings { height, color } = ctx.settings.icon.duration_bar; let [start_x, _] = start; @@ -146,7 +146,7 @@ impl Icon { // render duration text if self.duration_text { - if let Some(remain) = active.current(ctx.now) { + if let Some(remain) = active.current(ProgressValue::Primary, ctx.now) { let DurationTextSettings { max_remain, scale, @@ -157,7 +157,7 @@ impl Icon { } = ctx.settings.icon.duration_text; if remain < max_remain { - let text = active.current_text(ctx.now, false); + let text = active.current_text(ProgressValue::Primary, ctx.now, false); let font_size = scale * small_size; let font_scale = font_size / ui.current_font_size(); diff --git a/reffect/src/elements/text/mod.rs b/reffect/src/elements/text/mod.rs index 7f3cdbd..5601701 100644 --- a/reffect/src/elements/text/mod.rs +++ b/reffect/src/elements/text/mod.rs @@ -1,8 +1,6 @@ mod decoration; mod props; -pub use self::{decoration::*, props::*}; - use super::{align::AlignHorizontal, Props, RenderState}; use crate::{ context::{Context, ContextUpdate}, @@ -12,10 +10,13 @@ use crate::{ debug_optional, draw_text_bg, helper, input_text_multi_with_menu, LoadedFont, Rect, }, tree::TreeNode, - trigger::ProgressActive, + trigger::{ProgressActive, ProgressValue}, }; use nexus::imgui::{InputTextFlags, Ui}; use serde::{Deserialize, Serialize}; +use std::{iter::Peekable, str::Chars}; + +pub use self::{decoration::*, props::*}; #[derive(Debug, Serialize, Deserialize)] #[serde(default)] @@ -47,6 +48,10 @@ impl Text { } } + pub fn load(&mut self) { + self.font.reload(); + } + fn process_text( &mut self, active: &ProgressActive, @@ -57,55 +62,105 @@ impl Text { let mut result = String::with_capacity(self.text.len()); // always same or larger size - let mut prefix = false; let is_timed = active.is_timed(); - for el in self.text.chars() { - if prefix { - prefix = false; - match el { - 'n' => result.push_str(&state.common.name), - 'i' | 's' => result.push_str(&active.intensity().to_string()), - 'I' => result.push_str(&Pretty(active.intensity()).to_string()), - 'c' | 'r' => { - result.push_str(&active.current_text(ctx.now, false)); - self.frequent = is_timed; - } - 'C' => { - result.push_str(&active.current_text(ctx.now, true)); - self.frequent = is_timed; - } - 'f' => result.push_str(&active.max_text(false)), - 'F' => result.push_str(&active.max_text(true)), - 'p' | 'P' => { - let progress = active.progress_or_default(ctx.now); - result.push_str(&format!("{:.1}", (100.0 * progress))); - self.frequent = is_timed; - } - PREFIX => result.push(PREFIX), - other => { - result.push(PREFIX); - result.push(other); + let mut iter = self.text.chars().peekable(); + while let Some(el) = iter.next() { + if el == PREFIX { + if let Some(next) = iter.peek() { + match next { + 'n' => { + iter.next(); + result.push_str(&state.common.name); + } + 'i' | 's' => { + iter.next(); + result.push_str(&active.intensity().to_string()); + } + 'I' => { + iter.next(); + result.push_str(&Pretty(active.intensity()).to_string()); + } + 'c' | 'r' => { + iter.next(); + result.push_str(&active.current_text( + Self::parse_value(&mut iter), + ctx.now, + false, + )); + self.frequent = is_timed; + } + 'C' => { + iter.next(); + result.push_str(&active.current_text( + Self::parse_value(&mut iter), + ctx.now, + true, + )); + self.frequent = is_timed; + } + 'f' => { + iter.next(); + result.push_str(&active.max_text(Self::parse_value(&mut iter), false)); + } + 'F' => { + iter.next(); + result.push_str(&active.max_text(Self::parse_value(&mut iter), true)); + } + 'p' | 'P' => { + iter.next(); + let progress = + active.progress_or_default(Self::parse_value(&mut iter), ctx.now); + result.push_str(&format!("{:.1}", (100.0 * progress))); + self.frequent = is_timed; + } + &PREFIX => { + iter.next(); + result.push(PREFIX); + } + _ => { + result.push(PREFIX); + } } + } else { + result.push(el); } - } else if el == PREFIX { - prefix = true; } else { result.push(el); } } - if prefix { - result.push(PREFIX); // handle ending prefix - } result } - fn calc_offset(&self, ui: &Ui, text: &str) -> [f32; 2] { - self.align.text_offset(ui, text, self.props.scale) + fn parse_value(iter: &mut Peekable) -> ProgressValue { + match iter.peek() { + Some('1') => { + iter.next(); + ProgressValue::Primary + } + Some('2') => { + iter.next(); + ProgressValue::Secondary + } + _ => ProgressValue::Primary, + } } - pub fn load(&mut self) { - self.font.reload(); + fn helper(ui: &Ui) { + helper(ui, || { + ui.text("Uppercase for pretty format"); + ui.text("Suffix 1 or 2 for primary/secondary"); + ui.text("%n for name"); + ui.text("%i for intensity"); + ui.text("%c for current amount"); + ui.text("%f for full/max amount"); + ui.text("%p for progress percent"); + ui.text("%% for % sign"); + }); + } + + fn calc_offset(&self, ui: &Ui, text: &str) -> [f32; 2] { + self.align.text_offset(ui, text, self.props.scale) } } @@ -158,15 +213,7 @@ impl RenderOptions for Text { ui.same_line(); ui.text("Text"); // own label to fix helper position - helper(ui, || { - ui.text("Uppercase for pretty format"); - ui.text("%n for name"); - ui.text("%i for intensity"); - ui.text("%c for current amount"); - ui.text("%f for full/max amount"); - ui.text("%p for progress percent"); - ui.text("%% for % sign"); - }); + Self::helper(ui); self.align.render_combo(ui); diff --git a/reffect/src/trigger/progress/active.rs b/reffect/src/trigger/progress/active.rs index 49f6fb6..4f5bc3d 100644 --- a/reffect/src/trigger/progress/active.rs +++ b/reffect/src/trigger/progress/active.rs @@ -1,8 +1,7 @@ use crate::{ fmt::Pretty, - internal::{Buff, Resource}, + internal::{Ability, Buff, Recharge, Resource, Skillbar}, }; -use reffect_internal::{Ability, Skillbar}; #[derive(Debug, Clone)] pub enum ProgressActive { @@ -10,12 +9,20 @@ pub enum ProgressActive { current: u32, max: u32, }, - Timed { + Buff { id: u32, - intensity: u32, + stacks: u32, duration: u32, end: u32, + }, + Ability { + id: u32, + ammo: u32, rate: f32, + duration: u32, + end: u32, + ammo_duration: u32, + ammo_end: u32, }, } @@ -25,88 +32,129 @@ impl ProgressActive { Self::Fixed { current: 1, max: 1 } } - /// Creates a new resource progress from percent & maximum. - pub const fn from_percent(progress: f32, max: u32) -> Self { - Self::Fixed { - current: (progress * max as f32) as u32, - max, - } - } - /// Creates an empty timed active progress. - pub const fn empy_timed(id: u32) -> Self { - Self::Timed { + pub const fn empy_buff(id: u32) -> Self { + Self::Buff { id, - intensity: 0, + stacks: 0, duration: 0, end: 0, - rate: 1.0, } } /// Creates new timed active progress from a buff. pub fn from_buff(id: u32, buff: &Buff) -> Self { - Self::Timed { + Self::Buff { id, - intensity: buff.stacks, + stacks: buff.stacks, duration: if buff.is_infinite() { u32::MAX } else { Self::time_between(buff.apply_time, buff.runout_time) }, end: buff.runout_time, + } + } + + /// Creates new timed active progress from a recharge. + pub fn from_recharge(recharge: &Recharge) -> Self { + let duration = recharge.recharge; + Self::Ability { + id: 0, + ammo: if duration > 0 { 1 } else { 0 }, + duration, + end: recharge.end(), + ammo_duration: 0, + ammo_end: 0, rate: 1.0, } } /// Creates new timed active progress from a skillbar and ability. pub fn from_ability(skillbar: &Skillbar, ability: &Ability) -> Self { - Self::Timed { + Self::Ability { id: ability.id, - intensity: ability.ammo, + ammo: ability.ammo, duration: ability.recharge, end: skillbar.last_update + Self::unscale(ability.recharge_remaining, skillbar.recharge_rate), + ammo_duration: ability.ammo_recharge, + ammo_end: skillbar.last_update + + Self::unscale(ability.ammo_recharge_remaining, skillbar.recharge_rate), rate: skillbar.recharge_rate, } } - /// Creates new timed active progress from a skillbar and ability. - pub fn from_ability_ammo(skillbar: &Skillbar, ability: &Ability) -> Self { - Self::Timed { - id: ability.id, - intensity: ability.ammo, - duration: ability.ammo_recharge, - end: skillbar.last_update - + Self::unscale(ability.ammo_recharge_remaining, skillbar.recharge_rate), - rate: skillbar.recharge_rate, + /// Creates a resource progress for edit mode. + pub const fn edit_resource(progress: f32, max: u32) -> Self { + Self::Fixed { + current: (progress * max as f32) as u32, + max, + } + } + + /// Creates a uff progress for edit mode. + pub const fn edit_buff(id: u32, progress: f32, now: u32) -> Self { + Self::Buff { + id, + stacks: (25.0 * progress) as u32, + duration: 5000, + end: now + (5000.0 * progress) as u32, + } + } + + /// Creates an ability progress for edit mode. + pub const fn edit_ability(id: u32, progress: f32, now: u32) -> Self { + // half speed + let slow = if progress < 0.5 { + 2.0 * progress + } else { + 2.0 * progress - 1.0 + }; + Self::Ability { + id, + ammo: (5.0 * progress) as u32, + duration: 5000, + end: now + (5000.0 * progress) as u32, + rate: 1.0, + ammo_duration: 10_000, + ammo_end: now + (10000.0 * slow) as u32, } } pub const fn id(&self) -> Option { match *self { Self::Fixed { .. } => None, - Self::Timed { id, .. } => Some(id), + Self::Buff { id, .. } | Self::Ability { id, .. } => Some(id), } } /// Whether the progress uses timestamps. pub const fn is_timed(&self) -> bool { - matches!(self, Self::Timed { .. }) + matches!(self, Self::Buff { .. } | Self::Ability { .. }) } /// Returns the intensity (alternative progress). pub const fn intensity(&self) -> u32 { match *self { Self::Fixed { current, .. } => current, - Self::Timed { intensity, .. } => intensity, + Self::Buff { stacks, .. } => stacks, + Self::Ability { ammo, .. } => ammo, + } + } + + /// Returns the current progress rate. + pub fn progress_rate(&self) -> f32 { + match *self { + Self::Fixed { .. } | Self::Buff { .. } => 1.0, + Self::Ability { rate, .. } => rate, } } /// Returns the current progress between `0.0` and `1.0`. - pub fn progress(&self, now: u32) -> Option { - self.current(now).map(|current| { - match (current, self.max()) { + pub fn progress(&self, value: ProgressValue, now: u32) -> Option { + self.current(value, now).map(|current| { + match (current, self.max(value)) { (0, 0) => 0.0, // treat 0/0 as 0% progress (_, 0) => 1.0, // treat x/0 as 100% progress (current, max) => current as f32 / max as f32, @@ -115,65 +163,91 @@ impl ProgressActive { } /// Returns the current progress between `0.0` and `1.0`. - pub fn progress_or_default(&self, now: u32) -> f32 { - self.progress(now).unwrap_or(1.0) - } - - pub fn progress_rate(&self) -> f32 { - match *self { - Self::Fixed { .. } => 1.0, - Self::Timed { rate, .. } => rate, - } + pub fn progress_or_default(&self, value: ProgressValue, now: u32) -> f32 { + self.progress(value, now).unwrap_or(1.0) } /// Returns the current amount in its native unit. - pub fn current(&self, now: u32) -> Option { + pub fn current(&self, value: ProgressValue, now: u32) -> Option { match *self { Self::Fixed { current, .. } => Some(current), - Self::Timed { end, rate, .. } => { - (end != u32::MAX).then(|| Self::time_between_scaled(now, end, rate)) + Self::Buff { end, .. } => (end != u32::MAX).then(|| Self::time_between(now, end)), + Self::Ability { + end, + ammo_end, + rate, + .. + } => { + let end = match value { + ProgressValue::Primary => end, + ProgressValue::Secondary => ammo_end, + }; + Some(Self::time_between_scaled(now, end, rate)) } } } /// Returns the current amount as text. - pub fn current_text(&self, now: u32, pretty: bool) -> String { + pub fn current_text(&self, value: ProgressValue, now: u32, pretty: bool) -> String { match *self { Self::Fixed { current, .. } => Pretty::string_if(current, pretty), - Self::Timed { end, rate, .. } => { + Self::Buff { end, .. } => { if end == u32::MAX { "?".into() } else { - let time = Self::time_between_scaled(now, end, rate); - if time > 0 { - Self::format_seconds(time) - } else { - String::new() - } + Self::duration_text(Self::time_between(now, end)) } } + Self::Ability { + end, + ammo_end, + rate, + .. + } => { + let end = match value { + ProgressValue::Primary => end, + ProgressValue::Secondary => ammo_end, + }; + Self::duration_text(Self::time_between_scaled(now, end, rate)) + } } } /// Returns the maximum amount in its native unit. - pub fn max(&self) -> u32 { + pub fn max(&self, value: ProgressValue) -> u32 { match *self { Self::Fixed { max, .. } => max, - Self::Timed { duration, .. } => duration, + Self::Buff { duration, .. } => duration, + Self::Ability { + duration, + ammo_duration, + .. + } => match value { + ProgressValue::Primary => duration, + ProgressValue::Secondary => ammo_duration, + }, } } /// Returns the maximum amount as text. - pub fn max_text(&self, pretty: bool) -> String { + pub fn max_text(&self, value: ProgressValue, pretty: bool) -> String { match *self { Self::Fixed { max, .. } => Pretty::string_if(max, pretty), - Self::Timed { duration, .. } => { + Self::Buff { duration, .. } => { if duration != u32::MAX { Self::format_seconds(duration) } else { "?".into() } } + Self::Ability { + duration, + ammo_duration, + .. + } => Self::format_seconds(match value { + ProgressValue::Primary => duration, + ProgressValue::Secondary => ammo_duration, + }), } } @@ -192,6 +266,14 @@ impl ProgressActive { fn format_seconds(time: u32) -> String { format!("{:.1}", time as f32 / 1000.0) } + + fn duration_text(time: u32) -> String { + if time > 0 { + Self::format_seconds(time) + } else { + String::new() + } + } } impl TryFrom for ProgressActive { @@ -206,3 +288,9 @@ impl TryFrom for ProgressActive { } } } + +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub enum ProgressValue { + Primary, + Secondary, +} diff --git a/reffect/src/trigger/progress/mod.rs b/reffect/src/trigger/progress/mod.rs index 11d9ac0..e8ee643 100644 --- a/reffect/src/trigger/progress/mod.rs +++ b/reffect/src/trigger/progress/mod.rs @@ -1,8 +1,9 @@ mod active; +mod slot; mod source; mod threshold; -pub use self::{active::*, source::*, threshold::*}; +pub use self::{active::*, slot::*, source::*, threshold::*}; use crate::{ context::{Context, ContextUpdate}, diff --git a/reffect/src/trigger/progress/slot.rs b/reffect/src/trigger/progress/slot.rs new file mode 100644 index 0000000..a21bfc9 --- /dev/null +++ b/reffect/src/trigger/progress/slot.rs @@ -0,0 +1,133 @@ +use super::ProgressActive; +use crate::internal::{self, Skillbar}; +use serde::{Deserialize, Serialize}; +use strum::{AsRefStr, Display, EnumCount, EnumIter, IntoStaticStr, VariantArray}; + +/// Skillbar slot. +#[derive( + Debug, + Default, + Clone, + Copy, + PartialEq, + Eq, + PartialOrd, + Ord, + Hash, + Display, + AsRefStr, + IntoStaticStr, + EnumCount, + EnumIter, + VariantArray, + Serialize, + Deserialize, +)] +pub enum Slot { + #[default] + #[strum(serialize = "Weapon Swap")] + WeaponSwap, + + #[strum(serialize = "Weapon 1")] + Weapon1, + + #[strum(serialize = "Weapon 2")] + Weapon2, + + #[strum(serialize = "Weapon 3")] + Weapon3, + + #[strum(serialize = "Weapon 4")] + Weapon4, + + #[strum(serialize = "Weapon 5")] + Weapon5, + + Heal, + + #[strum(serialize = "Utility 1")] + Utility1, + + #[strum(serialize = "Utility 2")] + Utility2, + + #[strum(serialize = "Utility 3")] + Utility3, + + Elite, + + #[strum(serialize = "Profession 1")] + Profession1, + + #[strum(serialize = "Profession 2")] + Profession2, + + #[strum(serialize = "Profession 3")] + Profession3, + + #[strum(serialize = "Profession 4")] + Profession4, + + #[strum(serialize = "Profession 5")] + Profession5, + + #[strum(serialize = "Special Action")] + SpecialAction, + + Mount, +} + +impl Slot { + pub fn get_id(&self, skillbar: &Skillbar) -> Option { + let slot = (*self).try_into().ok()?; + Some(skillbar.slot(slot)?.id) + } + + pub fn get_progress(&self, skillbar: &Skillbar) -> Option { + match *self { + Self::WeaponSwap => { + let swap = skillbar.weapon_swap.as_ref()?; + Some(ProgressActive::from_recharge(swap)) + } + Self::Profession1 => { + if let Some(swap) = &skillbar.legend_swap { + Some(ProgressActive::from_recharge(swap)) + } else { + let ability = skillbar.slot(internal::Slot::Profession1)?; + Some(ProgressActive::from_ability(skillbar, ability)) + } + } + slot => { + let ability = skillbar.slot(slot.try_into().expect("failed to convert slot"))?; + Some(ProgressActive::from_ability(skillbar, ability)) + } + } + } +} + +impl TryFrom for internal::Slot { + type Error = (); + + fn try_from(slot: Slot) -> Result { + match slot { + Slot::WeaponSwap => Err(()), + Slot::Weapon1 => Ok(Self::Weapon1), + Slot::Weapon2 => Ok(Self::Weapon2), + Slot::Weapon3 => Ok(Self::Weapon3), + Slot::Weapon4 => Ok(Self::Weapon4), + Slot::Weapon5 => Ok(Self::Weapon5), + Slot::Heal => Ok(Self::Heal), + Slot::Utility1 => Ok(Self::Utility1), + Slot::Utility2 => Ok(Self::Utility2), + Slot::Utility3 => Ok(Self::Utility3), + Slot::Elite => Ok(Self::Elite), + Slot::Profession1 => Ok(Self::Profession1), + Slot::Profession2 => Ok(Self::Profession2), + Slot::Profession3 => Ok(Self::Profession3), + Slot::Profession4 => Ok(Self::Profession4), + Slot::Profession5 => Ok(Self::Profession5), + Slot::SpecialAction => Ok(Self::SpecialAction), + Slot::Mount => Ok(Self::Mount), + } + } +} diff --git a/reffect/src/trigger/progress/source.rs b/reffect/src/trigger/progress/source.rs index 086cc03..339820b 100644 --- a/reffect/src/trigger/progress/source.rs +++ b/reffect/src/trigger/progress/source.rs @@ -1,13 +1,12 @@ -use super::ProgressActive; +use super::{ProgressActive, Slot}; use crate::{ action::Action, context::Context, - internal::{Interface, Internal}, + internal::{Buff, Category, Error, Interface, Internal, SkillInfo}, render::RenderOptions, render_util::{enum_combo, helper, impl_static_variants, input_skill_id, Validation}, }; use nexus::imgui::{ComboBoxFlags, InputTextFlags, Ui}; -use reffect_internal::{Buff, Category, Error, SkillInfo, Slot}; use serde::{Deserialize, Serialize}; use strum::{AsRefStr, EnumIter, IntoStaticStr}; @@ -35,15 +34,9 @@ pub enum ProgressSource { #[strum(serialize = "Ability Recharge")] Ability(u32), - #[strum(serialize = "Ability Ammo Recharge")] - AbilityAmmo(u32), - #[strum(serialize = "Slot Recharge")] SkillbarSlot(Slot), - #[strum(serialize = "Slot Ammo Recharge")] - SkillbarSlotAmmo(Slot), - /// Health. Health, @@ -82,7 +75,7 @@ impl ProgressSource { .get(&id) .filter(|buff| buff.runout_time > ctx.now) .map(|buff| ProgressActive::from_buff(id, buff)) - .unwrap_or_else(|| ProgressActive::empy_timed(id)) + .unwrap_or_else(|| ProgressActive::empy_buff(id)) }), Self::AnyBuff(ref ids) => ctx.own_buffs().map(|buffs| { let mut combined = Buff::empty(); @@ -95,26 +88,12 @@ impl ProgressSource { } ProgressActive::from_buff(ids.first().copied().unwrap_or(0), &combined) }), - Self::SkillbarSlot(slot) => { - let skillbar = ctx.own_skillbar()?; - let ability = skillbar.slot(slot)?; - Some(ProgressActive::from_ability(skillbar, ability)) - } - Self::SkillbarSlotAmmo(slot) => { - let skillbar = ctx.own_skillbar()?; - let ability = skillbar.slot(slot)?; - Some(ProgressActive::from_ability_ammo(skillbar, ability)) - } + Self::SkillbarSlot(slot) => slot.get_progress(ctx.own_skillbar()?), Self::Ability(id) => { let skillbar = ctx.own_skillbar()?; let ability = skillbar.ability(id)?; Some(ProgressActive::from_ability(skillbar, ability)) } - Self::AbilityAmmo(id) => { - let skillbar = ctx.own_skillbar()?; - let ability = skillbar.ability(id)?; - Some(ProgressActive::from_ability_ammo(skillbar, ability)) - } Self::Health => ctx.own_resources()?.health.clone().try_into().ok(), Self::Barrier => ctx.own_resources()?.barrier.clone().try_into().ok(), Self::Endurance => ctx.own_resources()?.endurance.clone().try_into().ok(), @@ -131,48 +110,24 @@ impl ProgressSource { match *self { Self::Inherit => parent.cloned().unwrap_or(ProgressActive::dummy()), Self::Always => ProgressActive::dummy(), - Self::Buff(id) | Self::Ability(id) | Self::AbilityAmmo(id) => { - let apply = ctx.now - passed; - ProgressActive::Timed { - id, - intensity: (progress * 25.0) as u32, - duration: apply, - end: apply + CYCLE, - rate: 1.0, - } - } - Self::SkillbarSlot(slot) | Self::SkillbarSlotAmmo(slot) => { - let apply = ctx.now - passed; - ProgressActive::Timed { - id: ctx - .state - .own_skillbar - .as_ref() - .ok() - .and_then(|skillbar| skillbar.slot(slot)) - .map(|ability| ability.id) - .unwrap_or(0), - intensity: (progress * 25.0) as u32, - duration: apply, - end: apply + CYCLE, - rate: 1.0, - } + Self::Buff(id) => ProgressActive::edit_buff(id, progress, ctx.now), + Self::Ability(id) => ProgressActive::edit_ability(id, progress, ctx.now), + Self::SkillbarSlot(slot) => { + let id = ctx + .own_skillbar() + .and_then(|skillbar| slot.get_id(skillbar)) + .unwrap_or(0); + ProgressActive::edit_ability(id, progress, ctx.now) } Self::AnyBuff(ref ids) => { - let apply = ctx.now - passed; - ProgressActive::Timed { - id: ids.first().copied().unwrap_or(0), - intensity: (progress * 25.0) as u32, - duration: apply, - end: apply + CYCLE, - rate: 1.0, - } + let id = ids.first().copied().unwrap_or(0); + ProgressActive::edit_buff(id, progress, ctx.now) } - Self::Health => ProgressActive::from_percent(progress, 15_000), - Self::Barrier => ProgressActive::from_percent(0.5 * progress, 15_000), - Self::Endurance => ProgressActive::from_percent(progress, 100), + Self::Health => ProgressActive::edit_resource(progress, 15_000), + Self::Barrier => ProgressActive::edit_resource(0.5 * progress, 15_000), + Self::Endurance => ProgressActive::edit_resource(progress, 100), Self::PrimaryResource | Self::SecondaryResource => { - ProgressActive::from_percent(progress, 30) + ProgressActive::edit_resource(progress, 30) } } } @@ -263,13 +218,13 @@ impl RenderOptions for ProgressSource { action.perform(ids); } - Self::Ability(id) | Self::AbilityAmmo(id) => { + Self::Ability(id) => { Self::ability_validate(*id).for_item(ui, || { input_skill_id(ui, "Ability Id", id, InputTextFlags::empty()) }); Self::id_helper(ui); } - Self::SkillbarSlot(slot) | Self::SkillbarSlotAmmo(slot) => { + Self::SkillbarSlot(slot) => { enum_combo(ui, "Slot", slot, ComboBoxFlags::HEIGHT_LARGEST); } _ => {} diff --git a/reffect/src/trigger/progress/threshold/amount_type.rs b/reffect/src/trigger/progress/threshold/amount_type.rs index 3debd25..c5d7b18 100644 --- a/reffect/src/trigger/progress/threshold/amount_type.rs +++ b/reffect/src/trigger/progress/threshold/amount_type.rs @@ -2,7 +2,7 @@ use crate::{ context::Context, render::RenderOptions, render_util::{enum_combo, helper, input_float_with_format}, - trigger::ProgressActive, + trigger::{ProgressActive, ProgressValue}, }; use nexus::imgui::{ComboBoxFlags, InputTextFlags, Slider, SliderFlags, Ui}; use serde::{Deserialize, Serialize}; @@ -37,6 +37,12 @@ pub enum AmountType { /// Progress percent. #[strum(serialize = "Progress %")] Percent, + + #[strum(serialize = "Secondary Duration")] + SecondaryDuration, + + #[strum(serialize = "Secondary Progress %")] + SecondaryPercent, } impl AmountType { @@ -44,10 +50,17 @@ impl AmountType { match self { Self::Intensity => active.intensity() as f32, Self::Duration => active - .current(ctx.now) + .current(ProgressValue::Primary, ctx.now) .map(|current| current as f32 / 1000.0) .unwrap_or(f32::INFINITY), - Self::Percent => 100.0 * active.progress_or_default(ctx.now), + Self::Percent => 100.0 * active.progress_or_default(ProgressValue::Primary, ctx.now), + Self::SecondaryDuration => active + .current(ProgressValue::Secondary, ctx.now) + .map(|current| current as f32 / 1000.0) + .unwrap_or(f32::INFINITY), + &Self::SecondaryPercent => { + 100.0 * active.progress_or_default(ProgressValue::Secondary, ctx.now) + } } } @@ -73,7 +86,7 @@ impl AmountType { helper(ui, || ui.text("Intensity in stacks or resource units")); changed } - Self::Duration => { + Self::Duration | Self::SecondaryDuration => { let changed = input_float_with_format( label, value, @@ -85,7 +98,7 @@ impl AmountType { helper(ui, || ui.text("Duration in seconds")); changed } - Self::Percent => { + Self::Percent | Self::SecondaryPercent => { let changed = Slider::new(label, 0.0, 100.0) .flags(SliderFlags::ALWAYS_CLAMP) .display_format("%.2f") diff --git a/reffect_api/src/ability.rs b/reffect_api/src/ability.rs index 0db8d3a..c567e42 100644 --- a/reffect_api/src/ability.rs +++ b/reffect_api/src/ability.rs @@ -188,6 +188,12 @@ impl Recharge { /// Returns the remaining recharge. #[inline] pub fn recharge_remaining(&self, now: u32) -> u32 { - (self.last_update + self.recharge).saturating_sub(now) + self.end().saturating_sub(now) + } + + /// Returns the end timestamp. + #[inline] + pub fn end(&self) -> u32 { + self.last_update + self.recharge } } diff --git a/reffect_api/src/lib.rs b/reffect_api/src/lib.rs index 4b94eb1..155e36c 100644 --- a/reffect_api/src/lib.rs +++ b/reffect_api/src/lib.rs @@ -15,7 +15,12 @@ pub type Texture = ID3D11ShaderResourceView; /// Interface for API. pub trait Interface { /// Initializes the API. - fn init(); + #[inline] + fn init() {} + + /// Deinitializes the API. + #[inline] + fn deinit() {} /// Updates and returns the current state. fn update_state(state: &mut State); diff --git a/reffect_internal/src/lib.rs b/reffect_internal/src/lib.rs index 72c8ff9..1a81b41 100644 --- a/reffect_internal/src/lib.rs +++ b/reffect_internal/src/lib.rs @@ -8,9 +8,6 @@ pub type Internal = Dummy; pub struct Dummy; impl Interface for Dummy { - #[inline] - fn init() {} - #[inline] fn update_state(state: &mut State) { *state = State::disabled();