mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Code changes and msm
This commit is contained in:
parent
0e39345243
commit
5e57eabd11
@ -21,3 +21,4 @@ tracy-voxygen = "run --bin veloren-voxygen --no-default-features --features trac
|
||||
dbg-voxygen = "run --bin veloren-voxygen --profile debuginfo"
|
||||
# misc
|
||||
swarm = "run --bin swarm --features client/bin_bot,client/tick_network --"
|
||||
ci-clippy = "clippy --all-targets --locked --features=bin_cmd_doc_gen,bin_compression,bin_csv,bin_graphviz,bin_bot,bin_asset_migrate,asset_tweak"
|
@ -1,5 +1,6 @@
|
||||
// Keep in mind that material stats are multiplied by the form stats, not added (e.g. equip_time_secs is most sensitive to this)
|
||||
({
|
||||
(
|
||||
tool_stats: {
|
||||
// Metals
|
||||
"common.items.mineral.ingot.bronze": (
|
||||
equip_time_secs: 1.0,
|
||||
@ -122,4 +123,195 @@
|
||||
energy_efficiency: 1.0,
|
||||
buff_strength: 1.0,
|
||||
),
|
||||
})
|
||||
},
|
||||
armor_stats: {
|
||||
// Metals
|
||||
"common.items.mineral.ingot.bronze": (
|
||||
protection: Some(Normal(40.0)),
|
||||
poise_resilience: Some(Normal(10.0)),
|
||||
),
|
||||
"common.items.mineral.ingot.iron": (
|
||||
protection: Some(Normal(80.0)),
|
||||
poise_resilience: Some(Normal(20.0)),
|
||||
),
|
||||
"common.items.mineral.ingot.steel": (
|
||||
protection: Some(Normal(120.0)),
|
||||
poise_resilience: Some(Normal(30.0)),
|
||||
),
|
||||
"common.items.mineral.ingot.cobalt": (
|
||||
protection: Some(Normal(160.0)),
|
||||
poise_resilience: Some(Normal(40.0)),
|
||||
),
|
||||
"common.items.mineral.ingot.bloodsteel": (
|
||||
protection: Some(Normal(200.0)),
|
||||
poise_resilience: Some(Normal(50.0)),
|
||||
),
|
||||
"common.items.mineral.ingot.orichalcum": (
|
||||
protection: Some(Normal(240.0)),
|
||||
poise_resilience: Some(Normal(60.0)),
|
||||
),
|
||||
// Hides
|
||||
"common.items.crafting_ing.leather.simple_leather": (
|
||||
protection: Some(Normal(20.0)),
|
||||
crit_power: Some(0.333),
|
||||
stealth: Some(0.333),
|
||||
),
|
||||
"common.items.crafting_ing.leather.thick_leather": (
|
||||
protection: Some(Normal(40.0)),
|
||||
crit_power: Some(0.667),
|
||||
stealth: Some(0.667),
|
||||
),
|
||||
"common.items.crafting_ing.hide.scales": (
|
||||
protection: Some(Normal(60.0)),
|
||||
crit_power: Some(1.0),
|
||||
stealth: Some(1.0),
|
||||
),
|
||||
"common.items.crafting_ing.hide.carapace": (
|
||||
protection: Some(Normal(80.0)),
|
||||
crit_power: Some(1.33),
|
||||
stealth: Some(1.33),
|
||||
),
|
||||
"common.items.crafting_ing.hide.plate": (
|
||||
protection: Some(Normal(100.0)),
|
||||
crit_power: Some(1.67),
|
||||
stealth: Some(1.67),
|
||||
),
|
||||
"common.items.crafting_ing.hide.dragon_scale": (
|
||||
protection: Some(Normal(120.0)),
|
||||
crit_power: Some(2.0),
|
||||
stealth: Some(2.0),
|
||||
),
|
||||
// Cloths
|
||||
"common.items.crafting_ing.cloth.linen": (
|
||||
protection: Some(Normal(15.0)),
|
||||
energy_max: Some(16.7),
|
||||
energy_reward: Some(0.167),
|
||||
stealth: Some(0.167),
|
||||
),
|
||||
"common.items.crafting_ing.cloth.wool": (
|
||||
protection: Some(Normal(30.0)),
|
||||
energy_max: Some(33.3),
|
||||
energy_reward: Some(0.333),
|
||||
stealth: Some(0.333),
|
||||
),
|
||||
"common.items.crafting_ing.cloth.silk": (
|
||||
protection: Some(Normal(45.0)),
|
||||
energy_max: Some(50.0),
|
||||
energy_reward: Some(0.5),
|
||||
stealth: Some(0.5),
|
||||
),
|
||||
"common.items.crafting_ing.cloth.lifecloth": (
|
||||
protection: Some(Normal(60.0)),
|
||||
energy_max: Some(66.7),
|
||||
energy_reward: Some(0.667),
|
||||
stealth: Some(0.667),
|
||||
),
|
||||
"common.items.crafting_ing.cloth.moonweave": (
|
||||
protection: Some(Normal(75.0)),
|
||||
energy_max: Some(83.3),
|
||||
energy_reward: Some(0.833),
|
||||
stealth: Some(0.833),
|
||||
),
|
||||
"common.items.crafting_ing.cloth.sunsilk": (
|
||||
protection: Some(Normal(90.0)),
|
||||
energy_max: Some(100.0),
|
||||
energy_reward: Some(1.0),
|
||||
stealth: Some(1.0),
|
||||
),
|
||||
// Pseudo materials
|
||||
"common.items.modular.pseudo_material.alchemist": (
|
||||
protection: Some(Normal(160.0)),
|
||||
poise_resilience: Some(Normal(20.0)),
|
||||
energy_max: Some(45.0),
|
||||
energy_reward: Some(0.5),
|
||||
crit_power: Some(0.4),
|
||||
),
|
||||
"common.items.modular.pseudo_material.assassin": (
|
||||
protection: Some(Normal(50.0)),
|
||||
poise_resilience: Some(Normal(5.0)),
|
||||
),
|
||||
"common.items.modular.pseudo_material.blacksmith": (
|
||||
protection: Some(Normal(160.0)),
|
||||
poise_resilience: Some(Normal(20.0)),
|
||||
energy_max: Some(45.0),
|
||||
energy_reward: Some(0.5),
|
||||
crit_power: Some(0.4),
|
||||
),
|
||||
"common.items.modular.pseudo_material.bonerattler": (
|
||||
protection: Some(Normal(100.0)),
|
||||
),
|
||||
"common.items.modular.pseudo_material.chef": (
|
||||
protection: Some(Normal(160.0)),
|
||||
poise_resilience: Some(Normal(20.0)),
|
||||
energy_max: Some(45.0),
|
||||
energy_reward: Some(0.5),
|
||||
crit_power: Some(0.4),
|
||||
),
|
||||
"common.items.modular.pseudo_material.cloth_blue": (
|
||||
protection: Some(Normal(5.0)),
|
||||
),
|
||||
"common.items.modular.pseudo_material.cloth_green": (
|
||||
protection: Some(Normal(5.0)),
|
||||
),
|
||||
"common.items.modular.pseudo_material.cloth_purple": (
|
||||
protection: Some(Normal(5.0)),
|
||||
),
|
||||
"common.items.modular.pseudo_material.cultist": (
|
||||
protection: Some(Normal(150.0)),
|
||||
poise_resilience: Some(Normal(20.0)),
|
||||
energy_max: Some(45.0),
|
||||
energy_reward: Some(0.5),
|
||||
crit_power: Some(0.4),
|
||||
stealth: Some(0.4),
|
||||
),
|
||||
"common.items.modular.pseudo_material.ferocious": (
|
||||
protection: Some(Normal(240.0)),
|
||||
),
|
||||
"common.items.modular.pseudo_material.leather_plate": (
|
||||
protection: Some(Normal(160.0)),
|
||||
poise_resilience: Some(Normal(40.0)),
|
||||
),
|
||||
"common.items.modular.pseudo_material.merchant": (
|
||||
protection: Some(Normal(160.0)),
|
||||
poise_resilience: Some(Normal(20.0)),
|
||||
energy_max: Some(45.0),
|
||||
energy_reward: Some(0.5),
|
||||
crit_power: Some(0.4),
|
||||
),
|
||||
"common.items.modular.pseudo_material.pirate": (
|
||||
protection: Some(Normal(160.0)),
|
||||
poise_resilience: Some(Normal(20.0)),
|
||||
energy_max: Some(45.0),
|
||||
energy_reward: Some(0.5),
|
||||
crit_power: Some(0.4),
|
||||
),
|
||||
"common.items.modular.pseudo_material.rugged": (
|
||||
protection: Some(Normal(5.0)),
|
||||
),
|
||||
"common.items.modular.pseudo_material.savage": (
|
||||
protection: Some(Normal(100.0)),
|
||||
),
|
||||
"common.items.modular.pseudo_material.tarasque": (
|
||||
protection: Some(Normal(100.0)),
|
||||
),
|
||||
"common.items.modular.pseudo_material.twigs": (
|
||||
protection: Some(Normal(60.0)),
|
||||
),
|
||||
"common.items.modular.pseudo_material.twigsflowers": (
|
||||
protection: Some(Normal(60.0)),
|
||||
),
|
||||
"common.items.modular.pseudo_material.twigsleaves": (
|
||||
protection: Some(Normal(60.0)),
|
||||
),
|
||||
"common.items.modular.pseudo_material.velorite_mage": (
|
||||
protection: Some(Normal(115.0)),
|
||||
),
|
||||
"common.items.modular.pseudo_material.witch": (
|
||||
protection: Some(Normal(160.0)),
|
||||
poise_resilience: Some(Normal(20.0)),
|
||||
energy_max: Some(45.0),
|
||||
energy_reward: Some(0.5),
|
||||
crit_power: Some(0.4),
|
||||
),
|
||||
},
|
||||
)
|
||||
|
@ -14,7 +14,7 @@ use veloren_common::{
|
||||
item::{
|
||||
armor::{ArmorKind, Protection},
|
||||
tool::{Hands, Tool, ToolKind},
|
||||
Item,
|
||||
Item, MaterialStatManifest,
|
||||
},
|
||||
},
|
||||
generation::{EntityConfig, EntityInfo},
|
||||
@ -56,20 +56,22 @@ fn armor_stats() -> Result<(), Box<dyn Error>> {
|
||||
continue;
|
||||
}
|
||||
|
||||
let protection = match armor.protection() {
|
||||
let msm = &MaterialStatManifest::load().read();
|
||||
|
||||
let protection = match armor.stats(msm).protection {
|
||||
Some(Protection::Invincible) => "Invincible".to_string(),
|
||||
Some(Protection::Normal(value)) => value.to_string(),
|
||||
None => "0.0".to_string(),
|
||||
};
|
||||
let poise_resilience = match armor.poise_resilience() {
|
||||
let poise_resilience = match armor.stats(msm).poise_resilience {
|
||||
Some(Protection::Invincible) => "Invincible".to_string(),
|
||||
Some(Protection::Normal(value)) => value.to_string(),
|
||||
None => "0.0".to_string(),
|
||||
};
|
||||
let max_energy = armor.energy_max().unwrap_or(0.0).to_string();
|
||||
let energy_reward = armor.energy_reward().unwrap_or(0.0).to_string();
|
||||
let crit_power = armor.crit_power().unwrap_or(0.0).to_string();
|
||||
let stealth = armor.stealth().unwrap_or(0.0).to_string();
|
||||
let max_energy = armor.stats(msm).energy_max.unwrap_or(0.0).to_string();
|
||||
let energy_reward = armor.stats(msm).energy_reward.unwrap_or(0.0).to_string();
|
||||
let crit_power = armor.stats(msm).crit_power.unwrap_or(0.0).to_string();
|
||||
let stealth = armor.stats(msm).stealth.unwrap_or(0.0).to_string();
|
||||
|
||||
wtr.write_record(&[
|
||||
item.item_definition_id()
|
||||
|
@ -12,7 +12,7 @@ use veloren_common::{
|
||||
comp::{
|
||||
self,
|
||||
item::{
|
||||
armor::{ArmorKind, Protection},
|
||||
armor::{ArmorKind, Protection, StatsSource},
|
||||
tool::{AbilitySpec, Hands, Stats, ToolKind},
|
||||
ItemDefinitionId, ItemKind, ItemTag, Material, Quality,
|
||||
},
|
||||
@ -124,7 +124,7 @@ fn armor_stats() -> Result<(), Box<dyn Error>> {
|
||||
None
|
||||
};
|
||||
|
||||
let max_energy =
|
||||
let energy_max =
|
||||
if let Some(max_energy_raw) = record.get(headers["Max Energy"]) {
|
||||
let value = max_energy_raw.parse().unwrap();
|
||||
if value == 0.0 { None } else { Some(value) }
|
||||
@ -174,15 +174,19 @@ fn armor_stats() -> Result<(), Box<dyn Error>> {
|
||||
};
|
||||
|
||||
let kind = armor.kind;
|
||||
let armor_stats = comp::item::armor::Stats::new(
|
||||
let armor_stats = comp::item::armor::Stats {
|
||||
protection,
|
||||
poise_resilience,
|
||||
max_energy,
|
||||
energy_max,
|
||||
energy_reward,
|
||||
crit_power,
|
||||
stealth,
|
||||
ground_contact: Default::default(),
|
||||
};
|
||||
let armor = comp::item::armor::Armor::new(
|
||||
kind,
|
||||
StatsSource::Direct(armor_stats),
|
||||
);
|
||||
let armor = comp::item::armor::Armor::new(kind, armor_stats);
|
||||
let quality = if let Some(quality_raw) = record.get(headers["Quality"])
|
||||
{
|
||||
match quality_raw {
|
||||
|
@ -133,11 +133,12 @@ impl Attack {
|
||||
source: AttackSource,
|
||||
dir: Dir,
|
||||
damage: Damage,
|
||||
msm: &MaterialStatManifest,
|
||||
mut emit: impl FnMut(ServerEvent),
|
||||
mut emit_outcome: impl FnMut(Outcome),
|
||||
) -> f32 {
|
||||
let damage_reduction =
|
||||
Damage::compute_damage_reduction(Some(damage), target.inventory, target.stats);
|
||||
Damage::compute_damage_reduction(Some(damage), target.inventory, target.stats, msm);
|
||||
let block_reduction = match source {
|
||||
AttackSource::Melee => {
|
||||
if let (Some(CharacterState::BasicBlock(data)), Some(ori)) =
|
||||
@ -186,6 +187,9 @@ impl Attack {
|
||||
mut emit: impl FnMut(ServerEvent),
|
||||
mut emit_outcome: impl FnMut(Outcome),
|
||||
) -> bool {
|
||||
// TODO: Maybe move this higher and pass it as argument into this function?
|
||||
let msm = &MaterialStatManifest::load().read();
|
||||
|
||||
let AttackOptions {
|
||||
target_dodging,
|
||||
may_harm,
|
||||
@ -220,6 +224,7 @@ impl Attack {
|
||||
attack_source,
|
||||
dir,
|
||||
damage.damage,
|
||||
msm,
|
||||
&mut emit,
|
||||
&mut emit_outcome,
|
||||
);
|
||||
@ -272,7 +277,7 @@ impl Attack {
|
||||
let reduced_damage =
|
||||
applied_damage * damage_reduction / (1.0 - damage_reduction);
|
||||
let poise = reduced_damage * CRUSHING_POISE_FRACTION;
|
||||
let change = -Poise::apply_poise_reduction(poise, target.inventory);
|
||||
let change = -Poise::apply_poise_reduction(poise, target.inventory, msm);
|
||||
let poise_change = PoiseChange {
|
||||
amount: change,
|
||||
impulse: *dir,
|
||||
@ -307,7 +312,7 @@ impl Attack {
|
||||
emit(ServerEvent::EnergyChange {
|
||||
entity: attacker.entity,
|
||||
change: *ec
|
||||
* compute_energy_reward_mod(attacker.inventory)
|
||||
* compute_energy_reward_mod(attacker.inventory, msm)
|
||||
* strength_modifier,
|
||||
});
|
||||
}
|
||||
@ -342,7 +347,7 @@ impl Attack {
|
||||
}
|
||||
},
|
||||
CombatEffect::Poise(p) => {
|
||||
let change = -Poise::apply_poise_reduction(*p, target.inventory)
|
||||
let change = -Poise::apply_poise_reduction(*p, target.inventory, msm)
|
||||
* strength_modifier;
|
||||
if change.abs() > Poise::POISE_EPSILON {
|
||||
let poise_change = PoiseChange {
|
||||
@ -450,7 +455,7 @@ impl Attack {
|
||||
emit(ServerEvent::EnergyChange {
|
||||
entity: attacker.entity,
|
||||
change: ec
|
||||
* compute_energy_reward_mod(attacker.inventory)
|
||||
* compute_energy_reward_mod(attacker.inventory, msm)
|
||||
* strength_modifier,
|
||||
});
|
||||
}
|
||||
@ -485,8 +490,8 @@ impl Attack {
|
||||
}
|
||||
},
|
||||
CombatEffect::Poise(p) => {
|
||||
let change =
|
||||
-Poise::apply_poise_reduction(p, target.inventory) * strength_modifier;
|
||||
let change = -Poise::apply_poise_reduction(p, target.inventory, msm)
|
||||
* strength_modifier;
|
||||
if change.abs() > Poise::POISE_EPSILON {
|
||||
let poise_change = PoiseChange {
|
||||
amount: change,
|
||||
@ -755,8 +760,9 @@ impl Damage {
|
||||
damage: Option<Self>,
|
||||
inventory: Option<&Inventory>,
|
||||
stats: Option<&Stats>,
|
||||
msm: &MaterialStatManifest,
|
||||
) -> f32 {
|
||||
let protection = compute_protection(inventory);
|
||||
let protection = compute_protection(inventory, msm);
|
||||
|
||||
let penetration = if let Some(damage) = damage {
|
||||
if let DamageKind::Piercing = damage.kind {
|
||||
@ -1058,20 +1064,20 @@ pub fn combat_rating(
|
||||
// Normalized with a standard max health of 100
|
||||
let health_rating = health.base_max()
|
||||
/ 100.0
|
||||
/ (1.0 - Damage::compute_damage_reduction(None, Some(inventory), None)).max(0.00001);
|
||||
/ (1.0 - Damage::compute_damage_reduction(None, Some(inventory), None, msm)).max(0.00001);
|
||||
|
||||
// Normalized with a standard max energy of 100 and energy reward multiplier of
|
||||
// x1
|
||||
let energy_rating = (energy.base_max() + compute_max_energy_mod(Some(inventory))) / 100.0
|
||||
* compute_energy_reward_mod(Some(inventory));
|
||||
let energy_rating = (energy.base_max() + compute_max_energy_mod(Some(inventory), msm)) / 100.0
|
||||
* compute_energy_reward_mod(Some(inventory), msm);
|
||||
|
||||
// Normalized with a standard max poise of 100
|
||||
let poise_rating = poise.base_max() as f32
|
||||
/ 100.0
|
||||
/ (1.0 - Poise::compute_poise_damage_reduction(inventory)).max(0.00001);
|
||||
/ (1.0 - Poise::compute_poise_damage_reduction(inventory, msm)).max(0.00001);
|
||||
|
||||
// Normalized with a standard crit multiplier of 1.2
|
||||
let crit_rating = compute_crit_mult(Some(inventory)) / 1.2;
|
||||
let crit_rating = compute_crit_mult(Some(inventory), msm) / 1.2;
|
||||
|
||||
// Assumes a standard person has earned 20 skill points in the general skill
|
||||
// tree and 10 skill points for the weapon skill tree
|
||||
@ -1100,14 +1106,14 @@ pub fn combat_rating(
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub fn compute_crit_mult(inventory: Option<&Inventory>) -> f32 {
|
||||
pub fn compute_crit_mult(inventory: Option<&Inventory>, msm: &MaterialStatManifest) -> f32 {
|
||||
// Starts with a value of 1.25 when summing the stats from each armor piece, and
|
||||
// defaults to a value of 1.25 if no inventory is equipped
|
||||
inventory.map_or(1.25, |inv| {
|
||||
inv.equipped_items()
|
||||
.filter_map(|item| {
|
||||
if let ItemKind::Armor(armor) = &*item.kind() {
|
||||
armor.crit_power()
|
||||
armor.stats(msm).crit_power
|
||||
} else {
|
||||
None
|
||||
}
|
||||
@ -1118,14 +1124,14 @@ pub fn compute_crit_mult(inventory: Option<&Inventory>) -> f32 {
|
||||
|
||||
/// Computes the energy reward modifer from worn armor
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub fn compute_energy_reward_mod(inventory: Option<&Inventory>) -> f32 {
|
||||
pub fn compute_energy_reward_mod(inventory: Option<&Inventory>, msm: &MaterialStatManifest) -> f32 {
|
||||
// Starts with a value of 1.0 when summing the stats from each armor piece, and
|
||||
// defaults to a value of 1.0 if no inventory is present
|
||||
inventory.map_or(1.0, |inv| {
|
||||
inv.equipped_items()
|
||||
.filter_map(|item| {
|
||||
if let ItemKind::Armor(armor) = &*item.kind() {
|
||||
armor.energy_reward()
|
||||
armor.stats(msm).energy_reward
|
||||
} else {
|
||||
None
|
||||
}
|
||||
@ -1137,13 +1143,13 @@ pub fn compute_energy_reward_mod(inventory: Option<&Inventory>) -> f32 {
|
||||
/// Computes the additive modifier that should be applied to max energy from the
|
||||
/// currently equipped items
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub fn compute_max_energy_mod(inventory: Option<&Inventory>) -> f32 {
|
||||
pub fn compute_max_energy_mod(inventory: Option<&Inventory>, msm: &MaterialStatManifest) -> f32 {
|
||||
// Defaults to a value of 0 if no inventory is present
|
||||
inventory.map_or(0.0, |inv| {
|
||||
inv.equipped_items()
|
||||
.filter_map(|item| {
|
||||
if let ItemKind::Armor(armor) = &*item.kind() {
|
||||
armor.energy_max()
|
||||
armor.stats(msm).energy_max
|
||||
} else {
|
||||
None
|
||||
}
|
||||
@ -1158,10 +1164,11 @@ pub fn compute_max_energy_mod(inventory: Option<&Inventory>) -> f32 {
|
||||
pub fn perception_dist_multiplier_from_stealth(
|
||||
inventory: Option<&Inventory>,
|
||||
character_state: Option<&CharacterState>,
|
||||
msm: &MaterialStatManifest,
|
||||
) -> f32 {
|
||||
const SNEAK_MULTIPLIER: f32 = 0.7;
|
||||
|
||||
let item_stealth_multiplier = stealth_multiplier_from_items(inventory);
|
||||
let item_stealth_multiplier = stealth_multiplier_from_items(inventory, msm);
|
||||
let is_sneaking = character_state.map_or(false, |state| state.is_stealthy());
|
||||
|
||||
let multiplier = item_stealth_multiplier * if is_sneaking { SNEAK_MULTIPLIER } else { 1.0 };
|
||||
@ -1170,12 +1177,15 @@ pub fn perception_dist_multiplier_from_stealth(
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub fn stealth_multiplier_from_items(inventory: Option<&Inventory>) -> f32 {
|
||||
pub fn stealth_multiplier_from_items(
|
||||
inventory: Option<&Inventory>,
|
||||
msm: &MaterialStatManifest,
|
||||
) -> f32 {
|
||||
let stealth_sum = inventory.map_or(0.0, |inv| {
|
||||
inv.equipped_items()
|
||||
.filter_map(|item| {
|
||||
if let ItemKind::Armor(armor) = &*item.kind() {
|
||||
armor.stealth()
|
||||
armor.stats(msm).stealth
|
||||
} else {
|
||||
None
|
||||
}
|
||||
@ -1190,12 +1200,15 @@ pub fn stealth_multiplier_from_items(inventory: Option<&Inventory>) -> f32 {
|
||||
/// damage reduction applied to damage received by an entity None indicates that
|
||||
/// the armor equipped makes the entity invulnerable
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub fn compute_protection(inventory: Option<&Inventory>) -> Option<f32> {
|
||||
pub fn compute_protection(
|
||||
inventory: Option<&Inventory>,
|
||||
msm: &MaterialStatManifest,
|
||||
) -> Option<f32> {
|
||||
inventory.map_or(Some(0.0), |inv| {
|
||||
inv.equipped_items()
|
||||
.filter_map(|item| {
|
||||
if let ItemKind::Armor(armor) = &*item.kind() {
|
||||
armor.protection()
|
||||
armor.stats(msm).protection
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
@ -1,11 +1,15 @@
|
||||
use crate::{
|
||||
comp::item::Rgb,
|
||||
comp::item::{MaterialStatManifest, Rgb},
|
||||
terrain::{Block, BlockKind},
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{cmp::Ordering, ops::Sub};
|
||||
use std::{
|
||||
cmp::Ordering,
|
||||
ops::{Mul, Sub},
|
||||
};
|
||||
use strum::{EnumIter, IntoEnumIterator};
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize, EnumIter)]
|
||||
pub enum ArmorKind {
|
||||
Shoulder,
|
||||
Chest,
|
||||
@ -69,66 +73,66 @@ impl Friction {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize, Default)]
|
||||
pub struct Stats {
|
||||
/// Protection is non-linearly transformed (following summation) to a damage
|
||||
/// reduction using (prot / (60 + prot))
|
||||
protection: Option<Protection>,
|
||||
pub protection: Option<Protection>,
|
||||
/// Poise protection is non-linearly transformed (following summation) to a
|
||||
/// poise damage reduction using (prot / (60 + prot))
|
||||
poise_resilience: Option<Protection>,
|
||||
pub poise_resilience: Option<Protection>,
|
||||
/// Energy max is summed, and then applied directly to the max energy stat
|
||||
energy_max: Option<f32>,
|
||||
pub energy_max: Option<f32>,
|
||||
/// Energy recovery is summed, and then added to 1.0. When attacks reward
|
||||
/// energy, it is then multiplied by this value before the energy is
|
||||
/// rewarded.
|
||||
energy_reward: Option<f32>,
|
||||
pub energy_reward: Option<f32>,
|
||||
/// Crit power is summed, and then added to the default crit multiplier of
|
||||
/// 1.25. Damage is multiplied by this value when an attack crits.
|
||||
crit_power: Option<f32>,
|
||||
pub crit_power: Option<f32>,
|
||||
/// Stealth is summed along with the base stealth bonus (2.0), and then
|
||||
/// the agent's perception distance is divided by this value
|
||||
stealth: Option<f32>,
|
||||
pub stealth: Option<f32>,
|
||||
/// Ground contact type, mostly for shoes
|
||||
#[serde(default)]
|
||||
ground_contact: Friction,
|
||||
pub ground_contact: Friction,
|
||||
}
|
||||
|
||||
impl Stats {
|
||||
// DO NOT USE UNLESS YOU KNOW WHAT YOU ARE DOING
|
||||
// Added for csv import of stats
|
||||
pub fn new(
|
||||
protection: Option<Protection>,
|
||||
poise_resilience: Option<Protection>,
|
||||
energy_max: Option<f32>,
|
||||
energy_reward: Option<f32>,
|
||||
crit_power: Option<f32>,
|
||||
stealth: Option<f32>,
|
||||
) -> Self {
|
||||
Self {
|
||||
protection,
|
||||
poise_resilience,
|
||||
energy_max,
|
||||
energy_reward,
|
||||
crit_power,
|
||||
stealth,
|
||||
fn none() -> Self {
|
||||
Stats {
|
||||
protection: None,
|
||||
poise_resilience: None,
|
||||
energy_max: None,
|
||||
energy_reward: None,
|
||||
crit_power: None,
|
||||
stealth: None,
|
||||
ground_contact: Friction::Normal,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn protection(&self) -> Option<Protection> { self.protection }
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
pub enum StatsSource {
|
||||
Direct(Stats),
|
||||
FromSet(String),
|
||||
}
|
||||
|
||||
pub fn poise_resilience(&self) -> Option<Protection> { self.poise_resilience }
|
||||
impl Mul<f32> for Stats {
|
||||
type Output = Self;
|
||||
|
||||
pub fn energy_max(&self) -> Option<f32> { self.energy_max }
|
||||
|
||||
pub fn energy_reward(&self) -> Option<f32> { self.energy_reward }
|
||||
|
||||
pub fn crit_power(&self) -> Option<f32> { self.crit_power }
|
||||
|
||||
pub fn stealth(&self) -> Option<f32> { self.stealth }
|
||||
|
||||
pub fn ground_contact(&self) -> Friction { self.ground_contact }
|
||||
fn mul(self, val: f32) -> Self::Output {
|
||||
Stats {
|
||||
protection: self.protection.map(|a| a * val),
|
||||
poise_resilience: self.poise_resilience.map(|a| a * val),
|
||||
energy_max: self.energy_max.map(|a| a * val),
|
||||
energy_reward: self.energy_reward.map(|a| a * val),
|
||||
crit_power: self.crit_power.map(|a| a * val),
|
||||
stealth: self.stealth.map(|a| a * val),
|
||||
// There is nothing to multiply, it is just an enum
|
||||
ground_contact: self.ground_contact,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Sub<Stats> for Stats {
|
||||
@ -177,6 +181,17 @@ impl Sub for Protection {
|
||||
}
|
||||
}
|
||||
|
||||
impl Mul<f32> for Protection {
|
||||
type Output = Self;
|
||||
|
||||
fn mul(self, val: f32) -> Self::Output {
|
||||
match self {
|
||||
Protection::Invincible => Protection::Invincible,
|
||||
Protection::Normal(a) => Protection::Normal(a * val),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for Protection {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
match (*self, *other) {
|
||||
@ -191,25 +206,39 @@ impl PartialOrd for Protection {
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub struct Armor {
|
||||
pub kind: ArmorKind,
|
||||
pub stats: Stats,
|
||||
stats: StatsSource,
|
||||
}
|
||||
|
||||
impl Armor {
|
||||
pub fn new(kind: ArmorKind, stats: Stats) -> Self { Self { kind, stats } }
|
||||
pub fn new(kind: ArmorKind, stats: StatsSource) -> Self { Self { kind, stats } }
|
||||
|
||||
pub fn protection(&self) -> Option<Protection> { self.stats.protection }
|
||||
pub fn stats(&self, msm: &MaterialStatManifest) -> Stats {
|
||||
match &self.stats {
|
||||
StatsSource::Direct(stats) => *stats,
|
||||
StatsSource::FromSet(set) => {
|
||||
let set_stats = msm.armor_stats(set).unwrap_or_else(Stats::none);
|
||||
let armor_kind_weight = |kind| match kind {
|
||||
ArmorKind::Shoulder => 2.0,
|
||||
ArmorKind::Chest => 3.0,
|
||||
ArmorKind::Belt => 0.5,
|
||||
ArmorKind::Hand => 1.0,
|
||||
ArmorKind::Pants => 2.0,
|
||||
ArmorKind::Foot => 1.0,
|
||||
ArmorKind::Back => 0.5,
|
||||
ArmorKind::Ring => 0.0,
|
||||
ArmorKind::Neck => 0.0,
|
||||
ArmorKind::Head => 0.0,
|
||||
ArmorKind::Tabard => 0.0,
|
||||
ArmorKind::Bag => 0.0,
|
||||
};
|
||||
|
||||
pub fn poise_resilience(&self) -> Option<Protection> { self.stats.poise_resilience }
|
||||
let armor_weights_sum: f32 = ArmorKind::iter().map(armor_kind_weight).sum();
|
||||
let multiplier = armor_kind_weight(self.kind) / armor_weights_sum;
|
||||
|
||||
pub fn energy_max(&self) -> Option<f32> { self.stats.energy_max }
|
||||
|
||||
pub fn energy_reward(&self) -> Option<f32> { self.stats.energy_reward }
|
||||
|
||||
pub fn crit_power(&self) -> Option<f32> { self.stats.crit_power }
|
||||
|
||||
pub fn stealth(&self) -> Option<f32> { self.stats.stealth }
|
||||
|
||||
pub fn ground_contact(&self) -> Friction { self.stats.ground_contact }
|
||||
set_stats * multiplier
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub fn test_armor(
|
||||
@ -219,7 +248,7 @@ impl Armor {
|
||||
) -> Armor {
|
||||
Armor {
|
||||
kind,
|
||||
stats: Stats {
|
||||
stats: StatsSource::Direct(Stats {
|
||||
protection: Some(protection),
|
||||
poise_resilience: Some(poise_resilience),
|
||||
energy_max: None,
|
||||
@ -227,7 +256,7 @@ impl Armor {
|
||||
crit_power: None,
|
||||
stealth: None,
|
||||
ground_contact: Friction::Normal,
|
||||
},
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,8 +4,8 @@ pub mod modular;
|
||||
pub mod tool;
|
||||
|
||||
// Reexports
|
||||
pub use modular::{ModularBase, ModularComponent};
|
||||
pub use tool::{AbilitySet, AbilitySpec, Hands, MaterialStatManifest, Tool, ToolKind};
|
||||
pub use modular::{MaterialStatManifest, ModularBase, ModularComponent};
|
||||
pub use tool::{AbilitySet, AbilitySpec, Hands, Tool, ToolKind};
|
||||
|
||||
use crate::{
|
||||
assets::{self, AssetExt, BoxedError, Error},
|
||||
|
@ -1,8 +1,12 @@
|
||||
use super::{
|
||||
tool::{self, AbilityMap, AbilitySpec, Hands, MaterialStatManifest},
|
||||
armor,
|
||||
tool::{self, AbilityMap, AbilitySpec, Hands},
|
||||
Item, ItemBase, ItemDef, ItemDesc, ItemKind, ItemTag, Material, Quality, ToolKind,
|
||||
};
|
||||
use crate::{assets::AssetExt, recipe};
|
||||
use crate::{
|
||||
assets::{self, Asset, AssetExt, AssetHandle},
|
||||
recipe,
|
||||
};
|
||||
use common_base::dev_panic;
|
||||
use hashbrown::HashMap;
|
||||
use lazy_static::lazy_static;
|
||||
@ -20,6 +24,28 @@ macro_rules! modular_item_id_prefix {
|
||||
};
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct MaterialStatManifest {
|
||||
tool_stats: HashMap<String, tool::Stats>,
|
||||
armor_stats: HashMap<String, armor::Stats>,
|
||||
}
|
||||
|
||||
impl MaterialStatManifest {
|
||||
pub fn load() -> AssetHandle<Self> { Self::load_expect("common.material_stats_manifest") }
|
||||
|
||||
pub fn armor_stats(&self, key: &str) -> Option<armor::Stats> {
|
||||
self.armor_stats.get(key).copied()
|
||||
}
|
||||
}
|
||||
|
||||
// This could be a Compound that also loads the keys, but the RecipeBook
|
||||
// Compound impl already does that, so checking for existence here is redundant.
|
||||
impl Asset for MaterialStatManifest {
|
||||
type Loader = assets::RonLoader;
|
||||
|
||||
const EXTENSION: &'static str = "ron";
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub enum ModularBase {
|
||||
Tool,
|
||||
@ -241,7 +267,7 @@ impl ModularComponent {
|
||||
.filter_map(|comp| {
|
||||
comp.item_definition_id()
|
||||
.itemdef_id()
|
||||
.and_then(|id| msm.0.get(id))
|
||||
.and_then(|id| msm.tool_stats.get(id))
|
||||
.copied()
|
||||
.zip(Some(1))
|
||||
})
|
||||
|
@ -225,21 +225,6 @@ impl DivAssign<usize> for Stats {
|
||||
fn div_assign(&mut self, scalar: usize) { *self = *self / (scalar as f32); }
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct MaterialStatManifest(pub HashMap<String, Stats>);
|
||||
|
||||
impl MaterialStatManifest {
|
||||
pub fn load() -> AssetHandle<Self> { Self::load_expect("common.material_stats_manifest") }
|
||||
}
|
||||
|
||||
// This could be a Compound that also loads the keys, but the RecipeBook
|
||||
// Compound impl already does that, so checking for existence here is redundant.
|
||||
impl Asset for MaterialStatManifest {
|
||||
type Loader = assets::RonLoader;
|
||||
|
||||
const EXTENSION: &'static str = "ron";
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct Tool {
|
||||
pub kind: ToolKind,
|
||||
|
@ -408,7 +408,7 @@ impl Loadout {
|
||||
pub fn persistence_update_all_item_states(
|
||||
&mut self,
|
||||
ability_map: &item::tool::AbilityMap,
|
||||
msm: &item::tool::MaterialStatManifest,
|
||||
msm: &item::MaterialStatManifest,
|
||||
) {
|
||||
self.slots.iter_mut().for_each(|slot| {
|
||||
if let Some(item) = &mut slot.slot {
|
||||
|
@ -808,7 +808,7 @@ impl Inventory {
|
||||
pub fn persistence_update_all_item_states(
|
||||
&mut self,
|
||||
ability_map: &item::tool::AbilityMap,
|
||||
msm: &item::tool::MaterialStatManifest,
|
||||
msm: &item::MaterialStatManifest,
|
||||
) {
|
||||
self.slots_mut().for_each(|slot| {
|
||||
if let Some(item) = slot {
|
||||
|
@ -686,14 +686,16 @@ impl TradePricing {
|
||||
|
||||
#[cfg(test)]
|
||||
fn print_sorted(&self) {
|
||||
use crate::comp::item::{armor, ItemKind};
|
||||
use crate::comp::item::{armor, ItemKind, MaterialStatManifest};
|
||||
|
||||
println!("Item, ForSale, Amount, Good, Quality, Deal, Unit,");
|
||||
|
||||
fn more_information(i: &Item, p: f32) -> (String, &'static str) {
|
||||
let msm = &MaterialStatManifest::load().read();
|
||||
|
||||
if let ItemKind::Armor(a) = &*i.kind() {
|
||||
(
|
||||
match a.protection() {
|
||||
match a.stats(msm).protection {
|
||||
Some(armor::Protection::Invincible) => "Invincible".into(),
|
||||
Some(armor::Protection::Normal(x)) => format!("{:.4}", x * p),
|
||||
None => "0.0".into(),
|
||||
|
@ -2,7 +2,7 @@ use crate::{
|
||||
combat::{DamageContributor, DamageSource},
|
||||
comp::{
|
||||
self,
|
||||
inventory::item::{armor::Protection, ItemKind},
|
||||
inventory::item::{armor::Protection, ItemKind, MaterialStatManifest},
|
||||
CharacterState, Inventory,
|
||||
},
|
||||
resources::Time,
|
||||
@ -226,12 +226,15 @@ impl Poise {
|
||||
}
|
||||
|
||||
/// Returns the total poise damage reduction provided by all equipped items
|
||||
pub fn compute_poise_damage_reduction(inventory: &Inventory) -> f32 {
|
||||
pub fn compute_poise_damage_reduction(
|
||||
inventory: &Inventory,
|
||||
msm: &MaterialStatManifest,
|
||||
) -> f32 {
|
||||
let protection = inventory
|
||||
.equipped_items()
|
||||
.filter_map(|item| {
|
||||
if let ItemKind::Armor(armor) = &*item.kind() {
|
||||
armor.poise_resilience()
|
||||
armor.stats(msm).poise_resilience
|
||||
} else {
|
||||
None
|
||||
}
|
||||
@ -249,9 +252,13 @@ impl Poise {
|
||||
|
||||
/// Modifies a poise change when optionally given an inventory to aid in
|
||||
/// calculation of poise damage reduction
|
||||
pub fn apply_poise_reduction(value: f32, inventory: Option<&Inventory>) -> f32 {
|
||||
pub fn apply_poise_reduction(
|
||||
value: f32,
|
||||
inventory: Option<&Inventory>,
|
||||
msm: &MaterialStatManifest,
|
||||
) -> f32 {
|
||||
inventory.map_or(value, |inv| {
|
||||
value * (1.0 - Poise::compute_poise_damage_reduction(inv))
|
||||
value * (1.0 - Poise::compute_poise_damage_reduction(inv, msm))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -312,7 +312,7 @@ pub fn handle_skating(data: &JoinData, update: &mut StateUpdate) {
|
||||
footwear = data.inventory.and_then(|inv| {
|
||||
inv.equipped(EquipSlot::Armor(ArmorSlot::Feet))
|
||||
.map(|armor| match armor.kind().as_ref() {
|
||||
ItemKind::Armor(a) => a.ground_contact(),
|
||||
ItemKind::Armor(a) => a.stats(data.msm).ground_contact,
|
||||
_ => crate::comp::inventory::item::armor::Friction::Normal,
|
||||
})
|
||||
});
|
||||
@ -1084,7 +1084,7 @@ pub fn get_crit_data(data: &JoinData<'_>, ai: AbilityInfo) -> (f32, f32) {
|
||||
})
|
||||
.unwrap_or(DEFAULT_CRIT_CHANCE);
|
||||
|
||||
let crit_mult = combat::compute_crit_mult(data.inventory);
|
||||
let crit_mult = combat::compute_crit_mult(data.inventory, data.msm);
|
||||
|
||||
(crit_chance, crit_mult)
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ use common::{
|
||||
Buffs,
|
||||
},
|
||||
fluid_dynamics::{Fluid, LiquidKind},
|
||||
item::MaterialStatManifest,
|
||||
Energy, Group, Health, HealthChange, Inventory, LightEmitter, ModifierKind, PhysicsState,
|
||||
Stats,
|
||||
},
|
||||
@ -21,8 +22,8 @@ use common_ecs::{Job, Origin, ParMode, Phase, System};
|
||||
use hashbrown::HashMap;
|
||||
use rayon::iter::ParallelIterator;
|
||||
use specs::{
|
||||
saveload::MarkerAllocator, shred::ResourceId, Entities, Join, ParJoin, Read, ReadStorage,
|
||||
SystemData, World, WriteStorage,
|
||||
saveload::MarkerAllocator, shred::ResourceId, Entities, Join, ParJoin, Read, ReadExpect,
|
||||
ReadStorage, SystemData, World, WriteStorage,
|
||||
};
|
||||
use std::time::Duration;
|
||||
|
||||
@ -38,6 +39,7 @@ pub struct ReadData<'a> {
|
||||
groups: ReadStorage<'a, Group>,
|
||||
uid_allocator: Read<'a, UidAllocator>,
|
||||
time: Read<'a, Time>,
|
||||
msm: ReadExpect<'a, MaterialStatManifest>,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
@ -207,6 +209,7 @@ impl<'a> System<'a> for Sys {
|
||||
None,
|
||||
read_data.inventories.get(entity),
|
||||
Some(&stat),
|
||||
&read_data.msm,
|
||||
);
|
||||
if (damage_reduction - 1.0).abs() < f32::EPSILON {
|
||||
for (id, buff) in buff_comp.buffs.iter() {
|
||||
|
@ -2,6 +2,7 @@ use common::{
|
||||
combat,
|
||||
comp::{
|
||||
self,
|
||||
item::MaterialStatManifest,
|
||||
skills::{GeneralSkill, Skill},
|
||||
Body, CharacterState, Combo, Energy, Health, Inventory, Poise, PoiseChange, Pos, SkillSet,
|
||||
Stats, StatsModifier,
|
||||
@ -11,7 +12,8 @@ use common::{
|
||||
};
|
||||
use common_ecs::{Job, Origin, Phase, System};
|
||||
use specs::{
|
||||
shred::ResourceId, Entities, Join, Read, ReadStorage, SystemData, World, Write, WriteStorage,
|
||||
shred::ResourceId, Entities, Join, Read, ReadExpect, ReadStorage, SystemData, World, Write,
|
||||
WriteStorage,
|
||||
};
|
||||
use vek::Vec3;
|
||||
|
||||
@ -28,6 +30,7 @@ pub struct ReadData<'a> {
|
||||
bodies: ReadStorage<'a, Body>,
|
||||
char_states: ReadStorage<'a, CharacterState>,
|
||||
inventories: ReadStorage<'a, Inventory>,
|
||||
msm: ReadExpect<'a, MaterialStatManifest>,
|
||||
}
|
||||
|
||||
/// This system kills players, levels them up, and regenerates energy.
|
||||
@ -100,7 +103,7 @@ impl<'a> System<'a> for Sys {
|
||||
// Calculates energy scaling from stats and inventory
|
||||
let energy_mods = StatsModifier {
|
||||
add_mod: stat.max_energy_modifiers.add_mod
|
||||
+ combat::compute_max_energy_mod(inventory),
|
||||
+ combat::compute_max_energy_mod(inventory, &read_data.msm),
|
||||
mult_mod: stat.max_energy_modifiers.mult_mod,
|
||||
};
|
||||
|
||||
|
@ -558,7 +558,8 @@ pub fn handle_land_on_ground(server: &Server, entity: EcsEntity, vel: Vec3<f32>)
|
||||
|
||||
let inventories = ecs.read_storage::<Inventory>();
|
||||
let stats = ecs.read_storage::<Stats>();
|
||||
let time = server.state.ecs().read_resource::<Time>();
|
||||
let time = ecs.read_resource::<Time>();
|
||||
let msm = ecs.read_resource::<MaterialStatManifest>();
|
||||
|
||||
// Handle health change
|
||||
if let Some(mut health) = ecs.write_storage::<comp::Health>().get_mut(entity) {
|
||||
@ -571,6 +572,7 @@ pub fn handle_land_on_ground(server: &Server, entity: EcsEntity, vel: Vec3<f32>)
|
||||
Some(damage),
|
||||
inventories.get(entity),
|
||||
stats.get(entity),
|
||||
&msm,
|
||||
);
|
||||
let change =
|
||||
damage.calculate_health_change(damage_reduction, None, false, 0.0, 1.0, *time);
|
||||
@ -579,7 +581,8 @@ pub fn handle_land_on_ground(server: &Server, entity: EcsEntity, vel: Vec3<f32>)
|
||||
// Handle poise change
|
||||
if let Some(mut poise) = ecs.write_storage::<comp::Poise>().get_mut(entity) {
|
||||
let poise_damage = -(mass.0 * vel.magnitude_squared() / 1500.0);
|
||||
let poise_change = Poise::apply_poise_reduction(poise_damage, inventories.get(entity));
|
||||
let poise_change =
|
||||
Poise::apply_poise_reduction(poise_damage, inventories.get(entity), &msm);
|
||||
let poise_change = comp::PoiseChange {
|
||||
amount: poise_change,
|
||||
impulse: Vec3::unit_z(),
|
||||
|
@ -1092,7 +1092,7 @@ impl Server {
|
||||
material_stats: (&*self
|
||||
.state
|
||||
.ecs()
|
||||
.read_resource::<comp::item::tool::MaterialStatManifest>())
|
||||
.read_resource::<comp::item::MaterialStatManifest>())
|
||||
.clone(),
|
||||
ability_map: (&*self
|
||||
.state
|
||||
|
@ -15,6 +15,7 @@ use common::{
|
||||
combat::DamageContributor,
|
||||
comp::{
|
||||
self,
|
||||
item::MaterialStatManifest,
|
||||
skills::{GeneralSkill, Skill},
|
||||
Group, Inventory, Item, Poise,
|
||||
},
|
||||
@ -127,6 +128,7 @@ pub trait StateExt {
|
||||
|
||||
impl StateExt for State {
|
||||
fn apply_effect(&self, entity: EcsEntity, effects: Effect, source: Option<Uid>) {
|
||||
let msm = self.ecs().read_resource::<MaterialStatManifest>();
|
||||
match effects {
|
||||
Effect::Health(change) => {
|
||||
self.ecs()
|
||||
@ -150,6 +152,7 @@ impl StateExt for State {
|
||||
Some(damage),
|
||||
inventories.get(entity),
|
||||
stats.get(entity),
|
||||
&msm,
|
||||
),
|
||||
damage_contributor,
|
||||
false,
|
||||
@ -164,7 +167,7 @@ impl StateExt for State {
|
||||
},
|
||||
Effect::Poise(poise) => {
|
||||
let inventories = self.ecs().read_storage::<Inventory>();
|
||||
let change = Poise::apply_poise_reduction(poise, inventories.get(entity));
|
||||
let change = Poise::apply_poise_reduction(poise, inventories.get(entity), &msm);
|
||||
// Check to make sure the entity is not already stunned
|
||||
if let Some(character_state) = self
|
||||
.ecs()
|
||||
|
@ -236,6 +236,7 @@ impl<'a> System<'a> for Sys {
|
||||
char_state,
|
||||
active_abilities,
|
||||
cached_spatial_grid: &read_data.cached_spatial_grid,
|
||||
msm: &read_data.msm,
|
||||
};
|
||||
///////////////////////////////////////////////////////////
|
||||
// Behavior tree
|
||||
@ -2502,7 +2503,7 @@ impl<'a> AgentData<'a> {
|
||||
let other_inventory = read_data.inventories.get(other);
|
||||
let other_char_state = read_data.char_states.get(other);
|
||||
|
||||
perception_dist_multiplier_from_stealth(other_inventory, other_char_state)
|
||||
perception_dist_multiplier_from_stealth(other_inventory, other_char_state, self.msm)
|
||||
};
|
||||
|
||||
let within_sight_dist = {
|
||||
|
@ -1,9 +1,9 @@
|
||||
use crate::rtsim::Entity as RtSimData;
|
||||
use common::{
|
||||
comp::{
|
||||
buff::Buffs, group, ActiveAbilities, Alignment, Body, CharacterState, Combo, Energy,
|
||||
Health, Inventory, LightEmitter, LootOwner, Ori, PhysicsState, Pos, Scale, SkillSet, Stats,
|
||||
Vel,
|
||||
buff::Buffs, group, item::MaterialStatManifest, ActiveAbilities, Alignment, Body,
|
||||
CharacterState, Combo, Energy, Health, Inventory, LightEmitter, LootOwner, Ori,
|
||||
PhysicsState, Pos, Scale, SkillSet, Stats, Vel,
|
||||
},
|
||||
link::Is,
|
||||
mounting::Mount,
|
||||
@ -43,6 +43,7 @@ pub struct AgentData<'a> {
|
||||
pub char_state: &'a CharacterState,
|
||||
pub active_abilities: &'a ActiveAbilities,
|
||||
pub cached_spatial_grid: &'a common::CachedSpatialGrid,
|
||||
pub msm: &'a MaterialStatManifest,
|
||||
}
|
||||
|
||||
pub struct TargetData<'a> {
|
||||
@ -154,6 +155,7 @@ pub struct ReadData<'a> {
|
||||
pub combos: ReadStorage<'a, Combo>,
|
||||
pub active_abilities: ReadStorage<'a, ActiveAbilities>,
|
||||
pub loot_owners: ReadStorage<'a, LootOwner>,
|
||||
pub msm: ReadExpect<'a, MaterialStatManifest>,
|
||||
}
|
||||
|
||||
pub enum Path {
|
||||
|
@ -937,19 +937,28 @@ impl<'a> Widget for Bag<'a> {
|
||||
let protection_txt = format!(
|
||||
"{}%",
|
||||
(100.0
|
||||
* Damage::compute_damage_reduction(None, Some(inventory), Some(self.stats)))
|
||||
as i32
|
||||
* Damage::compute_damage_reduction(
|
||||
None,
|
||||
Some(inventory),
|
||||
Some(self.stats),
|
||||
self.msm
|
||||
)) as i32
|
||||
);
|
||||
let health_txt = format!("{}", self.health.maximum().round() as usize);
|
||||
let energy_txt = format!("{}", self.energy.maximum().round() as usize);
|
||||
let combat_rating_txt = format!("{}", (combat_rating * 10.0) as usize);
|
||||
let stun_res_txt = format!(
|
||||
"{}",
|
||||
(100.0 * Poise::compute_poise_damage_reduction(inventory)) as i32
|
||||
(100.0 * Poise::compute_poise_damage_reduction(inventory, self.msm)) as i32
|
||||
);
|
||||
let stealth_txt = format!(
|
||||
"{:.1}%",
|
||||
((1.0 - perception_dist_multiplier_from_stealth(Some(inventory), None))
|
||||
((1.0
|
||||
- perception_dist_multiplier_from_stealth(
|
||||
Some(inventory),
|
||||
None,
|
||||
self.msm
|
||||
))
|
||||
* 100.0)
|
||||
);
|
||||
let btn = if i.0 == 0 {
|
||||
|
@ -33,11 +33,7 @@ use common::{
|
||||
self,
|
||||
ability::{Ability, ActiveAbilities, AuxiliaryAbility, MAX_ABILITIES},
|
||||
inventory::{
|
||||
item::{
|
||||
item_key::ItemKey,
|
||||
tool::{MaterialStatManifest, ToolKind},
|
||||
ItemKind,
|
||||
},
|
||||
item::{item_key::ItemKey, tool::ToolKind, ItemKind, MaterialStatManifest},
|
||||
slot::EquipSlot,
|
||||
},
|
||||
skills::{
|
||||
@ -1184,23 +1180,25 @@ impl<'a> Widget for Diary<'a> {
|
||||
format!("{:.2}", cr * 10.0)
|
||||
},
|
||||
"Protection" => {
|
||||
let protection = combat::compute_protection(Some(self.inventory));
|
||||
let protection =
|
||||
combat::compute_protection(Some(self.inventory), self.msm);
|
||||
match protection {
|
||||
Some(prot) => format!("{}", prot),
|
||||
None => String::from("Invincible"),
|
||||
}
|
||||
},
|
||||
"Stun-Resistance" => {
|
||||
let stun_res = Poise::compute_poise_damage_reduction(self.inventory);
|
||||
let stun_res =
|
||||
Poise::compute_poise_damage_reduction(self.inventory, self.msm);
|
||||
format!("{:.2}%", stun_res * 100.0)
|
||||
},
|
||||
"Crit-Power" => {
|
||||
let critpwr = combat::compute_crit_mult(Some(self.inventory));
|
||||
let critpwr = combat::compute_crit_mult(Some(self.inventory), self.msm);
|
||||
format!("x{:.2}", critpwr)
|
||||
},
|
||||
"Energy Reward" => {
|
||||
let energy_rew =
|
||||
combat::compute_energy_reward_mod(Some(self.inventory));
|
||||
combat::compute_energy_reward_mod(Some(self.inventory), self.msm);
|
||||
format!("{:+.0}%", (energy_rew - 1.0) * 100.0)
|
||||
},
|
||||
"Stealth" => {
|
||||
@ -1208,6 +1206,7 @@ impl<'a> Widget for Diary<'a> {
|
||||
combat::perception_dist_multiplier_from_stealth(
|
||||
Some(self.inventory),
|
||||
None,
|
||||
self.msm,
|
||||
);
|
||||
let txt =
|
||||
format!("{:+.1}%", (1.0 - stealth_perception_multiplier) * 100.0);
|
||||
|
@ -5,7 +5,7 @@ use common::{
|
||||
item::{
|
||||
armor::{Armor, ArmorKind, Protection},
|
||||
tool::{Hands, Tool, ToolKind},
|
||||
ItemDesc, ItemKind, MaterialKind,
|
||||
ItemDesc, ItemKind, MaterialKind, MaterialStatManifest,
|
||||
},
|
||||
BuffKind,
|
||||
},
|
||||
@ -109,17 +109,17 @@ pub fn material_kind_text<'a>(kind: &MaterialKind, i18n: &'a Localization) -> &'
|
||||
}
|
||||
}
|
||||
|
||||
pub fn stats_count(item: &dyn ItemDesc) -> usize {
|
||||
pub fn stats_count(item: &dyn ItemDesc, msm: &MaterialStatManifest) -> usize {
|
||||
let mut count = match &*item.kind() {
|
||||
ItemKind::Armor(armor) => {
|
||||
if matches!(armor.kind, ArmorKind::Bag) {
|
||||
0
|
||||
} else {
|
||||
armor.stats.energy_reward().is_some() as usize
|
||||
+ armor.stats.energy_max().is_some() as usize
|
||||
+ armor.stats.stealth().is_some() as usize
|
||||
+ armor.stats.crit_power().is_some() as usize
|
||||
+ armor.stats.poise_resilience().is_some() as usize
|
||||
armor.stats(msm).energy_reward.is_some() as usize
|
||||
+ armor.stats(msm).energy_max.is_some() as usize
|
||||
+ armor.stats(msm).stealth.is_some() as usize
|
||||
+ armor.stats(msm).crit_power.is_some() as usize
|
||||
+ armor.stats(msm).poise_resilience.is_some() as usize
|
||||
}
|
||||
},
|
||||
ItemKind::Tool(_) => 7,
|
||||
|
@ -18,8 +18,8 @@ use common::{
|
||||
inventory::slot::{EquipSlot, Slot},
|
||||
invite::InviteKind,
|
||||
item::{
|
||||
tool::{AbilityMap, MaterialStatManifest, ToolKind},
|
||||
ItemDesc,
|
||||
tool::{AbilityMap, ToolKind},
|
||||
ItemDesc, MaterialStatManifest,
|
||||
},
|
||||
ChatMsg, ChatType, InputKind, InventoryUpdateEvent, Pos, Stats, UtteranceKind, Vel,
|
||||
},
|
||||
|
@ -505,17 +505,19 @@ impl<'a> Widget for ItemTooltip<'a> {
|
||||
_ => self.imgs.inv_slot_red,
|
||||
};
|
||||
|
||||
let stats_count = util::stats_count(item, self.msm);
|
||||
|
||||
// Update widget array size
|
||||
state.update(|s| {
|
||||
s.ids
|
||||
.stats
|
||||
.resize(util::stats_count(item), &mut ui.widget_id_generator())
|
||||
.resize(stats_count, &mut ui.widget_id_generator())
|
||||
});
|
||||
|
||||
state.update(|s| {
|
||||
s.ids
|
||||
.diffs
|
||||
.resize(util::stats_count(item), &mut ui.widget_id_generator())
|
||||
.resize(stats_count, &mut ui.widget_id_generator())
|
||||
});
|
||||
|
||||
// Background image frame
|
||||
@ -820,12 +822,22 @@ impl<'a> Widget for ItemTooltip<'a> {
|
||||
},
|
||||
_ => {
|
||||
// Armour
|
||||
let protection = armor.protection().unwrap_or(Protection::Normal(0.0));
|
||||
let poise_res = armor.poise_resilience().unwrap_or(Protection::Normal(0.0));
|
||||
let energy_max = armor.energy_max().unwrap_or(0.0);
|
||||
let energy_reward = armor.energy_reward().map(|x| x * 100.0).unwrap_or(0.0);
|
||||
let crit_power = armor.crit_power().unwrap_or(0.0);
|
||||
let stealth = armor.stealth().unwrap_or(0.0);
|
||||
let protection = armor
|
||||
.stats(self.msm)
|
||||
.protection
|
||||
.unwrap_or(Protection::Normal(0.0));
|
||||
let poise_res = armor
|
||||
.stats(self.msm)
|
||||
.poise_resilience
|
||||
.unwrap_or(Protection::Normal(0.0));
|
||||
let energy_max = armor.stats(self.msm).energy_max.unwrap_or(0.0);
|
||||
let energy_reward = armor
|
||||
.stats(self.msm)
|
||||
.energy_reward
|
||||
.map(|x| x * 100.0)
|
||||
.unwrap_or(0.0);
|
||||
let crit_power = armor.stats(self.msm).crit_power.unwrap_or(0.0);
|
||||
let stealth = armor.stats(self.msm).stealth.unwrap_or(0.0);
|
||||
|
||||
widget::Text::new(&util::protec2string(protection))
|
||||
.graphics_for(id)
|
||||
@ -847,7 +859,7 @@ impl<'a> Widget for ItemTooltip<'a> {
|
||||
.set(state.ids.main_stat_text, ui);
|
||||
|
||||
// Poise res
|
||||
if armor.stats.poise_resilience().is_some() {
|
||||
if armor.stats(self.msm).poise_resilience.is_some() {
|
||||
widget::Text::new(&format!(
|
||||
"{} : {}",
|
||||
i18n.get("common.stats.poise_res"),
|
||||
@ -863,7 +875,7 @@ impl<'a> Widget for ItemTooltip<'a> {
|
||||
}
|
||||
|
||||
// Max Energy
|
||||
if armor.stats.energy_max().is_some() {
|
||||
if armor.stats(self.msm).energy_max.is_some() {
|
||||
widget::Text::new(&format!(
|
||||
"{} : {:.1}",
|
||||
i18n.get("common.stats.energy_max"),
|
||||
@ -874,7 +886,7 @@ impl<'a> Widget for ItemTooltip<'a> {
|
||||
.with_style(self.style.desc)
|
||||
.color(text_color)
|
||||
.and(|t| {
|
||||
if armor.stats.poise_resilience().is_some() {
|
||||
if armor.stats(self.msm).poise_resilience.is_some() {
|
||||
t.down_from(state.ids.stats[0], V_PAD_STATS)
|
||||
} else {
|
||||
t.x_align_to(
|
||||
@ -885,13 +897,14 @@ impl<'a> Widget for ItemTooltip<'a> {
|
||||
}
|
||||
})
|
||||
.set(
|
||||
state.ids.stats[armor.stats.poise_resilience().is_some() as usize],
|
||||
state.ids.stats
|
||||
[armor.stats(self.msm).poise_resilience.is_some() as usize],
|
||||
ui,
|
||||
);
|
||||
}
|
||||
|
||||
// Energy Recovery
|
||||
if armor.stats.energy_reward().is_some() {
|
||||
if armor.stats(self.msm).energy_reward.is_some() {
|
||||
widget::Text::new(&format!(
|
||||
"{} : {:.1}%",
|
||||
i18n.get("common.stats.energy_reward"),
|
||||
@ -902,8 +915,8 @@ impl<'a> Widget for ItemTooltip<'a> {
|
||||
.with_style(self.style.desc)
|
||||
.color(text_color)
|
||||
.and(|t| {
|
||||
match armor.stats.poise_resilience().is_some() as usize
|
||||
+ armor.stats.energy_max().is_some() as usize
|
||||
match armor.stats(self.msm).poise_resilience.is_some() as usize
|
||||
+ armor.stats(self.msm).energy_max.is_some() as usize
|
||||
{
|
||||
0 => t
|
||||
.x_align_to(
|
||||
@ -915,14 +928,15 @@ impl<'a> Widget for ItemTooltip<'a> {
|
||||
}
|
||||
})
|
||||
.set(
|
||||
state.ids.stats[armor.stats.poise_resilience().is_some() as usize
|
||||
+ armor.stats.energy_max().is_some() as usize],
|
||||
state.ids.stats[armor.stats(self.msm).poise_resilience.is_some()
|
||||
as usize
|
||||
+ armor.stats(self.msm).energy_max.is_some() as usize],
|
||||
ui,
|
||||
);
|
||||
}
|
||||
|
||||
// Crit Power
|
||||
if armor.stats.crit_power().is_some() {
|
||||
if armor.stats(self.msm).crit_power.is_some() {
|
||||
widget::Text::new(&format!(
|
||||
"{} : {:.3}",
|
||||
i18n.get("common.stats.crit_power"),
|
||||
@ -933,9 +947,9 @@ impl<'a> Widget for ItemTooltip<'a> {
|
||||
.with_style(self.style.desc)
|
||||
.color(text_color)
|
||||
.and(|t| {
|
||||
match armor.stats.poise_resilience().is_some() as usize
|
||||
+ armor.stats.energy_max().is_some() as usize
|
||||
+ armor.stats.energy_reward().is_some() as usize
|
||||
match armor.stats(self.msm).poise_resilience.is_some() as usize
|
||||
+ armor.stats(self.msm).energy_max.is_some() as usize
|
||||
+ armor.stats(self.msm).energy_reward.is_some() as usize
|
||||
{
|
||||
0 => t
|
||||
.x_align_to(
|
||||
@ -947,15 +961,16 @@ impl<'a> Widget for ItemTooltip<'a> {
|
||||
}
|
||||
})
|
||||
.set(
|
||||
state.ids.stats[armor.stats.poise_resilience().is_some() as usize
|
||||
+ armor.stats.energy_max().is_some() as usize
|
||||
+ armor.stats.energy_reward().is_some() as usize],
|
||||
state.ids.stats[armor.stats(self.msm).poise_resilience.is_some()
|
||||
as usize
|
||||
+ armor.stats(self.msm).energy_max.is_some() as usize
|
||||
+ armor.stats(self.msm).energy_reward.is_some() as usize],
|
||||
ui,
|
||||
);
|
||||
}
|
||||
|
||||
// Stealth
|
||||
if armor.stats.stealth().is_some() {
|
||||
if armor.stats(self.msm).stealth.is_some() {
|
||||
widget::Text::new(&format!(
|
||||
"{} : {:.3}",
|
||||
i18n.get("common.stats.stealth"),
|
||||
@ -966,10 +981,10 @@ impl<'a> Widget for ItemTooltip<'a> {
|
||||
.with_style(self.style.desc)
|
||||
.color(text_color)
|
||||
.and(|t| {
|
||||
match armor.stats.poise_resilience().is_some() as usize
|
||||
+ armor.stats.energy_max().is_some() as usize
|
||||
+ armor.stats.energy_reward().is_some() as usize
|
||||
+ armor.stats.crit_power().is_some() as usize
|
||||
match armor.stats(self.msm).poise_resilience.is_some() as usize
|
||||
+ armor.stats(self.msm).energy_max.is_some() as usize
|
||||
+ armor.stats(self.msm).energy_reward.is_some() as usize
|
||||
+ armor.stats(self.msm).crit_power.is_some() as usize
|
||||
{
|
||||
0 => t
|
||||
.x_align_to(
|
||||
@ -981,10 +996,11 @@ impl<'a> Widget for ItemTooltip<'a> {
|
||||
}
|
||||
})
|
||||
.set(
|
||||
state.ids.stats[armor.stats.poise_resilience().is_some() as usize
|
||||
+ armor.stats.energy_max().is_some() as usize
|
||||
+ armor.stats.energy_reward().is_some() as usize
|
||||
+ armor.stats.crit_power().is_some() as usize],
|
||||
state.ids.stats[armor.stats(self.msm).poise_resilience.is_some()
|
||||
as usize
|
||||
+ armor.stats(self.msm).energy_max.is_some() as usize
|
||||
+ armor.stats(self.msm).energy_reward.is_some() as usize
|
||||
+ armor.stats(self.msm).crit_power.is_some() as usize],
|
||||
ui,
|
||||
);
|
||||
}
|
||||
@ -1001,11 +1017,11 @@ impl<'a> Widget for ItemTooltip<'a> {
|
||||
.with_style(self.style.desc)
|
||||
.color(text_color)
|
||||
.and(|t| {
|
||||
match armor.stats.poise_resilience().is_some() as usize
|
||||
+ armor.stats.energy_max().is_some() as usize
|
||||
+ armor.stats.energy_reward().is_some() as usize
|
||||
+ armor.stats.crit_power().is_some() as usize
|
||||
+ armor.stats.stealth().is_some() as usize
|
||||
match armor.stats(self.msm).poise_resilience.is_some() as usize
|
||||
+ armor.stats(self.msm).energy_max.is_some() as usize
|
||||
+ armor.stats(self.msm).energy_reward.is_some() as usize
|
||||
+ armor.stats(self.msm).crit_power.is_some() as usize
|
||||
+ armor.stats(self.msm).stealth.is_some() as usize
|
||||
{
|
||||
0 => t
|
||||
.x_align_to(
|
||||
@ -1017,11 +1033,12 @@ impl<'a> Widget for ItemTooltip<'a> {
|
||||
}
|
||||
})
|
||||
.set(
|
||||
state.ids.stats[armor.stats.poise_resilience().is_some() as usize
|
||||
+ armor.stats.energy_max().is_some() as usize
|
||||
+ armor.stats.energy_reward().is_some() as usize
|
||||
+ armor.stats.crit_power().is_some() as usize
|
||||
+ armor.stats.stealth().is_some() as usize],
|
||||
state.ids.stats[armor.stats(self.msm).poise_resilience.is_some()
|
||||
as usize
|
||||
+ armor.stats(self.msm).energy_max.is_some() as usize
|
||||
+ armor.stats(self.msm).energy_reward.is_some() as usize
|
||||
+ armor.stats(self.msm).crit_power.is_some() as usize
|
||||
+ armor.stats(self.msm).stealth.is_some() as usize],
|
||||
ui,
|
||||
);
|
||||
}
|
||||
@ -1030,31 +1047,33 @@ impl<'a> Widget for ItemTooltip<'a> {
|
||||
|
||||
if let Some(equipped_item) = equipped_item {
|
||||
if let ItemKind::Armor(equipped_armor) = &*equipped_item.kind() {
|
||||
let diff = armor.stats - equipped_armor.stats;
|
||||
let diff = armor.stats(self.msm) - equipped_armor.stats(self.msm);
|
||||
let protection_diff = util::option_comparison(
|
||||
&armor.protection(),
|
||||
&equipped_armor.protection(),
|
||||
&armor.stats(self.msm).protection,
|
||||
&equipped_armor.stats(self.msm).protection,
|
||||
);
|
||||
let poise_res_diff = util::option_comparison(
|
||||
&armor.poise_resilience(),
|
||||
&equipped_armor.poise_resilience(),
|
||||
&armor.stats(self.msm).poise_resilience,
|
||||
&equipped_armor.stats(self.msm).poise_resilience,
|
||||
);
|
||||
let energy_max_diff = util::option_comparison(
|
||||
&armor.energy_max(),
|
||||
&equipped_armor.energy_max(),
|
||||
&armor.stats(self.msm).energy_max,
|
||||
&equipped_armor.stats(self.msm).energy_max,
|
||||
);
|
||||
let energy_reward_diff = util::option_comparison(
|
||||
&armor.energy_reward(),
|
||||
&equipped_armor.energy_reward(),
|
||||
&armor.stats(self.msm).energy_reward,
|
||||
&equipped_armor.stats(self.msm).energy_reward,
|
||||
);
|
||||
let crit_power_diff = util::option_comparison(
|
||||
&armor.crit_power(),
|
||||
&equipped_armor.crit_power(),
|
||||
&armor.stats(self.msm).crit_power,
|
||||
&equipped_armor.stats(self.msm).crit_power,
|
||||
);
|
||||
let stealth_diff = util::option_comparison(
|
||||
&armor.stats(self.msm).stealth,
|
||||
&equipped_armor.stats(self.msm).stealth,
|
||||
);
|
||||
let stealth_diff =
|
||||
util::option_comparison(&armor.stealth(), &equipped_armor.stealth());
|
||||
|
||||
if let Some(p_diff) = diff.protection() {
|
||||
if let Some(p_diff) = diff.protection {
|
||||
if p_diff != Protection::Normal(0.0) {
|
||||
widget::Text::new(protection_diff.0)
|
||||
.right_from(state.ids.main_stat_text, H_PAD)
|
||||
@ -1077,7 +1096,7 @@ impl<'a> Widget for ItemTooltip<'a> {
|
||||
.set(state.ids.diffs[id_index], ui)
|
||||
};
|
||||
|
||||
if let Some(p_r_diff) = diff.poise_resilience() {
|
||||
if let Some(p_r_diff) = diff.poise_resilience {
|
||||
if p_r_diff != Protection::Normal(0.0) {
|
||||
let text = format!(
|
||||
"{} {}",
|
||||
@ -1088,53 +1107,53 @@ impl<'a> Widget for ItemTooltip<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(e_m_diff) = diff.energy_max() {
|
||||
if let Some(e_m_diff) = diff.energy_max {
|
||||
if e_m_diff.abs() > Energy::ENERGY_EPSILON {
|
||||
let text = format!("{} {:.1}", &energy_max_diff.0, e_m_diff);
|
||||
diff_text(
|
||||
text,
|
||||
energy_max_diff.1,
|
||||
armor.stats.poise_resilience().is_some() as usize,
|
||||
armor.stats(self.msm).poise_resilience.is_some() as usize,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(e_r_diff) = diff.energy_reward() {
|
||||
if let Some(e_r_diff) = diff.energy_reward {
|
||||
if e_r_diff.abs() > Energy::ENERGY_EPSILON {
|
||||
let text =
|
||||
format!("{} {:.1}", &energy_reward_diff.0, e_r_diff * 100.0);
|
||||
diff_text(
|
||||
text,
|
||||
energy_reward_diff.1,
|
||||
armor.stats.poise_resilience().is_some() as usize
|
||||
+ armor.stats.energy_max().is_some() as usize,
|
||||
armor.stats(self.msm).poise_resilience.is_some() as usize
|
||||
+ armor.stats(self.msm).energy_max.is_some() as usize,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(c_p_diff) = diff.crit_power() {
|
||||
if let Some(c_p_diff) = diff.crit_power {
|
||||
if c_p_diff != 0.0_f32 {
|
||||
let text = format!("{} {:.3}", &crit_power_diff.0, c_p_diff);
|
||||
diff_text(
|
||||
text,
|
||||
crit_power_diff.1,
|
||||
armor.stats.poise_resilience().is_some() as usize
|
||||
+ armor.stats.energy_max().is_some() as usize
|
||||
+ armor.stats.energy_reward().is_some() as usize,
|
||||
armor.stats(self.msm).poise_resilience.is_some() as usize
|
||||
+ armor.stats(self.msm).energy_max.is_some() as usize
|
||||
+ armor.stats(self.msm).energy_reward.is_some() as usize,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(s_diff) = diff.stealth() {
|
||||
if let Some(s_diff) = diff.stealth {
|
||||
if s_diff != 0.0_f32 {
|
||||
let text = format!("{} {:.3}", &stealth_diff.0, s_diff);
|
||||
diff_text(
|
||||
text,
|
||||
stealth_diff.1,
|
||||
armor.stats.poise_resilience().is_some() as usize
|
||||
+ armor.stats.energy_max().is_some() as usize
|
||||
+ armor.stats.energy_reward().is_some() as usize
|
||||
+ armor.stats.crit_power().is_some() as usize,
|
||||
armor.stats(self.msm).poise_resilience.is_some() as usize
|
||||
+ armor.stats(self.msm).energy_max.is_some() as usize
|
||||
+ armor.stats(self.msm).energy_reward.is_some() as usize
|
||||
+ armor.stats(self.msm).crit_power.is_some() as usize,
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -1326,7 +1345,7 @@ impl<'a> Widget for ItemTooltip<'a> {
|
||||
.with_style(self.style.desc)
|
||||
.color(conrod_core::color::GREY)
|
||||
.down_from(
|
||||
if util::stats_count(item) > 0 {
|
||||
if stats_count > 0 {
|
||||
state.ids.stats[state.ids.stats.len() - 1]
|
||||
} else {
|
||||
state.ids.item_frame
|
||||
@ -1352,7 +1371,7 @@ impl<'a> Widget for ItemTooltip<'a> {
|
||||
.down_from(
|
||||
if !desc.is_empty() {
|
||||
state.ids.desc
|
||||
} else if util::stats_count(item) > 0 {
|
||||
} else if stats_count > 0 {
|
||||
state.ids.stats[state.ids.stats.len() - 1]
|
||||
} else {
|
||||
state.ids.item_frame
|
||||
@ -1412,13 +1431,14 @@ impl<'a> Widget for ItemTooltip<'a> {
|
||||
let frame_h = ICON_SIZE[1] + V_PAD;
|
||||
|
||||
// Stats
|
||||
let stat_h = if util::stats_count(self.item) > 0 {
|
||||
let stats_count = util::stats_count(self.item, self.msm);
|
||||
let stat_h = if stats_count > 0 {
|
||||
widget::Text::new("placeholder")
|
||||
.with_style(self.style.desc)
|
||||
.get_h(ui)
|
||||
.unwrap_or(0.0)
|
||||
* util::stats_count(self.item) as f64
|
||||
+ (util::stats_count(self.item) - 1) as f64 * V_PAD_STATS
|
||||
* stats_count as f64
|
||||
+ (stats_count - 1) as f64 * V_PAD_STATS
|
||||
+ V_PAD
|
||||
} else {
|
||||
0.0
|
||||
|
Loading…
Reference in New Issue
Block a user