Show buff multiplicities as a number instead of duplicate icons. Add particle effects for the beginning of potion sickness.

This commit is contained in:
Avi Weinstock 2023-01-15 17:26:18 -05:00
parent a871d3880b
commit 1b00b18a7b
4 changed files with 106 additions and 25 deletions

View File

@ -559,8 +559,8 @@ impl Buffs {
// Iterates through all active buffs (the most powerful buff of each
// non-stacking kind, and all of the stacking ones)
pub fn iter_active(&self) -> impl Iterator<Item = &Buff> + '_ {
self.kinds.iter().flat_map(move |(kind, ids)| {
pub fn iter_active(&self) -> impl Iterator<Item = impl Iterator<Item = &Buff>> + '_ {
self.kinds.iter().map(move |(kind, ids)| {
if kind.stacks() {
Either::Left(ids.iter().filter_map(|id| self.buffs.get(id)))
} else {

View File

@ -28,6 +28,8 @@ widget_ids! {
debuffs[],
debuff_timers[],
buff_txts[],
buff_multiplicities[],
debuff_multiplicities[],
}
}
@ -181,6 +183,12 @@ impl<'a> Widget for BuffsBar<'a> {
if state.ids.debuff_timers.len() < debuff_count {
state.update(|state| state.ids.debuff_timers.resize(debuff_count, gen));
};
if state.ids.buff_multiplicities.len() < buff_count {
state.update(|state| state.ids.buff_multiplicities.resize(buff_count, gen));
};
if state.ids.debuff_multiplicities.len() < debuff_count {
state.update(|state| state.ids.debuff_multiplicities.resize(debuff_count, gen));
};
// Create Buff Widgets
let mut buff_vec = state
@ -189,16 +197,18 @@ impl<'a> Widget for BuffsBar<'a> {
.iter()
.copied()
.zip(state.ids.buff_timers.iter().copied())
.zip(state.ids.buff_multiplicities.iter().copied())
.zip(buff_icons.iter().filter(|info| info.is_buff))
.collect::<Vec<_>>();
// Sort the buffs by kind
buff_vec.sort_by_key(|((_id, _timer_id), buff)| std::cmp::Reverse(buff.kind));
buff_vec
.sort_by_key(|(((_id, _timer_id), _mult_id), buff)| std::cmp::Reverse(buff.kind));
buff_vec
.iter()
.enumerate()
.for_each(|(i, ((id, timer_id), buff))| {
.for_each(|(i, (((id, timer_id), mult_id), buff))| {
let max_duration = buff.kind.max_duration();
let current_duration = buff.dur;
let duration_percentage = current_duration.map_or(1000.0, |cur| {
@ -225,6 +235,15 @@ impl<'a> Widget for BuffsBar<'a> {
},
)
.set(*id, ui);
if buff.multiplicity() > 1 {
Text::new(&format!("{}", buff.multiplicity()))
.bottom_right_with_margins_on(*id, 1.0, 1.0)
.graphics_for(*id)
.font_size(self.fonts.cyri.scale(14))
.font_id(self.fonts.cyri.conrod_id)
.color(TEXT_COLOR)
.set(*mult_id, ui);
}
// Create Buff tooltip
let (title, desc_txt) = buff.kind.title_description(localized_strings);
let remaining_time = buff.get_buff_time();
@ -259,16 +278,17 @@ impl<'a> Widget for BuffsBar<'a> {
.iter()
.copied()
.zip(state.ids.debuff_timers.iter().copied())
.zip(state.ids.debuff_multiplicities.iter().copied())
.zip(buff_icons.iter().filter(|info| !info.is_buff))
.collect::<Vec<_>>();
// Sort the debuffs by kind
debuff_vec.sort_by_key(|((_id, _timer_id), debuff)| debuff.kind);
debuff_vec.sort_by_key(|(((_id, _timer_id), _mult_id), debuff)| debuff.kind);
debuff_vec
.iter()
.enumerate()
.for_each(|(i, ((id, timer_id), debuff))| {
.for_each(|(i, (((id, timer_id), mult_id), debuff))| {
let max_duration = debuff.kind.max_duration();
let current_duration = debuff.dur;
let duration_percentage = current_duration.map_or(1000.0, |cur| {
@ -295,6 +315,15 @@ impl<'a> Widget for BuffsBar<'a> {
},
)
.set(*id, ui);
if debuff.multiplicity() > 1 {
Text::new(&format!("{}", debuff.multiplicity()))
.bottom_right_with_margins_on(*id, 1.0, 1.0)
.graphics_for(*id)
.font_size(self.fonts.cyri.scale(14))
.font_id(self.fonts.cyri.conrod_id)
.color(TEXT_COLOR)
.set(*mult_id, ui);
}
// Create Debuff tooltip
let (title, desc_txt) = debuff.kind.title_description(localized_strings);
let remaining_time = debuff.get_buff_time();
@ -334,6 +363,9 @@ impl<'a> Widget for BuffsBar<'a> {
if state.ids.buff_txts.len() < buff_count {
state.update(|state| state.ids.buff_txts.resize(buff_count, gen));
};
if state.ids.buff_multiplicities.len() < buff_count {
state.update(|state| state.ids.buff_multiplicities.resize(buff_count, gen));
};
// Create Buff Widgets
@ -344,16 +376,15 @@ impl<'a> Widget for BuffsBar<'a> {
.copied()
.zip(state.ids.buff_timers.iter().copied())
.zip(state.ids.buff_txts.iter().copied())
.zip(state.ids.buff_multiplicities.iter().copied())
.zip(buff_icons.iter())
.collect::<Vec<_>>();
// Sort the buffs by kind
buff_vec.sort_by_key(|((_id, _timer_id), txt_id)| std::cmp::Reverse(txt_id.kind));
buff_vec
.iter()
.enumerate()
.for_each(|(i, (((id, timer_id), txt_id), buff))| {
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;
// Percentage to determine which frame of the timer overlay is displayed
@ -380,6 +411,15 @@ impl<'a> Widget for BuffsBar<'a> {
},
)
.set(*id, ui);
if buff.multiplicity() > 1 {
Text::new(&format!("{}", buff.multiplicity()))
.bottom_right_with_margins_on(*id, 1.0, 1.0)
.graphics_for(*id)
.font_size(self.fonts.cyri.scale(14))
.font_id(self.fonts.cyri.conrod_id)
.color(TEXT_COLOR)
.set(*mult_id, ui);
}
// Create Buff tooltip
let (title, desc_txt) = buff.kind.title_description(localized_strings);
let remaining_time = buff.get_buff_time();
@ -420,7 +460,8 @@ impl<'a> Widget for BuffsBar<'a> {
.graphics_for(*timer_id)
.color(TEXT_COLOR)
.set(*txt_id, ui);
});
},
);
}
event
}

View File

@ -454,8 +454,14 @@ impl<W: Positionable> Position for W {
#[derive(Clone, Copy)]
pub enum BuffIconKind<'a> {
Buff { kind: BuffKind, data: BuffData },
Ability { ability_id: &'a str },
Buff {
kind: BuffKind,
data: BuffData,
multiplicity: usize,
},
Ability {
ability_id: &'a str,
},
}
impl<'a> BuffIconKind<'a> {
@ -478,7 +484,11 @@ impl<'a> BuffIconKind<'a> {
localized_strings: &'b Localization,
) -> (Cow<'b, str>, Cow<'b, str>) {
match self {
Self::Buff { kind, data } => (
Self::Buff {
kind,
data,
multiplicity: _,
} => (
get_buff_title(*kind, localized_strings),
get_buff_desc(*kind, *data, localized_strings),
),
@ -548,6 +558,13 @@ pub struct BuffIcon<'a> {
}
impl<'a> BuffIcon<'a> {
pub fn multiplicity(&self) -> usize {
match self.kind {
BuffIconKind::Buff { multiplicity, .. } => multiplicity,
BuffIconKind::Ability { .. } => 1,
}
}
pub fn get_buff_time(&self) -> String {
if let Some(dur) = self.dur {
format!("{:.0}s", dur.as_secs_f32())
@ -559,7 +576,7 @@ impl<'a> BuffIcon<'a> {
pub fn icons_vec(buffs: &comp::Buffs, char_state: &comp::CharacterState) -> Vec<Self> {
buffs
.iter_active()
.map(BuffIcon::from_buff)
.filter_map(BuffIcon::from_buffs)
.chain(BuffIcon::from_char_state(char_state).into_iter())
.collect::<Vec<_>>()
}
@ -581,15 +598,20 @@ impl<'a> BuffIcon<'a> {
}
}
fn from_buff(buff: &comp::Buff) -> Self {
Self {
fn from_buffs<'b, I: Iterator<Item = &'b comp::Buff>>(buffs: I) -> Option<Self> {
let (buff, count) = buffs.fold((None, 0), |(strongest, count), buff| {
(strongest.or(Some(buff)), count + 1)
});
let buff = buff?;
Some(Self {
kind: BuffIconKind::Buff {
kind: buff.kind,
data: buff.data,
multiplicity: count,
},
is_buff: buff.kind.is_buff(),
dur: buff.time,
}
})
}
}

View File

@ -1158,13 +1158,31 @@ impl ParticleMgr {
{
let pos = interp.map_or(pos.0, |i| i.pos);
for (buff_kind, _) in buffs.kinds.iter() {
for (buff_kind, buff_ids) in buffs.kinds.iter() {
use buff::BuffKind;
match buff_kind {
BuffKind::Cursed | BuffKind::Burning => {
BuffKind::Cursed | BuffKind::Burning | BuffKind::PotionSickness => {
let mut multiplicity = if buff_kind.stacks() {
buff_ids.len()
} else {
1
};
if let BuffKind::PotionSickness = buff_kind {
// Only show particles for potion sickness at the beginning
if buff_ids
.iter()
.filter_map(|id| buffs.buffs.get(id))
.all(|buff| buff.delay.is_none())
{
multiplicity = 0;
}
}
self.particles.resize_with(
self.particles.len()
+ usize::from(self.scheduler.heartbeats(Duration::from_millis(15))),
+ multiplicity
* usize::from(
self.scheduler.heartbeats(Duration::from_millis(15)),
),
|| {
let start_pos = pos
+ Vec3::unit_z() * body.height() * 0.25
@ -1180,10 +1198,10 @@ impl ParticleMgr {
Particle::new_directed(
Duration::from_secs(1),
time,
if matches!(buff_kind, buff::BuffKind::Cursed) {
ParticleMode::CultistFlame
} else {
ParticleMode::FlameThrower
match buff_kind {
BuffKind::Cursed => ParticleMode::CultistFlame,
BuffKind::PotionSickness => ParticleMode::Blood,
_ => ParticleMode::FlameThrower,
},
start_pos,
end_pos,