mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Savage sense
This commit is contained in:
parent
8c66bf8f70
commit
4a690840e4
@ -1,22 +1,9 @@
|
||||
ComboMelee2(
|
||||
strikes: [
|
||||
(
|
||||
melee_constructor: (
|
||||
kind: Slash(
|
||||
damage: 4,
|
||||
poise: 5,
|
||||
knockback: 0,
|
||||
energy_regen: 5,
|
||||
),
|
||||
range: 3.0,
|
||||
angle: 45.0,
|
||||
),
|
||||
buildup_duration: 0.15,
|
||||
swing_duration: 0.05,
|
||||
hit_timing: 0.5,
|
||||
SelfBuff(
|
||||
buildup_duration: 0.1,
|
||||
cast_duration: 0.1,
|
||||
recover_duration: 0.1,
|
||||
ori_modifier: 0.6,
|
||||
),
|
||||
],
|
||||
energy_cost_per_strike: 0,
|
||||
buff_kind: ImminentCritical,
|
||||
buff_strength: 1.0,
|
||||
buff_duration: Some(30.0),
|
||||
energy_cost: /*15*/0,
|
||||
)
|
@ -88,9 +88,12 @@ buff-desc-frigid = Freeze your foes.
|
||||
## Lifesteal
|
||||
buff-title-lifesteal = Lifesteal
|
||||
buff-desc-lifesteal = Siphon your enemies life away.
|
||||
## Polymorped
|
||||
## Salamander's Aspect
|
||||
buff-title-salamanderaspect = Salamander's Aspect
|
||||
buff-desc-salamanderaspect = You cannot burn and move fast through lava.
|
||||
## Imminent Critical
|
||||
buff-title-imminentcritical = Imminent Critical
|
||||
buff-desc-imminentcritical = Your next attack will critically hit the enemy.
|
||||
## Util
|
||||
buff-text-over_seconds = over { $dur_secs } seconds
|
||||
buff-text-for_seconds = for { $dur_secs } seconds
|
||||
|
@ -172,6 +172,7 @@ lazy_static! {
|
||||
BuffKind::Frigid => "frigid",
|
||||
BuffKind::Lifesteal => "lifesteal",
|
||||
// BuffKind::SalamanderAspect => "salamander_aspect",
|
||||
BuffKind::ImminentCritical => "imminent_critical",
|
||||
};
|
||||
let mut buff_parser = HashMap::new();
|
||||
for kind in BuffKind::iter() {
|
||||
|
@ -214,11 +214,14 @@ impl Attack {
|
||||
matches!(attack_effect.target, Some(GroupTarget::OutOfGroup))
|
||||
&& (target_dodging || !may_harm)
|
||||
};
|
||||
let is_crit = rng.gen::<f32>()
|
||||
< self.crit_chance
|
||||
* attacker
|
||||
let crit_chance = attacker
|
||||
.and_then(|a| a.stats)
|
||||
.map_or(1.0, |s| s.crit_chance_modifier);
|
||||
.map(|s| s.crit_chance_modifier)
|
||||
.map_or(self.crit_chance, |cc_mod| {
|
||||
self.crit_chance * cc_mod.mult_mod + cc_mod.add_mod
|
||||
})
|
||||
.clamp(0.0, 1.0);
|
||||
let is_crit = rng.gen::<f32>() < crit_chance;
|
||||
let mut is_applied = false;
|
||||
let mut accumulated_damage = 0.0;
|
||||
let damage_modifier = attacker
|
||||
@ -687,6 +690,7 @@ impl Attack {
|
||||
if is_applied {
|
||||
emit(ServerEvent::EntityAttackedHook {
|
||||
entity: target.entity,
|
||||
attacker: attacker.map(|a| a.entity),
|
||||
});
|
||||
}
|
||||
is_applied
|
||||
|
@ -74,6 +74,10 @@ pub enum BuffKind {
|
||||
/// Provides immunity to burning and increases movement speed in lava.
|
||||
/// Movement speed increases linearly with strength, 1.0 is a 100% increase.
|
||||
// SalamanderAspect, TODO: Readd in second dwarven mine MR
|
||||
/// Guarantees that the next attack is a critical hit. Does this kind of
|
||||
/// hackily by adding 100% to the crit, will need to be adjusted if we ever
|
||||
/// allow double crits instead of treating 100 as a ceiling.
|
||||
ImminentCritical,
|
||||
// Debuffs
|
||||
/// Does damage to a creature over time.
|
||||
/// Strength should be the DPS of the debuff.
|
||||
@ -143,7 +147,7 @@ impl BuffKind {
|
||||
| BuffKind::Frigid
|
||||
| BuffKind::Lifesteal
|
||||
//| BuffKind::SalamanderAspect
|
||||
=> true,
|
||||
| BuffKind::ImminentCritical => true,
|
||||
BuffKind::Bleeding
|
||||
| BuffKind::Cursed
|
||||
| BuffKind::Burning
|
||||
@ -212,7 +216,7 @@ impl BuffKind {
|
||||
},
|
||||
BuffKind::CampfireHeal => vec![BuffEffect::HealthChangeOverTime {
|
||||
rate: data.strength,
|
||||
kind: ModifierKind::Fractional,
|
||||
kind: ModifierKind::Multiplicative,
|
||||
instance,
|
||||
tick_dur: Secs(2.0),
|
||||
}],
|
||||
@ -287,7 +291,10 @@ impl BuffKind {
|
||||
BuffKind::Hastened => vec![
|
||||
BuffEffect::MovementSpeed(1.0 + data.strength),
|
||||
BuffEffect::AttackSpeed(1.0 + data.strength),
|
||||
BuffEffect::CriticalChance(0.0),
|
||||
BuffEffect::CriticalChance {
|
||||
kind: ModifierKind::Multiplicative,
|
||||
val: 0.0,
|
||||
},
|
||||
],
|
||||
BuffKind::Fortitude => vec![
|
||||
BuffEffect::PoiseReduction(nn_scaling(data.strength)),
|
||||
@ -329,8 +336,24 @@ impl BuffKind {
|
||||
BuffEffect::BuffImmunity(BuffKind::Burning),
|
||||
BuffEffect::SwimSpeed(1.0 + data.strength),
|
||||
],*/
|
||||
BuffKind::ImminentCritical => vec![BuffEffect::CriticalChance {
|
||||
kind: ModifierKind::Additive,
|
||||
val: 1.0,
|
||||
}],
|
||||
}
|
||||
}
|
||||
|
||||
fn extend_cat_ids(&self, mut cat_ids: Vec<BuffCategory>) -> Vec<BuffCategory> {
|
||||
// TODO: Remove clippy allow after another buff needs this
|
||||
#[allow(clippy::single_match)]
|
||||
match self {
|
||||
BuffKind::ImminentCritical => {
|
||||
cat_ids.push(BuffCategory::RemoveOnAttack);
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
cat_ids
|
||||
}
|
||||
}
|
||||
|
||||
// Struct used to store data relevant to a buff
|
||||
@ -362,12 +385,13 @@ pub enum BuffCategory {
|
||||
Divine,
|
||||
PersistOnDeath,
|
||||
FromActiveAura(Uid, AuraKey),
|
||||
RemoveOnAttack,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub enum ModifierKind {
|
||||
Additive,
|
||||
Fractional,
|
||||
Multiplicative,
|
||||
}
|
||||
|
||||
/// Data indicating and configuring behaviour of a de/buff.
|
||||
@ -422,7 +446,10 @@ pub enum BuffEffect {
|
||||
/// Modifier to the amount of damage dealt with attacks
|
||||
AttackDamage(f32),
|
||||
/// Multiplies crit chance of attacks
|
||||
CriticalChance(f32),
|
||||
CriticalChance {
|
||||
kind: ModifierKind,
|
||||
val: f32,
|
||||
},
|
||||
/// Changes body.
|
||||
BodyChange(Body),
|
||||
/// Inflict buff to target
|
||||
@ -489,6 +516,7 @@ impl Buff {
|
||||
health: Option<&Health>,
|
||||
) -> Self {
|
||||
let effects = kind.effects(&data, stats, health);
|
||||
let cat_ids = kind.extend_cat_ids(cat_ids);
|
||||
let start_time = Time(time.0 + data.delay.map_or(0.0, |delay| delay.0));
|
||||
let end_time = if cat_ids
|
||||
.iter()
|
||||
|
@ -63,7 +63,7 @@ pub struct Stats {
|
||||
pub max_energy_modifiers: StatsModifier,
|
||||
pub poise_damage_modifier: f32,
|
||||
pub attack_damage_modifier: f32,
|
||||
pub crit_chance_modifier: f32,
|
||||
pub crit_chance_modifier: StatsModifier,
|
||||
pub buffs_on_hit: Vec<AttackEffect>,
|
||||
pub swim_speed_modifier: f32,
|
||||
}
|
||||
@ -83,7 +83,7 @@ impl Stats {
|
||||
max_energy_modifiers: StatsModifier::default(),
|
||||
poise_damage_modifier: 1.0,
|
||||
attack_damage_modifier: 1.0,
|
||||
crit_chance_modifier: 1.0,
|
||||
crit_chance_modifier: StatsModifier::default(),
|
||||
buffs_on_hit: Vec::new(),
|
||||
swim_speed_modifier: 1.0,
|
||||
}
|
||||
|
@ -297,6 +297,7 @@ pub enum ServerEvent {
|
||||
},
|
||||
EntityAttackedHook {
|
||||
entity: EcsEntity,
|
||||
attacker: Option<EcsEntity>,
|
||||
},
|
||||
ChangeAbility {
|
||||
entity: EcsEntity,
|
||||
|
@ -535,7 +535,7 @@ fn execute_effect(
|
||||
};
|
||||
let amount = match *kind {
|
||||
ModifierKind::Additive => amount,
|
||||
ModifierKind::Fractional => health.maximum() * amount,
|
||||
ModifierKind::Multiplicative => health.maximum() * amount,
|
||||
};
|
||||
let damage_contributor = by.and_then(|uid| {
|
||||
read_data.id_maps.uid_entity(uid).map(|entity| {
|
||||
@ -565,7 +565,7 @@ fn execute_effect(
|
||||
|
||||
let amount = match *kind {
|
||||
ModifierKind::Additive => amount,
|
||||
ModifierKind::Fractional => energy.maximum() * amount,
|
||||
ModifierKind::Multiplicative => energy.maximum() * amount,
|
||||
};
|
||||
server_emitter.emit(ServerEvent::EnergyChange {
|
||||
entity,
|
||||
@ -577,7 +577,7 @@ fn execute_effect(
|
||||
ModifierKind::Additive => {
|
||||
stat.max_health_modifiers.add_mod += *value;
|
||||
},
|
||||
ModifierKind::Fractional => {
|
||||
ModifierKind::Multiplicative => {
|
||||
stat.max_health_modifiers.mult_mod *= *value;
|
||||
},
|
||||
},
|
||||
@ -585,7 +585,7 @@ fn execute_effect(
|
||||
ModifierKind::Additive => {
|
||||
stat.max_energy_modifiers.add_mod += *value;
|
||||
},
|
||||
ModifierKind::Fractional => {
|
||||
ModifierKind::Multiplicative => {
|
||||
stat.max_energy_modifiers.mult_mod *= *value;
|
||||
},
|
||||
},
|
||||
@ -608,7 +608,7 @@ fn execute_effect(
|
||||
// creates fraction
|
||||
potential_amount / health.base_max()
|
||||
},
|
||||
ModifierKind::Fractional => {
|
||||
ModifierKind::Multiplicative => {
|
||||
// `rate * dt` is the fraction
|
||||
potential_amount
|
||||
},
|
||||
@ -664,8 +664,9 @@ fn execute_effect(
|
||||
BuffEffect::AttackDamage(dam) => {
|
||||
stat.attack_damage_modifier *= *dam;
|
||||
},
|
||||
BuffEffect::CriticalChance(cc) => {
|
||||
stat.crit_chance_modifier *= *cc;
|
||||
BuffEffect::CriticalChance { kind, val } => match kind {
|
||||
ModifierKind::Additive => stat.crit_chance_modifier.add_mod += val,
|
||||
ModifierKind::Multiplicative => stat.crit_chance_modifier.mult_mod *= val,
|
||||
},
|
||||
BuffEffect::BodyChange(b) => {
|
||||
// For when an entity is under the effects of multiple de/buffs that change the
|
||||
|
@ -659,7 +659,7 @@ impl<'a> AgentData<'a> {
|
||||
comp::BuffEffect::HealthChangeOverTime { rate, kind, .. } => {
|
||||
let amount = match kind {
|
||||
comp::ModifierKind::Additive => rate * duration.0 as f32,
|
||||
comp::ModifierKind::Fractional => {
|
||||
comp::ModifierKind::Multiplicative => {
|
||||
(1.0 + rate).powf(duration.0 as f32)
|
||||
},
|
||||
};
|
||||
|
@ -1445,11 +1445,25 @@ pub fn handle_teleport_to(server: &Server, entity: EcsEntity, target: Uid, max_r
|
||||
|
||||
/// Intended to handle things that should happen for any successful attack,
|
||||
/// regardless of the damages and effects specific to that attack
|
||||
pub fn handle_entity_attacked_hook(server: &Server, entity: EcsEntity) {
|
||||
pub fn handle_entity_attacked_hook(
|
||||
server: &Server,
|
||||
entity: EcsEntity,
|
||||
attacker: Option<EcsEntity>,
|
||||
) {
|
||||
let ecs = &server.state.ecs();
|
||||
let server_eventbus = ecs.read_resource::<EventBus<ServerEvent>>();
|
||||
|
||||
let time = ecs.read_resource::<Time>();
|
||||
if let Some(attacker) = attacker {
|
||||
server_eventbus.emit_now(ServerEvent::Buff {
|
||||
entity: attacker,
|
||||
buff_change: buff::BuffChange::RemoveByCategory {
|
||||
all_required: vec![buff::BuffCategory::RemoveOnAttack],
|
||||
any_required: vec![],
|
||||
none_required: vec![],
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
if let (Some(mut char_state), Some(mut poise), Some(pos)) = (
|
||||
ecs.write_storage::<CharacterState>().get_mut(entity),
|
||||
|
@ -264,8 +264,8 @@ impl Server {
|
||||
pet_entity,
|
||||
owner_entity,
|
||||
} => handle_tame_pet(self, pet_entity, owner_entity),
|
||||
ServerEvent::EntityAttackedHook { entity } => {
|
||||
handle_entity_attacked_hook(self, entity)
|
||||
ServerEvent::EntityAttackedHook { entity, attacker } => {
|
||||
handle_entity_attacked_hook(self, entity, attacker)
|
||||
},
|
||||
ServerEvent::ChangeAbility {
|
||||
entity,
|
||||
|
@ -194,6 +194,39 @@ impl Animation for SelfBuffAnimation {
|
||||
next.head.orientation.rotate_x(move2 * 0.6);
|
||||
next.chest.orientation.rotate_x(move2 * 0.4);
|
||||
},
|
||||
Some("common.abilities.axe.savage_sense") => {
|
||||
let (move1, move2, move3) = match stage_section {
|
||||
Some(StageSection::Movement) => (anim_time, 0.0, 0.0),
|
||||
Some(StageSection::Action) => (1.0, anim_time, 0.0),
|
||||
Some(StageSection::Recover) => (1.0, 1.0, anim_time),
|
||||
_ => (0.0, 0.0, 0.0),
|
||||
};
|
||||
let pullback = 1.0 - move3;
|
||||
let move1 = move1 * pullback;
|
||||
let move2 = move2 * pullback;
|
||||
|
||||
next.hand_l.position = Vec3::new(s_a.ahl.0, s_a.ahl.1, s_a.ahl.2);
|
||||
next.hand_l.orientation =
|
||||
Quaternion::rotation_x(s_a.ahl.3) * Quaternion::rotation_y(s_a.ahl.4);
|
||||
next.hand_r.position = Vec3::new(s_a.ahr.0, s_a.ahr.1, s_a.ahr.2);
|
||||
next.hand_r.orientation =
|
||||
Quaternion::rotation_x(s_a.ahr.3) * Quaternion::rotation_z(s_a.ahr.5);
|
||||
|
||||
next.control.position = Vec3::new(s_a.ac.0, s_a.ac.1, s_a.ac.2);
|
||||
next.control.orientation = Quaternion::rotation_x(s_a.ac.3)
|
||||
* Quaternion::rotation_y(s_a.ac.4)
|
||||
* Quaternion::rotation_z(s_a.ac.5);
|
||||
|
||||
next.chest.orientation = Quaternion::rotation_z(move1 * 0.6);
|
||||
next.head.orientation = Quaternion::rotation_z(move1 * -0.2);
|
||||
next.belt.orientation = Quaternion::rotation_z(move1 * -0.3);
|
||||
next.shorts.orientation = Quaternion::rotation_z(move1 * -0.1);
|
||||
next.foot_r.position += Vec3::new(0.0, move1 * 4.0, move1 * 4.0);
|
||||
next.foot_r.orientation.rotate_x(move1 * 1.2);
|
||||
|
||||
next.foot_r.position += Vec3::new(0.0, move2 * 4.0, move2 * -4.0);
|
||||
next.foot_r.orientation.rotate_x(move2 * -1.2);
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
|
||||
|
@ -121,7 +121,7 @@ pub fn localize_chat_message(
|
||||
| BuffKind::Frigid
|
||||
| BuffKind::Lifesteal
|
||||
// | BuffKind::SalamanderAspect
|
||||
=> {
|
||||
| BuffKind::ImminentCritical => {
|
||||
tracing::error!("Player was killed by a positive buff!");
|
||||
"hud-outcome-mysterious"
|
||||
},
|
||||
|
@ -5111,6 +5111,8 @@ pub fn get_buff_image(buff: BuffKind, imgs: &Imgs) -> conrod_core::image::Id {
|
||||
BuffKind::Lifesteal => imgs.buff_plus_0,
|
||||
// TODO: Get image
|
||||
// BuffKind::SalamanderAspect => imgs.debuff_burning_0,
|
||||
// TODO: Get buff image
|
||||
BuffKind::ImminentCritical => imgs.buff_reckless,
|
||||
// Debuffs
|
||||
BuffKind::Bleeding => imgs.debuff_bleed_0,
|
||||
BuffKind::Cursed => imgs.debuff_skull_0,
|
||||
@ -5147,6 +5149,7 @@ pub fn get_buff_title(buff: BuffKind, localized_strings: &Localization) -> Cow<s
|
||||
BuffKind::Fortitude => localized_strings.get_msg("buff-title-fortitude"),
|
||||
BuffKind::Reckless => localized_strings.get_msg("buff-title-reckless"),
|
||||
// BuffKind::SalamanderAspect => localized_strings.get_msg("buff-title-salamanderaspect"),
|
||||
BuffKind::ImminentCritical => localized_strings.get_msg("buff-title-imminentcritical"),
|
||||
// Debuffs
|
||||
BuffKind::Bleeding { .. } => localized_strings.get_msg("buff-title-bleed"),
|
||||
BuffKind::Cursed { .. } => localized_strings.get_msg("buff-title-cursed"),
|
||||
@ -5190,6 +5193,7 @@ pub fn get_buff_desc(buff: BuffKind, data: BuffData, localized_strings: &Localiz
|
||||
BuffKind::Fortitude => localized_strings.get_msg("buff-desc-fortitude"),
|
||||
BuffKind::Reckless => localized_strings.get_msg("buff-desc-reckless"),
|
||||
// BuffKind::SalamanderAspect => localized_strings.get_msg("buff-desc-salamanderaspect"),
|
||||
BuffKind::ImminentCritical => localized_strings.get_msg("buff-desc-imminentcritical"),
|
||||
// Debuffs
|
||||
BuffKind::Bleeding { .. } => localized_strings.get_msg("buff-desc-bleed"),
|
||||
BuffKind::Cursed { .. } => localized_strings.get_msg("buff-desc-cursed"),
|
||||
|
@ -210,7 +210,7 @@ pub fn consumable_desc(effects: &Effects, i18n: &Localization) -> Vec<String> {
|
||||
| BuffKind::Frigid
|
||||
| BuffKind::Lifesteal
|
||||
// | BuffKind::SalamanderAspect
|
||||
=> Cow::Borrowed(""),
|
||||
| BuffKind::ImminentCritical => Cow::Borrowed(""),
|
||||
};
|
||||
|
||||
write!(&mut description, "{}", buff_desc).unwrap();
|
||||
@ -253,7 +253,7 @@ pub fn consumable_desc(effects: &Effects, i18n: &Localization) -> Vec<String> {
|
||||
| BuffKind::Frigid
|
||||
| BuffKind::Lifesteal
|
||||
// | BuffKind::SalamanderAspect
|
||||
=> Cow::Borrowed(""),
|
||||
| BuffKind::ImminentCritical => Cow::Borrowed(""),
|
||||
}
|
||||
} else if let BuffKind::Saturation
|
||||
| BuffKind::Regeneration
|
||||
|
Loading…
Reference in New Issue
Block a user