2020-10-17 22:42:43 +00:00
|
|
|
use crate::{
|
2021-01-08 19:12:09 +00:00
|
|
|
comp::{
|
2021-01-08 20:53:52 +00:00
|
|
|
inventory::{
|
2021-01-16 17:01:57 +00:00
|
|
|
item::{
|
|
|
|
armor::Protection,
|
|
|
|
tool::{Tool, ToolKind},
|
|
|
|
ItemKind,
|
|
|
|
},
|
2021-01-08 20:53:52 +00:00
|
|
|
slot::EquipSlot,
|
|
|
|
},
|
2021-01-18 19:08:13 +00:00
|
|
|
skills::{SkillGroupKind, SkillSet},
|
2021-01-22 21:12:16 +00:00
|
|
|
Body, BuffKind, Health, HealthChange, HealthSource, Inventory, Stats,
|
2021-01-08 19:12:09 +00:00
|
|
|
},
|
2020-12-13 17:11:55 +00:00
|
|
|
uid::Uid,
|
2020-10-18 18:21:58 +00:00
|
|
|
util::Dir,
|
2020-10-17 22:42:43 +00:00
|
|
|
};
|
|
|
|
use serde::{Deserialize, Serialize};
|
2020-10-18 18:21:58 +00:00
|
|
|
use vek::*;
|
2020-10-17 22:42:43 +00:00
|
|
|
|
2020-11-02 00:26:01 +00:00
|
|
|
#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
|
|
|
|
pub enum GroupTarget {
|
|
|
|
InGroup,
|
|
|
|
OutOfGroup,
|
|
|
|
}
|
|
|
|
|
2020-11-05 01:21:42 +00:00
|
|
|
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
|
2020-10-30 20:41:21 +00:00
|
|
|
pub enum DamageSource {
|
2021-01-18 05:46:53 +00:00
|
|
|
Buff(BuffKind),
|
2020-10-30 20:41:21 +00:00
|
|
|
Melee,
|
|
|
|
Healing,
|
|
|
|
Projectile,
|
|
|
|
Explosion,
|
|
|
|
Falling,
|
|
|
|
Shockwave,
|
|
|
|
Energy,
|
2020-11-05 01:21:42 +00:00
|
|
|
Other,
|
2020-10-17 22:42:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
|
2020-10-30 20:41:21 +00:00
|
|
|
pub struct Damage {
|
|
|
|
pub source: DamageSource,
|
|
|
|
pub value: f32,
|
2020-10-17 22:42:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Damage {
|
2021-01-08 19:12:09 +00:00
|
|
|
/// Returns the total damage reduction provided by all equipped items
|
|
|
|
pub fn compute_damage_reduction(inventory: &Inventory) -> f32 {
|
|
|
|
let protection = inventory
|
|
|
|
.equipped_items()
|
|
|
|
.filter_map(|item| {
|
|
|
|
if let ItemKind::Armor(armor) = &item.kind() {
|
|
|
|
Some(armor.get_protection())
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.map(|protection| match protection {
|
|
|
|
Protection::Normal(protection) => Some(protection),
|
|
|
|
Protection::Invincible => None,
|
|
|
|
})
|
|
|
|
.sum::<Option<f32>>();
|
|
|
|
match protection {
|
|
|
|
Some(dr) => dr / (60.0 + dr.abs()),
|
|
|
|
None => 1.0,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn modify_damage(self, inventory: Option<&Inventory>, uid: Option<Uid>) -> HealthChange {
|
2020-10-30 20:41:21 +00:00
|
|
|
let mut damage = self.value;
|
2021-01-08 19:12:09 +00:00
|
|
|
let damage_reduction = inventory.map_or(0.0, |inv| Damage::compute_damage_reduction(inv));
|
2020-10-30 20:41:21 +00:00
|
|
|
match self.source {
|
|
|
|
DamageSource::Melee => {
|
2020-10-17 22:42:43 +00:00
|
|
|
// Critical hit
|
|
|
|
let mut critdamage = 0.0;
|
|
|
|
if rand::random() {
|
|
|
|
critdamage = damage * 0.3;
|
2020-12-26 11:25:50 +00:00
|
|
|
}
|
2020-10-17 22:42:43 +00:00
|
|
|
// Armor
|
|
|
|
damage *= 1.0 - damage_reduction;
|
|
|
|
|
|
|
|
// Critical damage applies after armor for melee
|
|
|
|
if (damage_reduction - 1.0).abs() > f32::EPSILON {
|
|
|
|
damage += critdamage;
|
|
|
|
}
|
|
|
|
|
2020-12-06 02:29:46 +00:00
|
|
|
HealthChange {
|
|
|
|
amount: -damage as i32,
|
|
|
|
cause: HealthSource::Damage {
|
|
|
|
kind: self.source,
|
|
|
|
by: uid,
|
2020-11-05 01:21:42 +00:00
|
|
|
},
|
2020-12-06 02:29:46 +00:00
|
|
|
}
|
2020-10-17 22:42:43 +00:00
|
|
|
},
|
2020-10-30 20:41:21 +00:00
|
|
|
DamageSource::Projectile => {
|
2020-10-17 22:42:43 +00:00
|
|
|
// Critical hit
|
2020-12-26 11:25:50 +00:00
|
|
|
if rand::random() {
|
2020-10-17 22:42:43 +00:00
|
|
|
damage *= 1.2;
|
2020-12-26 11:25:50 +00:00
|
|
|
}
|
2020-10-17 22:42:43 +00:00
|
|
|
// Armor
|
|
|
|
damage *= 1.0 - damage_reduction;
|
|
|
|
|
2020-12-06 02:29:46 +00:00
|
|
|
HealthChange {
|
|
|
|
amount: -damage as i32,
|
|
|
|
cause: HealthSource::Damage {
|
|
|
|
kind: self.source,
|
|
|
|
by: uid,
|
2020-11-05 01:21:42 +00:00
|
|
|
},
|
2020-12-06 02:29:46 +00:00
|
|
|
}
|
2020-10-17 22:42:43 +00:00
|
|
|
},
|
2020-10-30 20:41:21 +00:00
|
|
|
DamageSource::Explosion => {
|
2020-10-17 22:42:43 +00:00
|
|
|
// Armor
|
|
|
|
damage *= 1.0 - damage_reduction;
|
|
|
|
|
2020-12-06 02:29:46 +00:00
|
|
|
HealthChange {
|
|
|
|
amount: -damage as i32,
|
|
|
|
cause: HealthSource::Damage {
|
|
|
|
kind: self.source,
|
|
|
|
by: uid,
|
2020-11-05 01:21:42 +00:00
|
|
|
},
|
2020-12-06 02:29:46 +00:00
|
|
|
}
|
2020-10-17 22:42:43 +00:00
|
|
|
},
|
2020-10-30 20:41:21 +00:00
|
|
|
DamageSource::Shockwave => {
|
2020-10-17 22:42:43 +00:00
|
|
|
// Armor
|
|
|
|
damage *= 1.0 - damage_reduction;
|
|
|
|
|
2020-12-06 02:29:46 +00:00
|
|
|
HealthChange {
|
|
|
|
amount: -damage as i32,
|
|
|
|
cause: HealthSource::Damage {
|
|
|
|
kind: self.source,
|
|
|
|
by: uid,
|
2020-11-05 01:21:42 +00:00
|
|
|
},
|
2020-12-06 02:29:46 +00:00
|
|
|
}
|
2020-10-17 22:42:43 +00:00
|
|
|
},
|
2020-10-30 20:41:21 +00:00
|
|
|
DamageSource::Energy => {
|
2020-10-17 22:42:43 +00:00
|
|
|
// Armor
|
|
|
|
damage *= 1.0 - damage_reduction;
|
|
|
|
|
2020-12-06 02:29:46 +00:00
|
|
|
HealthChange {
|
|
|
|
amount: -damage as i32,
|
|
|
|
cause: HealthSource::Damage {
|
|
|
|
kind: self.source,
|
|
|
|
by: uid,
|
2020-11-05 01:21:42 +00:00
|
|
|
},
|
2020-12-06 02:29:46 +00:00
|
|
|
}
|
|
|
|
},
|
|
|
|
DamageSource::Healing => HealthChange {
|
|
|
|
amount: damage as i32,
|
|
|
|
cause: HealthSource::Heal { by: uid },
|
2020-10-17 22:42:43 +00:00
|
|
|
},
|
2020-10-30 20:41:21 +00:00
|
|
|
DamageSource::Falling => {
|
2020-10-17 22:42:43 +00:00
|
|
|
// Armor
|
|
|
|
if (damage_reduction - 1.0).abs() < f32::EPSILON {
|
|
|
|
damage = 0.0;
|
|
|
|
}
|
2020-12-06 02:29:46 +00:00
|
|
|
HealthChange {
|
|
|
|
amount: -damage as i32,
|
|
|
|
cause: HealthSource::World,
|
|
|
|
},
|
2020-10-17 22:42:43 +00:00
|
|
|
},
|
2021-01-18 05:46:53 +00:00
|
|
|
DamageSource::Buff(_) => HealthChange {
|
|
|
|
amount: -damage as i32,
|
|
|
|
cause: HealthSource::Damage {
|
|
|
|
kind: self.source,
|
|
|
|
by: uid,
|
|
|
|
},
|
|
|
|
},
|
2020-11-05 01:21:42 +00:00
|
|
|
DamageSource::Other => HealthChange {
|
|
|
|
amount: -damage as i32,
|
|
|
|
cause: HealthSource::Damage {
|
|
|
|
kind: self.source,
|
|
|
|
by: uid,
|
|
|
|
},
|
2020-12-06 02:29:46 +00:00
|
|
|
},
|
2020-10-17 22:42:43 +00:00
|
|
|
}
|
|
|
|
}
|
2020-10-30 20:41:21 +00:00
|
|
|
|
|
|
|
pub fn interpolate_damage(&mut self, frac: f32, min: f32) {
|
|
|
|
let new_damage = min + frac * (self.value - min);
|
|
|
|
self.value = new_damage;
|
|
|
|
}
|
2020-10-17 22:42:43 +00:00
|
|
|
}
|
2020-10-18 18:21:58 +00:00
|
|
|
|
|
|
|
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
|
|
|
|
pub enum Knockback {
|
|
|
|
Away(f32),
|
|
|
|
Towards(f32),
|
|
|
|
Up(f32),
|
|
|
|
TowardsUp(f32),
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Knockback {
|
2020-10-27 22:16:17 +00:00
|
|
|
pub fn calculate_impulse(self, dir: Dir) -> Vec3<f32> {
|
2020-10-18 18:21:58 +00:00
|
|
|
match self {
|
|
|
|
Knockback::Away(strength) => strength * *Dir::slerp(dir, Dir::new(Vec3::unit_z()), 0.5),
|
|
|
|
Knockback::Towards(strength) => {
|
|
|
|
strength * *Dir::slerp(-dir, Dir::new(Vec3::unit_z()), 0.5)
|
|
|
|
},
|
|
|
|
Knockback::Up(strength) => strength * Vec3::unit_z(),
|
|
|
|
Knockback::TowardsUp(strength) => {
|
|
|
|
strength * *Dir::slerp(-dir, Dir::new(Vec3::unit_z()), 0.85)
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
2020-12-24 17:54:00 +00:00
|
|
|
|
|
|
|
pub fn modify_strength(mut self, power: f32) -> Self {
|
|
|
|
use Knockback::*;
|
|
|
|
match self {
|
|
|
|
Away(ref mut f) | Towards(ref mut f) | Up(ref mut f) | TowardsUp(ref mut f) => {
|
|
|
|
*f *= power;
|
|
|
|
},
|
|
|
|
}
|
|
|
|
self
|
|
|
|
}
|
2020-10-18 18:21:58 +00:00
|
|
|
}
|
2020-11-15 02:05:18 +00:00
|
|
|
|
2021-01-16 17:01:57 +00:00
|
|
|
fn equipped_tool(inv: &Inventory, slot: EquipSlot) -> Option<&Tool> {
|
|
|
|
inv.equipped(slot).and_then(|i| {
|
|
|
|
if let ItemKind::Tool(tool) = &i.kind() {
|
|
|
|
Some(tool)
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2020-11-15 02:05:18 +00:00
|
|
|
pub fn get_weapons(inv: &Inventory) -> (Option<ToolKind>, Option<ToolKind>) {
|
|
|
|
(
|
2021-01-16 17:01:57 +00:00
|
|
|
equipped_tool(inv, EquipSlot::Mainhand).map(|tool| tool.kind),
|
|
|
|
equipped_tool(inv, EquipSlot::Offhand).map(|tool| tool.kind),
|
2020-11-15 02:05:18 +00:00
|
|
|
)
|
2021-01-07 05:23:24 +00:00
|
|
|
}
|
|
|
|
|
2021-01-18 19:08:13 +00:00
|
|
|
fn offensive_rating(inv: &Inventory, skillset: &SkillSet) -> f32 {
|
2021-01-16 17:01:57 +00:00
|
|
|
let active_damage = equipped_tool(inv, EquipSlot::Mainhand).map_or(0.0, |tool| {
|
|
|
|
tool.base_power()
|
|
|
|
* tool.base_speed()
|
2021-01-18 19:08:13 +00:00
|
|
|
* (1.0 + 0.05 * skillset.earned_sp(SkillGroupKind::Weapon(tool.kind)) as f32)
|
2021-01-07 05:23:24 +00:00
|
|
|
});
|
2021-01-16 17:01:57 +00:00
|
|
|
let second_damage = equipped_tool(inv, EquipSlot::Offhand).map_or(0.0, |tool| {
|
|
|
|
tool.base_power()
|
|
|
|
* tool.base_speed()
|
2021-01-18 19:08:13 +00:00
|
|
|
* (1.0 + 0.05 * skillset.earned_sp(SkillGroupKind::Weapon(tool.kind)) as f32)
|
2021-01-07 05:23:24 +00:00
|
|
|
});
|
2021-01-10 01:05:13 +00:00
|
|
|
active_damage.max(second_damage)
|
2021-01-07 05:23:24 +00:00
|
|
|
}
|
|
|
|
|
2021-01-22 21:12:16 +00:00
|
|
|
pub fn combat_rating(inventory: &Inventory, health: &Health, stats: &Stats, body: Body) -> f32 {
|
2021-01-16 17:01:57 +00:00
|
|
|
let defensive_weighting = 1.0;
|
|
|
|
let offensive_weighting = 1.0;
|
2021-01-10 01:05:13 +00:00
|
|
|
let defensive_rating = health.maximum() as f32
|
|
|
|
/ (1.0 - Damage::compute_damage_reduction(inventory)).max(0.00001)
|
|
|
|
/ 100.0;
|
2021-01-18 19:08:13 +00:00
|
|
|
let offensive_rating = offensive_rating(inventory, &stats.skill_set).max(0.1)
|
|
|
|
+ 0.05 * stats.skill_set.earned_sp(SkillGroupKind::General) as f32;
|
2021-01-07 05:23:24 +00:00
|
|
|
let combined_rating = (offensive_rating * offensive_weighting
|
|
|
|
+ defensive_rating * defensive_weighting)
|
2021-01-16 17:01:57 +00:00
|
|
|
/ (offensive_weighting + defensive_weighting);
|
2021-01-22 21:12:16 +00:00
|
|
|
combined_rating * body.combat_multiplier()
|
2021-01-07 05:23:24 +00:00
|
|
|
}
|