Merge branch 'juliancoffee/more-buffs' into 'master'

Add Golden Cheese debug potion

See merge request veloren/veloren!3597
This commit is contained in:
Samuel Keiffer 2022-09-08 01:01:18 +00:00
commit aa7569061b
12 changed files with 410 additions and 236 deletions

View File

@ -0,0 +1,55 @@
ItemDef(
name: "Golden Cheese",
description: "They say gods eat it to get eternal youth.",
kind: Consumable(
kind: Drink,
effects: [
Buff((
kind: Regeneration,
data: (
strength: 1000,
duration: Some((
secs: 999,
nanos: 0,
)),
),
cat_ids: [Natural],
)),
Buff((
kind: EnergyRegen,
data: (
strength: 1000,
duration: Some((
secs: 999,
nanos: 0,
)),
),
cat_ids: [Natural],
)),
Buff((
kind: IncreaseMaxHealth,
data: (
strength: 50000,
duration: Some((
secs: 999,
nanos: 0,
)),
),
cat_ids: [Natural],
)),
Buff((
kind: IncreaseMaxEnergy,
data: (
strength: 50000,
duration: Some((
secs: 999,
nanos: 0,
)),
),
cat_ids: [Natural],
)),
]
),
quality: Debug,
tags: [Food],
)

View File

