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"
|
dbg-voxygen = "run --bin veloren-voxygen --profile debuginfo"
|
||||||
# misc
|
# misc
|
||||||
swarm = "run --bin swarm --features client/bin_bot,client/tick_network --"
|
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)
|
// 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
|
// Metals
|
||||||
"common.items.mineral.ingot.bronze": (
|
"common.items.mineral.ingot.bronze": (
|
||||||
equip_time_secs: 1.0,
|
equip_time_secs: 1.0,
|
||||||
@ -122,4 +123,195 @@
|
|||||||
energy_efficiency: 1.0,
|
energy_efficiency: 1.0,
|
||||||
buff_strength: 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::{
|
item::{
|
||||||
armor::{ArmorKind, Protection},
|
armor::{ArmorKind, Protection},
|
||||||
tool::{Hands, Tool, ToolKind},
|
tool::{Hands, Tool, ToolKind},
|
||||||
Item,
|
Item, MaterialStatManifest,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
generation::{EntityConfig, EntityInfo},
|
generation::{EntityConfig, EntityInfo},
|
||||||
@ -56,20 +56,22 @@ fn armor_stats() -> Result<(), Box<dyn Error>> {
|
|||||||
continue;
|
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::Invincible) => "Invincible".to_string(),
|
||||||
Some(Protection::Normal(value)) => value.to_string(),
|
Some(Protection::Normal(value)) => value.to_string(),
|
||||||
None => "0.0".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::Invincible) => "Invincible".to_string(),
|
||||||
Some(Protection::Normal(value)) => value.to_string(),
|
Some(Protection::Normal(value)) => value.to_string(),
|
||||||
None => "0.0".to_string(),
|
None => "0.0".to_string(),
|
||||||
};
|
};
|
||||||
let max_energy = armor.energy_max().unwrap_or(0.0).to_string();
|
let max_energy = armor.stats(msm).energy_max.unwrap_or(0.0).to_string();
|
||||||
let energy_reward = armor.energy_reward().unwrap_or(0.0).to_string();
|
let energy_reward = armor.stats(msm).energy_reward.unwrap_or(0.0).to_string();
|
||||||
let crit_power = armor.crit_power().unwrap_or(0.0).to_string();
|
let crit_power = armor.stats(msm).crit_power.unwrap_or(0.0).to_string();
|
||||||
let stealth = armor.stealth().unwrap_or(0.0).to_string();
|
let stealth = armor.stats(msm).stealth.unwrap_or(0.0).to_string();
|
||||||
|
|
||||||
wtr.write_record(&[
|
wtr.write_record(&[
|
||||||
item.item_definition_id()
|
item.item_definition_id()
|
||||||
|
@ -12,7 +12,7 @@ use veloren_common::{
|
|||||||
comp::{
|
comp::{
|
||||||
self,
|
self,
|
||||||
item::{
|
item::{
|
||||||
armor::{ArmorKind, Protection},
|
armor::{ArmorKind, Protection, StatsSource},
|
||||||
tool::{AbilitySpec, Hands, Stats, ToolKind},
|
tool::{AbilitySpec, Hands, Stats, ToolKind},
|
||||||
ItemDefinitionId, ItemKind, ItemTag, Material, Quality,
|
ItemDefinitionId, ItemKind, ItemTag, Material, Quality,
|
||||||
},
|
},
|
||||||
@ -124,7 +124,7 @@ fn armor_stats() -> Result<(), Box<dyn Error>> {
|
|||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
let max_energy =
|
let energy_max =
|
||||||
if let Some(max_energy_raw) = record.get(headers["Max Energy"]) {
|
if let Some(max_energy_raw) = record.get(headers["Max Energy"]) {
|
||||||
let value = max_energy_raw.parse().unwrap();
|
let value = max_energy_raw.parse().unwrap();
|
||||||
if value == 0.0 { None } else { Some(value) }
|
if value == 0.0 { None } else { Some(value) }
|
||||||
@ -174,15 +174,19 @@ fn armor_stats() -> Result<(), Box<dyn Error>> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let kind = armor.kind;
|
let kind = armor.kind;
|
||||||
let armor_stats = comp::item::armor::Stats::new(
|
let armor_stats = comp::item::armor::Stats {
|
||||||
protection,
|
protection,
|
||||||
poise_resilience,
|
poise_resilience,
|
||||||
max_energy,
|
energy_max,
|
||||||
energy_reward,
|
energy_reward,
|
||||||
crit_power,
|
crit_power,
|
||||||
stealth,
|
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"])
|
let quality = if let Some(quality_raw) = record.get(headers["Quality"])
|
||||||
{
|
{
|
||||||
match quality_raw {
|
match quality_raw {
|
||||||
|
@ -133,11 +133,12 @@ impl Attack {
|
|||||||
source: AttackSource,
|
source: AttackSource,
|
||||||
dir: Dir,
|
dir: Dir,
|
||||||
damage: Damage,
|
damage: Damage,
|
||||||
|
msm: &MaterialStatManifest,
|
||||||
mut emit: impl FnMut(ServerEvent),
|
mut emit: impl FnMut(ServerEvent),
|
||||||
mut emit_outcome: impl FnMut(Outcome),
|
mut emit_outcome: impl FnMut(Outcome),
|
||||||
) -> f32 {
|
) -> f32 {
|
||||||
let damage_reduction =
|
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 {
|
let block_reduction = match source {
|
||||||
AttackSource::Melee => {
|
AttackSource::Melee => {
|
||||||
if let (Some(CharacterState::BasicBlock(data)), Some(ori)) =
|
if let (Some(CharacterState::BasicBlock(data)), Some(ori)) =
|
||||||
@ -186,6 +187,9 @@ impl Attack {
|
|||||||
mut emit: impl FnMut(ServerEvent),
|
mut emit: impl FnMut(ServerEvent),
|
||||||
mut emit_outcome: impl FnMut(Outcome),
|
mut emit_outcome: impl FnMut(Outcome),
|
||||||
) -> bool {
|
) -> bool {
|
||||||
|
// TODO: Maybe move this higher and pass it as argument into this function?
|
||||||
|
let msm = &MaterialStatManifest::load().read();
|
||||||
|
|
||||||
let AttackOptions {
|
let AttackOptions {
|
||||||
target_dodging,
|
target_dodging,
|
||||||
may_harm,
|
may_harm,
|
||||||
@ -220,6 +224,7 @@ impl Attack {
|
|||||||
attack_source,
|
attack_source,
|
||||||
dir,
|
dir,
|
||||||
damage.damage,
|
damage.damage,
|
||||||
|
msm,
|
||||||
&mut emit,
|
&mut emit,
|
||||||
&mut emit_outcome,
|
&mut emit_outcome,
|
||||||
);
|
);
|
||||||
@ -272,7 +277,7 @@ impl Attack {
|
|||||||
let reduced_damage =
|
let reduced_damage =
|
||||||
applied_damage * damage_reduction / (1.0 - damage_reduction);
|
applied_damage * damage_reduction / (1.0 - damage_reduction);
|
||||||
let poise = reduced_damage * CRUSHING_POISE_FRACTION;
|
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 {
|
let poise_change = PoiseChange {
|
||||||
amount: change,
|
amount: change,
|
||||||
impulse: *dir,
|
impulse: *dir,
|
||||||
@ -307,7 +312,7 @@ impl Attack {
|
|||||||
emit(ServerEvent::EnergyChange {
|
emit(ServerEvent::EnergyChange {
|
||||||
entity: attacker.entity,
|
entity: attacker.entity,
|
||||||
change: *ec
|
change: *ec
|
||||||
* compute_energy_reward_mod(attacker.inventory)
|
* compute_energy_reward_mod(attacker.inventory, msm)
|
||||||
* strength_modifier,
|
* strength_modifier,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -342,7 +347,7 @@ impl Attack {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
CombatEffect::Poise(p) => {
|
CombatEffect::Poise(p) => {
|
||||||
let change = -Poise::apply_poise_reduction(*p, target.inventory)
|
let change = -Poise::apply_poise_reduction(*p, target.inventory, msm)
|
||||||
* strength_modifier;
|
* strength_modifier;
|
||||||
if change.abs() > Poise::POISE_EPSILON {
|
if change.abs() > Poise::POISE_EPSILON {
|
||||||
let poise_change = PoiseChange {
|
let poise_change = PoiseChange {
|
||||||
@ -450,7 +455,7 @@ impl Attack {
|
|||||||
emit(ServerEvent::EnergyChange {
|
emit(ServerEvent::EnergyChange {
|
||||||
entity: attacker.entity,
|
entity: attacker.entity,
|
||||||
change: ec
|
change: ec
|
||||||
* compute_energy_reward_mod(attacker.inventory)
|
* compute_energy_reward_mod(attacker.inventory, msm)
|
||||||
* strength_modifier,
|
* strength_modifier,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -485,8 +490,8 @@ impl Attack {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
CombatEffect::Poise(p) => {
|
CombatEffect::Poise(p) => {
|
||||||
let change =
|
let change = -Poise::apply_poise_reduction(p, target.inventory, msm)
|
||||||
-Poise::apply_poise_reduction(p, target.inventory) * strength_modifier;
|
* strength_modifier;
|
||||||
if change.abs() > Poise::POISE_EPSILON {
|
if change.abs() > Poise::POISE_EPSILON {
|
||||||
let poise_change = PoiseChange {
|
let poise_change = PoiseChange {
|
||||||
amount: change,
|
amount: change,
|
||||||
@ -755,8 +760,9 @@ impl Damage {
|
|||||||
damage: Option<Self>,
|
damage: Option<Self>,
|
||||||
inventory: Option<&Inventory>,
|
inventory: Option<&Inventory>,
|
||||||
stats: Option<&Stats>,
|
stats: Option<&Stats>,
|
||||||
|
msm: &MaterialStatManifest,
|
||||||
) -> f32 {
|
) -> f32 {
|
||||||
let protection = compute_protection(inventory);
|
let protection = compute_protection(inventory, msm);
|
||||||
|
|
||||||
let penetration = if let Some(damage) = damage {
|
let penetration = if let Some(damage) = damage {
|
||||||
if let DamageKind::Piercing = damage.kind {
|
if let DamageKind::Piercing = damage.kind {
|
||||||
@ -1058,20 +1064,20 @@ pub fn combat_rating(
|
|||||||
// Normalized with a standard max health of 100
|
// Normalized with a standard max health of 100
|
||||||
let health_rating = health.base_max()
|
let health_rating = health.base_max()
|
||||||
/ 100.0
|
/ 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
|
// Normalized with a standard max energy of 100 and energy reward multiplier of
|
||||||
// x1
|
// x1
|
||||||
let energy_rating = (energy.base_max() + compute_max_energy_mod(Some(inventory))) / 100.0
|
let energy_rating = (energy.base_max() + compute_max_energy_mod(Some(inventory), msm)) / 100.0
|
||||||
* compute_energy_reward_mod(Some(inventory));
|
* compute_energy_reward_mod(Some(inventory), msm);
|
||||||
|
|
||||||
// Normalized with a standard max poise of 100
|
// Normalized with a standard max poise of 100
|
||||||
let poise_rating = poise.base_max() as f32
|
let poise_rating = poise.base_max() as f32
|
||||||
/ 100.0
|
/ 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
|
// 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
|
// Assumes a standard person has earned 20 skill points in the general skill
|
||||||
// tree and 10 skill points for the weapon skill tree
|
// tree and 10 skill points for the weapon skill tree
|
||||||
@ -1100,14 +1106,14 @@ pub fn combat_rating(
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[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
|
// 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
|
// defaults to a value of 1.25 if no inventory is equipped
|
||||||
inventory.map_or(1.25, |inv| {
|
inventory.map_or(1.25, |inv| {
|
||||||
inv.equipped_items()
|
inv.equipped_items()
|
||||||
.filter_map(|item| {
|
.filter_map(|item| {
|
||||||
if let ItemKind::Armor(armor) = &*item.kind() {
|
if let ItemKind::Armor(armor) = &*item.kind() {
|
||||||
armor.crit_power()
|
armor.stats(msm).crit_power
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
@ -1118,14 +1124,14 @@ pub fn compute_crit_mult(inventory: Option<&Inventory>) -> f32 {
|
|||||||
|
|
||||||
/// Computes the energy reward modifer from worn armor
|
/// Computes the energy reward modifer from worn armor
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[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
|
// 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
|
// defaults to a value of 1.0 if no inventory is present
|
||||||
inventory.map_or(1.0, |inv| {
|
inventory.map_or(1.0, |inv| {
|
||||||
inv.equipped_items()
|
inv.equipped_items()
|
||||||
.filter_map(|item| {
|
.filter_map(|item| {
|
||||||
if let ItemKind::Armor(armor) = &*item.kind() {
|
if let ItemKind::Armor(armor) = &*item.kind() {
|
||||||
armor.energy_reward()
|
armor.stats(msm).energy_reward
|
||||||
} else {
|
} else {
|
||||||
None
|
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
|
/// Computes the additive modifier that should be applied to max energy from the
|
||||||
/// currently equipped items
|
/// currently equipped items
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[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
|
// Defaults to a value of 0 if no inventory is present
|
||||||
inventory.map_or(0.0, |inv| {
|
inventory.map_or(0.0, |inv| {
|
||||||
inv.equipped_items()
|
inv.equipped_items()
|
||||||
.filter_map(|item| {
|
.filter_map(|item| {
|
||||||
if let ItemKind::Armor(armor) = &*item.kind() {
|
if let ItemKind::Armor(armor) = &*item.kind() {
|
||||||
armor.energy_max()
|
armor.stats(msm).energy_max
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
@ -1158,10 +1164,11 @@ pub fn compute_max_energy_mod(inventory: Option<&Inventory>) -> f32 {
|
|||||||
pub fn perception_dist_multiplier_from_stealth(
|
pub fn perception_dist_multiplier_from_stealth(
|
||||||
inventory: Option<&Inventory>,
|
inventory: Option<&Inventory>,
|
||||||
character_state: Option<&CharacterState>,
|
character_state: Option<&CharacterState>,
|
||||||
|
msm: &MaterialStatManifest,
|
||||||
) -> f32 {
|
) -> f32 {
|
||||||
const SNEAK_MULTIPLIER: f32 = 0.7;
|
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 is_sneaking = character_state.map_or(false, |state| state.is_stealthy());
|
||||||
|
|
||||||
let multiplier = item_stealth_multiplier * if is_sneaking { SNEAK_MULTIPLIER } else { 1.0 };
|
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"))]
|
#[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| {
|
let stealth_sum = inventory.map_or(0.0, |inv| {
|
||||||
inv.equipped_items()
|
inv.equipped_items()
|
||||||
.filter_map(|item| {
|
.filter_map(|item| {
|
||||||
if let ItemKind::Armor(armor) = &*item.kind() {
|
if let ItemKind::Armor(armor) = &*item.kind() {
|
||||||
armor.stealth()
|
armor.stats(msm).stealth
|
||||||
} else {
|
} else {
|
||||||
None
|
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
|
/// damage reduction applied to damage received by an entity None indicates that
|
||||||
/// the armor equipped makes the entity invulnerable
|
/// the armor equipped makes the entity invulnerable
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[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| {
|
inventory.map_or(Some(0.0), |inv| {
|
||||||
inv.equipped_items()
|
inv.equipped_items()
|
||||||
.filter_map(|item| {
|
.filter_map(|item| {
|
||||||
if let ItemKind::Armor(armor) = &*item.kind() {
|
if let ItemKind::Armor(armor) = &*item.kind() {
|
||||||
armor.protection()
|
armor.stats(msm).protection
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,15 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
comp::item::Rgb,
|
comp::item::{MaterialStatManifest, Rgb},
|
||||||
terrain::{Block, BlockKind},
|
terrain::{Block, BlockKind},
|
||||||
};
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
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 {
|
pub enum ArmorKind {
|
||||||
Shoulder,
|
Shoulder,
|
||||||
Chest,
|
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 {
|
pub struct Stats {
|
||||||
/// Protection is non-linearly transformed (following summation) to a damage
|
/// Protection is non-linearly transformed (following summation) to a damage
|
||||||
/// reduction using (prot / (60 + prot))
|
/// reduction using (prot / (60 + prot))
|
||||||
protection: Option<Protection>,
|
pub protection: Option<Protection>,
|
||||||
/// Poise protection is non-linearly transformed (following summation) to a
|
/// Poise protection is non-linearly transformed (following summation) to a
|
||||||
/// poise damage reduction using (prot / (60 + prot))
|
/// 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 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 recovery is summed, and then added to 1.0. When attacks reward
|
||||||
/// energy, it is then multiplied by this value before the energy is
|
/// energy, it is then multiplied by this value before the energy is
|
||||||
/// rewarded.
|
/// rewarded.
|
||||||
energy_reward: Option<f32>,
|
pub energy_reward: Option<f32>,
|
||||||
/// Crit power is summed, and then added to the default crit multiplier of
|
/// 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.
|
/// 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
|
/// Stealth is summed along with the base stealth bonus (2.0), and then
|
||||||
/// the agent's perception distance is divided by this value
|
/// the agent's perception distance is divided by this value
|
||||||
stealth: Option<f32>,
|
pub stealth: Option<f32>,
|
||||||
/// Ground contact type, mostly for shoes
|
/// Ground contact type, mostly for shoes
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
ground_contact: Friction,
|
pub ground_contact: Friction,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Stats {
|
impl Stats {
|
||||||
// DO NOT USE UNLESS YOU KNOW WHAT YOU ARE DOING
|
fn none() -> Self {
|
||||||
// Added for csv import of stats
|
Stats {
|
||||||
pub fn new(
|
protection: None,
|
||||||
protection: Option<Protection>,
|
poise_resilience: None,
|
||||||
poise_resilience: Option<Protection>,
|
energy_max: None,
|
||||||
energy_max: Option<f32>,
|
energy_reward: None,
|
||||||
energy_reward: Option<f32>,
|
crit_power: None,
|
||||||
crit_power: Option<f32>,
|
stealth: None,
|
||||||
stealth: Option<f32>,
|
|
||||||
) -> Self {
|
|
||||||
Self {
|
|
||||||
protection,
|
|
||||||
poise_resilience,
|
|
||||||
energy_max,
|
|
||||||
energy_reward,
|
|
||||||
crit_power,
|
|
||||||
stealth,
|
|
||||||
ground_contact: Friction::Normal,
|
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 }
|
fn mul(self, val: f32) -> Self::Output {
|
||||||
|
Stats {
|
||||||
pub fn energy_reward(&self) -> Option<f32> { self.energy_reward }
|
protection: self.protection.map(|a| a * val),
|
||||||
|
poise_resilience: self.poise_resilience.map(|a| a * val),
|
||||||
pub fn crit_power(&self) -> Option<f32> { self.crit_power }
|
energy_max: self.energy_max.map(|a| a * val),
|
||||||
|
energy_reward: self.energy_reward.map(|a| a * val),
|
||||||
pub fn stealth(&self) -> Option<f32> { self.stealth }
|
crit_power: self.crit_power.map(|a| a * val),
|
||||||
|
stealth: self.stealth.map(|a| a * val),
|
||||||
pub fn ground_contact(&self) -> Friction { self.ground_contact }
|
// There is nothing to multiply, it is just an enum
|
||||||
|
ground_contact: self.ground_contact,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Sub<Stats> for Stats {
|
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 {
|
impl PartialOrd for Protection {
|
||||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||||
match (*self, *other) {
|
match (*self, *other) {
|
||||||
@ -191,25 +206,39 @@ impl PartialOrd for Protection {
|
|||||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||||
pub struct Armor {
|
pub struct Armor {
|
||||||
pub kind: ArmorKind,
|
pub kind: ArmorKind,
|
||||||
pub stats: Stats,
|
stats: StatsSource,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Armor {
|
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 }
|
set_stats * multiplier
|
||||||
|
},
|
||||||
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 }
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub fn test_armor(
|
pub fn test_armor(
|
||||||
@ -219,7 +248,7 @@ impl Armor {
|
|||||||
) -> Armor {
|
) -> Armor {
|
||||||
Armor {
|
Armor {
|
||||||
kind,
|
kind,
|
||||||
stats: Stats {
|
stats: StatsSource::Direct(Stats {
|
||||||
protection: Some(protection),
|
protection: Some(protection),
|
||||||
poise_resilience: Some(poise_resilience),
|
poise_resilience: Some(poise_resilience),
|
||||||
energy_max: None,
|
energy_max: None,
|
||||||
@ -227,7 +256,7 @@ impl Armor {
|
|||||||
crit_power: None,
|
crit_power: None,
|
||||||
stealth: None,
|
stealth: None,
|
||||||
ground_contact: Friction::Normal,
|
ground_contact: Friction::Normal,
|
||||||
},
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,8 +4,8 @@ pub mod modular;
|
|||||||
pub mod tool;
|
pub mod tool;
|
||||||
|
|
||||||
// Reexports
|
// Reexports
|
||||||
pub use modular::{ModularBase, ModularComponent};
|
pub use modular::{MaterialStatManifest, ModularBase, ModularComponent};
|
||||||
pub use tool::{AbilitySet, AbilitySpec, Hands, MaterialStatManifest, Tool, ToolKind};
|
pub use tool::{AbilitySet, AbilitySpec, Hands, Tool, ToolKind};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
assets::{self, AssetExt, BoxedError, Error},
|
assets::{self, AssetExt, BoxedError, Error},
|
||||||
|
@ -1,8 +1,12 @@
|
|||||||
use super::{
|
use super::{
|
||||||
tool::{self, AbilityMap, AbilitySpec, Hands, MaterialStatManifest},
|
armor,
|
||||||
|
tool::{self, AbilityMap, AbilitySpec, Hands},
|
||||||
Item, ItemBase, ItemDef, ItemDesc, ItemKind, ItemTag, Material, Quality, ToolKind,
|
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 common_base::dev_panic;
|
||||||
use hashbrown::HashMap;
|
use hashbrown::HashMap;
|
||||||
use lazy_static::lazy_static;
|
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)]
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
pub enum ModularBase {
|
pub enum ModularBase {
|
||||||
Tool,
|
Tool,
|
||||||
@ -241,7 +267,7 @@ impl ModularComponent {
|
|||||||
.filter_map(|comp| {
|
.filter_map(|comp| {
|
||||||
comp.item_definition_id()
|
comp.item_definition_id()
|
||||||
.itemdef_id()
|
.itemdef_id()
|
||||||
.and_then(|id| msm.0.get(id))
|
.and_then(|id| msm.tool_stats.get(id))
|
||||||
.copied()
|
.copied()
|
||||||
.zip(Some(1))
|
.zip(Some(1))
|
||||||
})
|
})
|
||||||
|
@ -225,21 +225,6 @@ impl DivAssign<usize> for Stats {
|
|||||||
fn div_assign(&mut self, scalar: usize) { *self = *self / (scalar as f32); }
|
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)]
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
pub struct Tool {
|
pub struct Tool {
|
||||||
pub kind: ToolKind,
|
pub kind: ToolKind,
|
||||||
|
@ -408,7 +408,7 @@ impl Loadout {
|
|||||||
pub fn persistence_update_all_item_states(
|
pub fn persistence_update_all_item_states(
|
||||||
&mut self,
|
&mut self,
|
||||||
ability_map: &item::tool::AbilityMap,
|
ability_map: &item::tool::AbilityMap,
|
||||||
msm: &item::tool::MaterialStatManifest,
|
msm: &item::MaterialStatManifest,
|
||||||
) {
|
) {
|
||||||
self.slots.iter_mut().for_each(|slot| {
|
self.slots.iter_mut().for_each(|slot| {
|
||||||
if let Some(item) = &mut slot.slot {
|
if let Some(item) = &mut slot.slot {
|
||||||
|
@ -808,7 +808,7 @@ impl Inventory {
|
|||||||
pub fn persistence_update_all_item_states(
|
pub fn persistence_update_all_item_states(
|
||||||
&mut self,
|
&mut self,
|
||||||
ability_map: &item::tool::AbilityMap,
|
ability_map: &item::tool::AbilityMap,
|
||||||
msm: &item::tool::MaterialStatManifest,
|
msm: &item::MaterialStatManifest,
|
||||||
) {
|
) {
|
||||||
self.slots_mut().for_each(|slot| {
|
self.slots_mut().for_each(|slot| {
|
||||||
if let Some(item) = slot {
|
if let Some(item) = slot {
|
||||||
|
@ -686,14 +686,16 @@ impl TradePricing {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
fn print_sorted(&self) {
|
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,");
|
println!("Item, ForSale, Amount, Good, Quality, Deal, Unit,");
|
||||||
|
|
||||||
fn more_information(i: &Item, p: f32) -> (String, &'static str) {
|
fn more_information(i: &Item, p: f32) -> (String, &'static str) {
|
||||||
|
let msm = &MaterialStatManifest::load().read();
|
||||||
|
|
||||||
if let ItemKind::Armor(a) = &*i.kind() {
|
if let ItemKind::Armor(a) = &*i.kind() {
|
||||||
(
|
(
|
||||||
match a.protection() {
|
match a.stats(msm).protection {
|
||||||
Some(armor::Protection::Invincible) => "Invincible".into(),
|
Some(armor::Protection::Invincible) => "Invincible".into(),
|
||||||
Some(armor::Protection::Normal(x)) => format!("{:.4}", x * p),
|
Some(armor::Protection::Normal(x)) => format!("{:.4}", x * p),
|
||||||
None => "0.0".into(),
|
None => "0.0".into(),
|
||||||
|
@ -2,7 +2,7 @@ use crate::{
|
|||||||
combat::{DamageContributor, DamageSource},
|
combat::{DamageContributor, DamageSource},
|
||||||
comp::{
|
comp::{
|
||||||
self,
|
self,
|
||||||
inventory::item::{armor::Protection, ItemKind},
|
inventory::item::{armor::Protection, ItemKind, MaterialStatManifest},
|
||||||
CharacterState, Inventory,
|
CharacterState, Inventory,
|
||||||
},
|
},
|
||||||
resources::Time,
|
resources::Time,
|
||||||
@ -226,12 +226,15 @@ impl Poise {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the total poise damage reduction provided by all equipped items
|
/// 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
|
let protection = inventory
|
||||||
.equipped_items()
|
.equipped_items()
|
||||||
.filter_map(|item| {
|
.filter_map(|item| {
|
||||||
if let ItemKind::Armor(armor) = &*item.kind() {
|
if let ItemKind::Armor(armor) = &*item.kind() {
|
||||||
armor.poise_resilience()
|
armor.stats(msm).poise_resilience
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
@ -249,9 +252,13 @@ impl Poise {
|
|||||||
|
|
||||||
/// Modifies a poise change when optionally given an inventory to aid in
|
/// Modifies a poise change when optionally given an inventory to aid in
|
||||||
/// calculation of poise damage reduction
|
/// 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| {
|
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| {
|
footwear = data.inventory.and_then(|inv| {
|
||||||
inv.equipped(EquipSlot::Armor(ArmorSlot::Feet))
|
inv.equipped(EquipSlot::Armor(ArmorSlot::Feet))
|
||||||
.map(|armor| match armor.kind().as_ref() {
|
.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,
|
_ => 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);
|
.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)
|
(crit_chance, crit_mult)
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,7 @@ use common::{
|
|||||||
Buffs,
|
Buffs,
|
||||||
},
|
},
|
||||||
fluid_dynamics::{Fluid, LiquidKind},
|
fluid_dynamics::{Fluid, LiquidKind},
|
||||||
|
item::MaterialStatManifest,
|
||||||
Energy, Group, Health, HealthChange, Inventory, LightEmitter, ModifierKind, PhysicsState,
|
Energy, Group, Health, HealthChange, Inventory, LightEmitter, ModifierKind, PhysicsState,
|
||||||
Stats,
|
Stats,
|
||||||
},
|
},
|
||||||
@ -21,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, ReadStorage,
|
saveload::MarkerAllocator, shred::ResourceId, Entities, Join, ParJoin, Read, ReadExpect,
|
||||||
SystemData, World, WriteStorage,
|
ReadStorage, SystemData, World, WriteStorage,
|
||||||
};
|
};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
@ -38,6 +39,7 @@ pub struct ReadData<'a> {
|
|||||||
groups: ReadStorage<'a, Group>,
|
groups: ReadStorage<'a, Group>,
|
||||||
uid_allocator: Read<'a, UidAllocator>,
|
uid_allocator: Read<'a, UidAllocator>,
|
||||||
time: Read<'a, Time>,
|
time: Read<'a, Time>,
|
||||||
|
msm: ReadExpect<'a, MaterialStatManifest>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
@ -207,6 +209,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
None,
|
None,
|
||||||
read_data.inventories.get(entity),
|
read_data.inventories.get(entity),
|
||||||
Some(&stat),
|
Some(&stat),
|
||||||
|
&read_data.msm,
|
||||||
);
|
);
|
||||||
if (damage_reduction - 1.0).abs() < f32::EPSILON {
|
if (damage_reduction - 1.0).abs() < f32::EPSILON {
|
||||||
for (id, buff) in buff_comp.buffs.iter() {
|
for (id, buff) in buff_comp.buffs.iter() {
|
||||||
|
@ -2,6 +2,7 @@ use common::{
|
|||||||
combat,
|
combat,
|
||||||
comp::{
|
comp::{
|
||||||
self,
|
self,
|
||||||
|
item::MaterialStatManifest,
|
||||||
skills::{GeneralSkill, Skill},
|
skills::{GeneralSkill, Skill},
|
||||||
Body, CharacterState, Combo, Energy, Health, Inventory, Poise, PoiseChange, Pos, SkillSet,
|
Body, CharacterState, Combo, Energy, Health, Inventory, Poise, PoiseChange, Pos, SkillSet,
|
||||||
Stats, StatsModifier,
|
Stats, StatsModifier,
|
||||||
@ -11,7 +12,8 @@ use common::{
|
|||||||
};
|
};
|
||||||
use common_ecs::{Job, Origin, Phase, System};
|
use common_ecs::{Job, Origin, Phase, System};
|
||||||
use specs::{
|
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;
|
use vek::Vec3;
|
||||||
|
|
||||||
@ -28,6 +30,7 @@ pub struct ReadData<'a> {
|
|||||||
bodies: ReadStorage<'a, Body>,
|
bodies: ReadStorage<'a, Body>,
|
||||||
char_states: ReadStorage<'a, CharacterState>,
|
char_states: ReadStorage<'a, CharacterState>,
|
||||||
inventories: ReadStorage<'a, Inventory>,
|
inventories: ReadStorage<'a, Inventory>,
|
||||||
|
msm: ReadExpect<'a, MaterialStatManifest>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This system kills players, levels them up, and regenerates energy.
|
/// 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
|
// Calculates energy scaling from stats and inventory
|
||||||
let energy_mods = StatsModifier {
|
let energy_mods = StatsModifier {
|
||||||
add_mod: stat.max_energy_modifiers.add_mod
|
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,
|
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 inventories = ecs.read_storage::<Inventory>();
|
||||||
let stats = ecs.read_storage::<Stats>();
|
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
|
// Handle health change
|
||||||
if let Some(mut health) = ecs.write_storage::<comp::Health>().get_mut(entity) {
|
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),
|
Some(damage),
|
||||||
inventories.get(entity),
|
inventories.get(entity),
|
||||||
stats.get(entity),
|
stats.get(entity),
|
||||||
|
&msm,
|
||||||
);
|
);
|
||||||
let change =
|
let change =
|
||||||
damage.calculate_health_change(damage_reduction, None, false, 0.0, 1.0, *time);
|
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
|
// Handle poise change
|
||||||
if let Some(mut poise) = ecs.write_storage::<comp::Poise>().get_mut(entity) {
|
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_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 {
|
let poise_change = comp::PoiseChange {
|
||||||
amount: poise_change,
|
amount: poise_change,
|
||||||
impulse: Vec3::unit_z(),
|
impulse: Vec3::unit_z(),
|
||||||
|
@ -1092,7 +1092,7 @@ impl Server {
|
|||||||
material_stats: (&*self
|
material_stats: (&*self
|
||||||
.state
|
.state
|
||||||
.ecs()
|
.ecs()
|
||||||
.read_resource::<comp::item::tool::MaterialStatManifest>())
|
.read_resource::<comp::item::MaterialStatManifest>())
|
||||||
.clone(),
|
.clone(),
|
||||||
ability_map: (&*self
|
ability_map: (&*self
|
||||||
.state
|
.state
|
||||||
|
@ -15,6 +15,7 @@ use common::{
|
|||||||
combat::DamageContributor,
|
combat::DamageContributor,
|
||||||
comp::{
|
comp::{
|
||||||
self,
|
self,
|
||||||
|
item::MaterialStatManifest,
|
||||||
skills::{GeneralSkill, Skill},
|
skills::{GeneralSkill, Skill},
|
||||||
Group, Inventory, Item, Poise,
|
Group, Inventory, Item, Poise,
|
||||||
},
|
},
|
||||||
@ -127,6 +128,7 @@ pub trait StateExt {
|
|||||||
|
|
||||||
impl StateExt for State {
|
impl StateExt for State {
|
||||||
fn apply_effect(&self, entity: EcsEntity, effects: Effect, source: Option<Uid>) {
|
fn apply_effect(&self, entity: EcsEntity, effects: Effect, source: Option<Uid>) {
|
||||||
|
let msm = self.ecs().read_resource::<MaterialStatManifest>();
|
||||||
match effects {
|
match effects {
|
||||||
Effect::Health(change) => {
|
Effect::Health(change) => {
|
||||||
self.ecs()
|
self.ecs()
|
||||||
@ -150,6 +152,7 @@ impl StateExt for State {
|
|||||||
Some(damage),
|
Some(damage),
|
||||||
inventories.get(entity),
|
inventories.get(entity),
|
||||||
stats.get(entity),
|
stats.get(entity),
|
||||||
|
&msm,
|
||||||
),
|
),
|
||||||
damage_contributor,
|
damage_contributor,
|
||||||
false,
|
false,
|
||||||
@ -164,7 +167,7 @@ impl StateExt for State {
|
|||||||
},
|
},
|
||||||
Effect::Poise(poise) => {
|
Effect::Poise(poise) => {
|
||||||
let inventories = self.ecs().read_storage::<Inventory>();
|
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
|
// Check to make sure the entity is not already stunned
|
||||||
if let Some(character_state) = self
|
if let Some(character_state) = self
|
||||||
.ecs()
|
.ecs()
|
||||||
|
@ -236,6 +236,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
char_state,
|
char_state,
|
||||||
active_abilities,
|
active_abilities,
|
||||||
cached_spatial_grid: &read_data.cached_spatial_grid,
|
cached_spatial_grid: &read_data.cached_spatial_grid,
|
||||||
|
msm: &read_data.msm,
|
||||||
};
|
};
|
||||||
///////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////
|
||||||
// Behavior tree
|
// Behavior tree
|
||||||
@ -2502,7 +2503,7 @@ impl<'a> AgentData<'a> {
|
|||||||
let other_inventory = read_data.inventories.get(other);
|
let other_inventory = read_data.inventories.get(other);
|
||||||
let other_char_state = read_data.char_states.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 = {
|
let within_sight_dist = {
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
use crate::rtsim::Entity as RtSimData;
|
use crate::rtsim::Entity as RtSimData;
|
||||||
use common::{
|
use common::{
|
||||||
comp::{
|
comp::{
|
||||||
buff::Buffs, group, ActiveAbilities, Alignment, Body, CharacterState, Combo, Energy,
|
buff::Buffs, group, item::MaterialStatManifest, ActiveAbilities, Alignment, Body,
|
||||||
Health, Inventory, LightEmitter, LootOwner, Ori, PhysicsState, Pos, Scale, SkillSet, Stats,
|
CharacterState, Combo, Energy, Health, Inventory, LightEmitter, LootOwner, Ori,
|
||||||
Vel,
|
PhysicsState, Pos, Scale, SkillSet, Stats, Vel,
|
||||||
},
|
},
|
||||||
link::Is,
|
link::Is,
|
||||||
mounting::Mount,
|
mounting::Mount,
|
||||||
@ -43,6 +43,7 @@ pub struct AgentData<'a> {
|
|||||||
pub char_state: &'a CharacterState,
|
pub char_state: &'a CharacterState,
|
||||||
pub active_abilities: &'a ActiveAbilities,
|
pub active_abilities: &'a ActiveAbilities,
|
||||||
pub cached_spatial_grid: &'a common::CachedSpatialGrid,
|
pub cached_spatial_grid: &'a common::CachedSpatialGrid,
|
||||||
|
pub msm: &'a MaterialStatManifest,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct TargetData<'a> {
|
pub struct TargetData<'a> {
|
||||||
@ -154,6 +155,7 @@ pub struct ReadData<'a> {
|
|||||||
pub combos: ReadStorage<'a, Combo>,
|
pub combos: ReadStorage<'a, Combo>,
|
||||||
pub active_abilities: ReadStorage<'a, ActiveAbilities>,
|
pub active_abilities: ReadStorage<'a, ActiveAbilities>,
|
||||||
pub loot_owners: ReadStorage<'a, LootOwner>,
|
pub loot_owners: ReadStorage<'a, LootOwner>,
|
||||||
|
pub msm: ReadExpect<'a, MaterialStatManifest>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum Path {
|
pub enum Path {
|
||||||
|
@ -937,19 +937,28 @@ impl<'a> Widget for Bag<'a> {
|
|||||||
let protection_txt = format!(
|
let protection_txt = format!(
|
||||||
"{}%",
|
"{}%",
|
||||||
(100.0
|
(100.0
|
||||||
* Damage::compute_damage_reduction(None, Some(inventory), Some(self.stats)))
|
* Damage::compute_damage_reduction(
|
||||||
as i32
|
None,
|
||||||
|
Some(inventory),
|
||||||
|
Some(self.stats),
|
||||||
|
self.msm
|
||||||
|
)) as i32
|
||||||
);
|
);
|
||||||
let health_txt = format!("{}", self.health.maximum().round() as usize);
|
let health_txt = format!("{}", self.health.maximum().round() as usize);
|
||||||
let energy_txt = format!("{}", self.energy.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 combat_rating_txt = format!("{}", (combat_rating * 10.0) as usize);
|
||||||
let stun_res_txt = format!(
|
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!(
|
let stealth_txt = format!(
|
||||||
"{:.1}%",
|
"{:.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)
|
* 100.0)
|
||||||
);
|
);
|
||||||
let btn = if i.0 == 0 {
|
let btn = if i.0 == 0 {
|
||||||
|
@ -33,11 +33,7 @@ use common::{
|
|||||||
self,
|
self,
|
||||||
ability::{Ability, ActiveAbilities, AuxiliaryAbility, MAX_ABILITIES},
|
ability::{Ability, ActiveAbilities, AuxiliaryAbility, MAX_ABILITIES},
|
||||||
inventory::{
|
inventory::{
|
||||||
item::{
|
item::{item_key::ItemKey, tool::ToolKind, ItemKind, MaterialStatManifest},
|
||||||
item_key::ItemKey,
|
|
||||||
tool::{MaterialStatManifest, ToolKind},
|
|
||||||
ItemKind,
|
|
||||||
},
|
|
||||||
slot::EquipSlot,
|
slot::EquipSlot,
|
||||||
},
|
},
|
||||||
skills::{
|
skills::{
|
||||||
@ -1184,23 +1180,25 @@ impl<'a> Widget for Diary<'a> {
|
|||||||
format!("{:.2}", cr * 10.0)
|
format!("{:.2}", cr * 10.0)
|
||||||
},
|
},
|
||||||
"Protection" => {
|
"Protection" => {
|
||||||
let protection = combat::compute_protection(Some(self.inventory));
|
let protection =
|
||||||
|
combat::compute_protection(Some(self.inventory), self.msm);
|
||||||
match protection {
|
match protection {
|
||||||
Some(prot) => format!("{}", prot),
|
Some(prot) => format!("{}", prot),
|
||||||
None => String::from("Invincible"),
|
None => String::from("Invincible"),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Stun-Resistance" => {
|
"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)
|
format!("{:.2}%", stun_res * 100.0)
|
||||||
},
|
},
|
||||||
"Crit-Power" => {
|
"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)
|
format!("x{:.2}", critpwr)
|
||||||
},
|
},
|
||||||
"Energy Reward" => {
|
"Energy Reward" => {
|
||||||
let energy_rew =
|
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)
|
format!("{:+.0}%", (energy_rew - 1.0) * 100.0)
|
||||||
},
|
},
|
||||||
"Stealth" => {
|
"Stealth" => {
|
||||||
@ -1208,6 +1206,7 @@ impl<'a> Widget for Diary<'a> {
|
|||||||
combat::perception_dist_multiplier_from_stealth(
|
combat::perception_dist_multiplier_from_stealth(
|
||||||
Some(self.inventory),
|
Some(self.inventory),
|
||||||
None,
|
None,
|
||||||
|
self.msm,
|
||||||
);
|
);
|
||||||
let txt =
|
let txt =
|
||||||
format!("{:+.1}%", (1.0 - stealth_perception_multiplier) * 100.0);
|
format!("{:+.1}%", (1.0 - stealth_perception_multiplier) * 100.0);
|
||||||
|
@ -5,7 +5,7 @@ use common::{
|
|||||||
item::{
|
item::{
|
||||||
armor::{Armor, ArmorKind, Protection},
|
armor::{Armor, ArmorKind, Protection},
|
||||||
tool::{Hands, Tool, ToolKind},
|
tool::{Hands, Tool, ToolKind},
|
||||||
ItemDesc, ItemKind, MaterialKind,
|
ItemDesc, ItemKind, MaterialKind, MaterialStatManifest,
|
||||||
},
|
},
|
||||||
BuffKind,
|
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() {
|
let mut count = match &*item.kind() {
|
||||||
ItemKind::Armor(armor) => {
|
ItemKind::Armor(armor) => {
|
||||||
if matches!(armor.kind, ArmorKind::Bag) {
|
if matches!(armor.kind, ArmorKind::Bag) {
|
||||||
0
|
0
|
||||||
} else {
|
} else {
|
||||||
armor.stats.energy_reward().is_some() as usize
|
armor.stats(msm).energy_reward.is_some() as usize
|
||||||
+ armor.stats.energy_max().is_some() as usize
|
+ armor.stats(msm).energy_max.is_some() as usize
|
||||||
+ armor.stats.stealth().is_some() as usize
|
+ armor.stats(msm).stealth.is_some() as usize
|
||||||
+ armor.stats.crit_power().is_some() as usize
|
+ armor.stats(msm).crit_power.is_some() as usize
|
||||||
+ armor.stats.poise_resilience().is_some() as usize
|
+ armor.stats(msm).poise_resilience.is_some() as usize
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
ItemKind::Tool(_) => 7,
|
ItemKind::Tool(_) => 7,
|
||||||
|
@ -18,8 +18,8 @@ use common::{
|
|||||||
inventory::slot::{EquipSlot, Slot},
|
inventory::slot::{EquipSlot, Slot},
|
||||||
invite::InviteKind,
|
invite::InviteKind,
|
||||||
item::{
|
item::{
|
||||||
tool::{AbilityMap, MaterialStatManifest, ToolKind},
|
tool::{AbilityMap, ToolKind},
|
||||||
ItemDesc,
|
ItemDesc, MaterialStatManifest,
|
||||||
},
|
},
|
||||||
ChatMsg, ChatType, InputKind, InventoryUpdateEvent, Pos, Stats, UtteranceKind, Vel,
|
ChatMsg, ChatType, InputKind, InventoryUpdateEvent, Pos, Stats, UtteranceKind, Vel,
|
||||||
},
|
},
|
||||||
|
@ -505,17 +505,19 @@ impl<'a> Widget for ItemTooltip<'a> {
|
|||||||
_ => self.imgs.inv_slot_red,
|
_ => self.imgs.inv_slot_red,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let stats_count = util::stats_count(item, self.msm);
|
||||||
|
|
||||||
// Update widget array size
|
// Update widget array size
|
||||||
state.update(|s| {
|
state.update(|s| {
|
||||||
s.ids
|
s.ids
|
||||||
.stats
|
.stats
|
||||||
.resize(util::stats_count(item), &mut ui.widget_id_generator())
|
.resize(stats_count, &mut ui.widget_id_generator())
|
||||||
});
|
});
|
||||||
|
|
||||||
state.update(|s| {
|
state.update(|s| {
|
||||||
s.ids
|
s.ids
|
||||||
.diffs
|
.diffs
|
||||||
.resize(util::stats_count(item), &mut ui.widget_id_generator())
|
.resize(stats_count, &mut ui.widget_id_generator())
|
||||||
});
|
});
|
||||||
|
|
||||||
// Background image frame
|
// Background image frame
|
||||||
@ -820,12 +822,22 @@ impl<'a> Widget for ItemTooltip<'a> {
|
|||||||
},
|
},
|
||||||
_ => {
|
_ => {
|
||||||
// Armour
|
// Armour
|
||||||
let protection = armor.protection().unwrap_or(Protection::Normal(0.0));
|
let protection = armor
|
||||||
let poise_res = armor.poise_resilience().unwrap_or(Protection::Normal(0.0));
|
.stats(self.msm)
|
||||||
let energy_max = armor.energy_max().unwrap_or(0.0);
|
.protection
|
||||||
let energy_reward = armor.energy_reward().map(|x| x * 100.0).unwrap_or(0.0);
|
.unwrap_or(Protection::Normal(0.0));
|
||||||
let crit_power = armor.crit_power().unwrap_or(0.0);
|
let poise_res = armor
|
||||||
let stealth = armor.stealth().unwrap_or(0.0);
|
.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))
|
widget::Text::new(&util::protec2string(protection))
|
||||||
.graphics_for(id)
|
.graphics_for(id)
|
||||||
@ -847,7 +859,7 @@ impl<'a> Widget for ItemTooltip<'a> {
|
|||||||
.set(state.ids.main_stat_text, ui);
|
.set(state.ids.main_stat_text, ui);
|
||||||
|
|
||||||
// Poise res
|
// Poise res
|
||||||
if armor.stats.poise_resilience().is_some() {
|
if armor.stats(self.msm).poise_resilience.is_some() {
|
||||||
widget::Text::new(&format!(
|
widget::Text::new(&format!(
|
||||||
"{} : {}",
|
"{} : {}",
|
||||||
i18n.get("common.stats.poise_res"),
|
i18n.get("common.stats.poise_res"),
|
||||||
@ -863,7 +875,7 @@ impl<'a> Widget for ItemTooltip<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Max Energy
|
// Max Energy
|
||||||
if armor.stats.energy_max().is_some() {
|
if armor.stats(self.msm).energy_max.is_some() {
|
||||||
widget::Text::new(&format!(
|
widget::Text::new(&format!(
|
||||||
"{} : {:.1}",
|
"{} : {:.1}",
|
||||||
i18n.get("common.stats.energy_max"),
|
i18n.get("common.stats.energy_max"),
|
||||||
@ -874,7 +886,7 @@ impl<'a> Widget for ItemTooltip<'a> {
|
|||||||
.with_style(self.style.desc)
|
.with_style(self.style.desc)
|
||||||
.color(text_color)
|
.color(text_color)
|
||||||
.and(|t| {
|
.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)
|
t.down_from(state.ids.stats[0], V_PAD_STATS)
|
||||||
} else {
|
} else {
|
||||||
t.x_align_to(
|
t.x_align_to(
|
||||||
@ -885,13 +897,14 @@ impl<'a> Widget for ItemTooltip<'a> {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
.set(
|
.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,
|
ui,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Energy Recovery
|
// Energy Recovery
|
||||||
if armor.stats.energy_reward().is_some() {
|
if armor.stats(self.msm).energy_reward.is_some() {
|
||||||
widget::Text::new(&format!(
|
widget::Text::new(&format!(
|
||||||
"{} : {:.1}%",
|
"{} : {:.1}%",
|
||||||
i18n.get("common.stats.energy_reward"),
|
i18n.get("common.stats.energy_reward"),
|
||||||
@ -902,8 +915,8 @@ impl<'a> Widget for ItemTooltip<'a> {
|
|||||||
.with_style(self.style.desc)
|
.with_style(self.style.desc)
|
||||||
.color(text_color)
|
.color(text_color)
|
||||||
.and(|t| {
|
.and(|t| {
|
||||||
match armor.stats.poise_resilience().is_some() as usize
|
match armor.stats(self.msm).poise_resilience.is_some() as usize
|
||||||
+ armor.stats.energy_max().is_some() as usize
|
+ armor.stats(self.msm).energy_max.is_some() as usize
|
||||||
{
|
{
|
||||||
0 => t
|
0 => t
|
||||||
.x_align_to(
|
.x_align_to(
|
||||||
@ -915,14 +928,15 @@ impl<'a> Widget for ItemTooltip<'a> {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
.set(
|
.set(
|
||||||
state.ids.stats[armor.stats.poise_resilience().is_some() as usize
|
state.ids.stats[armor.stats(self.msm).poise_resilience.is_some()
|
||||||
+ armor.stats.energy_max().is_some() as usize],
|
as usize
|
||||||
|
+ armor.stats(self.msm).energy_max.is_some() as usize],
|
||||||
ui,
|
ui,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Crit Power
|
// Crit Power
|
||||||
if armor.stats.crit_power().is_some() {
|
if armor.stats(self.msm).crit_power.is_some() {
|
||||||
widget::Text::new(&format!(
|
widget::Text::new(&format!(
|
||||||
"{} : {:.3}",
|
"{} : {:.3}",
|
||||||
i18n.get("common.stats.crit_power"),
|
i18n.get("common.stats.crit_power"),
|
||||||
@ -933,9 +947,9 @@ impl<'a> Widget for ItemTooltip<'a> {
|
|||||||
.with_style(self.style.desc)
|
.with_style(self.style.desc)
|
||||||
.color(text_color)
|
.color(text_color)
|
||||||
.and(|t| {
|
.and(|t| {
|
||||||
match armor.stats.poise_resilience().is_some() as usize
|
match armor.stats(self.msm).poise_resilience.is_some() as usize
|
||||||
+ armor.stats.energy_max().is_some() as usize
|
+ armor.stats(self.msm).energy_max.is_some() as usize
|
||||||
+ armor.stats.energy_reward().is_some() as usize
|
+ armor.stats(self.msm).energy_reward.is_some() as usize
|
||||||
{
|
{
|
||||||
0 => t
|
0 => t
|
||||||
.x_align_to(
|
.x_align_to(
|
||||||
@ -947,15 +961,16 @@ impl<'a> Widget for ItemTooltip<'a> {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
.set(
|
.set(
|
||||||
state.ids.stats[armor.stats.poise_resilience().is_some() as usize
|
state.ids.stats[armor.stats(self.msm).poise_resilience.is_some()
|
||||||
+ armor.stats.energy_max().is_some() as usize
|
as usize
|
||||||
+ armor.stats.energy_reward().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,
|
ui,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stealth
|
// Stealth
|
||||||
if armor.stats.stealth().is_some() {
|
if armor.stats(self.msm).stealth.is_some() {
|
||||||
widget::Text::new(&format!(
|
widget::Text::new(&format!(
|
||||||
"{} : {:.3}",
|
"{} : {:.3}",
|
||||||
i18n.get("common.stats.stealth"),
|
i18n.get("common.stats.stealth"),
|
||||||
@ -966,10 +981,10 @@ impl<'a> Widget for ItemTooltip<'a> {
|
|||||||
.with_style(self.style.desc)
|
.with_style(self.style.desc)
|
||||||
.color(text_color)
|
.color(text_color)
|
||||||
.and(|t| {
|
.and(|t| {
|
||||||
match armor.stats.poise_resilience().is_some() as usize
|
match armor.stats(self.msm).poise_resilience.is_some() as usize
|
||||||
+ armor.stats.energy_max().is_some() as usize
|
+ armor.stats(self.msm).energy_max.is_some() as usize
|
||||||
+ armor.stats.energy_reward().is_some() as usize
|
+ armor.stats(self.msm).energy_reward.is_some() as usize
|
||||||
+ armor.stats.crit_power().is_some() as usize
|
+ armor.stats(self.msm).crit_power.is_some() as usize
|
||||||
{
|
{
|
||||||
0 => t
|
0 => t
|
||||||
.x_align_to(
|
.x_align_to(
|
||||||
@ -981,10 +996,11 @@ impl<'a> Widget for ItemTooltip<'a> {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
.set(
|
.set(
|
||||||
state.ids.stats[armor.stats.poise_resilience().is_some() as usize
|
state.ids.stats[armor.stats(self.msm).poise_resilience.is_some()
|
||||||
+ armor.stats.energy_max().is_some() as usize
|
as usize
|
||||||
+ armor.stats.energy_reward().is_some() as usize
|
+ armor.stats(self.msm).energy_max.is_some() as usize
|
||||||
+ armor.stats.crit_power().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,
|
ui,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -1001,11 +1017,11 @@ impl<'a> Widget for ItemTooltip<'a> {
|
|||||||
.with_style(self.style.desc)
|
.with_style(self.style.desc)
|
||||||
.color(text_color)
|
.color(text_color)
|
||||||
.and(|t| {
|
.and(|t| {
|
||||||
match armor.stats.poise_resilience().is_some() as usize
|
match armor.stats(self.msm).poise_resilience.is_some() as usize
|
||||||
+ armor.stats.energy_max().is_some() as usize
|
+ armor.stats(self.msm).energy_max.is_some() as usize
|
||||||
+ armor.stats.energy_reward().is_some() as usize
|
+ armor.stats(self.msm).energy_reward.is_some() as usize
|
||||||
+ armor.stats.crit_power().is_some() as usize
|
+ armor.stats(self.msm).crit_power.is_some() as usize
|
||||||
+ armor.stats.stealth().is_some() as usize
|
+ armor.stats(self.msm).stealth.is_some() as usize
|
||||||
{
|
{
|
||||||
0 => t
|
0 => t
|
||||||
.x_align_to(
|
.x_align_to(
|
||||||
@ -1017,11 +1033,12 @@ impl<'a> Widget for ItemTooltip<'a> {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
.set(
|
.set(
|
||||||
state.ids.stats[armor.stats.poise_resilience().is_some() as usize
|
state.ids.stats[armor.stats(self.msm).poise_resilience.is_some()
|
||||||
+ armor.stats.energy_max().is_some() as usize
|
as usize
|
||||||
+ armor.stats.energy_reward().is_some() as usize
|
+ armor.stats(self.msm).energy_max.is_some() as usize
|
||||||
+ armor.stats.crit_power().is_some() as usize
|
+ armor.stats(self.msm).energy_reward.is_some() as usize
|
||||||
+ armor.stats.stealth().is_some() as usize],
|
+ armor.stats(self.msm).crit_power.is_some() as usize
|
||||||
|
+ armor.stats(self.msm).stealth.is_some() as usize],
|
||||||
ui,
|
ui,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -1030,31 +1047,33 @@ impl<'a> Widget for ItemTooltip<'a> {
|
|||||||
|
|
||||||
if let Some(equipped_item) = equipped_item {
|
if let Some(equipped_item) = equipped_item {
|
||||||
if let ItemKind::Armor(equipped_armor) = &*equipped_item.kind() {
|
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(
|
let protection_diff = util::option_comparison(
|
||||||
&armor.protection(),
|
&armor.stats(self.msm).protection,
|
||||||
&equipped_armor.protection(),
|
&equipped_armor.stats(self.msm).protection,
|
||||||
);
|
);
|
||||||
let poise_res_diff = util::option_comparison(
|
let poise_res_diff = util::option_comparison(
|
||||||
&armor.poise_resilience(),
|
&armor.stats(self.msm).poise_resilience,
|
||||||
&equipped_armor.poise_resilience(),
|
&equipped_armor.stats(self.msm).poise_resilience,
|
||||||
);
|
);
|
||||||
let energy_max_diff = util::option_comparison(
|
let energy_max_diff = util::option_comparison(
|
||||||
&armor.energy_max(),
|
&armor.stats(self.msm).energy_max,
|
||||||
&equipped_armor.energy_max(),
|
&equipped_armor.stats(self.msm).energy_max,
|
||||||
);
|
);
|
||||||
let energy_reward_diff = util::option_comparison(
|
let energy_reward_diff = util::option_comparison(
|
||||||
&armor.energy_reward(),
|
&armor.stats(self.msm).energy_reward,
|
||||||
&equipped_armor.energy_reward(),
|
&equipped_armor.stats(self.msm).energy_reward,
|
||||||
);
|
);
|
||||||
let crit_power_diff = util::option_comparison(
|
let crit_power_diff = util::option_comparison(
|
||||||
&armor.crit_power(),
|
&armor.stats(self.msm).crit_power,
|
||||||
&equipped_armor.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) {
|
if p_diff != Protection::Normal(0.0) {
|
||||||
widget::Text::new(protection_diff.0)
|
widget::Text::new(protection_diff.0)
|
||||||
.right_from(state.ids.main_stat_text, H_PAD)
|
.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)
|
.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) {
|
if p_r_diff != Protection::Normal(0.0) {
|
||||||
let text = format!(
|
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 {
|
if e_m_diff.abs() > Energy::ENERGY_EPSILON {
|
||||||
let text = format!("{} {:.1}", &energy_max_diff.0, e_m_diff);
|
let text = format!("{} {:.1}", &energy_max_diff.0, e_m_diff);
|
||||||
diff_text(
|
diff_text(
|
||||||
text,
|
text,
|
||||||
energy_max_diff.1,
|
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 {
|
if e_r_diff.abs() > Energy::ENERGY_EPSILON {
|
||||||
let text =
|
let text =
|
||||||
format!("{} {:.1}", &energy_reward_diff.0, e_r_diff * 100.0);
|
format!("{} {:.1}", &energy_reward_diff.0, e_r_diff * 100.0);
|
||||||
diff_text(
|
diff_text(
|
||||||
text,
|
text,
|
||||||
energy_reward_diff.1,
|
energy_reward_diff.1,
|
||||||
armor.stats.poise_resilience().is_some() as usize
|
armor.stats(self.msm).poise_resilience.is_some() as usize
|
||||||
+ armor.stats.energy_max().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 {
|
if c_p_diff != 0.0_f32 {
|
||||||
let text = format!("{} {:.3}", &crit_power_diff.0, c_p_diff);
|
let text = format!("{} {:.3}", &crit_power_diff.0, c_p_diff);
|
||||||
diff_text(
|
diff_text(
|
||||||
text,
|
text,
|
||||||
crit_power_diff.1,
|
crit_power_diff.1,
|
||||||
armor.stats.poise_resilience().is_some() as usize
|
armor.stats(self.msm).poise_resilience.is_some() as usize
|
||||||
+ armor.stats.energy_max().is_some() as usize
|
+ armor.stats(self.msm).energy_max.is_some() as usize
|
||||||
+ armor.stats.energy_reward().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 {
|
if s_diff != 0.0_f32 {
|
||||||
let text = format!("{} {:.3}", &stealth_diff.0, s_diff);
|
let text = format!("{} {:.3}", &stealth_diff.0, s_diff);
|
||||||
diff_text(
|
diff_text(
|
||||||
text,
|
text,
|
||||||
stealth_diff.1,
|
stealth_diff.1,
|
||||||
armor.stats.poise_resilience().is_some() as usize
|
armor.stats(self.msm).poise_resilience.is_some() as usize
|
||||||
+ armor.stats.energy_max().is_some() as usize
|
+ armor.stats(self.msm).energy_max.is_some() as usize
|
||||||
+ armor.stats.energy_reward().is_some() as usize
|
+ armor.stats(self.msm).energy_reward.is_some() as usize
|
||||||
+ armor.stats.crit_power().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)
|
.with_style(self.style.desc)
|
||||||
.color(conrod_core::color::GREY)
|
.color(conrod_core::color::GREY)
|
||||||
.down_from(
|
.down_from(
|
||||||
if util::stats_count(item) > 0 {
|
if stats_count > 0 {
|
||||||
state.ids.stats[state.ids.stats.len() - 1]
|
state.ids.stats[state.ids.stats.len() - 1]
|
||||||
} else {
|
} else {
|
||||||
state.ids.item_frame
|
state.ids.item_frame
|
||||||
@ -1352,7 +1371,7 @@ impl<'a> Widget for ItemTooltip<'a> {
|
|||||||
.down_from(
|
.down_from(
|
||||||
if !desc.is_empty() {
|
if !desc.is_empty() {
|
||||||
state.ids.desc
|
state.ids.desc
|
||||||
} else if util::stats_count(item) > 0 {
|
} else if stats_count > 0 {
|
||||||
state.ids.stats[state.ids.stats.len() - 1]
|
state.ids.stats[state.ids.stats.len() - 1]
|
||||||
} else {
|
} else {
|
||||||
state.ids.item_frame
|
state.ids.item_frame
|
||||||
@ -1412,13 +1431,14 @@ 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 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")
|
widget::Text::new("placeholder")
|
||||||
.with_style(self.style.desc)
|
.with_style(self.style.desc)
|
||||||
.get_h(ui)
|
.get_h(ui)
|
||||||
.unwrap_or(0.0)
|
.unwrap_or(0.0)
|
||||||
* util::stats_count(self.item) as f64
|
* stats_count as f64
|
||||||
+ (util::stats_count(self.item) - 1) as f64 * V_PAD_STATS
|
+ (stats_count - 1) as f64 * V_PAD_STATS
|
||||||
+ V_PAD
|
+ V_PAD
|
||||||
} else {
|
} else {
|
||||||
0.0
|
0.0
|
||||||
|
Loading…
Reference in New Issue
Block a user