@ -5,6 +5,7 @@
(Item("common.items.debug.admin_sword"),1), (Item("common.items.debug.admin_sword"),1),
(Item("common.items.debug.velorite_bow_debug"), 1), (Item("common.items.debug.velorite_bow_debug"), 1),
(Item("common.items.debug.admin"),1), (Item("common.items.debug.admin"),1),
(Item("common.items.debug.golden_cheese"),100),
], ],
"consumables": [ "consumables": [
(Item("common.items.consumable.potion_big"), 100), (Item("common.items.consumable.potion_big"), 100),

View File

@ -1,39 +1,67 @@
buff-remove = Click to remove ## Regeneration
buff-title-missing = Missing Title
buff-desc-missing = Missing Description
buff-title-heal = Heal buff-title-heal = Heal
buff-desc-heal = Gain health over time. buff-desc-heal = Gain health over time.
buff-stat-health = Restores { $str_total } Health
## Potion
buff-title-potion = Potion buff-title-potion = Potion
buff-desc-potion = Drinking... buff-desc-potion = Drinking...
## Saturation
buff-title-saturation = Saturation buff-title-saturation = Saturation
buff-desc-saturation = Gain health over time from consumables. buff-desc-saturation = Gain health over time from consumables.
## Campfire
buff-title-campfire_heal = Campfire Heal buff-title-campfire_heal = Campfire Heal
buff-desc-campfire_heal = Resting at a campfire heals { $rate }% per second. buff-desc-campfire_heal = Resting at a campfire heals { $rate }% per second.
## Energy Regen
buff-title-energy_regen = Energy Regeneration
buff-desc-energy_regen = Faster energy regeneration
buff-stat-energy_regen = Restores { $str_total } Energy
## Health Increase
buff-title-increase_max_health = Increase Max Health
buff-desc-increase_max_health = Raise your max health cap
buff-stat-increase_max_health =
Raises maximum health
by { $strength }
## Energy Increase
buff-title-increase_max_energy = Increase Max Energy
buff-desc-increase_max_energy = Raise your max energy cap
buff-stat-increase_max_energy =
Raises maximum energy
by { $strength }
## Invulnerability
buff-title-invulnerability = Invulnerability buff-title-invulnerability = Invulnerability
buff-desc-invulnerability = You cannot be damaged by any attack. buff-desc-invulnerability = You cannot be damaged by any attack.
buff-stat-invulnerability = Grants invulnerability
## Protection Ward
buff-title-protectingward = Protecting Ward buff-title-protectingward = Protecting Ward
buff-desc-protectingward = You are protected, somewhat, from attacks. buff-desc-protectingward = You are protected, somewhat, from attacks.
## Frenzied
buff-title-frenzied = Frenzied buff-title-frenzied = Frenzied
buff-desc-frenzied = You are imbued with unnatural speed and can ignore minor injuries. buff-desc-frenzied = You are imbued with unnatural speed and can ignore minor injuries.
## Haste
buff-title-hastened = Hastened buff-title-hastened = Hastened
buff-desc-hastened = Your movements and attacks are faster. buff-desc-hastened = Your movements and attacks are faster.
## Bleeding
buff-title-bleed = Bleeding buff-title-bleed = Bleeding
buff-desc-bleed = Inflicts regular damage. buff-desc-bleed = Inflicts regular damage.
## Curse
buff-title-cursed = Cursed buff-title-cursed = Cursed
buff-desc-cursed = You are cursed. buff-desc-cursed = You are cursed.
## Burning
buff-title-burn = On Fire buff-title-burn = On Fire
buff-desc-burn = You are burning alive buff-desc-burn = You are burning alive
## Crippled
buff-title-crippled = Crippled buff-title-crippled = Crippled
buff-desc-crippled = Your movement is crippled as your legs are heavily injured. buff-desc-crippled = Your movement is crippled as your legs are heavily injured.
## Freeze
buff-title-frozen = Frozen buff-title-frozen = Frozen
buff-desc-frozen = Your movements and attacks are slowed. buff-desc-frozen = Your movements and attacks are slowed.
## Wet
buff-title-wet = Wet buff-title-wet = Wet
buff-desc-wet = The ground rejects your feet, making it hard to stop. buff-desc-wet = The ground rejects your feet, making it hard to stop.
## Ensnared
buff-title-ensnared = Ensnared buff-title-ensnared = Ensnared
buff-desc-ensnared = Vines grasp at your legs, impeding your movement. buff-desc-ensnared = Vines grasp at your legs, impeding your movement.
buff-stat-health = Restores { $str_total } Health ## Util
buff-stat-increase_max_energy = Raises Maximum Energy by { $strength }
buff-stat-increase_max_health = Raises Maximum Health by { $strength }
buff-stat-invulnerability = Grants invulnerability
buff-text-over_seconds = over { $dur_secs } seconds buff-text-over_seconds = over { $dur_secs } seconds
buff-text-for_seconds = for { $dur_secs } seconds buff-text-for_seconds = for { $dur_secs } seconds
buff-remove = Click to remove

View File

@ -1,37 +1,64 @@
buff-remove = Клікніть, щоб видалити ## Regeneration
buff-title-missing = Назва відсутня
buff-desc-missing = Опис відсутній
buff-title-heal = Зцілення buff-title-heal = Зцілення
buff-desc-heal = Поступово відновлює Здоров'я. buff-desc-heal = Поступово відновлює Здоров'я.
buff-stat-health = Відновлює { $str_total } ОЗ
## Potion
buff-title-potion = Зілля buff-title-potion = Зілля
buff-desc-potion = Пиття... buff-desc-potion = Пиття...
## Saturation
buff-title-saturation = Насичення buff-title-saturation = Насичення
buff-desc-saturation = Поступово відновлює Здоров'я з їжі. buff-desc-saturation = Поступово відновлює Здоров'я з їжі.
## Campfire
buff-title-campfire_heal = Відновлення біля ватри buff-title-campfire_heal = Відновлення біля ватри
buff-desc-campfire_heal = Відпочинок біля ватри лікує на { $rate }% за секунду. buff-desc-campfire_heal = Відпочинок біля ватри лікує на { $rate }% за секунду.
## Energy Regen
buff-title-energy_regen = Відновлення Енергії
buff-desc-energy_regen = Поступово відновляє Eнергію
buff-stat-energy-regen = Відновлює { $str_total } енергії
## Health Increase
buff-title-increase_max_health = Підвищення Максимального Здоров'я
buff-desc-increase_max_health = Піднімає ліміт вашого здоров'я
buff-stat-increase_max_health =
Підвищує максимальне здоров'я
на { $strength }
## Energy Increase
buff-title-increase_max_energy = Підвищення Максимальної Енергії
buff-desc-increase_max_energy = Піднімає ліміт вашої енергії
buff-stat-increase_max_energy =
Підвищує максимальну енергію
на { $strength }
## Invulnerability
buff-title-invulnerability = Невразливість buff-title-invulnerability = Невразливість
buff-desc-invulnerability = Ви невразливий, тільки тримайтесь подалі від омели. buff-desc-invulnerability = Ви невразливий, тільки тримайтесь подалі від омели.
buff-stat-invulnerability = Дає невразливість
## Protection Ward
buff-title-protectingward = Захисна Аура buff-title-protectingward = Захисна Аура
buff-desc-protectingward = Ви захищені від атак, у якомусь сенсі. buff-desc-protectingward = Ви захищені від атак, у якомусь сенсі.
## Frenized
buff-title-frenzied = Манія buff-title-frenzied = Манія
buff-desc-frenzied = Кров тече швидше, прискоруючи ваш рух та помалу зцілюючи вас. buff-desc-frenzied = Кров тече швидше, прискоруючи ваш рух та помалу зцілюючи вас.
## Cursed
buff-title-cursed = Проклін buff-title-cursed = Проклін
buff-desc-cursed = Вас прокляли. buff-desc-cursed = Вас прокляли.
## Bleeding
buff-title-bleed = Кровотеча buff-title-bleed = Кровотеча
buff-desc-bleed = Завдає регулярних пошкодженнь. buff-desc-bleed = Завдає регулярних пошкодженнь.
## Burning
buff-title-burn = У Вогні buff-title-burn = У Вогні
buff-desc-burn = Ви згораєте заживо. buff-desc-burn = Ви згораєте заживо.
## Crippled
buff-title-crippled = Калічення buff-title-crippled = Калічення
buff-desc-crippled = Ваші рухи дуже скуті через отримані травми. buff-desc-crippled = Ваші рухи дуже скуті через отримані травми.
## Freeze
buff-title-frozen = Обмороження buff-title-frozen = Обмороження
buff-desc-frozen = Швидкість пересування та атак знижена. buff-desc-frozen = Швидкість пересування та атак знижена.
## Wet
buff-title-wet = Волога buff-title-wet = Волога
buff-desc-wet = Земля плутає ваші ноги ускладнючи пересування. buff-desc-wet = Земля плутає ваші ноги ускладнючи пересування.
## Ensnared
buff-title-ensnared = Пастка buff-title-ensnared = Пастка
buff-desc-ensnared = Ліани опутують ваші ноги, перешкоджаючи ходьбі. buff-desc-ensnared = Ліани опутують ваші ноги, перешкоджаючи ходьбі.
buff-stat-health = Відновлює { $str_total } ОЗ ## Util
buff-stat-increase_max_health = Підвищує Максимальне Здоров'я на { $strength }
buff-stat-increase_max_energy = Підвищує Максимальну Енергію на { $strength }
buff-stat-invulnerability = Дає невразливість
buff-text-for_seconds = протягом { $dur_secs } сек. buff-text-for_seconds = протягом { $dur_secs } сек.
buff-text-over_seconds = впродовж { $dur_secs } сек. buff-text-over_seconds = впродовж { $dur_secs } сек.
buff-remove = Клікніть, щоб видалити

View File

@ -3189,6 +3189,11 @@
"voxel.sprite.food.salad_tomato", "voxel.sprite.food.salad_tomato",
(0.0, 0.0, 0.0), (-50.0, 30.0, 20.0), 0.8, (0.0, 0.0, 0.0), (-50.0, 30.0, 20.0), 0.8,
), ),
// Admin consumables
Simple("common.items.debug.golden_cheese"): VoxTrans(
"voxel.object.item_cheese",
(0.0, 0.0, 0.0), (-50.0, 30.0, 20.0), 0.7,
),
// Throwables // Throwables
Simple("common.items.utility.bomb"): VoxTrans( Simple("common.items.utility.bomb"): VoxTrans(
"voxel.object.bomb", "voxel.object.bomb",

View File

@ -141,6 +141,7 @@ lazy_static! {
BuffKind::Cursed => "cursed", BuffKind::Cursed => "cursed",
BuffKind::Potion => "potion", BuffKind::Potion => "potion",
BuffKind::CampfireHeal => "campfire_heal", BuffKind::CampfireHeal => "campfire_heal",
BuffKind::EnergyRegen => "energy_regen",
BuffKind::IncreaseMaxEnergy => "increase_max_energy", BuffKind::IncreaseMaxEnergy => "increase_max_energy",
BuffKind::IncreaseMaxHealth => "increase_max_health", BuffKind::IncreaseMaxHealth => "increase_max_health",
BuffKind::Invulnerability => "invulnerability", BuffKind::Invulnerability => "invulnerability",
@ -154,7 +155,9 @@ lazy_static! {
BuffKind::Hastened => "hastened", BuffKind::Hastened => "hastened",
}; };
let mut buff_parser = HashMap::new(); let mut buff_parser = HashMap::new();
BuffKind::iter().for_each(|kind| {buff_parser.insert(string_from_buff(kind).to_string(), kind);}); for kind in BuffKind::iter() {
buff_parser.insert(string_from_buff(kind).to_string(), kind);
}
buff_parser buff_parser
}; };

View File

@ -27,6 +27,9 @@ pub enum BuffKind {
/// Applied when sitting at a campfire /// Applied when sitting at a campfire
/// Strength is fraction of health restored per second /// Strength is fraction of health restored per second
CampfireHeal, CampfireHeal,
/// Restores energy/time for some period
/// Strength should be the healing per second
EnergyRegen,
/// Raises maximum energy /// Raises maximum energy
/// Strength should be 10x the effect to max energy /// Strength should be 10x the effect to max energy
IncreaseMaxEnergy, IncreaseMaxEnergy,
@ -91,6 +94,7 @@ impl BuffKind {
| BuffKind::Potion | BuffKind::Potion
| BuffKind::CampfireHeal | BuffKind::CampfireHeal
| BuffKind::Frenzied | BuffKind::Frenzied
| BuffKind::EnergyRegen
| BuffKind::IncreaseMaxEnergy | BuffKind::IncreaseMaxEnergy
| BuffKind::IncreaseMaxHealth | BuffKind::IncreaseMaxHealth
| BuffKind::Invulnerability | BuffKind::Invulnerability
@ -279,6 +283,14 @@ impl Buff {
], ],
data.duration, data.duration,
), ),
BuffKind::EnergyRegen => (
vec![BuffEffect::EnergyChangeOverTime {
rate: data.strength,
accumulated: 0.0,
kind: ModifierKind::Additive,
}],
data.duration,
),
BuffKind::IncreaseMaxEnergy => ( BuffKind::IncreaseMaxEnergy => (
vec![BuffEffect::MaxEnergyModifier { vec![BuffEffect::MaxEnergyModifier {
value: data.strength, value: data.strength,
@ -296,9 +308,9 @@ impl Buff {
BuffKind::Invulnerability => (vec![BuffEffect::DamageReduction(1.0)], data.duration), BuffKind::Invulnerability => (vec![BuffEffect::DamageReduction(1.0)], data.duration),
BuffKind::ProtectingWard => ( BuffKind::ProtectingWard => (
vec![BuffEffect::DamageReduction( vec![BuffEffect::DamageReduction(
// Causes non-linearity in effect strength, but necessary to allow for tool // Causes non-linearity in effect strength, but necessary
// power and other things to affect the strength. 0.5 also still provides 50% // to allow for tool power and other things to affect the
// damage reduction. // strength. 0.5 also still provides 50% damage reduction.
nn_scaling(data.strength), nn_scaling(data.strength),
)], )],
data.duration, data.duration,

View File

@ -11,10 +11,10 @@ use common::{
Energy, Group, Health, HealthChange, Inventory, LightEmitter, ModifierKind, PhysicsState, Energy, Group, Health, HealthChange, Inventory, LightEmitter, ModifierKind, PhysicsState,
Stats, Stats,
}, },
event::{EventBus, ServerEvent}, event::{Emitter, EventBus, ServerEvent},
resources::{DeltaTime, Time}, resources::{DeltaTime, Time},
terrain::SpriteKind, terrain::SpriteKind,
uid::UidAllocator, uid::{Uid, UidAllocator},
Damage, DamageSource, Damage, DamageSource,
}; };
use common_base::prof_span; use common_base::prof_span;
@ -22,8 +22,8 @@ use common_ecs::{Job, Origin, ParMode, Phase, System};
use hashbrown::HashMap; use hashbrown::HashMap;
use rayon::iter::ParallelIterator; use rayon::iter::ParallelIterator;
use specs::{ use specs::{
saveload::MarkerAllocator, shred::ResourceId, Entities, Join, ParJoin, Read, ReadExpect, saveload::MarkerAllocator, shred::ResourceId, Entities, Entity, Join, ParJoin, Read,
ReadStorage, SystemData, World, WriteStorage, ReadExpect, ReadStorage, SystemData, World, WriteStorage,
}; };
use std::time::Duration; use std::time::Duration;
@ -257,165 +257,19 @@ impl<'a> System<'a> for Sys {
// Now, execute the buff, based on it's delta // Now, execute the buff, based on it's delta
for effect in &mut buff.effects { for effect in &mut buff.effects {
match effect { execute_effect(
BuffEffect::HealthChangeOverTime { effect,
rate, buff.kind,
accumulated, buff.time,
kind, &read_data,
instance, &mut *stat,
} => { health,
*accumulated += *rate * dt; energy,
// Apply health change only once per second, per health, or entity,
// when a buff is removed buff_owner,
if accumulated.abs() > rate.abs().min(1.0) &mut server_emitter,
|| buff.time.map_or(false, |dur| dur == Duration::default()) dt,
{ );
let (cause, by) = if *accumulated != 0.0 {
(Some(DamageSource::Buff(buff.kind)), buff_owner)
} else {
(None, None)
};
let amount = match *kind {
ModifierKind::Additive => *accumulated,
ModifierKind::Fractional => health.maximum() * *accumulated,
};
let damage_contributor = by.and_then(|uid| {
read_data.uid_allocator.retrieve_entity_internal(uid.0).map(
|entity| {
DamageContributor::new(
uid,
read_data.groups.get(entity).cloned(),
)
},
)
});
server_emitter.emit(ServerEvent::HealthChange {
entity,
change: HealthChange {
amount,
by: damage_contributor,
cause,
time: *read_data.time,
crit: false,
instance: *instance,
},
});
*accumulated = 0.0;
};
},
BuffEffect::EnergyChangeOverTime {
rate,
accumulated,
kind,
} => {
*accumulated += *rate * dt;
// 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())
{
let amount = match *kind {
ModifierKind::Additive => *accumulated,
ModifierKind::Fractional => {
energy.maximum() as f32 * *accumulated
},
};
server_emitter.emit(ServerEvent::EnergyChange {
entity,
change: amount,
});
*accumulated = 0.0;
};
},
BuffEffect::MaxHealthModifier { value, kind } => match kind {
ModifierKind::Additive => {
stat.max_health_modifiers.add_mod += *value;
},
ModifierKind::Fractional => {
stat.max_health_modifiers.mult_mod *= *value;
},
},
BuffEffect::MaxEnergyModifier { value, kind } => match kind {
ModifierKind::Additive => {
stat.max_energy_modifiers.add_mod += *value;
},
ModifierKind::Fractional => {
stat.max_energy_modifiers.mult_mod *= *value;
},
},
BuffEffect::DamageReduction(dr) => {
stat.damage_reduction = stat.damage_reduction.max(*dr).min(1.0);
},
BuffEffect::MaxHealthChangeOverTime {
rate,
kind,
target_fraction,
achieved_fraction,
} => {
// Current fraction uses information from last tick, which is
// necessary as buffs from this tick are not guaranteed to have
// finished applying
let current_fraction = health.maximum() / health.base_max();
// If achieved_fraction not initialized, initialize it to health
// fraction
if achieved_fraction.is_none() {
*achieved_fraction = Some(current_fraction)
}
if let Some(achieved_fraction) = achieved_fraction {
// Percentage change that should be applied to max_health
let health_tick = match kind {
ModifierKind::Additive => {
// `rate * dt` is amount of health, dividing by base max
// creates fraction
*rate * dt / health.base_max() as f32
},
ModifierKind::Fractional => {
// `rate * dt` is the fraction
*rate * dt
},
};
let potential_fraction = *achieved_fraction + health_tick;
// Potential progress towards target fraction, if
// target_fraction ~ 1.0 then set progress to 1.0 to avoid
// divide by zero
let progress = if (1.0 - *target_fraction).abs() > f32::EPSILON
{
(1.0 - potential_fraction) / (1.0 - *target_fraction)
} else {
1.0
};
// Change achieved_fraction depending on what other buffs have
// occurred
if progress > 1.0 {
// If potential fraction already beyond target fraction,
// simply multiply max_health_modifier by the target
// fraction, and set achieved fraction to target_fraction
*achieved_fraction = *target_fraction;
} else {
// Else have not achieved target yet, update
// achieved_fraction
*achieved_fraction = potential_fraction;
}
// Apply achieved_fraction to max_health_modifier
stat.max_health_modifiers.mult_mod *= *achieved_fraction;
}
},
BuffEffect::MovementSpeed(speed) => {
stat.move_speed_modifier *= *speed;
},
BuffEffect::AttackSpeed(speed) => {
stat.attack_speed_modifier *= *speed;
},
BuffEffect::GroundFriction(gf) => {
stat.friction_modifier *= *gf;
},
};
} }
} }
} }
@ -446,6 +300,175 @@ impl<'a> System<'a> for Sys {
} }
} }
fn execute_effect(
effect: &mut BuffEffect,
buff_kind: BuffKind,
buff_time: Option<std::time::Duration>,
read_data: &ReadData,
stat: &mut Stats,
health: &Health,
energy: &Energy,
entity: Entity,
buff_owner: Option<Uid>,
server_emitter: &mut Emitter<ServerEvent>,
dt: f32,
) {
match effect {
BuffEffect::HealthChangeOverTime {
rate,
accumulated,
kind,
instance,
} => {
*accumulated += *rate * dt;
// 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())
{
let (cause, by) = if *accumulated != 0.0 {
(Some(DamageSource::Buff(buff_kind)), buff_owner)
} else {
(None, None)
};
let amount = match *kind {
ModifierKind::Additive => *accumulated,
ModifierKind::Fractional => health.maximum() * *accumulated,
};
let damage_contributor = by.and_then(|uid| {
read_data
.uid_allocator
.retrieve_entity_internal(uid.0)
.map(|entity| {
DamageContributor::new(uid, read_data.groups.get(entity).cloned())
})
});
server_emitter.emit(ServerEvent::HealthChange {
entity,
change: HealthChange {
amount,
by: damage_contributor,
cause,
time: *read_data.time,
crit: false,
instance: *instance,
},
});
*accumulated = 0.0;
};
},
BuffEffect::EnergyChangeOverTime {
rate,
accumulated,
kind,
} => {
*accumulated += *rate * dt;
// 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())
{
let amount = match *kind {
ModifierKind::Additive => *accumulated,
ModifierKind::Fractional => energy.maximum() as f32 * *accumulated,
};
server_emitter.emit(ServerEvent::EnergyChange {
entity,
change: amount,
});
*accumulated = 0.0;
};
},
BuffEffect::MaxHealthModifier { value, kind } => match kind {
ModifierKind::Additive => {
stat.max_health_modifiers.add_mod += *value;
},
ModifierKind::Fractional => {
stat.max_health_modifiers.mult_mod *= *value;
},
},
BuffEffect::MaxEnergyModifier { value, kind } => match kind {
ModifierKind::Additive => {
stat.max_energy_modifiers.add_mod += *value;
},
ModifierKind::Fractional => {
stat.max_energy_modifiers.mult_mod *= *value;
},
},
BuffEffect::DamageReduction(dr) => {
stat.damage_reduction = stat.damage_reduction.max(*dr).min(1.0);
},
BuffEffect::MaxHealthChangeOverTime {
rate,
kind,
target_fraction,
achieved_fraction,
} => {
// Current fraction uses information from last tick, which is
// necessary as buffs from this tick are not guaranteed to have
// finished applying
let current_fraction = health.maximum() / health.base_max();
// If achieved_fraction not initialized, initialize it to health
// fraction
if achieved_fraction.is_none() {
*achieved_fraction = Some(current_fraction)
}
if let Some(achieved_fraction) = achieved_fraction {
// Percentage change that should be applied to max_health
let health_tick = match kind {
ModifierKind::Additive => {
// `rate * dt` is amount of health, dividing by base max
// creates fraction
*rate * dt / health.base_max() as f32
},
ModifierKind::Fractional => {
// `rate * dt` is the fraction
*rate * dt
},
};
let potential_fraction = *achieved_fraction + health_tick;
// Potential progress towards target fraction, if
// target_fraction ~ 1.0 then set progress to 1.0 to avoid
// divide by zero
let progress = if (1.0 - *target_fraction).abs() > f32::EPSILON {
(1.0 - potential_fraction) / (1.0 - *target_fraction)
} else {
1.0
};
// Change achieved_fraction depending on what other buffs have
// occurred
if progress > 1.0 {
// If potential fraction already beyond target fraction,
// simply multiply max_health_modifier by the target
// fraction, and set achieved fraction to target_fraction
*achieved_fraction = *target_fraction;
} else {
// Else have not achieved target yet, update
// achieved_fraction
*achieved_fraction = potential_fraction;
}
// Apply achieved_fraction to max_health_modifier
stat.max_health_modifiers.mult_mod *= *achieved_fraction;
}
},
BuffEffect::MovementSpeed(speed) => {
stat.move_speed_modifier *= *speed;
},
BuffEffect::AttackSpeed(speed) => {
stat.attack_speed_modifier *= *speed;
},
BuffEffect::GroundFriction(gf) => {
stat.friction_modifier *= *gf;
},
};
}
fn tick_buff(id: u64, buff: &mut Buff, dt: f32, mut expire_buff: impl FnMut(u64)) { 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 a buff is recently applied from an aura, do not tick duration
if buff if buff

View File

@ -788,6 +788,7 @@ fn insert_killing_buff(buff: BuffKind, localized_strings: &Localization, templat
| BuffKind::Saturation | BuffKind::Saturation
| BuffKind::Potion | BuffKind::Potion
| BuffKind::CampfireHeal | BuffKind::CampfireHeal
| BuffKind::EnergyRegen
| BuffKind::IncreaseMaxEnergy | BuffKind::IncreaseMaxEnergy
| BuffKind::IncreaseMaxHealth | BuffKind::IncreaseMaxHealth
| BuffKind::Invulnerability | BuffKind::Invulnerability

View File

@ -4562,6 +4562,7 @@ pub fn get_buff_image(buff: BuffKind, imgs: &Imgs) -> conrod_core::image::Id {
BuffKind::Saturation { .. } => imgs.buff_saturation_0, BuffKind::Saturation { .. } => imgs.buff_saturation_0,
BuffKind::Potion { .. } => imgs.buff_potion_0, BuffKind::Potion { .. } => imgs.buff_potion_0,
BuffKind::CampfireHeal { .. } => imgs.buff_campfire_heal_0, BuffKind::CampfireHeal { .. } => imgs.buff_campfire_heal_0,
BuffKind::EnergyRegen { .. } => imgs.buff_energyplus_0,
BuffKind::IncreaseMaxEnergy { .. } => imgs.buff_energyplus_0, BuffKind::IncreaseMaxEnergy { .. } => imgs.buff_energyplus_0,
BuffKind::IncreaseMaxHealth { .. } => imgs.buff_healthplus_0, BuffKind::IncreaseMaxHealth { .. } => imgs.buff_healthplus_0,
BuffKind::Invulnerability => imgs.buff_invincibility_0, BuffKind::Invulnerability => imgs.buff_invincibility_0,
@ -4587,10 +4588,13 @@ pub fn get_buff_title(buff: BuffKind, localized_strings: &Localization) -> Cow<s
BuffKind::Saturation { .. } => localized_strings.get_msg("buff-title-saturation"), BuffKind::Saturation { .. } => localized_strings.get_msg("buff-title-saturation"),
BuffKind::Potion { .. } => localized_strings.get_msg("buff-title-potion"), BuffKind::Potion { .. } => localized_strings.get_msg("buff-title-potion"),
BuffKind::CampfireHeal { .. } => localized_strings.get_msg("buff-title-campfire_heal"), BuffKind::CampfireHeal { .. } => localized_strings.get_msg("buff-title-campfire_heal"),
BuffKind::EnergyRegen { .. } => localized_strings.get_msg("buff-title-energy_regen"),
BuffKind::IncreaseMaxHealth { .. } => { BuffKind::IncreaseMaxHealth { .. } => {
localized_strings.get_msg("buff-title-IncreaseMaxHealth") localized_strings.get_msg("buff-title-increase_max_health")
},
BuffKind::IncreaseMaxEnergy { .. } => {
localized_strings.get_msg("buff-title-increase_max_energy")
}, },
BuffKind::IncreaseMaxEnergy { .. } => localized_strings.get_msg("buff-title-energyup"),
BuffKind::Invulnerability => localized_strings.get_msg("buff-title-invulnerability"), BuffKind::Invulnerability => localized_strings.get_msg("buff-title-invulnerability"),
BuffKind::ProtectingWard => localized_strings.get_msg("buff-title-protectingward"), BuffKind::ProtectingWard => localized_strings.get_msg("buff-title-protectingward"),
BuffKind::Frenzied => localized_strings.get_msg("buff-title-frenzied"), BuffKind::Frenzied => localized_strings.get_msg("buff-title-frenzied"),
@ -4618,11 +4622,12 @@ pub fn get_buff_desc(buff: BuffKind, data: BuffData, localized_strings: &Localiz
"rate" => data.strength * 100.0 "rate" => data.strength * 100.0
}) })
}, },
BuffKind::EnergyRegen { .. } => localized_strings.get_msg("buff-desc-energy_regen"),
BuffKind::IncreaseMaxHealth { .. } => { BuffKind::IncreaseMaxHealth { .. } => {
localized_strings.get_msg("buff-desc-IncreaseMaxHealth") localized_strings.get_msg("buff-desc-increase_max_health")
}, },
BuffKind::IncreaseMaxEnergy { .. } => { BuffKind::IncreaseMaxEnergy { .. } => {
localized_strings.get_msg("buff-desc-IncreaseMaxEnergy") localized_strings.get_msg("buff-desc-increase_max_energy")
}, },
BuffKind::Invulnerability => localized_strings.get_msg("buff-desc-invulnerability"), BuffKind::Invulnerability => localized_strings.get_msg("buff-desc-invulnerability"),
BuffKind::ProtectingWard => localized_strings.get_msg("buff-desc-protectingward"), BuffKind::ProtectingWard => localized_strings.get_msg("buff-desc-protectingward"),

View File

@ -21,51 +21,43 @@ pub fn price_desc<'a>(
item_definition_id: ItemDefinitionId<'_>, item_definition_id: ItemDefinitionId<'_>,
i18n: &'a Localization, i18n: &'a Localization,
) -> Option<(Cow<'a, str>, Cow<'a, str>, f32)> { ) -> Option<(Cow<'a, str>, Cow<'a, str>, f32)> {
if let Some(prices) = prices { let prices = prices.as_ref()?;
if let Some(materials) = TradePricing::get_materials(&item_definition_id) { let materials = TradePricing::get_materials(&item_definition_id)?;
let coinprice = prices.values.get(&Good::Coin).cloned().unwrap_or(1.0); let coinprice = prices.values.get(&Good::Coin).cloned().unwrap_or(1.0);
let buyprice: f32 = materials let buyprice: f32 = materials
.iter() .iter()
.map(|e| prices.values.get(&e.1).cloned().unwrap_or_default() * e.0) .map(|e| prices.values.get(&e.1).cloned().unwrap_or_default() * e.0)
.sum(); .sum();
let sellprice: f32 = materials let sellprice: f32 = materials
.iter() .iter()
.map(|e| { .map(|e| prices.values.get(&e.1).cloned().unwrap_or_default() * e.0 * e.1.trade_margin())
prices.values.get(&e.1).cloned().unwrap_or_default() * e.0 * e.1.trade_margin() .sum();
})
.sum();
let deal_goodness: f32 = materials let deal_goodness: f32 = materials
.iter() .iter()
.map(|e| prices.values.get(&e.1).cloned().unwrap_or(0.0)) .map(|e| prices.values.get(&e.1).cloned().unwrap_or(0.0))
.sum::<f32>() .sum::<f32>()
/ prices.values.get(&Good::Coin).cloned().unwrap_or(1.0) / prices.values.get(&Good::Coin).cloned().unwrap_or(1.0)
/ (materials.len() as f32); / (materials.len() as f32);
let deal_goodness = deal_goodness.log(2.0); let deal_goodness = deal_goodness.log(2.0);
let buy_string = i18n.get_msg_ctx("hud-trade-buy", &fluent_args! { let buy_string = i18n.get_msg_ctx("hud-trade-buy", &fluent_args! {
"coin_num" => buyprice / coinprice, "coin_num" => buyprice / coinprice,
"coin_formatted" => format!("{:0.1}", buyprice / coinprice), "coin_formatted" => format!("{:0.1}", buyprice / coinprice),
}); });
let sell_string = i18n.get_msg_ctx("hud-trade-sell", &fluent_args! { let sell_string = i18n.get_msg_ctx("hud-trade-sell", &fluent_args! {
"coin_num" => sellprice / coinprice, "coin_num" => sellprice / coinprice,
"coin_formatted" => format!("{:0.1}", sellprice / coinprice), "coin_formatted" => format!("{:0.1}", sellprice / coinprice),
}); });
let deal_goodness = match deal_goodness { let deal_goodness = match deal_goodness {
x if x < -2.5 => 0.0, x if x < -2.5 => 0.0,
x if x < -1.05 => 0.25, x if x < -1.05 => 0.25,
x if x < -0.95 => 0.5, x if x < -0.95 => 0.5,
x if x < 0.0 => 0.75, x if x < 0.0 => 0.75,
_ => 1.0, _ => 1.0,
}; };
Some((buy_string, sell_string, deal_goodness)) Some((buy_string, sell_string, deal_goodness))
} else {
None
}
} else {
None
}
} }
pub fn kind_text<'a>(kind: &ItemKind, i18n: &'a Localization) -> Cow<'a, str> { pub fn kind_text<'a>(kind: &ItemKind, i18n: &'a Localization) -> Cow<'a, str> {
@ -126,6 +118,21 @@ pub fn stats_count(item: &dyn ItemDesc, msm: &MaterialStatManifest) -> usize {
} }
} }
pub fn line_count(item: &dyn ItemDesc, msm: &MaterialStatManifest, i18n: &Localization) -> usize {
match &*item.kind() {
ItemKind::Consumable { effects, .. } => {
let descs = consumable_desc(effects, i18n);
let mut lines = 0;
for desc in descs {
lines += desc.matches('\n').count() + 1;
}
lines
},
_ => stats_count(item, msm),
}
}
/// Takes N `effects` and returns N effect descriptions /// Takes N `effects` and returns N effect descriptions
/// If effect isn't intended to have description, returns empty string /// If effect isn't intended to have description, returns empty string
/// ///
@ -149,6 +156,11 @@ pub fn consumable_desc(effects: &[Effect], i18n: &Localization) -> Vec<String> {
.get_msg_ctx("buff-stat-health", &i18n::fluent_args! { .get_msg_ctx("buff-stat-health", &i18n::fluent_args! {
"str_total" => format_float(str_total), "str_total" => format_float(str_total),
}), }),
BuffKind::EnergyRegen => {
i18n.get_msg_ctx("buff-stat-energy_regen", &i18n::fluent_args! {
"str_total" => format_float(str_total),
})
},
BuffKind::IncreaseMaxEnergy => { BuffKind::IncreaseMaxEnergy => {
i18n.get_msg_ctx("buff-stat-increase_max_energy", &i18n::fluent_args! { i18n.get_msg_ctx("buff-stat-increase_max_energy", &i18n::fluent_args! {
"strength" => format_float(strength), "strength" => format_float(strength),
@ -178,11 +190,10 @@ pub fn consumable_desc(effects: &[Effect], i18n: &Localization) -> Vec<String> {
let dur_desc = if let Some(dur_secs) = dur_secs { let dur_desc = if let Some(dur_secs) = dur_secs {
match buff.kind { match buff.kind {
BuffKind::Saturation | BuffKind::Regeneration => { BuffKind::Saturation | BuffKind::Regeneration | BuffKind::EnergyRegen => i18n
i18n.get_msg_ctx("buff-text-over_seconds", &i18n::fluent_args! { .get_msg_ctx("buff-text-over_seconds", &i18n::fluent_args! {
"dur_secs" => dur_secs "dur_secs" => dur_secs
}) }),
},
BuffKind::IncreaseMaxEnergy BuffKind::IncreaseMaxEnergy
| BuffKind::IncreaseMaxHealth | BuffKind::IncreaseMaxHealth
| BuffKind::Invulnerability => { | BuffKind::Invulnerability => {
@ -204,7 +215,9 @@ pub fn consumable_desc(effects: &[Effect], i18n: &Localization) -> Vec<String> {
| BuffKind::Poisoned | BuffKind::Poisoned
| BuffKind::Hastened => Cow::Borrowed(""), | BuffKind::Hastened => Cow::Borrowed(""),
} }
} else if let BuffKind::Saturation | BuffKind::Regeneration = buff.kind { } else if let BuffKind::Saturation | BuffKind::Regeneration | BuffKind::EnergyRegen =
buff.kind
{
i18n.get_msg("buff-text-every_second") i18n.get_msg("buff-text-every_second")
} else { } else {
Cow::Borrowed("") Cow::Borrowed("")

View File

@ -1248,7 +1248,7 @@ impl<'a> Widget for ItemTooltip<'a> {
let frame_h = ICON_SIZE[1] + V_PAD; let frame_h = ICON_SIZE[1] + V_PAD;
// Stats // Stats
let stats_count = util::stats_count(self.item, self.msm); let stats_count = util::line_count(self.item, self.msm, self.localized_strings);
let stat_h = if stats_count > 0 { let stat_h = if stats_count > 0 {
widget::Text::new("placeholder") widget::Text::new("placeholder")
.with_style(self.style.desc) .with_style(self.style.desc)
@ -1294,7 +1294,8 @@ impl<'a> Widget for ItemTooltip<'a> {
0.0 0.0
}; };
let height = frame_h + stat_h + desc_h + price_h + V_PAD + 5.0; // extra padding to fit frame top padding // extra padding to fit frame top padding
let height = frame_h + stat_h + desc_h + price_h + V_PAD + 5.0;
Dimension::Absolute(height) Dimension::Absolute(height)
} }
} }